bin

bin
git clone git://git.zepp.club/bin.git
Log | Files | Refs | README | LICENSE

pfetch (59490B)


      1 #!/bin/sh
      2 #
      3 # pfetch - Simple POSIX sh fetch script.
      4 
      5 # Wrapper around all escape sequences used by pfetch to allow for
      6 # greater control over which sequences are used (if any at all).
      7 esc() {
      8     case $1 in
      9         CUU) e="${esc_c}[${2}A" ;; # cursor up
     10         CUD) e="${esc_c}[${2}B" ;; # cursor down
     11         CUF) e="${esc_c}[${2}C" ;; # cursor right
     12         CUB) e="${esc_c}[${2}D" ;; # cursor left
     13 
     14         # text formatting
     15         SGR)
     16             case ${PF_COLOR:=1} in
     17                 (1)
     18                     e="${esc_c}[${2}m"
     19                 ;;
     20 
     21                 (0)
     22                     # colors disabled
     23                     e=
     24                 ;;
     25             esac
     26         ;;
     27 
     28         # line wrap
     29         DECAWM)
     30             case $TERM in
     31                 (dumb | minix | cons25)
     32                     # not supported
     33                     e=
     34                 ;;
     35 
     36                 (*)
     37                     e="${esc_c}[?7${2}"
     38                 ;;
     39             esac
     40         ;;
     41     esac
     42 }
     43 
     44 # Print a sequence to the terminal.
     45 esc_p() {
     46     esc "$@"
     47     printf '%s' "$e"
     48 }
     49 
     50 # This is just a simple wrapper around 'command -v' to avoid
     51 # spamming '>/dev/null' throughout this function. This also guards
     52 # against aliases and functions.
     53 has() {
     54     _cmd=$(command -v "$1") 2>/dev/null || return 1
     55     [ -x "$_cmd" ] || return 1
     56 }
     57 
     58 log() {
     59     # The 'log()' function handles the printing of information.
     60     # In 'pfetch' (and 'neofetch'!) the printing of the ascii art and info
     61     # happen independently of each other.
     62     #
     63     # The size of the ascii art is stored and the ascii is printed first.
     64     # Once the ascii is printed, the cursor is located right below the art
     65     # (See marker $[1]).
     66     #
     67     # Using the stored ascii size, the cursor is then moved to marker $[2].
     68     # This is simply a cursor up escape sequence using the "height" of the
     69     # ascii art.
     70     #
     71     # 'log()' then moves the cursor to the right the "width" of the ascii art
     72     # with an additional amount of padding to add a gap between the art and
     73     # the information (See marker $[3]).
     74     #
     75     # When 'log()' has executed, the cursor is then located at marker $[4].
     76     # When 'log()' is run a second time, the next line of information is
     77     # printed, moving the cursor to marker $[5].
     78     #
     79     # Markers $[4] and $[5] repeat all the way down through the ascii art
     80     # until there is no more information left to print.
     81     #
     82     # Every time 'log()' is called the script keeps track of how many lines
     83     # were printed. When printing is complete the cursor is then manually
     84     # placed below the information and the art according to the "heights"
     85     # of both.
     86     #
     87     # The math is simple: move cursor down $((ascii_height - info_height)).
     88     # If the aim is to move the cursor from marker $[5] to marker $[6],
     89     # plus the ascii height is 8 while the info height is 2 it'd be a move
     90     # of 6 lines downwards.
     91     #
     92     # However, if the information printed is "taller" (takes up more lines)
     93     # than the ascii art, the cursor isn't moved at all!
     94     #
     95     # Once the cursor is at marker $[6], the script exits. This is the gist
     96     # of how this "dynamic" printing and layout works.
     97     #
     98     # This method allows ascii art to be stored without markers for info
     99     # and it allows for easy swapping of info order and amount.
    100     #
    101     # $[2] ___      $[3] goldie@KISS
    102     # $[4](.· |     $[5] os KISS Linux
    103     #     (<> |
    104     #    / __  \
    105     #   ( /  \ /|
    106     #  _/\ __)/_)
    107     #  \/-____\/
    108     # $[1]
    109     #
    110     # $[6] /home/goldie $
    111 
    112     # End here if no data was found.
    113     [ "$2" ] || return
    114 
    115     # Store the values of '$1' and '$3' as we reset the argument list below.
    116     name=$1
    117     use_seperator=$3
    118 
    119     # Use 'set --' as a means of stripping all leading and trailing
    120     # white-space from the info string. This also normalizes all
    121     # white-space inside of the string.
    122     #
    123     # Disable the shellcheck warning for word-splitting
    124     # as it's safe and intended ('set -f' disables globbing).
    125     # shellcheck disable=2046,2086
    126     {
    127         set -f
    128         set +f -- $2
    129         info=$*
    130     }
    131 
    132     # Move the cursor to the right, the width of the ascii art with an
    133     # additional gap for text spacing.
    134     esc_p CUF "$ascii_width"
    135 
    136     # Print the info name and color the text.
    137     esc_p SGR "3${PF_COL1-4}";
    138     esc_p SGR 1
    139     printf '%s' "$name"
    140     esc_p SGR 0
    141 
    142     # Print the info name and info data separator, if applicable.
    143     [ "$use_seperator" ] || printf %s "$PF_SEP"
    144 
    145     # Move the cursor backward the length of the *current* info name and
    146     # then move it forwards the length of the *longest* info name. This
    147     # aligns each info data line.
    148     esc_p CUB "${#name}"
    149     esc_p CUF "${PF_ALIGN:-$info_length}"
    150 
    151     # Print the info data, color it and strip all leading whitespace
    152     # from the string.
    153     esc_p SGR "3${PF_COL2-9}"
    154     printf '%s' "$info"
    155     esc_p SGR 0
    156     printf '\n'
    157 
    158     # Keep track of the number of times 'log()' has been run.
    159     info_height=$((${info_height:-0} + 1))
    160 }
    161 
    162 get_title() {
    163     # Username is retrieved by first checking '$USER' with a fallback
    164     # to the 'id -un' command.
    165     user=${USER:-$(id -un)}
    166 
    167     # Hostname is retrieved by first checking '$HOSTNAME' with a fallback
    168     # to the 'hostname' command.
    169     #
    170     # Disable the warning about '$HOSTNAME' being undefined in POSIX sh as
    171     # the intention for using it is allowing the user to overwrite the
    172     # value on invocation.
    173     # shellcheck disable=3028,2039
    174     hostname=${HOSTNAME:-${hostname:-$(hostname)}}
    175 
    176     # If the hostname is still not found, fallback to the contents of the
    177     # /etc/hostname file.
    178     [ "$hostname" ] || read -r hostname < /etc/hostname
    179 
    180     # Add escape sequences for coloring to user and host name. As we embed
    181     # them directly in the arguments passed to log(), we cannot use esc_p().
    182     esc SGR 1
    183     user=$e$user
    184     esc SGR "3${PF_COL3:-1}"
    185     user=$e$user
    186     esc SGR 1
    187     user=$user$e
    188     esc SGR 1
    189     hostname=$e$hostname
    190     esc SGR "3${PF_COL3:-1}"
    191     hostname=$e$hostname
    192 
    193     log "${user}@${hostname}" " " " " >&6
    194 }
    195 
    196 get_os() {
    197     # This function is called twice, once to detect the distribution name
    198     # for the purposes of picking an ascii art early and secondly to display
    199     # the distribution name in the info output (if enabled).
    200     #
    201     # On first run, this function displays _nothing_, only on the second
    202     # invocation is 'log()' called.
    203     [ "$distro" ] && {
    204         log os "$distro" >&6
    205         return
    206     }
    207 
    208     case $os in
    209         (Linux*)
    210             # Some Linux distributions (which are based on others)
    211             # fail to identify as they **do not** change the upstream
    212             # distribution's identification packages or files.
    213             #
    214             # It is senseless to add a special case in the code for
    215             # each and every distribution (which _is_ technically no
    216             # different from what it is based on) as they're either too
    217             # lazy to modify upstream's identification files or they
    218             # don't have the know-how (or means) to ship their own
    219             # lsb-release package.
    220             #
    221             # This causes users to think there's a bug in system detection
    222             # tools like neofetch or pfetch when they technically *do*
    223             # function correctly.
    224             #
    225             # Exceptions are made for distributions which are independent,
    226             # not based on another distribution or follow different
    227             # standards.
    228             #
    229             # This applies only to distributions which follow the standard
    230             # by shipping unmodified identification files and packages
    231             # from their respective upstreams.
    232             if has lsb_release; then
    233                 distro=$(lsb_release -sd)
    234 
    235             # Android detection works by checking for the existence of
    236             # the follow two directories. I don't think there's a simpler
    237             # method than this.
    238             elif [ -d /system/app ] && [ -d /system/priv-app ]; then
    239                 distro="Android $(getprop ro.build.version.release)"
    240 
    241             elif [ -f /etc/os-release ]; then
    242                 # This used to be a simple '. /etc/os-release' but I believe
    243                 # this is insecure as we blindly executed whatever was in the
    244                 # file. This parser instead simply handles 'key=val', treating
    245                 # the file contents as plain-text.
    246                 while IFS='=' read -r key val; do
    247                     case $key in
    248                         (PRETTY_NAME)
    249                             distro=$val
    250                         ;;
    251                     esac
    252                 done < /etc/os-release
    253 
    254             else
    255                 # Special cases for (independent) distributions which
    256                 # don't follow any os-release/lsb standards whatsoever.
    257                 has crux && distro=$(crux)
    258                 has guix && distro='Guix System'
    259             fi
    260 
    261             # 'os-release' and 'lsb_release' sometimes add quotes
    262             # around the distribution name, strip them.
    263             distro=${distro##[\"\']}
    264             distro=${distro%%[\"\']}
    265 
    266             # Check to see if we're running Bedrock Linux which is
    267             # very unique. This simply checks to see if the user's
    268             # PATH contains a Bedrock specific value.
    269             case $PATH in
    270                 (*/bedrock/cross/*)
    271                     distro='Bedrock Linux'
    272                 ;;
    273             esac
    274 
    275             # Check to see if Linux is running in Windows 10 under
    276             # WSL1 (Windows subsystem for Linux [version 1]) and
    277             # append a string accordingly.
    278             #
    279             # If the kernel version string ends in "-Microsoft",
    280             # we're very likely running under Windows 10 in WSL1.
    281             if [ "$WSLENV" ]; then
    282                 distro="${distro}${WSLENV+ on Windows 10 [WSL2]}"
    283 
    284             # Check to see if Linux is running in Windows 10 under
    285             # WSL2 (Windows subsystem for Linux [version 2]) and
    286             # append a string accordingly.
    287             #
    288             # This checks to see if '$WSLENV' is defined. This
    289             # appends the Windows 10 string even if '$WSLENV' is
    290             # empty. We only need to check that is has been _exported_.
    291             elif [ -z "${kernel%%*-Microsoft}" ]; then
    292                 distro="$distro on Windows 10 [WSL1]"
    293             fi
    294         ;;
    295 
    296         (Darwin*)
    297             # Parse the SystemVersion.plist file to grab the macOS
    298             # version. The file is in the following format:
    299             #
    300             # <key>ProductVersion</key>
    301             # <string>10.14.6</string>
    302             #
    303             # 'IFS' is set to '<>' to enable splitting between the
    304             # keys and a second 'read' is used to operate on the
    305             # next line directly after a match.
    306             #
    307             # '_' is used to nullify a field. '_ _ line _' basically
    308             # says "populate $line with the third field's contents".
    309             while IFS='<>' read -r _ _ line _; do
    310                 case $line in
    311                     # Match 'ProductVersion' and read the next line
    312                     # directly as it contains the key's value.
    313                     ProductVersion)
    314                         IFS='<>' read -r _ _ mac_version _
    315                         continue
    316                     ;;
    317 
    318                     ProductName)
    319                         IFS='<>' read -r _ _ mac_product _
    320                         continue
    321                     ;;
    322                 esac
    323             done < /System/Library/CoreServices/SystemVersion.plist
    324 
    325             # Use the ProductVersion to determine which macOS/OS X codename
    326             # the system has. As far as I'm aware there's no "dynamic" way
    327             # of grabbing this information.
    328             case $mac_version in
    329                 (10.4*)  distro='Mac OS X Tiger' ;;
    330                 (10.5*)  distro='Mac OS X Leopard' ;;
    331                 (10.6*)  distro='Mac OS X Snow Leopard' ;;
    332                 (10.7*)  distro='Mac OS X Lion' ;;
    333                 (10.8*)  distro='OS X Mountain Lion' ;;
    334                 (10.9*)  distro='OS X Mavericks' ;;
    335                 (10.10*) distro='OS X Yosemite' ;;
    336                 (10.11*) distro='OS X El Capitan' ;;
    337                 (10.12*) distro='macOS Sierra' ;;
    338                 (10.13*) distro='macOS High Sierra' ;;
    339                 (10.14*) distro='macOS Mojave' ;;
    340                 (10.15*) distro='macOS Catalina' ;;
    341                 (11*)    distro='macOS Big Sur' ;;
    342                 (12*)    distro='macOS Monterey' ;;
    343                 (*)      distro='macOS' ;;
    344             esac
    345 
    346             # Use the ProductName to determine if we're running in iOS.
    347             case $mac_product in
    348                 (iP*) distro='iOS' ;;
    349             esac
    350 
    351             distro="$distro $mac_version"
    352         ;;
    353 
    354         (Haiku)
    355             # Haiku uses 'uname -v' for version information
    356             # instead of 'uname -r' which only prints '1'.
    357             distro=$(uname -sv)
    358         ;;
    359 
    360         (Minix|DragonFly)
    361             distro="$os $kernel"
    362 
    363             # Minix and DragonFly don't support the escape
    364             # sequences used, clear the exit trap.
    365             trap '' EXIT
    366         ;;
    367 
    368         (SunOS)
    369             # Grab the first line of the '/etc/release' file
    370             # discarding everything after '('.
    371             IFS='(' read -r distro _ < /etc/release
    372         ;;
    373 
    374         (OpenBSD*)
    375             # Show the OpenBSD version type (current if present).
    376             # kern.version=OpenBSD 6.6-current (GENERIC.MP) ...
    377             IFS=' =' read -r _ distro openbsd_ver _ <<-EOF
    378 				$(sysctl kern.version)
    379 			EOF
    380 
    381             distro="$distro $openbsd_ver"
    382         ;;
    383 
    384         (FreeBSD)
    385             distro="$os $(freebsd-version)"
    386         ;;
    387 
    388         (*)
    389             # Catch all to ensure '$distro' is never blank.
    390             # This also handles the BSDs.
    391             distro="$os $kernel"
    392         ;;
    393     esac
    394 }
    395 
    396 get_kernel() {
    397     case $os in
    398         # Don't print kernel output on some systems as the
    399         # OS name includes it.
    400         (*BSD*|Haiku|Minix)
    401             return
    402         ;;
    403     esac
    404 
    405     # '$kernel' is the cached output of 'uname -r'.
    406     log kernel "$kernel" >&6
    407 }
    408 
    409 get_host() {
    410     case $os in
    411         (Linux*)
    412             # Despite what these files are called, version doesn't
    413             # always contain the version nor does name always contain
    414             # the name.
    415             read -r name    < /sys/devices/virtual/dmi/id/product_name
    416             read -r version < /sys/devices/virtual/dmi/id/product_version
    417             read -r model   < /sys/firmware/devicetree/base/model
    418 
    419             host="$name $version $model"
    420         ;;
    421 
    422         (Darwin* | FreeBSD* | DragonFly*)
    423             host=$(sysctl -n hw.model)
    424         ;;
    425 
    426         (NetBSD*)
    427             host=$(sysctl -n machdep.dmi.system-vendor \
    428                              machdep.dmi.system-product)
    429         ;;
    430 
    431         (OpenBSD*)
    432             host=$(sysctl -n hw.version)
    433         ;;
    434 
    435         (*BSD* | Minix)
    436             host=$(sysctl -n hw.vendor hw.product)
    437         ;;
    438     esac
    439 
    440     # Turn the host string into an argument list so we can iterate
    441     # over it and remove OEM strings and other information which
    442     # shouldn't be displayed.
    443     #
    444     # Disable the shellcheck warning for word-splitting
    445     # as it's safe and intended ('set -f' disables globbing).
    446     # shellcheck disable=2046,2086
    447     {
    448         set -f
    449         set +f -- $host
    450         host=
    451     }
    452 
    453     # Iterate over the host string word by word as a means of stripping
    454     # unwanted and OEM information from the string as a whole.
    455     #
    456     # This could have been implemented using a long 'sed' command with
    457     # a list of word replacements, however I want to show that something
    458     # like this is possible in pure sh.
    459     #
    460     # This string reconstruction is needed as some OEMs either leave the
    461     # identification information as "To be filled by OEM", "Default",
    462     # "undefined" etc and we shouldn't print this to the screen.
    463     for word do
    464         # This works by reconstructing the string by excluding words
    465         # found in the "blacklist" below. Only non-matches are appended
    466         # to the final host string.
    467         case $word in
    468            (To      | [Bb]e      | [Ff]illed | [Bb]y  | O.E.M.  | OEM  |\
    469             Not     | Applicable | Specified | System | Product | Name |\
    470             Version | Undefined  | Default   | string | INVALID | �    | os |\
    471             Type1ProductConfigId )
    472                 continue
    473             ;;
    474         esac
    475 
    476         host="$host$word "
    477     done
    478 
    479     # '$arch' is the cached output from 'uname -m'.
    480     log host "${host:-$arch}" >&6
    481 }
    482 
    483 get_uptime() {
    484     # Uptime works by retrieving the data in total seconds and then
    485     # converting that data into days, hours and minutes using simple
    486     # math.
    487     case $os in
    488         (Linux* | Minix* | SerenityOS*)
    489             IFS=. read -r s _ < /proc/uptime
    490         ;;
    491 
    492         (Darwin* | *BSD* | DragonFly*)
    493             s=$(sysctl -n kern.boottime)
    494 
    495             # Extract the uptime in seconds from the following output:
    496             # [...] { sec = 1271934886, usec = 667779 } Thu Apr 22 12:14:46 2010
    497             s=${s#*=}
    498             s=${s%,*}
    499 
    500             # The uptime format from 'sysctl' needs to be subtracted from
    501             # the current time in seconds.
    502             s=$(($(date +%s) - s))
    503         ;;
    504 
    505         (Haiku)
    506             # The boot time is returned in microseconds, convert it to
    507             # regular seconds.
    508             s=$(($(system_time) / 1000000))
    509         ;;
    510 
    511         (SunOS)
    512             # Split the output of 'kstat' on '.' and any white-space
    513             # which exists in the command output.
    514             #
    515             # The output is as follows:
    516             # unix:0:system_misc:snaptime	14809.906993005
    517             #
    518             # The parser extracts:          ^^^^^
    519             IFS='	.' read -r _ s _ <<-EOF
    520 				$(kstat -p unix:0:system_misc:snaptime)
    521 			EOF
    522         ;;
    523 
    524         (IRIX)
    525             # Grab the uptime in a pretty format. Usually,
    526             # 00:00:00 from the 'ps' command.
    527             t=$(LC_ALL=POSIX ps -o etime= -p 1)
    528 
    529             # Split the pretty output into days or hours
    530             # based on the uptime.
    531             case $t in
    532                 (*-*)   d=${t%%-*} t=${t#*-} ;;
    533                 (*:*:*) h=${t%%:*} t=${t#*:} ;;
    534             esac
    535 
    536             h=${h#0} t=${t#0}
    537 
    538             # Convert the split pretty fields back into
    539             # seconds so we may re-convert them to our format.
    540             s=$((${d:-0}*86400 + ${h:-0}*3600 + ${t%%:*}*60 + ${t#*:}))
    541         ;;
    542     esac
    543 
    544     # Convert the uptime from seconds into days, hours and minutes.
    545     d=$((s / 60 / 60 / 24))
    546     h=$((s / 60 / 60 % 24))
    547     m=$((s / 60 % 60))
    548 
    549     # Only append days, hours and minutes if they're non-zero.
    550     case "$d" in ([!0]*) uptime="${uptime}${d}d "; esac
    551     case "$h" in ([!0]*) uptime="${uptime}${h}h "; esac
    552     case "$m" in ([!0]*) uptime="${uptime}${m}m "; esac
    553 
    554     log uptime "${uptime:-0m}" >&6
    555 }
    556 
    557 get_pkgs() {
    558     # This works by first checking for which package managers are
    559     # installed and finally by printing each package manager's
    560     # package list with each package one per line.
    561     #
    562     # The output from this is then piped to 'wc -l' to count each
    563     # line, giving us the total package count of whatever package
    564     # managers are installed.
    565     packages=$(
    566         case $os in
    567             (Linux*)
    568                 # Commands which print packages one per line.
    569                 has bonsai     && bonsai list
    570                 has crux       && pkginfo -i
    571                 has pacman-key && pacman -Qq
    572                 has dpkg       && dpkg-query -f '.\n' -W
    573                 has rpm        && rpm -qa
    574                 has xbps-query && xbps-query -l
    575                 has apk        && apk info
    576                 has guix       && guix package --list-installed
    577                 has opkg       && opkg list-installed
    578 
    579                 # Directories containing packages.
    580                 has kiss       && printf '%s\n' /var/db/kiss/installed/*/
    581                 has cpt-list   && printf '%s\n' /var/db/cpt/installed/*/
    582                 has brew       && printf '%s\n' "$(brew --cellar)/"*
    583                 has emerge     && printf '%s\n' /var/db/pkg/*/*/
    584                 has pkgtool    && printf '%s\n' /var/log/packages/*
    585                 has eopkg      && printf '%s\n' /var/lib/eopkg/package/*
    586 
    587                 # 'nix' requires two commands.
    588                 has nix-store  && {
    589                     nix-store -q --requisites /run/current-system/sw
    590                     nix-store -q --requisites ~/.nix-profile
    591                 }
    592             ;;
    593 
    594             (Darwin*)
    595                 # Commands which print packages one per line.
    596                 has pkgin      && pkgin list
    597                 has dpkg       && dpkg-query -f '.\n' -W
    598 
    599                 # Directories containing packages.
    600                 has brew       && printf '%s\n' /usr/local/Cellar/*
    601 
    602                 # 'port' prints a single line of output to 'stdout'
    603                 # when no packages are installed and exits with
    604                 # success causing a false-positive of 1 package
    605                 # installed.
    606                 #
    607                 # 'port' should really exit with a non-zero code
    608                 # in this case to allow scripts to cleanly handle
    609                 # this behavior.
    610                 has port       && {
    611                     pkg_list=$(port installed)
    612 
    613                     case "$pkg_list" in
    614                         ("No ports are installed.")
    615                             # do nothing
    616                         ;;
    617 
    618                         (*)
    619                             printf '%s\n' "$pkg_list"
    620                         ;;
    621                     esac
    622                 }
    623             ;;
    624 
    625             (FreeBSD*|DragonFly*)
    626                 pkg info
    627             ;;
    628 
    629             (OpenBSD*)
    630                 printf '%s\n' /var/db/pkg/*/
    631             ;;
    632 
    633             (NetBSD*)
    634                 pkg_info
    635             ;;
    636 
    637             (Haiku)
    638                 printf '%s\n' /boot/system/package-links/*
    639             ;;
    640 
    641             (Minix)
    642                 printf '%s\n' /usr/pkg/var/db/pkg/*/
    643             ;;
    644 
    645             (SunOS)
    646                 has pkginfo && pkginfo -i
    647                 has pkg     && pkg list
    648             ;;
    649 
    650             (IRIX)
    651                 versions -b
    652             ;;
    653 
    654             (SerenityOS)
    655                 while IFS=" " read -r type _; do
    656                     [ "$type" != dependency ] &&
    657                         printf "\n"
    658                 done < /usr/Ports/packages.db
    659             ;;
    660         esac | wc -l
    661     )
    662 
    663     # 'wc -l' can have leading and/or trailing whitespace
    664     # depending on the implementation, so strip them.
    665     # Procedure explained at https://github.com/dylanaraps/pure-sh-bible
    666     # (trim-leading-and-trailing-white-space-from-string)
    667     packages=${packages#"${packages%%[![:space:]]*}"}
    668     packages=${packages%"${packages##*[![:space:]]}"}
    669 
    670     case $os in
    671         # IRIX's package manager adds 3 lines of extra
    672         # output which we must account for here.
    673         (IRIX)
    674             packages=$((packages - 3))
    675         ;;
    676 
    677         # OpenBSD's wc prints whitespace before the output
    678         # which needs to be stripped.
    679         (OpenBSD)
    680             packages=$((packages))
    681         ;;
    682     esac
    683 
    684     case $packages in
    685         (1?*|[2-9]*)
    686             log pkgs "$packages" >&6
    687         ;;
    688     esac
    689 }
    690 
    691 get_memory() {
    692     case $os in
    693         # Used memory is calculated using the following "formula":
    694         # MemUsed = MemTotal + Shmem - MemFree - Buffers - Cached - SReclaimable
    695         # Source: https://github.com/KittyKatt/screenFetch/issues/386
    696         (Linux*)
    697             # Parse the '/proc/meminfo' file splitting on ':' and 'k'.
    698             # The format of the file is 'key:   000kB' and an additional
    699             # split is used on 'k' to filter out 'kB'.
    700             while IFS=':k '  read -r key val _; do
    701                 case $key in
    702                     (MemTotal)
    703                         mem_used=$((mem_used + val))
    704                         mem_full=$val
    705                     ;;
    706 
    707                     (Shmem)
    708                         mem_used=$((mem_used + val))
    709                     ;;
    710 
    711                     (MemFree | Buffers | Cached | SReclaimable)
    712                         mem_used=$((mem_used - val))
    713                     ;;
    714 
    715                     # If detected this will be used over the above calculation
    716                     # for mem_used. Available since Linux 3.14rc.
    717                     # See kernel commit 34e431b0ae398fc54ea69ff85ec700722c9da773
    718                     (MemAvailable)
    719                         mem_avail=$val
    720                     ;;
    721                 esac
    722             done < /proc/meminfo
    723 
    724             case $mem_avail in
    725                 (*[0-9]*)
    726                     mem_used=$(((mem_full - mem_avail) / 1024))
    727                 ;;
    728 
    729                 *)
    730                     mem_used=$((mem_used / 1024))
    731                 ;;
    732             esac
    733 
    734             mem_full=$((mem_full / 1024))
    735         ;;
    736 
    737         # Used memory is calculated using the following "formula":
    738         # (wired + active + occupied) * 4 / 1024
    739         (Darwin*)
    740             mem_full=$(($(sysctl -n hw.memsize) / 1024 / 1024))
    741 
    742             # Parse the 'vmstat' file splitting on ':' and '.'.
    743             # The format of the file is 'key:   000.' and an additional
    744             # split is used on '.' to filter it out.
    745             while IFS=:. read -r key val; do
    746                 case $key in
    747                     (*' wired'*|*' active'*|*' occupied'*)
    748                         mem_used=$((mem_used + ${val:-0}))
    749                     ;;
    750                 esac
    751 
    752             # Using '<<-EOF' is the only way to loop over a command's
    753             # output without the use of a pipe ('|').
    754             # This ensures that any variables defined in the while loop
    755             # are still accessible in the script.
    756             done <<-EOF
    757                 $(vm_stat)
    758 			EOF
    759 
    760             mem_used=$((mem_used * 4 / 1024))
    761         ;;
    762 
    763         (OpenBSD*)
    764             mem_full=$(($(sysctl -n hw.physmem) / 1024 / 1024))
    765 
    766             # This is a really simpler parser for 'vmstat' which grabs
    767             # the used memory amount in a lazy way. 'vmstat' prints 3
    768             # lines of output with the needed value being stored in the
    769             # final line.
    770             #
    771             # This loop simply grabs the 3rd element of each line until
    772             # the EOF is reached. Each line overwrites the value of the
    773             # previous one so we're left with what we wanted. This isn't
    774             # slow as only 3 lines are parsed.
    775             while read -r _ _ line _; do
    776                 mem_used=${line%%M}
    777 
    778             # Using '<<-EOF' is the only way to loop over a command's
    779             # output without the use of a pipe ('|').
    780             # This ensures that any variables defined in the while loop
    781             # are still accessible in the script.
    782             done <<-EOF
    783                 $(vmstat)
    784 			EOF
    785         ;;
    786 
    787         # Used memory is calculated using the following "formula":
    788         # mem_full - ((inactive + free + cache) * page_size / 1024)
    789         (FreeBSD*|DragonFly*)
    790             mem_full=$(($(sysctl -n hw.physmem) / 1024 / 1024))
    791 
    792             # Use 'set --' to store the output of the command in the
    793             # argument list. POSIX sh has no arrays but this is close enough.
    794             #
    795             # Disable the shellcheck warning for word-splitting
    796             # as it's safe and intended ('set -f' disables globbing).
    797             # shellcheck disable=2046
    798             {
    799                 set -f
    800                 set +f -- $(sysctl -n hw.pagesize \
    801                                       vm.stats.vm.v_inactive_count \
    802                                       vm.stats.vm.v_free_count \
    803                                       vm.stats.vm.v_cache_count)
    804             }
    805 
    806             # Calculate the amount of used memory.
    807             # $1: hw.pagesize
    808             # $2: vm.stats.vm.v_inactive_count
    809             # $3: vm.stats.vm.v_free_count
    810             # $4: vm.stats.vm.v_cache_count
    811             mem_used=$((mem_full - (($2 + $3 + $4) * $1 / 1024 / 1024)))
    812         ;;
    813 
    814         (NetBSD*)
    815             mem_full=$(($(sysctl -n hw.physmem64) / 1024 / 1024))
    816 
    817             # NetBSD implements a lot of the Linux '/proc' filesystem,
    818             # this uses the same parser as the Linux memory detection.
    819             while IFS=':k ' read -r key val _; do
    820                 case $key in
    821                     (MemFree)
    822                         mem_free=$((val / 1024))
    823                         break
    824                     ;;
    825                 esac
    826             done < /proc/meminfo
    827 
    828             mem_used=$((mem_full - mem_free))
    829         ;;
    830 
    831         (Haiku)
    832             # Read the first line of 'sysinfo -mem' splitting on
    833             # '(', ' ', and ')'. The needed information is then
    834             # stored in the 5th and 7th elements. Using '_' "consumes"
    835             # an element allowing us to proceed to the next one.
    836             #
    837             # The parsed format is as follows:
    838             # 3501142016 bytes free      (used/max  792645632 / 4293787648)
    839             IFS='( )' read -r _ _ _ _ mem_used _ mem_full <<-EOF
    840                 $(sysinfo -mem)
    841 			EOF
    842 
    843             mem_used=$((mem_used / 1024 / 1024))
    844             mem_full=$((mem_full / 1024 / 1024))
    845         ;;
    846 
    847         (Minix)
    848             # Minix includes the '/proc' filesystem though the format
    849             # differs from Linux. The '/proc/meminfo' file is only a
    850             # single line with space separated elements and elements
    851             # 2 and 3 contain the total and free memory numbers.
    852             read -r _ mem_full mem_free _ < /proc/meminfo
    853 
    854             mem_used=$(((mem_full - mem_free) / 1024))
    855             mem_full=$(( mem_full / 1024))
    856         ;;
    857 
    858         (SunOS)
    859             hw_pagesize=$(pagesize)
    860 
    861             # 'kstat' outputs memory in the following format:
    862             # unix:0:system_pages:pagestotal	1046397
    863             # unix:0:system_pages:pagesfree		885018
    864             #
    865             # This simply uses the first "element" (white-space
    866             # separated) as the key and the second element as the
    867             # value.
    868             #
    869             # A variable is then assigned based on the key.
    870             while read -r key val; do
    871                 case $key in
    872                     (*total)
    873                         pages_full=$val
    874                     ;;
    875 
    876                     (*free)
    877                         pages_free=$val
    878                     ;;
    879                 esac
    880             done <<-EOF
    881 				$(kstat -p unix:0:system_pages:pagestotal \
    882                            unix:0:system_pages:pagesfree)
    883 			EOF
    884 
    885             mem_full=$((pages_full * hw_pagesize / 1024 / 1024))
    886             mem_free=$((pages_free * hw_pagesize / 1024 / 1024))
    887             mem_used=$((mem_full - mem_free))
    888         ;;
    889 
    890         (IRIX)
    891             # Read the memory information from the 'top' command. Parse
    892             # and split each line until we reach the line starting with
    893             # "Memory".
    894             #
    895             # Example output: Memory: 160M max, 147M avail, .....
    896             while IFS=' :' read -r label mem_full _ mem_free _; do
    897                 case $label in
    898                     (Memory)
    899                         mem_full=${mem_full%M}
    900                         mem_free=${mem_free%M}
    901                         break
    902                     ;;
    903                 esac
    904             done <<-EOF
    905                 $(top -n)
    906 			EOF
    907 
    908             mem_used=$((mem_full - mem_free))
    909         ;;
    910 
    911         (SerenityOS)
    912             IFS='{}' read -r _ memstat _ < /proc/memstat
    913 
    914             set -f -- "$IFS"
    915             IFS=,
    916 
    917             for pair in $memstat; do
    918                 case $pair in
    919                     (*user_physical_allocated*)
    920                         mem_used=${pair##*:}
    921                     ;;
    922 
    923                     (*user_physical_available*)
    924                         mem_free=${pair##*:}
    925                     ;;
    926                 esac
    927             done
    928 
    929             IFS=$1
    930             set +f --
    931 
    932             mem_used=$((mem_used * 4096 / 1024 / 1024))
    933             mem_free=$((mem_free * 4096 / 1024 / 1024))
    934 
    935             mem_full=$((mem_used + mem_free))
    936         ;;
    937     esac
    938 
    939     log memory "${mem_used:-?}M / ${mem_full:-?}M" >&6
    940 }
    941 
    942 get_wm() {
    943     case $os in
    944         (Darwin*)
    945             # Don't display window manager on macOS.
    946         ;;
    947 
    948         (*)
    949             # xprop can be used to grab the window manager's properties
    950             # which contains the window manager's name under '_NET_WM_NAME'.
    951             #
    952             # The upside to using 'xprop' is that you don't need to hardcode
    953             # a list of known window manager names. The downside is that
    954             # not all window managers conform to setting the '_NET_WM_NAME'
    955             # atom..
    956             #
    957             # List of window managers which fail to set the name atom:
    958             # catwm, fvwm, dwm, 2bwm, monster, wmaker and sowm [mine! ;)].
    959             #
    960             # The final downside to this approach is that it does _not_
    961             # support Wayland environments. The only solution which supports
    962             # Wayland is the 'ps' parsing mentioned below.
    963             #
    964             # A more naive implementation is to parse the last line of
    965             # '~/.xinitrc' to extract the second white-space separated
    966             # element.
    967             #
    968             # The issue with an approach like this is that this line data
    969             # does not always equate to the name of the window manager and
    970             # could in theory be _anything_.
    971             #
    972             # This also fails when the user launches xorg through a display
    973             # manager or other means.
    974             #
    975             #
    976             # Another naive solution is to parse 'ps' with a hardcoded list
    977             # of window managers to detect the current window manager (based
    978             # on what is running).
    979             #
    980             # The issue with this approach is the need to hardcode and
    981             # maintain a list of known window managers.
    982             #
    983             # Another issue is that process names do not always equate to
    984             # the name of the window manager. False-positives can happen too.
    985             #
    986             # This is the only solution which supports Wayland based
    987             # environments sadly. It'd be nice if some kind of standard were
    988             # established to identify Wayland environments.
    989             #
    990             # pfetch's goal is to remain _simple_, if you'd like a "full"
    991             # implementation of window manager detection use 'neofetch'.
    992             #
    993             # Neofetch use a combination of 'xprop' and 'ps' parsing to
    994             # support all window managers (including non-conforming and
    995             # Wayland) though it's a lot more complicated!
    996 
    997             # Don't display window manager if X isn't running.
    998             [ "$DISPLAY" ] || return
    999 
   1000             # This is a two pass call to xprop. One call to get the window
   1001             # manager's ID and another to print its properties.
   1002             has xprop && {
   1003                 # The output of the ID command is as follows:
   1004                 # _NET_SUPPORTING_WM_CHECK: window id # 0x400000
   1005                 #
   1006                 # To extract the ID, everything before the last space
   1007                 # is removed.
   1008                 id=$(xprop -root -notype _NET_SUPPORTING_WM_CHECK)
   1009                 id=${id##* }
   1010 
   1011                 # The output of the property command is as follows:
   1012                 # _NAME 8t
   1013                 # _NET_WM_PID = 252
   1014                 # _NET_WM_NAME = "bspwm"
   1015                 # _NET_SUPPORTING_WM_CHECK: window id # 0x400000
   1016                 # WM_CLASS = "wm", "Bspwm"
   1017                 #
   1018                 # To extract the name, everything before '_NET_WM_NAME = \"'
   1019                 # is removed and everything after the next '"' is removed.
   1020                 wm=$(xprop -id "$id" -notype -len 25 -f _NET_WM_NAME 8t)
   1021             }
   1022 
   1023             # Handle cases of a window manager _not_ populating the
   1024             # '_NET_WM_NAME' atom. Display nothing in this case.
   1025             case $wm in
   1026                 (*'_NET_WM_NAME = '*)
   1027                     wm=${wm##*_NET_WM_NAME = \"}
   1028                     wm=${wm%%\"*}
   1029                 ;;
   1030 
   1031                 (*)
   1032                     # Fallback to checking the process list
   1033                     # for the select few window managers which
   1034                     # don't set '_NET_WM_NAME'.
   1035                     while read -r ps_line; do
   1036                         case $ps_line in
   1037                             (*catwm*)     wm=catwm ;;
   1038                             (*fvwm*)      wm=fvwm ;;
   1039                             (*dwm*)       wm=dwm ;;
   1040                             (*2bwm*)      wm=2bwm ;;
   1041                             (*monsterwm*) wm=monsterwm ;;
   1042                             (*wmaker*)    wm='Window Maker' ;;
   1043                             (*sowm*)      wm=sowm ;;
   1044 							(*penrose*)   wm=penrose ;;
   1045                         esac
   1046                     done <<-EOF
   1047                         $(ps x)
   1048 					EOF
   1049                 ;;
   1050             esac
   1051         ;;
   1052     esac
   1053 
   1054     log wm "$wm" >&6
   1055 }
   1056 
   1057 
   1058 get_de() {
   1059     # This only supports Xorg related desktop environments though
   1060     # this is fine as knowing the desktop environment on Windows,
   1061     # macOS etc is useless (they'll always report the same value).
   1062     #
   1063     # Display the value of '$XDG_CURRENT_DESKTOP', if it's empty,
   1064     # display the value of '$DESKTOP_SESSION'.
   1065     log de "${XDG_CURRENT_DESKTOP:-$DESKTOP_SESSION}" >&6
   1066 }
   1067 
   1068 get_shell() {
   1069     # Display the basename of the '$SHELL' environment variable.
   1070     log shell "${SHELL##*/}" >&6
   1071 }
   1072 
   1073 get_editor() {
   1074     # Display the value of '$VISUAL', if it's empty, display the
   1075     # value of '$EDITOR'.
   1076     editor=${VISUAL:-"$EDITOR"}
   1077 
   1078     log editor "${editor##*/}" >&6
   1079 }
   1080 
   1081 get_palette() {
   1082     # Print the first 8 terminal colors. This uses the existing
   1083     # sequences to change text color with a sequence prepended
   1084     # to reverse the foreground and background colors.
   1085     #
   1086     # This allows us to save hardcoding a second set of sequences
   1087     # for background colors.
   1088     #
   1089     # False positive.
   1090     # shellcheck disable=2154
   1091     {
   1092         esc SGR 7
   1093         palette="$e$c1 $c1 $c2 $c2 $c3 $c3 $c4 $c4 $c5 $c5 $c6 $c6 "
   1094         esc SGR 0
   1095         palette="$palette$e"
   1096     }
   1097 
   1098     # Print the palette with a new-line before and afterwards but no seperator.
   1099     printf '\n' >&6
   1100     log "$palette
   1101         " " " " " >&6
   1102 }
   1103 
   1104 get_ascii() {
   1105     # This is a simple function to read the contents of
   1106     # an ascii file from 'stdin'. It allows for the use
   1107     # of '<<-EOF' to prevent the break in indentation in
   1108     # this source code.
   1109     #
   1110     # This function also sets the text colors according
   1111     # to the ascii color.
   1112     read_ascii() {
   1113         # 'PF_COL1': Set the info name color according to ascii color.
   1114         # 'PF_COL3': Set the title color to some other color. ¯\_(ツ)_/¯
   1115         PF_COL1=${PF_COL1:-${1:-7}}
   1116         PF_COL3=${PF_COL3:-$((${1:-7}%8+1))}
   1117 
   1118         # POSIX sh has no 'var+=' so 'var=${var}append' is used. What's
   1119         # interesting is that 'var+=' _is_ supported inside '$(())'
   1120         # (arithmetic) though there's no support for 'var++/var--'.
   1121         #
   1122         # There is also no $'\n' to add a "literal"(?) newline to the
   1123         # string. The simplest workaround being to break the line inside
   1124         # the string (though this has the caveat of breaking indentation).
   1125         while IFS= read -r line; do
   1126             ascii="$ascii$line
   1127 "
   1128         done
   1129     }
   1130 
   1131     # This checks for ascii art in the following order:
   1132     # '$1':        Argument given to 'get_ascii()' directly.
   1133     # '$PF_ASCII': Environment variable set by user.
   1134     # '$distro':   The detected distribution name.
   1135     # '$os':       The name of the operating system/kernel.
   1136     #
   1137     # NOTE: Each ascii art below is indented using tabs, this
   1138     #       allows indentation to continue naturally despite
   1139     #       the use of '<<-EOF'.
   1140     #
   1141     # False positive.
   1142     # shellcheck disable=2154
   1143     case ${1:-${PF_ASCII:-${distro:-$os}}} in
   1144         ([Aa]lpine*)
   1145             read_ascii 4 <<-EOF
   1146 				${c4}   /\\ /\\
   1147 				  /${c7}/ ${c4}\\  \\
   1148 				 /${c7}/   ${c4}\\  \\
   1149 				/${c7}//    ${c4}\\  \\
   1150 				${c7}//      ${c4}\\  \\
   1151 				         ${c4}\\
   1152 			EOF
   1153         ;;
   1154 
   1155         ([Aa]ndroid*)
   1156             read_ascii 2 <<-EOF
   1157 				${c2}  ;,           ,;
   1158 				${c2}   ';,.-----.,;'
   1159 				${c2}  ,'           ',
   1160 				${c2} /    O     O    \\
   1161 				${c2}|                 |
   1162 				${c2}'-----------------'
   1163 			EOF
   1164         ;;
   1165 
   1166         ([Aa]rch*)
   1167             read_ascii 4 <<-EOF
   1168 				${c6}       /\\
   1169 				${c6}      /  \\
   1170 				${c6}     /\\   \\
   1171 				${c4}    /      \\
   1172 				${c4}   /   ,,   \\
   1173 				${c4}  /   |  |  -\\
   1174 				${c4} /_-''    ''-_\\
   1175 			EOF
   1176         ;;
   1177 
   1178         ([Aa]rco*)
   1179             read_ascii 4 <<-EOF
   1180 				${c4}      /\\
   1181 				${c4}     /  \\
   1182 				${c4}    / /\\ \\
   1183 				${c4}   / /  \\ \\
   1184 				${c4}  / /    \\ \\
   1185 				${c4} / / _____\\ \\
   1186 				${c4}/_/  \`----.\\_\\
   1187 			EOF
   1188         ;;
   1189 
   1190         ([Aa]rtix*)
   1191             read_ascii 6 <<-EOF
   1192 				${c4}      /\\
   1193 				${c4}     /  \\
   1194 				${c4}    /\`'.,\\
   1195 				${c4}   /     ',
   1196 				${c4}  /      ,\`\\
   1197 				${c4} /   ,.'\`.  \\
   1198 				${c4}/.,'\`     \`'.\\
   1199 			EOF
   1200         ;;
   1201 
   1202         ([Bb]edrock*)
   1203             read_ascii 4 <<-EOF
   1204 				${c7}__
   1205 				${c7}\\ \\___
   1206 				${c7} \\  _ \\
   1207 				${c7}  \\___/
   1208 			EOF
   1209         ;;
   1210 
   1211         ([Bb]uildroot*)
   1212             read_ascii 3 <<-EOF
   1213 				${c3}   ___
   1214 				${c3} / \`   \\
   1215 				${c3}|   :  :|
   1216 				${c3}-. _:__.-
   1217 				${c3}  \` ---- \`
   1218 			EOF
   1219         ;;
   1220 
   1221         ([Cc]el[Oo][Ss]*)
   1222             read_ascii 5 0 <<-EOF
   1223 				${c5}      .////\\\\\//\\.
   1224 				${c5}     //_         \\\\
   1225 				${c5}    /_  ${c7}##############
   1226 				${c5}   //              *\\
   1227 				${c7}###############    ${c5}|#
   1228 				${c5}   \/              */
   1229 				${c5}    \*   ${c7}##############
   1230 				${c5}     */,        .//
   1231 				${c5}      '_///\\\\\//_'
   1232 			EOF
   1233         ;;
   1234 
   1235         ([Cc]ent[Oo][Ss]*)
   1236             read_ascii 5 <<-EOF
   1237 				${c2} ____${c3}^${c5}____
   1238 				${c2} |\\  ${c3}|${c5}  /|
   1239 				${c2} | \\ ${c3}|${c5} / |
   1240 				${c5}<---- ${c4}---->
   1241 				${c4} | / ${c2}|${c3} \\ |
   1242 				${c4} |/__${c2}|${c3}__\\|
   1243 				${c2}     v
   1244 			EOF
   1245         ;;
   1246 
   1247         ([Cc]rystal*[Ll]inux)
   1248             read_ascii 5 5 <<-EOF
   1249 				${c5}        -//.     
   1250 				${c5}      -//.       
   1251 				${c5}    -//. .       
   1252 				${c5}  -//.  '//-     
   1253 				${c5} /+:      :+/    
   1254 				${c5}  .//'  .//.     
   1255 				${c5}    . .//.       
   1256 				${c5}    .//.         
   1257 				${c5}  .//.           
   1258 			EOF
   1259         ;;
   1260 
   1261         ([Dd]ahlia*)
   1262             read_ascii 1 <<-EOF
   1263 				${c1}      _
   1264 				${c1}  ___/ \\___
   1265 				${c1} |   _-_   |
   1266 				${c1} | /     \ |
   1267 				${c1}/ |       | \\
   1268 				${c1}\\ |       | /
   1269 				${c1} | \ _ _ / |
   1270 				${c1} |___ - ___|
   1271 				${c1}     \\_/
   1272 			EOF
   1273         ;;
   1274 
   1275         ([Dd]ebian*)
   1276             read_ascii 1 <<-EOF
   1277 				${c1}  _____
   1278 				${c1} /  __ \\
   1279 				${c1}|  /    |
   1280 				${c1}|  \\___-
   1281 				${c1}-_
   1282 				${c1}  --_
   1283 			EOF
   1284         ;;
   1285 
   1286 		([Dd]evuan*)
   1287 			read_ascii 6 <<-EOF
   1288 				${c4} ..:::.      
   1289 				${c4}    ..-==-   
   1290 				${c4}        .+#: 
   1291 				${c4}         =@@ 
   1292 				${c4}      :+%@#: 
   1293 				${c4}.:=+#@@%*:   
   1294 				${c4}#@@@#=:      
   1295 			EOF
   1296 		;;
   1297 
   1298         ([Dd]ragon[Ff]ly*)
   1299             read_ascii 1 <<-EOF
   1300 				    ,${c1}_${c7},
   1301 				 ('-_${c1}|${c7}_-')
   1302 				  >--${c1}|${c7}--<
   1303 				 (_-'${c1}|${c7}'-_)
   1304 				     ${c1}|
   1305 				     ${c1}|
   1306 				     ${c1}|
   1307 			EOF
   1308         ;;
   1309 
   1310         ([Ee]lementary*)
   1311             read_ascii <<-EOF
   1312 				${c7}  _______
   1313 				${c7} / ____  \\
   1314 				${c7}/  |  /  /\\
   1315 				${c7}|__\\ /  / |
   1316 				${c7}\\   /__/  /
   1317 				 ${c7}\\_______/
   1318 			EOF
   1319         ;;
   1320 
   1321         ([Ee]ndeavour*)
   1322             read_ascii 4 <<-EOF
   1323 				      ${c1}/${c4}\\
   1324 				    ${c1}/${c4}/  \\${c6}\\
   1325 				   ${c1}/${c4}/    \\ ${c6}\\
   1326 				 ${c1}/ ${c4}/     _) ${c6})
   1327 				${c1}/_${c4}/___-- ${c6}__-
   1328 				 ${c6}/____--
   1329 			EOF
   1330         ;;
   1331 
   1332         ([Ff]edora*)
   1333             read_ascii 4 <<-EOF
   1334 				        ${c4},'''''.
   1335 				       ${c4}|   ,.  |
   1336 				       ${c4}|  |  '_'
   1337 				${c4}  ,....|  |..
   1338 				${c4}.'  ,_;|   ..'
   1339 				${c4}|  |   |  |
   1340 				${c4}|  ',_,'  |
   1341 				${c4} '.     ,'
   1342 				   ${c4}'''''
   1343 			EOF
   1344         ;;
   1345 
   1346         ([Ff]ree[Bb][Ss][Dd]*)
   1347             read_ascii 1 <<-EOF
   1348 				${c1}/\\,-'''''-,/\\
   1349 				${c1}\\_)       (_/
   1350 				${c1}|           |
   1351 				${c1}|           |
   1352 				 ${c1};         ;
   1353 				  ${c1}'-_____-'
   1354 			EOF
   1355         ;;
   1356 
   1357         ([Gg]aruda*)
   1358             read_ascii 4 <<-EOF
   1359 				${c3}         _______
   1360 				${c3}      __/       \\_
   1361 				${c3}    _/     /      \\_
   1362 				${c7}  _/      /_________\\
   1363 				${c7}_/                  |
   1364 				${c2}\\     ____________
   1365 				${c2} \\_            __/
   1366 				${c2}   \\__________/
   1367 			EOF
   1368         ;;
   1369 
   1370         ([Gg]entoo*)
   1371             read_ascii 5 <<-EOF
   1372 				${c5} _-----_
   1373 				${c5}(       \\
   1374 				${c5}\\    0   \\
   1375 				${c7} \\        )
   1376 				${c7} /      _/
   1377 				${c7}(     _-
   1378 				${c7}\\____-
   1379 			EOF
   1380         ;;
   1381 
   1382         ([Gg][Nn][Uu]*)
   1383             read_ascii 3 <<-EOF
   1384 				${c2}    _-\`\`-,   ,-\`\`-_
   1385 				${c2}  .'  _-_|   |_-_  '.
   1386 				${c2}./    /_._   _._\\    \\.
   1387 				${c2}:    _/_._\`:'_._\\_    :
   1388 				${c2}\\:._/  ,\`   \\   \\ \\_.:/
   1389 				${c2}   ,-';'.@)  \\ @) \\
   1390 				${c2}   ,'/'  ..- .\\,-.|
   1391 				${c2}   /'/' \\(( \\\` ./ )
   1392 				${c2}    '/''  \\_,----'
   1393 				${c2}      '/''   ,;/''
   1394 				${c2}         \`\`;'
   1395 			EOF
   1396         ;;
   1397 
   1398         ([Gg]uix[Ss][Dd]*|[Gg]uix*)
   1399             read_ascii 3 <<-EOF
   1400 				${c3}|.__          __.|
   1401 				${c3}|__ \\        / __|
   1402 				   ${c3}\\ \\      / /
   1403 				    ${c3}\\ \\    / /
   1404 				     ${c3}\\ \\  / /
   1405 				      ${c3}\\ \\/ /
   1406 				       ${c3}\\__/
   1407 			EOF
   1408         ;;
   1409 
   1410         ([Hh]aiku*)
   1411             read_ascii 3 <<-EOF
   1412 				${c3}       ,^,
   1413 				 ${c3}     /   \\
   1414 				${c3}*--_ ;     ; _--*
   1415 				${c3}\\   '"     "'   /
   1416 				 ${c3}'.           .'
   1417 				${c3}.-'"         "'-.
   1418 				 ${c3}'-.__.   .__.-'
   1419 				       ${c3}|_|
   1420 			EOF
   1421         ;;
   1422 
   1423         ([Hh]ydroOS*)
   1424 			read_ascii 4 <<-EOF
   1425 				${c1}╔╗╔╗──╔╗───╔═╦══╗
   1426 				${c1}║╚╝╠╦╦╝╠╦╦═╣║║══╣
   1427 				${c1}║╔╗║║║╬║╔╣╬║║╠══║
   1428 				${c1}╚╝╚╬╗╠═╩╝╚═╩═╩══╝
   1429 				${c1}───╚═╝
   1430 			EOF
   1431         ;;
   1432 
   1433         ([Hh]yperbola*)
   1434             read_ascii <<-EOF
   1435 				${c7}    |\`__.\`/
   1436 				   ${c7} \____/
   1437 				   ${c7} .--.
   1438 				  ${c7} /    \\
   1439 				 ${c7} /  ___ \\
   1440 				 ${c7}/ .\`   \`.\\
   1441 				${c7}/.\`      \`.\\
   1442 			EOF
   1443         ;;
   1444 
   1445         ([Ii]glunix*)
   1446             read_ascii <<-EOF
   1447 				${c0}       |
   1448 				${c0}       |          |
   1449 				${c0}                  |
   1450 				${c0}  |    ________
   1451 				${c0}  |  /\\   |    \\
   1452 				${c0}    /  \\  |     \\  |
   1453 				${c0}   /    \\        \\ |
   1454 				${c0}  /      \\________\\
   1455 				${c0}  \\      /        /
   1456 				${c0}   \\    /        /
   1457 				${c0}    \\  /        /
   1458 				${c0}     \\/________/
   1459 			EOF
   1460         ;;
   1461 
   1462         ([Ii]nstant[Oo][Ss]*)
   1463             read_ascii <<-EOF
   1464 				${c0} ,-''-,
   1465 				${c0}: .''. :
   1466 				${c0}: ',,' :
   1467 				${c0} '-____:__
   1468 				${c0}       :  \`.
   1469 				${c0}       \`._.'
   1470 			EOF
   1471         ;;
   1472 
   1473         ([Ii][Rr][Ii][Xx]*)
   1474             read_ascii 1 <<-EOF
   1475 				${c1} __
   1476 				${c1} \\ \\   __
   1477 				${c1}  \\ \\ / /
   1478 				${c1}   \\ v /
   1479 				${c1}   / . \\
   1480 				${c1}  /_/ \\ \\
   1481 				${c1}       \\_\\
   1482 			EOF
   1483         ;;
   1484 
   1485         ([Kk][Dd][Ee]*[Nn]eon*)
   1486             read_ascii 6 <<-EOF
   1487 				${c7}   .${c6}__${c7}.${c6}__${c7}.
   1488 				${c6}  /  _${c7}.${c6}_  \\
   1489 				${c6} /  /   \\  \\
   1490 				${c7} . ${c6}|  ${c7}O${c6}  | ${c7}.
   1491 				${c6} \\  \\_${c7}.${c6}_/  /
   1492 				${c6}  \\${c7}.${c6}__${c7}.${c6}__${c7}.${c6}/
   1493 			EOF
   1494         ;;
   1495 
   1496         ([Ll]inux*[Ll]ite*|[Ll]ite*)
   1497             read_ascii 3 <<-EOF
   1498 				${c3}   /\\
   1499 				${c3}  /  \\
   1500 				${c3} / ${c7}/ ${c3}/
   1501 			${c3}> ${c7}/ ${c3}/
   1502 				${c3}\\ ${c7}\\ ${c3}\\
   1503 				 ${c3}\\_${c7}\\${c3}_\\
   1504 				${c7}    \\
   1505 			EOF
   1506         ;;
   1507 
   1508         ([Ll]inux*[Mm]int*|[Mm]int)
   1509             read_ascii 2 <<-EOF
   1510 				${c2} ___________
   1511 				${c2}|_          \\
   1512 				  ${c2}| ${c7}| _____ ${c2}|
   1513 				  ${c2}| ${c7}| | | | ${c2}|
   1514 				  ${c2}| ${c7}| | | | ${c2}|
   1515 				  ${c2}| ${c7}\\__${c7}___/ ${c2}|
   1516 				  ${c2}\\_________/
   1517 			EOF
   1518         ;;
   1519 
   1520 
   1521         ([Ll]inux*)
   1522             read_ascii 4 <<-EOF
   1523 				${c4}    ___
   1524 				   ${c4}(${c7}.. ${c4}|
   1525 				   ${c4}(${c5}<> ${c4}|
   1526 				  ${c4}/ ${c7}__  ${c4}\\
   1527 				 ${c4}( ${c7}/  \\ ${c4}/|
   1528 				${c5}_${c4}/\\ ${c7}__)${c4}/${c5}_${c4})
   1529 				${c5}\/${c4}-____${c5}\/
   1530 			EOF
   1531         ;;
   1532 
   1533         ([Mm]ac[Oo][Ss]*|[Dd]arwin*)
   1534             read_ascii 1 <<-EOF
   1535 				${c2}       .:'
   1536 				${c2}    _ :'_
   1537 				${c3} .'\`_\`-'_\`\`.
   1538 				${c1}:________.-'
   1539 				${c1}:_______:
   1540 				${c4} :_______\`-;
   1541 				${c5}  \`._.-._.'
   1542 			EOF
   1543         ;;
   1544 
   1545         ([Mm]ageia*)
   1546             read_ascii 2 <<-EOF
   1547 				${c6}   *
   1548 				${c6}    *
   1549 				${c6}   **
   1550 				${c7} /\\__/\\
   1551 				${c7}/      \\
   1552 				${c7}\\      /
   1553 				${c7} \\____/
   1554 			EOF
   1555         ;;
   1556 
   1557         ([Mm]anjaro*)
   1558             read_ascii 2 <<-EOF
   1559 				${c2}||||||||| ||||
   1560 				${c2}||||||||| ||||
   1561 				${c2}||||      ||||
   1562 				${c2}|||| |||| ||||
   1563 				${c2}|||| |||| ||||
   1564 				${c2}|||| |||| ||||
   1565 				${c2}|||| |||| ||||
   1566 			EOF
   1567         ;;
   1568 
   1569         ([Mm]inix*)
   1570             read_ascii 4 <<-EOF
   1571 				${c4} ,,        ,,
   1572 				${c4};${c7},${c4} ',    ,' ${c7},${c4};
   1573 				${c4}; ${c7}',${c4} ',,' ${c7},'${c4} ;
   1574 				${c4};   ${c7}',${c4}  ${c7},'${c4}   ;
   1575 				${c4};  ${c7};, '' ,;${c4}  ;
   1576 				${c4};  ${c7};${c4};${c7}',,'${c4};${c7};${c4}  ;
   1577 				${c4}', ${c7};${c4};;  ;;${c7};${c4} ,'
   1578 				 ${c4} '${c7};${c4}'    '${c7};${c4}'
   1579 			EOF
   1580         ;;
   1581 
   1582         ([Mm][Xx]*)
   1583             read_ascii <<-EOF
   1584 				${c7}    \\\\  /
   1585 				 ${c7}    \\\\/
   1586 				 ${c7}     \\\\
   1587 				 ${c7}  /\\/ \\\\
   1588 				${c7}  /  \\  /\\
   1589 				${c7} /    \\/  \\
   1590 			${c7}/__________\\
   1591 			EOF
   1592         ;;
   1593 
   1594         ([Nn]et[Bb][Ss][Dd]*)
   1595             read_ascii 3 <<-EOF
   1596 				${c7}\\\\${c3}\`-______,----__
   1597 				${c7} \\\\        ${c3}__,---\`_
   1598 				${c7}  \\\\       ${c3}\`.____
   1599 				${c7}   \\\\${c3}-______,----\`-
   1600 				${c7}    \\\\
   1601 				${c7}     \\\\
   1602 				${c7}      \\\\
   1603 			EOF
   1604         ;;
   1605 
   1606         ([Nn]ix[Oo][Ss]*)
   1607             read_ascii 4 <<-EOF
   1608 				${c4}  \\\\  \\\\ //
   1609 				${c4} ==\\\\__\\\\/ //
   1610 				${c4}   //   \\\\//
   1611 				${c4}==//     //==
   1612 				${c4} //\\\\___//
   1613 				${c4}// /\\\\  \\\\==
   1614 				${c4}  // \\\\  \\\\
   1615 			EOF
   1616         ;;
   1617 
   1618         ([Oo]pen[Bb][Ss][Dd]*)
   1619             read_ascii 3 <<-EOF
   1620 				${c3}      _____
   1621 				${c3}    \\-     -/
   1622 				${c3} \\_/         \\
   1623 				${c3} |        ${c7}O O${c3} |
   1624 				${c3} |_  <   )  3 )
   1625 				${c3} / \\         /
   1626 				 ${c3}   /-_____-\\
   1627 			EOF
   1628         ;;
   1629 
   1630         ([Oo]pen[Ss][Uu][Ss][Ee]*[Tt]umbleweed*)
   1631             read_ascii 2 <<-EOF
   1632 				${c2}  _____   ______
   1633 				${c2} / ____\\ / ____ \\
   1634 				${c2}/ /    \`/ /    \\ \\
   1635 				${c2}\\ \\____/ /,____/ /
   1636 				${c2} \\______/ \\_____/
   1637 			EOF
   1638         ;;
   1639 
   1640         ([Oo]pen[Ss][Uu][Ss][Ee]*|[Oo]pen*SUSE*|SUSE*|suse*)
   1641             read_ascii 2 <<-EOF
   1642 				${c2}  _______
   1643 				${c2}__|   __ \\
   1644 				${c2}     / .\\ \\
   1645 				${c2}     \\__/ |
   1646 				${c2}   _______|
   1647 				${c2}   \\_______
   1648 				${c2}__________/
   1649 			EOF
   1650         ;;
   1651 
   1652         ([Oo]pen[Ww]rt*)
   1653             read_ascii 1 <<-EOF
   1654 				${c1} _______
   1655 				${c1}|       |.-----.-----.-----.
   1656 				${c1}|   -   ||  _  |  -__|     |
   1657 				${c1}|_______||   __|_____|__|__|
   1658 				${c1} ________|__|    __
   1659 				${c1}|  |  |  |.----.|  |_
   1660 				${c1}|  |  |  ||   _||   _|
   1661 				${c1}|________||__|  |____|
   1662 			EOF
   1663         ;;
   1664 
   1665         ([Pp]arabola*)
   1666             read_ascii 5 <<-EOF
   1667 				${c5}  __ __ __  _
   1668 				${c5}.\`_//_//_/ / \`.
   1669 				${c5}          /  .\`
   1670 				${c5}         / .\`
   1671 				${c5}        /.\`
   1672 				${c5}       /\`
   1673 			EOF
   1674         ;;
   1675 
   1676         ([Pp]op!_[Oo][Ss]*)
   1677             read_ascii 6 <<-EOF
   1678 				${c6}______
   1679 				${c6}\\   _ \\        __
   1680 				 ${c6}\\ \\ \\ \\      / /
   1681 				  ${c6}\\ \\_\\ \\    / /
   1682 				   ${c6}\\  ___\\  /_/
   1683 				   ${c6} \\ \\    _
   1684 				  ${c6} __\\_\\__(_)_
   1685 				  ${c6}(___________)
   1686 			EOF
   1687         ;;
   1688 
   1689         ([Pp]ure[Oo][Ss]*)
   1690             read_ascii <<-EOF
   1691 				${c7} _____________
   1692 				${c7}|  _________  |
   1693 				${c7}| |         | |
   1694 				${c7}| |         | |
   1695 				${c7}| |_________| |
   1696 				${c7}|_____________|
   1697 			EOF
   1698         ;;
   1699 
   1700         ([Rr]aspbian*)
   1701             read_ascii 1 <<-EOF
   1702 				${c2}  __  __
   1703 				${c2} (_\\)(/_)
   1704 				${c1} (_(__)_)
   1705 				${c1}(_(_)(_)_)
   1706 				${c1} (_(__)_)
   1707 				${c1}   (__)
   1708 			EOF
   1709         ;;
   1710 
   1711         ([Ss]erenity[Oo][Ss]*)
   1712             read_ascii 4 <<-EOF
   1713 				${c7}    _____
   1714 				${c1}  ,-${c7}     -,
   1715 				${c1} ;${c7} (       ;
   1716 				${c1}| ${c7}. \_${c1}.,${c7}    |
   1717 				${c1}|  ${c7}o  _${c1} ',${c7}  |
   1718 				${c1} ;   ${c7}(_)${c1} )${c7} ;
   1719 				${c1}  '-_____-${c7}'
   1720 			EOF
   1721         ;;
   1722 
   1723         ([Ss]lackware*)
   1724             read_ascii 4 <<-EOF
   1725 				${c4}   ________
   1726 				${c4}  /  ______|
   1727 				${c4}  | |______
   1728 				${c4}  \\______  \\
   1729 				${c4}   ______| |
   1730 				${c4}| |________/
   1731 				${c4}|____________
   1732 			EOF
   1733         ;;
   1734 
   1735         ([Ss]olus*)
   1736             read_ascii 4 <<-EOF
   1737 				${c6} 
   1738 				${c6}     /|
   1739 				${c6}    / |\\
   1740 				${c6}   /  | \\ _
   1741 				${c6}  /___|__\\_\\
   1742 				${c6} \\         /
   1743 				${c6}  \`-------´
   1744 			EOF
   1745         ;;
   1746 
   1747         ([Ss]un[Oo][Ss]|[Ss]olaris*)
   1748             read_ascii 3 <<-EOF
   1749 				${c3}       .   .;   .
   1750 				${c3}   .   :;  ::  ;:   .
   1751 				${c3}   .;. ..      .. .;.
   1752 				${c3}..  ..             ..  ..
   1753 				${c3} .;,                 ,;.
   1754 			EOF
   1755         ;;
   1756 
   1757         ([Uu]buntu*)
   1758             read_ascii 3 <<-EOF
   1759 				${c3}         _
   1760 				${c3}     ---(_)
   1761 				${c3} _/  ---  \\
   1762 				${c3}(_) |   |
   1763 				 ${c3} \\  --- _/
   1764 				    ${c3} ---(_)
   1765 			EOF
   1766         ;;
   1767 
   1768         ([Vv]oid*)
   1769             read_ascii 2 <<-EOF
   1770 				${c2}    _______
   1771 				${c2} _ \\______ -
   1772 				${c2}| \\  ___  \\ |
   1773 				${c2}| | /   \ | |
   1774 				${c2}| | \___/ | |
   1775 				${c2}| \\______ \\_|
   1776 				${c2} -_______\\
   1777 			EOF
   1778         ;;
   1779 
   1780         ([Xx]eonix*)
   1781             read_ascii 2 <<-EOF
   1782 				${c2}    ___  ___
   1783 				${c2}___ \  \/  / ___
   1784 				${c2}\  \ \    / /  /
   1785 				${c2} \  \/    \/  /
   1786 				${c2}  \    /\    /
   1787 				${c2}   \__/  \__/
   1788 			EOF
   1789         ;;
   1790 
   1791         (*)
   1792             # On no match of a distribution ascii art, this function calls
   1793             # itself again, this time to look for a more generic OS related
   1794             # ascii art (KISS Linux -> Linux).
   1795             [ "$1" ] || {
   1796                 get_ascii "$os"
   1797                 return
   1798             }
   1799 
   1800             printf 'error: %s is not currently supported.\n' "$os" >&6
   1801             printf 'error: Open an issue for support to be added.\n' >&6
   1802             exit 1
   1803         ;;
   1804     esac
   1805 
   1806     # Store the "width" (longest line) and "height" (number of lines)
   1807     # of the ascii art for positioning. This script prints to the screen
   1808     # *almost* like a TUI does. It uses escape sequences to allow dynamic
   1809     # printing of the information through user configuration.
   1810     #
   1811     # Iterate over each line of the ascii art to retrieve the above
   1812     # information. The 'sed' is used to strip '\033[3Xm' color codes from
   1813     # the ascii art so they don't affect the width variable.
   1814     while read -r line; do
   1815         ascii_height=$((${ascii_height:-0} + 1))
   1816 
   1817         # This was a ternary operation but they aren't supported in
   1818         # Minix's shell.
   1819         [ "${#line}" -gt "${ascii_width:-0}" ] &&
   1820             ascii_width=${#line}
   1821 
   1822     # Using '<<-EOF' is the only way to loop over a command's
   1823     # output without the use of a pipe ('|').
   1824     # This ensures that any variables defined in the while loop
   1825     # are still accessible in the script.
   1826     done <<-EOF
   1827  		$(printf %s "$ascii" | sed 's/\[3.m//g')
   1828 	EOF
   1829 
   1830     # Add a gap between the ascii art and the information.
   1831     ascii_width=$((ascii_width + 4))
   1832 
   1833     # Print the ascii art and position the cursor back where we
   1834     # started prior to printing it.
   1835     {
   1836         esc_p SGR 1
   1837         printf '%s' "$ascii"
   1838         esc_p SGR 0
   1839         esc_p CUU "$ascii_height"
   1840     } >&6
   1841 }
   1842 
   1843 main() {
   1844     case $* in
   1845         -v)
   1846             printf '%s 0.7.0\n' "${0##*/}"
   1847             return 0
   1848         ;;
   1849 
   1850         -d)
   1851             # Below exec is not run, stderr is shown.
   1852         ;;
   1853 
   1854         '')
   1855             exec 2>/dev/null
   1856         ;;
   1857 
   1858         *)
   1859             cat <<EOF
   1860 ${0##*/}     show system information
   1861 ${0##*/} -d  show stderr (debug mode)
   1862 ${0##*/} -v  show version information
   1863 EOF
   1864             return 0
   1865         ;;
   1866     esac
   1867 
   1868     # Hide 'stdout' and selectively print to it using '>&6'.
   1869     # This gives full control over what it displayed on the screen.
   1870     exec 6>&1 >/dev/null
   1871 
   1872     # Store raw escape sequence character for later reuse.
   1873     esc_c=$(printf '\033')
   1874 
   1875     # Allow the user to execute their own script and modify or
   1876     # extend pfetch's behavior.
   1877     # shellcheck source=/dev/null
   1878     ! [ -f "$PF_SOURCE" ] || . "$PF_SOURCE"
   1879 
   1880     # Ensure that the 'TMPDIR' is writable as heredocs use it and
   1881     # fail without the write permission. This was found to be the
   1882     # case on Android where the temporary directory requires root.
   1883     [ -w "${TMPDIR:-/tmp}" ] || export TMPDIR=~
   1884 
   1885     # Generic color list.
   1886     # Disable warning about unused variables.
   1887     # shellcheck disable=2034
   1888     for _c in c1 c2 c3 c4 c5 c6 c7 c8; do
   1889         esc SGR "3${_c#?}" 0
   1890         export "$_c=$e"
   1891     done
   1892 
   1893     # Disable line wrapping and catch the EXIT signal to enable it again
   1894     # on exit. Ideally you'd somehow query the current value and retain
   1895     # it but I'm yet to see this irk anyone.
   1896     esc_p DECAWM l >&6
   1897     trap 'esc_p DECAWM h >&6' EXIT
   1898 
   1899     # Store the output of 'uname' to avoid calling it multiple times
   1900     # throughout the script. 'read <<EOF' is the simplest way of reading
   1901     # a command into a list of variables.
   1902     read -r os kernel arch <<-EOF
   1903 		$(uname -srm)
   1904 	EOF
   1905 
   1906     # Always run 'get_os' for the purposes of detecting which ascii
   1907     # art to display.
   1908     get_os
   1909 
   1910     # Allow the user to specify the order and inclusion of information
   1911     # functions through the 'PF_INFO' environment variable.
   1912     # shellcheck disable=2086
   1913     {
   1914         # Disable globbing and set the positional parameters to the
   1915         # contents of 'PF_INFO'.
   1916         set -f
   1917         set +f -- ${PF_INFO-ascii title os host kernel uptime pkgs memory}
   1918 
   1919         # Iterate over the info functions to determine the lengths of the
   1920         # "info names" for output alignment. The option names and subtitles
   1921         # match 1:1 so this is thankfully simple.
   1922         for info do
   1923             command -v "get_$info" >/dev/null || continue
   1924 
   1925             # This was a ternary operation but they aren't supported in
   1926             # Minix's shell.
   1927             [ "${#info}" -gt "${info_length:-0}" ] &&
   1928                 info_length=${#info}
   1929         done
   1930 
   1931         # Add an additional space of length to act as a gap.
   1932         info_length=$((info_length + 1))
   1933 
   1934         # Iterate over the above list and run any existing "get_" functions.
   1935         for info do
   1936             "get_$info"
   1937         done
   1938     }
   1939 
   1940     # Position the cursor below both the ascii art and information lines
   1941     # according to the height of both. If the information exceeds the ascii
   1942     # art in height, don't touch the cursor (0/unset), else move it down
   1943     # N lines.
   1944     #
   1945     # This was a ternary operation but they aren't supported in Minix's shell.
   1946     [ "${info_height:-0}" -lt "${ascii_height:-0}" ] &&
   1947         cursor_pos=$((ascii_height - info_height))
   1948 
   1949     # Print '$cursor_pos' amount of newlines to correctly position the
   1950     # cursor. This used to be a 'printf $(seq X X)' however 'seq' is only
   1951     # typically available (by default) on GNU based systems!
   1952     while [ "${i:=0}" -le "${cursor_pos:-0}" ]; do
   1953         printf '\n'
   1954         i=$((i + 1))
   1955     done >&6
   1956 }
   1957 
   1958 main "$@"