[download]

local/bin/pfetch

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