[download]

local/bin/pfetch

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