scripts

random assortment of scripts
git clone git://git.andersuno.nu/scripts.git
Log | Files | Refs

pfetch (44273B)


      1 #!/bin/sh
      2 #
      3 # pfetch - Simple POSIX sh fetch script.
      4 
      5 log() {
      6     # The 'log()' function handles the printing of information.
      7     # In 'pfetch' (and 'neofetch'!) the printing of the ascii art and info
      8     # happen independently of each other.
      9     #
     10     # The size of the ascii art is stored and the ascii is printed first.
     11     # Once the ascii is printed, the cursor is located right below the art
     12     # (See marker $[1]).
     13     #
     14     # Using the stored ascii size, the cursor is then moved to marker $[2].
     15     # This is simply a cursor up escape sequence using the "height" of the
     16     # ascii art.
     17     #
     18     # 'log()' then moves the cursor to the right the "width" of the ascii art
     19     # with an additional amount of padding to add a gap between the art and
     20     # the information (See marker $[3]).
     21     #
     22     # When 'log()' has executed, the cursor is then located at marker $[4].
     23     # When 'log()' is run a second time, the next line of information is
     24     # printed, moving the cursor to marker $[5].
     25     #
     26     # Markers $[4] and $[5] repeat all the way down through the ascii art
     27     # until there is no more information left to print.
     28     #
     29     # Every time 'log()' is called the script keeps track of how many lines
     30     # were printed. When printing is complete the cursor is then manually
     31     # placed below the information and the art according to the "heights"
     32     # of both.
     33     #
     34     # The math is simple: move cursor down $((ascii_height - info_height)).
     35     # If the aim is to move the cursor from marker $[5] to marker $[6],
     36     # plus the ascii height is 8 while the info height is 2 it'd be a move
     37     # of 6 lines downwards.
     38     #
     39     # However, if the information printed is "taller" (takes up more lines)
     40     # than the ascii art, the cursor isn't moved at all!
     41     #
     42     # Once the cursor is at marker $[6], the script exits. This is the gist
     43     # of how this "dynamic" printing and layout works.
     44     #
     45     # This method allows ascii art to be stored without markers for info
     46     # and it allows for easy swapping of info order and amount.
     47     #
     48     # $[2] ___      $[3] goldie@KISS
     49     # $[4](.· |     $[5] os KISS Linux
     50     #     (<> |
     51     #    / __  \
     52     #   ( /  \ /|
     53     #  _/\ __)/_)
     54     #  \/-____\/
     55     # $[1]
     56     #
     57     # $[6] /home/goldie $
     58 
     59     # End here if no data was found.
     60     [ "$2" ] || return
     61 
     62     # Store the value of '$1' as we reset the argument list below.
     63     name=$1
     64 
     65     # Use 'set --' as a means of stripping all leading and trailing
     66     # white-space from the info string. This also normalizes all
     67     # white-space inside of the string.
     68     #
     69     # Disable the shellcheck warning for word-splitting
     70     # as it's safe and intended ('set -f' disables globbing).
     71     # shellcheck disable=2046,2086
     72     {
     73         set -f
     74         set +f -- $2
     75         info=$*
     76     }
     77 
     78     # Move the cursor to the right, the width of the ascii art with an
     79     # additional gap for text spacing.
     80     printf '[%sC' "${ascii_width--1}"
     81 
     82     # Print the info name and color the text.
     83     printf '[3%s;1m%s' "${PF_COL1-4}" "$name"
     84 
     85     # Print the info name and info data separator.
     86     printf '%s' "$PF_SEP"
     87 
     88     # Move the cursor backward the length of the *current* info name and
     89     # then move it forwards the length of the *longest* info name. This
     90     # aligns each info data line.
     91     printf '[%sD[%sC' "${#name}" "${PF_ALIGN-$info_length}"
     92 
     93     # Print the info data, color it and strip all leading whitespace
     94     # from the string.
     95     printf '[3%sm%s\n' "${PF_COL2-7}" "$info"
     96 
     97     # Keep track of the number of times 'log()' has been run.
     98     info_height=$((${info_height:-0} + 1))
     99 }
    100 
    101 get_title() {
    102     # Username is retrieved by first checking '$USER' with a fallback
    103     # to the 'id -un' command.
    104     user=${USER:-$(id -un)}
    105 
    106     # Hostname is retrieved by first checking '$HOSTNAME' with a fallback
    107     # to the 'hostname' command.
    108     #
    109     # Disable the warning about '$HOSTNAME' being undefined in POSIX sh as
    110     # the intention for using it is allowing the user to overwrite the
    111     # value on invocation.
    112     # shellcheck disable=SC2039
    113     hostname=${HOSTNAME:-${hostname:-$(hostname)}}
    114 
    115     log "[3${PF_COL3:-1}m${user}${c7}@[3${PF_COL3:-1}m${hostname}" " " >&6
    116 }
    117 
    118 get_os() {
    119     # This function is called twice, once to detect the distribution name
    120     # for the purposes of picking an ascii art early and secondly to display
    121     # the distribution name in the info output (if enabled).
    122     #
    123     # On first run, this function displays _nothing_, only on the second
    124     # invocation is 'log()' called.
    125     [ "$distro" ] && {
    126         log os "$distro" >&6
    127         return
    128     }
    129 
    130     case $os in
    131         Linux*)
    132             # Some Linux disttributions (which are based on others)
    133             # fail to identify as they **do not** change the upstream
    134             # distributions identification packages or files.
    135             #
    136             # It is senseless to add a special case in the code for
    137             # each and every distribution (which _is_ technically no
    138             # different from what it is based on) as they're either too
    139             # lazy to modify upstream's identification files or they
    140             # don't have the know-how (or means) to ship their own
    141             # lsb-release package.
    142             #
    143             # This causes users to think there's a bug in system detection
    144             # tools like neofetch or pfetch when they technically *do*
    145             # function correctly.
    146             #
    147             # Exceptions are made for distributions which are independent,
    148             # not based on another distribution or follow different
    149             # standards.
    150             #
    151             # This applies only to distributions which follow the standard
    152             # by shipping unmodified identification files and packages
    153             # from their respective upstreams.
    154             if command -v lsb_release; then
    155                 distro=$(lsb_release -sd)
    156 
    157             else
    158                 # This used to be a simple '. /etc/os-release' but I believe
    159                 # this is insecure as we blindly executed whatever was in the
    160                 # file. This parser instead simply handles 'key=val', treating
    161                 # the file contents as plain-text.
    162                 while IFS='=' read -r key val; do
    163                     case $key in
    164                         PRETTY_NAME) distro=$val ;;
    165                     esac
    166                 done < /etc/os-release
    167             fi
    168 
    169             # 'os-release' and 'lsb_release' sometimes add quotes
    170             # around the distribution name, strip them.
    171             distro=${distro##[\"\']}
    172             distro=${distro%%[\"\']}
    173 
    174             # Special cases for (independent) distributions which
    175             # don't follow any os-release/lsb standards whatsoever.
    176             command -v crux && distro=$(crux)
    177             command -v guix && distro='Guix System'
    178 
    179             # Check to see if Linux is running in Windows 10 under
    180             # WSL1 (Windows subsystem for Linux [version 1]) and
    181             # append a string accordingly.
    182             #
    183             # If the kernel version string ends in "-Microsoft",
    184             # we're very likely running under Windows 10 in WSL1.
    185             [ "${kernel%%*-Microsoft}" ] ||
    186                 distro="$distro on Windows 10 [WSL1]"
    187 
    188             # Check to see if Linux is running in Windows 10 under
    189             # WSL2 (Windows subsystem for Linux [version 2]) and
    190             # append a string accordingly.
    191             #
    192             # This checks to see if '$WSLENV' is defined. This
    193             # appends the Windows 10 string even if '$WSLENV' is
    194             # empty. We only need to check that is has been _exported_.
    195             distro="${distro}${WSLENV+ on Windows 10 [WSL2]}"
    196         ;;
    197 
    198         Darwin*)
    199             # Parse the SystemVersion.plist file to grab the macOS
    200             # version. The file is in the following format:
    201             #
    202             # <key>ProductVersion</key>
    203             # <string>10.14.6</string>
    204             #
    205             # 'IFS' is set to '<>' to enable splitting between the
    206             # keys and a second 'read' is used to operate on the
    207             # next line directly after a match.
    208             #
    209             # '_' is used to nullify a field. '_ _ line _' basically
    210             # says "populate $line with the third field's contents".
    211             while IFS='<>' read -r _ _ line _; do
    212                 case $line in
    213                     # Match 'ProductVersion' and read the next line
    214                     # directly as it contains the key's value.
    215                     ProductVersion)
    216                         IFS='<>' read -r _ _ mac_version _
    217                         break
    218                     ;;
    219                 esac
    220             done < /System/Library/CoreServices/SystemVersion.plist
    221 
    222             # Use the ProductVersion to determine which macOS/OS X codename
    223             # the system has. As far as I'm aware there's no "dynamic" way
    224             # of grabbing this information.
    225             case $mac_version in
    226                 10.4*)  distro='Mac OS X Tiger' ;;
    227                 10.5*)  distro='Mac OS X Leopard' ;;
    228                 10.6*)  distro='Mac OS X Snow Leopard' ;;
    229                 10.7*)  distro='Mac OS X Lion' ;;
    230                 10.8*)  distro='OS X Mountain Lion' ;;
    231                 10.9*)  distro='OS X Mavericks' ;;
    232                 10.10*) distro='OS X Yosemite' ;;
    233                 10.11*) distro='OS X El Capitan' ;;
    234                 10.12*) distro='macOS Sierra' ;;
    235                 10.13*) distro='macOS High Sierra' ;;
    236                 10.14*) distro='macOS Mojave' ;;
    237                 10.15*) distro='macOS Catalina' ;;
    238                 *)      distro='macOS' ;;
    239             esac
    240 
    241             distro="$distro $mac_version"
    242         ;;
    243 
    244         Haiku)
    245             # Haiku uses 'uname -v' for version information
    246             # instead of 'uname -r' which only prints '1'.
    247             distro=$(uname -sv)
    248         ;;
    249 
    250         Minix|DragonFly)
    251             distro="$os $kernel"
    252 
    253             # Minix and DragonFly don't support the escape
    254             # sequences used, clear the exit trap.
    255             trap '' EXIT
    256         ;;
    257 
    258         SunOS)
    259             # Grab the first line of the '/etc/release' file
    260             # discarding everything after '('.
    261             IFS='(' read -r distro _ < /etc/release
    262         ;;
    263 
    264         *)
    265             # Catch all to ensure '$distro' is never blank.
    266             # This also handles the BSDs.
    267             distro="$os $kernel"
    268         ;;
    269     esac
    270 }
    271 
    272 get_kernel() {
    273     case $os in
    274         # Don't print kernel output on some systems as the
    275         # OS name includes it.
    276         *BSD*|Haiku|Minix) ;;
    277 
    278         *)
    279             # '$kernel' is the cached output of 'uname -r'.
    280             log kernel "$kernel" >&6
    281         ;;
    282    esac
    283 }
    284 
    285 get_host() {
    286     case $os in
    287         Linux*)
    288             # Despite what these files are called, version doesn't
    289             # always contain the version nor does name always contain
    290             # the name.
    291             read -r name    < /sys/devices/virtual/dmi/id/product_name
    292             read -r version < /sys/devices/virtual/dmi/id/product_version
    293             read -r model   < /sys/firmware/devicetree/base/model
    294 
    295             host="$name $version $model"
    296         ;;
    297 
    298         Darwin*|FreeBSD*|DragonFly*)
    299             host=$(sysctl -n hw.model)
    300         ;;
    301 
    302         NetBSD*)
    303             host=$(sysctl -n machdep.dmi.system-vendor \
    304                              machdep.dmi.system-product)
    305         ;;
    306 
    307         *BSD*)
    308             host=$(sysctl -n hw.vendor hw.product)
    309         ;;
    310     esac
    311 
    312     # Turn the host string into an argument list so we can iterate
    313     # over it and remove OEM strings and other information which
    314     # shouldn't be displayed.
    315     #
    316     # Disable the shellcheck warning for word-splitting
    317     # as it's safe and intended ('set -f' disables globbing).
    318     # shellcheck disable=2046,2086
    319     {
    320         set -f
    321         set +f -- $host
    322         host=
    323     }
    324 
    325     # Iterate over the host string word by word as a means of stripping
    326     # unwanted and OEM information from the string as a whole.
    327     #
    328     # This could have been implemented using a long 'sed' command with
    329     # a list of word replacements, however I want to show that something
    330     # like this is possible in pure sh.
    331     #
    332     # This string reconstruction is needed as some OEMs either leave the
    333     # identification information as "To be filled by OEM", "Default",
    334     # "undefined" etc and we shouldn't print this to the screen.
    335     for word; do
    336         # This works by reconstructing the string by excluding words
    337         # found in the "blacklist" below. Only non-matches are appended
    338         # to the final host string.
    339         case $word in
    340             To      | [Bb]e      | [Ff]illed | by     | O.E.M.  | OEM  |\
    341             Not     | Applicable | Specified | System | Product | Name |\
    342             Version | Undefined  | Default   | string | INVALID | �    )
    343                 continue
    344             ;;
    345         esac
    346 
    347         host="$host$word "
    348     done
    349 
    350     # '$arch' is the cached output from 'uname -m'.
    351     log host "${host:-$arch}" >&6
    352 }
    353 
    354 get_uptime() {
    355     # Uptime works by retrieving the data in total seconds and then
    356     # converting that data into days, hours and minutes using simple
    357     # math.
    358     case $os in
    359         Linux*|Minix*)
    360             IFS=. read -r s _ < /proc/uptime
    361         ;;
    362 
    363         Darwin*|*BSD*|DragonFly*)
    364             s=$(sysctl -n kern.boottime)
    365 
    366             # Extract the uptime in seconds from the following output:
    367             # [...] { sec = 1271934886, usec = 667779 } Thu Apr 22 12:14:46 2010
    368             s=${s#*=}
    369             s=${s%,*}
    370 
    371             # The uptime format from 'sysctl' needs to be subtracted from
    372             # the current time in seconds.
    373             s=$(($(date +%s) - s))
    374         ;;
    375 
    376         Haiku)
    377             # The boot time is returned in microseconds, convert it to
    378             # regular seconds.
    379             s=$(($(system_time) / 1000000))
    380         ;;
    381 
    382         SunOS)
    383             # Split the output of 'kstat' on '.' and any white-space
    384             # which exists in the command output.
    385             #
    386             # The output is as follows:
    387             # unix:0:system_misc:snaptime	14809.906993005
    388             #
    389             # The parser extracts:          ^^^^^
    390             IFS='	.' read -r _ s _ <<-EOF
    391 				$(kstat -p unix:0:system_misc:snaptime)
    392 			EOF
    393         ;;
    394     esac
    395 
    396     # Convert the uptime from seconds into days, hours and minutes.
    397     d=$((s / 60 / 60 / 24))
    398     h=$((s / 60 / 60 % 24))
    399     m=$((s / 60 % 60))
    400 
    401     # Only append days, hours and minutes if they're non-zero.
    402     [ "$d" = 0 ] || uptime="${uptime}${d}d "
    403     [ "$h" = 0 ] || uptime="${uptime}${h}h "
    404     [ "$m" = 0 ] || uptime="${uptime}${m}m "
    405 
    406     log uptime "${uptime:-0m}" >&6
    407 }
    408 
    409 get_pkgs() {
    410     # This is just a simple wrapper around 'command -v' to avoid
    411     # spamming '>/dev/null' throughout this function.
    412     has() { command -v "$1" >/dev/null; }
    413 
    414     # This works by first checking for which package managers are
    415     # installed and finally by printing each package manager's
    416     # package list with each package one per line.
    417     #
    418     # The output from this is then piped to 'wc -l' to count each
    419     # line, giving us the total package count of whatever package
    420     # managers are installed.
    421     #
    422     # Backticks are *required* here as '/bin/sh' on macOS is
    423     # 'bash 3.2' and it can't handle the following:
    424     #
    425     # var=$(
    426     #    code here
    427     # )
    428     #
    429     # shellcheck disable=2006
    430     packages=`
    431         case $os in
    432             Linux*)
    433                 # Commands which print packages one per line.
    434                 has bonsai     && bonsai list
    435                 has pacman-key && pacman -Qq
    436                 has dpkg       && dpkg-query -f '.\n' -W
    437                 has rpm        && rpm -qa
    438                 has xbps-query && xbps-query -l
    439                 has apk        && apk info
    440                 has guix       && guix package --list-installed
    441 
    442                 # Directories containing packages.
    443                 has kiss       && printf '%s\n' /var/db/kiss/installed/*/
    444                 has brew       && printf '%s\n' "$(brew --cellar)/"*
    445                 has emerge     && printf '%s\n' /var/db/pkg/*/*/
    446                 has pkgtool    && printf '%s\n' /var/log/packages/*
    447 
    448                 # 'nix' requires two commands.
    449                 has nix-store  && {
    450                     nix-store -q --requisites /run/current-system/sw
    451                     nix-store -q --requisites ~.nix-profile
    452                 }
    453             ;;
    454 
    455             Darwin*)
    456                 # Commands which print packages one per line.
    457                 has pkgin      && pkgin list
    458 
    459                 # Directories containing packages.
    460                 has brew       && printf '%s\n' /usr/local/Cellar/*
    461 
    462                 # 'port' prints a single line of output to 'stdout'
    463                 # when no packages are installed and exits with
    464                 # success causing a false-positive of 1 package
    465                 # installed.
    466                 #
    467                 # 'port' should really exit with a non-zero code
    468                 # in this case to allow scripts to cleanly handle
    469                 # this behavior.
    470                 has port       && {
    471                     pkg_list=$(port installed)
    472 
    473                     [ "$pkg_list" = "No ports are installed." ] ||
    474                         printf '%s\n' "$pkg_list"
    475                 }
    476             ;;
    477 
    478             FreeBSD*|DragonFly*)
    479                 pkg info
    480             ;;
    481 
    482             OpenBSD*)
    483                 printf '%s\n' /var/db/pkg/*/
    484             ;;
    485 
    486             NetBSD*)
    487                 pkg_info
    488             ;;
    489 
    490             Haiku)
    491                 printf '%s\n' /boot/system/package-links/*
    492             ;;
    493 
    494             Minix)
    495                 printf '%s\n' /usr/pkg/var/db/pkg/*/
    496             ;;
    497 
    498             SunOS)
    499                 has pkginfo && pkginfo -i
    500                 has pkg     && pkg list
    501             ;;
    502         esac | wc -l
    503     `
    504 
    505     log pkgs "${packages:-?}" >&6
    506 }
    507 
    508 get_memory() {
    509     case $os in
    510         # Used memory is calculated using the following "formula":
    511         # MemUsed = MemTotal + Shmem - MemFree - Buffers - Cached - SReclaimable
    512         # Source: https://github.com/KittyKatt/screenFetch/issues/386
    513         Linux*)
    514             # Parse the '/proc/meminfo' file splitting on ':' and 'k'.
    515             # The format of the file is 'key:   000kB' and an additional
    516             # split is used on 'k' to filter out 'kB'.
    517             while IFS=':k '  read -r key val _; do
    518                 case $key in
    519                     MemTotal)
    520                         mem_used=$((mem_used + val))
    521                         mem_full=$val
    522                     ;;
    523 
    524                     Shmem)
    525                         mem_used=$((mem_used + val))
    526                     ;;
    527 
    528                     MemFree|Buffers|Cached|SReclaimable)
    529                         mem_used=$((mem_used - val))
    530                     ;;
    531                 esac
    532             done < /proc/meminfo
    533 
    534             mem_used=$((mem_used / 1024))
    535             mem_full=$((mem_full / 1024))
    536         ;;
    537 
    538         # Used memory is calculated using the following "formula":
    539         # (wired + active + occupied) * 4 / 1024
    540         Darwin*)
    541             mem_full=$(($(sysctl -n hw.memsize) / 1024 / 1024))
    542 
    543             # Parse the 'vmstat' file splitting on ':' and '.'.
    544             # The format of the file is 'key:   000.' and an additional
    545             # split is used on '.' to filter it out.
    546             while IFS=:. read -r key val; do
    547                 case $key in
    548                     *wired*|*active*|*occupied*)
    549                         mem_used=$((mem_used + ${val:-0}))
    550                     ;;
    551                 esac
    552 
    553             # Using '<<-EOF' is the only way to loop over a command's
    554             # output without the use of a pipe ('|').
    555             # This ensures that any variables defined in the while loop
    556             # are still accessible in the script.
    557             done <<-EOF
    558                 $(vm_stat)
    559 			EOF
    560 
    561             mem_used=$((mem_used * 4 / 1024))
    562         ;;
    563 
    564         OpenBSD*)
    565             mem_full=$(($(sysctl -n hw.physmem) / 1024 / 1024))
    566 
    567             # This is a really simpler parser for 'vmstat' which grabs
    568             # the used memory amount in a lazy way. 'vmstat' prints 3
    569             # lines of output with the needed value being stored in the
    570             # final line.
    571             #
    572             # This loop simply grabs the 3rd element of each line until
    573             # the EOF is reached. Each line overwrites the value of the
    574             # previous one so we're left with what we wanted. This isn't
    575             # slow as only 3 lines are parsed.
    576             while read -r _ _ line _; do
    577                 mem_used=${line%%M}
    578 
    579             # Using '<<-EOF' is the only way to loop over a command's
    580             # output without the use of a pipe ('|').
    581             # This ensures that any variables defined in the while loop
    582             # are still accessible in the script.
    583             done <<-EOF
    584                 $(vmstat)
    585 			EOF
    586         ;;
    587 
    588         # Used memory is calculated using the following "formula":
    589         # mem_full - ((inactive + free + cache) * page_size / 1024)
    590         FreeBSD*|DragonFly*)
    591             mem_full=$(($(sysctl -n hw.physmem) / 1024 / 1024))
    592 
    593             # Use 'set --' to store the output of the command in the
    594             # argument list. POSIX sh has no arrays but this is close enough.
    595             #
    596             # Disable the shellcheck warning for word-splitting
    597             # as it's safe and intended ('set -f' disables globbing).
    598             # shellcheck disable=2046
    599             {
    600                 set -f
    601                 set +f -- $(sysctl -n hw.pagesize \
    602                                       vm.stats.vm.v_inactive_count \
    603                                       vm.stats.vm.v_free_count \
    604                                       vm.stats.vm.v_cache_count)
    605             }
    606 
    607             # Calculate the amount of used memory.
    608             # $1: hw.pagesize
    609             # $2: vm.stats.vm.v_inactive_count
    610             # $3: vm.stats.vm.v_free_count
    611             # $4: vm.stats.vm.v_cache_count
    612             mem_used=$((mem_full - (($2 + $3 + $4) * $1 / 1024 / 1024)))
    613         ;;
    614 
    615         NetBSD*)
    616             mem_full=$(($(sysctl -n hw.physmem64) / 1024 / 1024))
    617 
    618             # NetBSD implements a lot of the Linux '/proc' filesystem,
    619             # this uses the same parser as the Linux memory detection.
    620             while IFS=':k ' read -r key val _; do
    621                 case $key in
    622                     MemFree)
    623                         mem_free=$((val / 1024))
    624                         break
    625                     ;;
    626                 esac
    627             done < /proc/meminfo
    628 
    629             mem_used=$((mem_full - mem_free))
    630         ;;
    631 
    632         Haiku)
    633             # Read the first line of 'sysinfo -mem' splitting on
    634             # '(', ' ', and ')'. The needed information is then
    635             # stored in the 5th and 7th elements. Using '_' "consumes"
    636             # an element allowing us to proceed to the next one.
    637             #
    638             # The parsed format is as follows:
    639             # 3501142016 bytes free      (used/max  792645632 / 4293787648)
    640             IFS='( )' read -r _ _ _ _ mem_used _ mem_full <<-EOF
    641                 $(sysinfo -mem)
    642 			EOF
    643 
    644             mem_used=$((mem_used / 1024 / 1024))
    645             mem_full=$((mem_full / 1024 / 1024))
    646         ;;
    647 
    648         Minix)
    649             # Minix includes the '/proc' filesystem though the format
    650             # differs from Linux. The '/proc/meminfo' file is only a
    651             # single line with space separated elements and elements
    652             # 2 and 3 contain the total and free memory numbers.
    653             read -r _ mem_full mem_free _ < /proc/meminfo
    654 
    655             mem_used=$(((mem_full - mem_free) / 1024))
    656             mem_full=$(( mem_full / 1024))
    657         ;;
    658 
    659         SunOS)
    660             hw_pagesize=$(pagesize)
    661 
    662             # 'kstat' outputs memory in the following format:
    663             # unix:0:system_pages:pagestotal	1046397
    664             # unix:0:system_pages:pagesfree		885018
    665             #
    666             # This simply uses the first "element" (white-space
    667             # separated) as the key and the second element as the
    668             # value.
    669             #
    670             # A variable is then assigned based on the key.
    671             while read -r key val; do
    672                 case $key in
    673                     *total) pages_full=$val ;;
    674                     *free)  pages_free=$val ;;
    675                 esac
    676             done <<-EOF
    677 				$(kstat -p unix:0:system_pages:pagestotal \
    678                            unix:0:system_pages:pagesfree)
    679 			EOF
    680 
    681             mem_full=$((pages_full * hw_pagesize / 1024 / 1024))
    682             mem_free=$((pages_free * hw_pagesize / 1024 / 1024))
    683             mem_used=$((mem_full - mem_free))
    684         ;;
    685     esac
    686 
    687     log memory "${mem_used:-?}M / ${mem_full:-?}M" >&6
    688 }
    689 
    690 get_wm() {
    691     case $os in
    692         # Don't display window manager on macOS.
    693         Darwin*) ;;
    694 
    695         *)
    696             # xprop can be used to grab the window manager's properties
    697             # which contains the window manager's name under '_NET_WM_NAME'.
    698             #
    699             # The upside to using 'xprop' is that you don't need to hardcode
    700             # a list of known window manager names. The downside is that
    701             # not all window managers conform to setting the '_NET_WM_NAME'
    702             # atom..
    703             #
    704             # List of window managers which fail to set the name atom:
    705             # catwm, fvwm, dwm, 2bwm and monster.
    706             #
    707             # The final downside to this approach is that it does _not_
    708             # support Wayland environments. The only solution which supports
    709             # Wayland is the 'ps' parsing mentioned below.
    710             #
    711             # A more naive implementation is to parse the last line of
    712             # '~/.xinitrc' to extract the second white-space separated
    713             # element.
    714             #
    715             # The issue with an approach like this is that this line data
    716             # does not always equate to the name of the window manager and
    717             # could in theory be _anything_.
    718             #
    719             # This also fails when the user launches xorg through a display
    720             # manager or other means.
    721             #
    722             #
    723             # Another naive solution is to parse 'ps' with a hardcoded list
    724             # of window managers to detect the current window manager (based
    725             # on what is running).
    726             #
    727             # The issue with this approach is the need to hardcode and
    728             # maintain a list of known window managers.
    729             #
    730             # Another issue is that process names do not always equate to
    731             # the name of the window manager. False-positives can happen too.
    732             #
    733             # This is the only solution which supports Wayland based
    734             # environments sadly. It'd be nice if some kind of standard were
    735             # established to identify Wayland environments.
    736             #
    737             # pfetch's goal is to remain _simple_, if you'd like a "full"
    738             # implementation of window manager detection use 'neofetch'.
    739             #
    740             # Neofetch use a combination of 'xprop' and 'ps' parsing to
    741             # support all window managers (including non-conforming and
    742             # Wayland) though it's a lot more complicated!
    743 
    744             # Don't display window manager if X isn't running.
    745             [ "$DISPLAY" ] || return
    746 
    747             # This is a two pass call to xprop. One call to get the window
    748             # manager's ID and another to print its properties.
    749             command -v xprop && {
    750                 # The output of the ID command is as follows:
    751                 # _NET_SUPPORTING_WM_CHECK: window id # 0x400000
    752                 #
    753                 # To extract the ID, everything before the last space
    754                 # is removed.
    755                 id=$(xprop -root -notype _NET_SUPPORTING_WM_CHECK)
    756                 id=${id##* }
    757 
    758                 # The output of the property command is as follows:
    759                 # _NAME 8t
    760                 # _NET_WM_PID = 252
    761                 # _NET_WM_NAME = "bspwm"
    762                 # _NET_SUPPORTING_WM_CHECK: window id # 0x400000
    763                 # WM_CLASS = "wm", "Bspwm"
    764                 #
    765                 # To extract the name, everything before '_NET_WM_NAME = \"'
    766                 # is removed and everything after the next '"' is removed.
    767                 wm=$(xprop -id "$id" -notype -len 25 -f _NET_WM_NAME 8t)
    768                 wm=${wm##*_NET_WM_NAME = \"}
    769                 wm=${wm%%\"*}
    770             }
    771         ;;
    772     esac
    773 
    774     log wm "$wm" >&6
    775 }
    776 
    777 
    778 get_de() {
    779     # This only supports Xorg related desktop environments though
    780     # this is fine as knowing the desktop envrionment on Windows,
    781     # macOS etc is useless (they'll always report the same value).
    782     #
    783     # Display the value of '$XDG_CURRENT_DESKTOP', if it's empty,
    784     # display the value of '$DESKTOP_SESSION'.
    785     log de "${XDG_CURRENT_DESKTOP:-$DESKTOP_SESSION}" >&6
    786 }
    787 
    788 get_shell() {
    789     # Display the basename of the '$SHELL' environment variable.
    790     log shell "${SHELL##*/}" >&6
    791 }
    792 
    793 get_editor() {
    794     # Display the value of '$VISUAL', if it's empty, display the
    795     # value of '$EDITOR'.
    796     log editor "${VISUAL:-$EDITOR}" >&6
    797 }
    798 
    799 get_palette() {
    800     # Print the first 8 terminal colors. This uses the existing
    801     # sequences to change text color with a sequence prepended
    802     # to reverse the foreground and background colors.
    803     #
    804     # This allows us to save hardcoding a second set of sequences
    805     # for background colors.
    806     palette="  $c1  $c2  $c3  $c4  $c5  $c6  $c7  "
    807 
    808     # Print the palette with a newline before and after.
    809     # The '\033[%sC' moves the text to the right, the
    810     # length of the ascii art.
    811     printf '\n[%sC%s\n' "${ascii_width-1}" "$palette" >&6
    812 }
    813 
    814 get_ascii() {
    815     # This is a simple function to read the contents of
    816     # an ascii file from 'stdin'. It allows for the use
    817     # of '<<-EOF' to prevent the break in indentation in
    818     # this source code.
    819     #
    820     # This function also sets the text colors according
    821     # to the ascii color.
    822     read_ascii() {
    823         # 'PF_COL1': Set the info name color according to ascii color.
    824         # 'PF_COL3': Set the title color to some other color. ¯\_(ツ)_/¯
    825         PF_COL1=${PF_COL1:-${1:-7}}
    826         PF_COL3=${PF_COL3:-$((${1:-7}%8+1))}
    827 
    828         # POSIX sh has no 'var+=' so 'var=${var}append' is used. What's
    829         # interesting is that 'var+=' _is_ supported inside '$(())'
    830         # (arithmetic) though there's no support for 'var++/var--'.
    831         #
    832         # There is also no $'\n' to add a "literal"(?) newline to the
    833         # string. The simplest workaround being to break the line inside
    834         # the string (though this has the caveat of breaking indentation).
    835         while IFS= read -r line; do
    836             ascii="$ascii$line
    837 "
    838         done
    839     }
    840 
    841     # This checks for ascii art in the following order:
    842     # '$1':        Argument given to 'get_ascii()' directly.
    843     # '$PF_ASCII': Environment variable set by user.
    844     # '$distro':   The detected distribution name.
    845     # '$os':       The name of the operating system/kernel.
    846     #
    847     # NOTE: Each ascii art below is indented using tabs, this
    848     #       allows indentation to continue naturally despite
    849     #       the use of '<<-EOF'.
    850     case ${1:-${PF_ASCII:-${distro:-$os}}} in
    851         [Aa]lpine*)
    852             read_ascii 4 <<-EOF
    853 				${c4}   /\\ /\\
    854 				  /${c7}/ ${c4}\\  \\
    855 				 /${c7}/   ${c4}\\  \\
    856 				/${c7}//    ${c4}\\  \\
    857 				${c7}//      ${c4}\\  \\
    858 				         \\
    859 			EOF
    860         ;;
    861 
    862         [Aa]ndroid*)
    863             read_ascii 2 <<-EOF
    864 				${c2}  ;,           ,;
    865 				   ';,.-----.,;'
    866 				  ,'           ',
    867 				 /    O     O    \\
    868 				|                 |
    869 				'-----------------'
    870 			EOF
    871         ;;
    872 
    873         [Aa]rch*)
    874             read_ascii 4 <<-EOF
    875 				${c6}      /\\
    876 				     /^^\\
    877 				    /\\   \\
    878 				   /${c7}  __  \\
    879 				  /  (  )  \\
    880 				 / __|  |__\\\\
    881 				///        \\\\\\
    882 			EOF
    883         ;;
    884 
    885         [Aa]rco*)
    886             read_ascii 4 <<-EOF
    887 				${c4}      /\\
    888 				     /  \\
    889 				    / /\\ \\
    890 				   / /  \\ \\
    891 				  / /    \\ \\
    892 				 / / _____\\ \\
    893 				/_/  \`----.\\_\\
    894 			EOF
    895         ;;
    896 
    897         [Aa]rtix*)
    898             read_ascii 6 <<-EOF
    899 				${c4}      /\\
    900 				     /  \\
    901 				    /\`'.,\\
    902 				   /     ',
    903 				  /      ,\`\\
    904 				 /   ,.'\`.  \\
    905 				/.,'\`     \`'.\\
    906 			EOF
    907         ;;
    908 
    909         [Cc]ent[Oo][Ss]*)
    910             read_ascii 5 <<-EOF
    911 				${c2} ____${c3}^${c5}____
    912 				${c2} |\\  ${c3}|${c5}  /|
    913 				${c2} | \\ ${c3}|${c5} / |
    914 				${c5}<---- ${c4}---->
    915 				${c4} | / ${c2}|${c3} \\ |
    916 				${c4} |/__${c2}|${c3}__\\|
    917 				${c2}     v
    918 			EOF
    919         ;;
    920 
    921         [Dd]ebian*)
    922             read_ascii 1 <<-EOF
    923 				${c1}  _____
    924 				 /  __ \\
    925 				|  /    |
    926 				|  \\___-
    927 				-_
    928 				  --_
    929 			EOF
    930         ;;
    931 
    932         [Dd]ragon[Ff]ly*)
    933             read_ascii 1 <<-EOF
    934 				    ,${c1}_${c7},
    935 				 ('-_${c1}|${c7}_-')
    936 				  >--${c1}|${c7}--<
    937 				 (_-'${c1}|${c7}'-_)
    938 				     ${c1}|
    939 				     |
    940 				     |
    941 			EOF
    942         ;;
    943 
    944         [Ee]lementary*)
    945             read_ascii <<-EOF
    946 				${c7}  _______
    947 				 / ____  \\
    948 				/  |  /  /\\
    949 				|__\\ /  / |
    950 				\\   /__/  /
    951 				 \\_______/
    952 			EOF
    953         ;;
    954 
    955         [Ff]edora*)
    956             read_ascii 4 <<-EOF
    957 				${c7}      _____
    958 				     /   __)${c4}\\${c7}
    959 				     |  /  ${c4}\\ \\${c7}
    960 				  ${c4}__${c7}_|  |_${c4}_/ /${c7}
    961 				 ${c4}/ ${c7}(_    _)${c4}_/${c7}
    962 				${c4}/ /${c7}  |  |
    963 				${c4}\\ \\${c7}__/  |
    964 				 ${c4}\\${c7}(_____/
    965 			EOF
    966         ;;
    967 
    968         [Ff]ree[Bb][Ss][Dd]*)
    969             read_ascii 1 <<-EOF
    970 				${c1} /\\ _____ /\\
    971 				 \\_)     (_/
    972 				 /         \\
    973 				|           |
    974 				|           |
    975 				 \         /
    976 				  --_____--
    977 			EOF
    978         ;;
    979 
    980         [Gg]entoo*)
    981             read_ascii 5 <<-EOF
    982 				${c5} _-----_
    983 				(       \\
    984 				\\    0   \\
    985 				${c7} \\        )
    986 				 /      _/
    987 				(     _-
    988 				\\____-
    989 			EOF
    990         ;;
    991 
    992         [Gg]uix[Ss][Dd]*|[Gg]uix*)
    993             read_ascii 3 <<-EOF
    994 				${c3}|.__          __.|
    995 				|__ \\        / __|
    996 				   \\ \\      / /
    997 				    \\ \\    / /
    998 				     \\ \\  / /
    999 				      \\ \\/ /
   1000 				       \\__/
   1001 			EOF
   1002         ;;
   1003 
   1004         [Hh]aiku*)
   1005             read_ascii 3 <<-EOF
   1006 				${c3}       ,^,
   1007 				      /   \\
   1008 				*--_ ;     ; _--*
   1009 				\\   '"     "'   /
   1010 				 '.           .'
   1011 				.-'"         "'-.
   1012 				 '-.__.   .__.-'
   1013 				       |_|
   1014 			EOF
   1015         ;;
   1016 
   1017         [Hh]yperbola*)
   1018             read_ascii <<-EOF
   1019 				${c7}    |\`__.\`/
   1020 				    \____/
   1021 				    .--.
   1022 				   /    \\
   1023 				  /  ___ \\
   1024 				 / .\`   \`.\\
   1025 				/.\`      \`.\\
   1026 			EOF
   1027         ;;
   1028 
   1029         [Ll]inux*[Ll]ite*|[Ll]ite*)
   1030             read_ascii 3 <<-EOF
   1031 				${c3}   /\\
   1032 				  /  \\
   1033 				 / ${c7}/ ${c3}/
   1034 				> ${c7}/ ${c3}/
   1035 				\\ ${c7}\\ ${c3}\\
   1036 				 \\_${c7}\\${c3}_\\
   1037 				${c7}    \\
   1038 			EOF
   1039         ;;
   1040 
   1041         [Ll]inux*[Mm]int*|[Mm]int)
   1042             read_ascii 2 <<-EOF
   1043 				${c2} ___________
   1044 				|_          \\
   1045 				  | ${c7}| _____ ${c2}|
   1046 				  | ${c7}| | | | ${c2}|
   1047 				  | ${c7}| | | | ${c2}|
   1048 				  | ${c7}\\__${c7}___/ ${c2}|
   1049 				  \\_________/
   1050 			EOF
   1051         ;;
   1052 
   1053 
   1054         [Ll]inux*)
   1055             read_ascii 4 <<-EOF
   1056 				${c4}    ___
   1057 				   (${c7}.· ${c4}|
   1058 				   (${c5}<> ${c4}|
   1059 				  / ${c7}__  ${c4}\\
   1060 				 ( ${c7}/  \\ ${c4}/|
   1061 				${c5}_${c4}/\\ ${c7}__)${c4}/${c5}_${c4})
   1062 				${c5}\/${c4}-____${c5}\/
   1063 			EOF
   1064         ;;
   1065 
   1066         [Mm]ac[Oo][Ss]*|[Dd]arwin*)
   1067             read_ascii 1 <<-EOF
   1068 				${c1}       .:'
   1069 				    _ :'_
   1070 				${c2} .'\`_\`-'_\`\`.
   1071 				:________.-'
   1072 				${c3}:_______:
   1073 				${c4} :_______\`-;
   1074 				${c5}  \`._.-._.'
   1075 			EOF
   1076         ;;
   1077 
   1078         [Mm]ageia*)
   1079             read_ascii 2 <<-EOF
   1080 				${c6}   *
   1081 				    *
   1082 				   **
   1083 				${c7} /\\__/\\
   1084 				/      \\
   1085 				\\      /
   1086 				 \\____/
   1087 			EOF
   1088         ;;
   1089 
   1090         [Mm]anjaro*)
   1091             read_ascii 2 <<-EOF
   1092 				${c2}||||||||| ||||
   1093 				||||||||| ||||
   1094 				||||      ||||
   1095 				|||| |||| ||||
   1096 				|||| |||| ||||
   1097 				|||| |||| ||||
   1098 				|||| |||| ||||
   1099 			EOF
   1100         ;;
   1101 
   1102         [Mm]inix*)
   1103             read_ascii 4 <<-EOF
   1104 				${c4} ,,        ,,
   1105 				;${c7},${c4} ',    ,' ${c7},${c4};
   1106 				; ${c7}',${c4} ',,' ${c7},'${c4} ;
   1107 				;   ${c7}',${c4}  ${c7},'${c4}   ;
   1108 				;  ${c7};, '' ,;${c4}  ;
   1109 				;  ${c7};${c4};${c7}',,'${c4};${c7};${c4}  ;
   1110 				', ${c7};${c4};;  ;;${c7};${c4} ,'
   1111 				  '${c7};${c4}'    '${c7};${c4}'
   1112 			EOF
   1113         ;;
   1114 
   1115         [Mm][Xx]*)
   1116             read_ascii <<-EOF
   1117 				${c7}    \\\\  /
   1118 				     \\\\/
   1119 				      \\\\
   1120 				   /\\/ \\\\
   1121 				  /  \\  /\\
   1122 				 /    \\/  \\
   1123 				/__________\\
   1124 			EOF
   1125         ;;
   1126 
   1127         [Nn]et[Bb][Ss][Dd]*)
   1128             read_ascii 3 <<-EOF
   1129 				${c7}\\\\${c3}\`-______,----__
   1130 				${c7} \\\\        ${c3}__,---\`_
   1131 				${c7}  \\\\       ${c3}\`.____
   1132 				${c7}   \\\\${c3}-______,----\`-
   1133 				${c7}    \\\\
   1134 				     \\\\
   1135 				      \\\\
   1136 			EOF
   1137         ;;
   1138 
   1139         [Nn]ix[Oo][Ss]*)
   1140             read_ascii 4 <<-EOF
   1141 				${c4}  \\\\  \\\\ //
   1142 				 ==\\\\__\\\\/ //
   1143 				   //   \\\\//
   1144 				==//     //==
   1145 				 //\\\\___//
   1146 				// /\\\\  \\\\==
   1147 				  // \\\\  \\\\
   1148 			EOF
   1149         ;;
   1150 
   1151         [Oo]pen[Bb][Ss][Dd]*)
   1152             read_ascii 3 <<-EOF
   1153 				${c3}      _____
   1154 				    \\-     -/
   1155 				 \\_/         \\
   1156 				 |        ${c7}O O${c3} |
   1157 				 |_  <   )  3 )
   1158 				 / \\         /
   1159 				    /-_____-\\
   1160 			EOF
   1161         ;;
   1162 
   1163         [Oo]penSUSE*|[Oo]pen*SUSE*|SUSE*|suse*)
   1164             read_ascii 2 <<-EOF
   1165 				${c2}  _______
   1166 				__|   __ \\
   1167 				     / .\\ \\
   1168 				     \\__/ |
   1169 				   _______|
   1170 				   \\_______
   1171 				__________/
   1172 			EOF
   1173         ;;
   1174 
   1175         [Pp]arabola*)
   1176             read_ascii 5 <<-EOF
   1177 				${c5}  __ __ __  _
   1178 				.\`_//_//_/ / \`.
   1179 				          /  .\`
   1180 				         / .\`
   1181 				        /.\`
   1182 				       /\`
   1183 			EOF
   1184         ;;
   1185 
   1186         [Pp]op!_[Oo][Ss]*)
   1187             read_ascii 6 <<-EOF
   1188 				${c6}______
   1189 				\\   _ \\        __
   1190 				 \\ \\ \\ \\      / /
   1191 				  \\ \\_\\ \\    / /
   1192 				   \\  ___\\  /_/
   1193 				    \\ \\    _
   1194 				   __\\_\\__(_)_
   1195 				  (___________)
   1196 			EOF
   1197         ;;
   1198 
   1199         [Pp]ure[Oo][Ss]*)
   1200             read_ascii <<-EOF
   1201 				${c7} _____________
   1202 				|  _________  |
   1203 				| |         | |
   1204 				| |         | |
   1205 				| |_________| |
   1206 				|_____________|
   1207 			EOF
   1208         ;;
   1209 
   1210         [Ss]lackware*)
   1211             read_ascii 4 <<-EOF
   1212 				${c4}   ________
   1213 				  /  ______|
   1214 				  | |______
   1215 				  \\______  \\
   1216 				   ______| |
   1217 				| |________/
   1218 				|____________
   1219 			EOF
   1220         ;;
   1221 
   1222         [Ss]un[Oo][Ss]|[Ss]olaris*)
   1223             read_ascii 3 <<-EOF
   1224 				${c3}       .   .;   .
   1225 				   .   :;  ::  ;:   .
   1226 				   .;. ..      .. .;.
   1227 				..  ..             ..  ..
   1228 				 .;,                 ,;.
   1229 			EOF
   1230         ;;
   1231 
   1232         [Uu]buntu*)
   1233             read_ascii 3 <<-EOF
   1234 				${c3}         _
   1235 				     ---(_)
   1236 				 _/  ---  \\
   1237 				(_) |   |
   1238 				  \\  --- _/
   1239 				     ---(_)
   1240 			EOF
   1241         ;;
   1242 
   1243         [Vv]oid*)
   1244             read_ascii 2 <<-EOF
   1245 				${c2}    _______
   1246 				 _ \\______ -
   1247 				| \\  ___  \\ |
   1248 				| | /   \ | |
   1249 				| | \___/ | |
   1250 				| \\______ \\_|
   1251 				 -_______\\
   1252 			EOF
   1253         ;;
   1254 
   1255         *)
   1256             # On no match of a distribution ascii art, this function calls
   1257             # itself again, this time to look for a more generic OS related
   1258             # ascii art (KISS Linux -> Linux).
   1259             [ "$1" ] || {
   1260                 get_ascii "$os"
   1261                 return
   1262             }
   1263 
   1264             printf 'error: %s is not currently supported.\n' "$os" >&6
   1265             printf 'error: Open an issue for support to be added.\n' >&6
   1266             exit 1
   1267         ;;
   1268     esac
   1269 
   1270     # Store the "width" (longest line) and "height" (number of lines)
   1271     # of the ascii art for positioning. This script prints to the screen
   1272     # *almost* like a TUI does. It uses escape sequences to allow dynamic
   1273     # printing of the information through user configuration.
   1274     #
   1275     # Iterate over each line of the ascii art to retrieve the above
   1276     # information. The 'sed' is used to strip 'm' color codes from
   1277     # the ascii art so they don't affect the width variable.
   1278     while read -r line; do
   1279         ascii_height=$((${ascii_height:-0} + 1))
   1280 
   1281         # This was a ternary operation but they aren't supported in
   1282         # Minix's shell.
   1283         [ "${#line}" -gt "${ascii_width:-0}" ] &&
   1284             ascii_width=${#line}
   1285 
   1286     # Using '<<-EOF' is the only way to loop over a command's
   1287     # output without the use of a pipe ('|').
   1288     # This ensures that any variables defined in the while loop
   1289     # are still accessible in the script.
   1290     done <<-EOF
   1291 		$(printf %s "$ascii" | sed 's/\[3.m//g')
   1292 	EOF
   1293 
   1294     # Add a gap between the ascii art and the information.
   1295     ascii_width=$((ascii_width + 4))
   1296 
   1297     # Print the ascii art and position the cursor back where we
   1298     # started prior to printing it.
   1299     # '[1m':   Print the ascii in bold.
   1300     # '[m':    Clear bold.
   1301     # '[%sA':  Move the cursor up '$ascii_height' amount of lines.
   1302     printf '%s[%sA' "$ascii" "$ascii_height" >&6
   1303 }
   1304 
   1305 main() {
   1306     # Hide 'stderr' unless the first argument is '-v'. This saves
   1307     # polluting the script with '2>/dev/null'.
   1308     [ "$1" = -v ] || exec 2>/dev/null
   1309 
   1310     # Hide 'stdout' and selectively print to it using '>&6'.
   1311     # This gives full control over what it displayed on the screen.
   1312     exec 6>&1 >/dev/null
   1313 
   1314     # Generic color list.
   1315     # Disable warning about unused variables.
   1316     # shellcheck disable=2034
   1317     {
   1318         c1=''; c2=''
   1319         c3=''; c4=''
   1320         c5=''; c6=''
   1321         c7=''; c8=''
   1322     }
   1323 
   1324     # Avoid text-wrapping from wrecking the program output
   1325     # and hide the cursor to hide its moving around during
   1326     # the printing process.
   1327     #
   1328     # Some terminals don't support these sequences, nor do they
   1329     # silently conceal them if they're printed resulting in
   1330     # partial sequences being printed to the terminal!
   1331     [ "$TERM" = dumb ]   ||
   1332     [ "$TERM" = minix ]  ||
   1333     [ "$TERM" = cons25 ] || {
   1334         # '[?7l':  Disable line-wrapping.
   1335         # '[?25l': Hide the cursor.
   1336         printf '[?7l[?25l' >&6
   1337 
   1338         # Leave the terminal how we found it on exit or Ctrl+C.
   1339         # '[?7h':  Enable line-wrapping.
   1340         # '[?25h': Show the cursor.
   1341         trap 'printf [?7h[?25h >&6' EXIT
   1342     }
   1343 
   1344     # Store the output of 'uname' to avoid calling it multiple times
   1345     # throughout the script. 'read <<EOF' is the simplest way of reading
   1346     # a command into a list of variables.
   1347     read -r os kernel arch <<-EOF
   1348 		$(uname -srm)
   1349 	EOF
   1350 
   1351     # Always run 'get_os' for the purposes of detecting which ascii
   1352     # art to display.
   1353     get_os
   1354 
   1355     # Allow the user to specify the order and inclusion of information
   1356     # functions through the 'PF_INFO' environment variable.
   1357     # shellcheck disable=2086
   1358     {
   1359         # Disable globbing and set the positional parameters to the
   1360         # contents of 'PF_INFO'.
   1361         set -f
   1362         set +f ${PF_INFO-ascii title os host kernel uptime pkgs memory}
   1363 
   1364         # Iterate over the info functions to determine the lengths of the
   1365         # "info names" for output alignment. The option names and subtitles
   1366         # match 1:1 so this is thankfully simple.
   1367         for info; do
   1368             command -v "get_$info" >/dev/null || continue
   1369 
   1370             # This was a ternary operation but they aren't supported in
   1371             # Minix's shell.
   1372             [ "${#info}" -gt "${info_length:-0}" ] &&
   1373                 info_length=${#info}
   1374         done
   1375 
   1376         # Add an additional space of length to act as a gap.
   1377         info_length=$((info_length + 1))
   1378 
   1379         # Iterate over the above list and run any existing "get_" functions.
   1380         for info; do "get_$info"; done
   1381     }
   1382 
   1383     # Position the cursor below both the ascii art and information lines
   1384     # according to the height of both. If the information exceeds the ascii
   1385     # art in height, don't touch the cursor (0/unset), else move it down
   1386     # N lines.
   1387     #
   1388     # This was a ternary operation but they aren't supported in Minix's shell.
   1389     [ "${info_height:-0}" -lt "${ascii_height:-0}" ] &&
   1390         cursor_pos=$((ascii_height - info_height))
   1391 
   1392     # Print '$cursor_pos' amount of newlines to correctly position the
   1393     # cursor. This used to be a 'printf $(seq X X)' however 'seq' is only
   1394     # typically available (by default) on GNU based systems!
   1395     while [ "${i:=0}" -le "${cursor_pos:-0}" ]; do
   1396         printf '\n'
   1397         i=$((i + 1))
   1398     done >&6
   1399 }
   1400 
   1401 main "$@"