summaryrefslogtreecommitdiff
path: root/home/dots/.local/bin/pfetch
diff options
context:
space:
mode:
authorMarc Coquand <marc@mccd.space>2024-10-22 11:07:59 +0300
committerMarc Coquand <marc@mccd.space>2024-10-22 11:07:59 +0300
commite24f418e3499afd1e9e529b8656a35376e69f52a (patch)
tree90e01f42d46dec91e3a6cace4de6d089152fad29 /home/dots/.local/bin/pfetch
downloadguix-e24f418e3499afd1e9e529b8656a35376e69f52a.tar.gz
guix-e24f418e3499afd1e9e529b8656a35376e69f52a.tar.bz2
guix-e24f418e3499afd1e9e529b8656a35376e69f52a.zip
initial commit
Diffstat (limited to '')
-rwxr-xr-xhome/dots/.local/bin/pfetch1956
1 files changed, 1956 insertions, 0 deletions
diff --git a/home/dots/.local/bin/pfetch b/home/dots/.local/bin/pfetch
new file mode 100755
index 0000000..bfc58b6
--- /dev/null
+++ b/home/dots/.local/bin/pfetch
@@ -0,0 +1,1956 @@
+#!/bin/sh
+#
+# pfetch - Simple POSIX sh fetch script.
+
+# Wrapper around all escape sequences used by pfetch to allow for
+# greater control over which sequences are used (if any at all).
+esc() {
+ case $1 in
+ CUU) e="${esc_c}[${2}A" ;; # cursor up
+ CUD) e="${esc_c}[${2}B" ;; # cursor down
+ CUF) e="${esc_c}[${2}C" ;; # cursor right
+ CUB) e="${esc_c}[${2}D" ;; # cursor left
+
+ # text formatting
+ SGR)
+ case ${PF_COLOR:=1} in
+ (1)
+ e="${esc_c}[${2}m"
+ ;;
+
+ (0)
+ # colors disabled
+ e=
+ ;;
+ esac
+ ;;
+
+ # line wrap
+ DECAWM)
+ case $TERM in
+ (dumb | minix | cons25)
+ # not supported
+ e=
+ ;;
+
+ (*)
+ e="${esc_c}[?7${2}"
+ ;;
+ esac
+ ;;
+ esac
+}
+
+# Print a sequence to the terminal.
+esc_p() {
+ esc "$@"
+ printf '%s' "$e"
+}
+
+# This is just a simple wrapper around 'command -v' to avoid
+# spamming '>/dev/null' throughout this function. This also guards
+# against aliases and functions.
+has() {
+ _cmd=$(command -v "$1") 2>/dev/null || return 1
+ [ -x "$_cmd" ] || return 1
+}
+
+log() {
+ # The 'log()' function handles the printing of information.
+ # In 'pfetch' (and 'neofetch'!) the printing of the ascii art and info
+ # happen independently of each other.
+ #
+ # The size of the ascii art is stored and the ascii is printed first.
+ # Once the ascii is printed, the cursor is located right below the art
+ # (See marker $[1]).
+ #
+ # Using the stored ascii size, the cursor is then moved to marker $[2].
+ # This is simply a cursor up escape sequence using the "height" of the
+ # ascii art.
+ #
+ # 'log()' then moves the cursor to the right the "width" of the ascii art
+ # with an additional amount of padding to add a gap between the art and
+ # the information (See marker $[3]).
+ #
+ # When 'log()' has executed, the cursor is then located at marker $[4].
+ # When 'log()' is run a second time, the next line of information is
+ # printed, moving the cursor to marker $[5].
+ #
+ # Markers $[4] and $[5] repeat all the way down through the ascii art
+ # until there is no more information left to print.
+ #
+ # Every time 'log()' is called the script keeps track of how many lines
+ # were printed. When printing is complete the cursor is then manually
+ # placed below the information and the art according to the "heights"
+ # of both.
+ #
+ # The math is simple: move cursor down $((ascii_height - info_height)).
+ # If the aim is to move the cursor from marker $[5] to marker $[6],
+ # plus the ascii height is 8 while the info height is 2 it'd be a move
+ # of 6 lines downwards.
+ #
+ # However, if the information printed is "taller" (takes up more lines)
+ # than the ascii art, the cursor isn't moved at all!
+ #
+ # Once the cursor is at marker $[6], the script exits. This is the gist
+ # of how this "dynamic" printing and layout works.
+ #
+ # This method allows ascii art to be stored without markers for info
+ # and it allows for easy swapping of info order and amount.
+ #
+ # $[2] ___ $[3] goldie@KISS
+ # $[4](.· | $[5] os KISS Linux
+ # (<> |
+ # / __ \
+ # ( / \ /|
+ # _/\ __)/_)
+ # \/-____\/
+ # $[1]
+ #
+ # $[6] /home/goldie $
+
+ # End here if no data was found.
+ [ "$2" ] || return
+
+ # Store the values of '$1' and '$3' as we reset the argument list below.
+ name=$1
+ use_seperator=$3
+
+ # Use 'set --' as a means of stripping all leading and trailing
+ # white-space from the info string. This also normalizes all
+ # white-space inside of the string.
+ #
+ # Disable the shellcheck warning for word-splitting
+ # as it's safe and intended ('set -f' disables globbing).
+ # shellcheck disable=2046,2086
+ {
+ set -f
+ set +f -- $2
+ info=$*
+ }
+
+ # Move the cursor to the right, the width of the ascii art with an
+ # additional gap for text spacing.
+ esc_p CUF "$ascii_width"
+
+ # Print the info name and color the text.
+ esc_p SGR "3${PF_COL1-4}";
+ esc_p SGR 1
+ printf '%s' "$name"
+ esc_p SGR 0
+
+ # Print the info name and info data separator, if applicable.
+ [ "$use_seperator" ] || printf %s "$PF_SEP"
+
+ # Move the cursor backward the length of the *current* info name and
+ # then move it forwards the length of the *longest* info name. This
+ # aligns each info data line.
+ esc_p CUB "${#name}"
+ esc_p CUF "${PF_ALIGN:-$info_length}"
+
+ # Print the info data, color it and strip all leading whitespace
+ # from the string.
+ esc_p SGR "3${PF_COL2-9}"
+ printf '%s' "$info"
+ esc_p SGR 0
+ printf '\n'
+
+ # Keep track of the number of times 'log()' has been run.
+ info_height=$((${info_height:-0} + 1))
+}
+
+get_title() {
+ # Username is retrieved by first checking '$USER' with a fallback
+ # to the 'id -un' command.
+ user=${USER:-$(id -un)}
+
+ # Hostname is retrieved by first checking '$HOSTNAME' with a fallback
+ # to the 'hostname' command.
+ #
+ # Disable the warning about '$HOSTNAME' being undefined in POSIX sh as
+ # the intention for using it is allowing the user to overwrite the
+ # value on invocation.
+ # shellcheck disable=3028,2039
+ hostname=${HOSTNAME:-${hostname:-$(hostname)}}
+
+ # If the hostname is still not found, fallback to the contents of the
+ # /etc/hostname file.
+ [ "$hostname" ] || read -r hostname < /etc/hostname
+
+ # Add escape sequences for coloring to user and host name. As we embed
+ # them directly in the arguments passed to log(), we cannot use esc_p().
+ esc SGR 1
+ user=$e$user
+ esc SGR "3${PF_COL3:-1}"
+ user=$e$user
+ esc SGR 1
+ user=$user$e
+ esc SGR 1
+ hostname=$e$hostname
+ esc SGR "3${PF_COL3:-1}"
+ hostname=$e$hostname
+
+ log "${user}@${hostname}" " " " " >&6
+}
+
+get_os() {
+ # This function is called twice, once to detect the distribution name
+ # for the purposes of picking an ascii art early and secondly to display
+ # the distribution name in the info output (if enabled).
+ #
+ # On first run, this function displays _nothing_, only on the second
+ # invocation is 'log()' called.
+ [ "$distro" ] && {
+ log os "$distro" >&6
+ return
+ }
+
+ case $os in
+ (Linux*)
+ # Some Linux distributions (which are based on others)
+ # fail to identify as they **do not** change the upstream
+ # distribution's identification packages or files.
+ #
+ # It is senseless to add a special case in the code for
+ # each and every distribution (which _is_ technically no
+ # different from what it is based on) as they're either too
+ # lazy to modify upstream's identification files or they
+ # don't have the know-how (or means) to ship their own
+ # lsb-release package.
+ #
+ # This causes users to think there's a bug in system detection
+ # tools like neofetch or pfetch when they technically *do*
+ # function correctly.
+ #
+ # Exceptions are made for distributions which are independent,
+ # not based on another distribution or follow different
+ # standards.
+ #
+ # This applies only to distributions which follow the standard
+ # by shipping unmodified identification files and packages
+ # from their respective upstreams.
+ if has lsb_release; then
+ distro=$(lsb_release -sd)
+
+ # Android detection works by checking for the existence of
+ # the follow two directories. I don't think there's a simpler
+ # method than this.
+ elif [ -d /system/app ] && [ -d /system/priv-app ]; then
+ distro="Android $(getprop ro.build.version.release)"
+
+ elif [ -f /etc/os-release ]; then
+ # This used to be a simple '. /etc/os-release' but I believe
+ # this is insecure as we blindly executed whatever was in the
+ # file. This parser instead simply handles 'key=val', treating
+ # the file contents as plain-text.
+ while IFS='=' read -r key val; do
+ case $key in
+ (NAME)
+ distro="$(echo $val | tr '[:upper:]' '[:lower:]')"
+ ;;
+ esac
+ done < /etc/os-release
+
+ else
+ # Special cases for (independent) distributions which
+ # don't follow any os-release/lsb standards whatsoever.
+ has crux && distro=$(crux)
+ has guix && distro='Guix System'
+ fi
+
+ # 'os-release' and 'lsb_release' sometimes add quotes
+ # around the distribution name, strip them.
+ distro=${distro##[\"\']}
+ distro=${distro%%[\"\']}
+
+ # Check to see if we're running Bedrock Linux which is
+ # very unique. This simply checks to see if the user's
+ # PATH contains a Bedrock specific value.
+ case $PATH in
+ (*/bedrock/cross/*)
+ distro='Bedrock Linux'
+ ;;
+ esac
+
+ # Check to see if Linux is running in Windows 10 under
+ # WSL1 (Windows subsystem for Linux [version 1]) and
+ # append a string accordingly.
+ #
+ # If the kernel version string ends in "-Microsoft",
+ # we're very likely running under Windows 10 in WSL1.
+ if [ "$WSLENV" ]; then
+ distro="${distro}${WSLENV+ on Windows 10 [WSL2]}"
+
+ # Check to see if Linux is running in Windows 10 under
+ # WSL2 (Windows subsystem for Linux [version 2]) and
+ # append a string accordingly.
+ #
+ # This checks to see if '$WSLENV' is defined. This
+ # appends the Windows 10 string even if '$WSLENV' is
+ # empty. We only need to check that is has been _exported_.
+ elif [ -z "${kernel%%*-Microsoft}" ]; then
+ distro="$distro on Windows 10 [WSL1]"
+ fi
+ ;;
+
+ (Darwin*)
+ # Parse the SystemVersion.plist file to grab the macOS
+ # version. The file is in the following format:
+ #
+ # <key>ProductVersion</key>
+ # <string>10.14.6</string>
+ #
+ # 'IFS' is set to '<>' to enable splitting between the
+ # keys and a second 'read' is used to operate on the
+ # next line directly after a match.
+ #
+ # '_' is used to nullify a field. '_ _ line _' basically
+ # says "populate $line with the third field's contents".
+ while IFS='<>' read -r _ _ line _; do
+ case $line in
+ # Match 'ProductVersion' and read the next line
+ # directly as it contains the key's value.
+ ProductVersion)
+ IFS='<>' read -r _ _ mac_version _
+ continue
+ ;;
+
+ ProductName)
+ IFS='<>' read -r _ _ mac_product _
+ continue
+ ;;
+ esac
+ done < /System/Library/CoreServices/SystemVersion.plist
+
+ # Use the ProductVersion to determine which macOS/OS X codename
+ # the system has. As far as I'm aware there's no "dynamic" way
+ # of grabbing this information.
+ case $mac_version in
+ (10.4*) distro='Mac OS X Tiger' ;;
+ (10.5*) distro='Mac OS X Leopard' ;;
+ (10.6*) distro='Mac OS X Snow Leopard' ;;
+ (10.7*) distro='Mac OS X Lion' ;;
+ (10.8*) distro='OS X Mountain Lion' ;;
+ (10.9*) distro='OS X Mavericks' ;;
+ (10.10*) distro='OS X Yosemite' ;;
+ (10.11*) distro='OS X El Capitan' ;;
+ (10.12*) distro='macOS Sierra' ;;
+ (10.13*) distro='macOS High Sierra' ;;
+ (10.14*) distro='macOS Mojave' ;;
+ (10.15*) distro='macOS Catalina' ;;
+ (11*) distro='macOS Big Sur' ;;
+ (12*) distro='macOS Monterey' ;;
+ (*) distro='macOS' ;;
+ esac
+
+ # Use the ProductName to determine if we're running in iOS.
+ case $mac_product in
+ (iP*) distro='iOS' ;;
+ esac
+
+ distro="$distro $mac_version"
+ ;;
+
+ (Haiku)
+ # Haiku uses 'uname -v' for version information
+ # instead of 'uname -r' which only prints '1'.
+ distro=$(uname -sv)
+ ;;
+
+ (Minix|DragonFly)
+ distro="$os $kernel"
+
+ # Minix and DragonFly don't support the escape
+ # sequences used, clear the exit trap.
+ trap '' EXIT
+ ;;
+
+ (SunOS)
+ # Grab the first line of the '/etc/release' file
+ # discarding everything after '('.
+ IFS='(' read -r distro _ < /etc/release
+ ;;
+
+ (OpenBSD*)
+ # Show the OpenBSD version type (current if present).
+ # kern.version=OpenBSD 6.6-current (GENERIC.MP) ...
+ IFS=' =' read -r _ distro openbsd_ver _ <<-EOF
+ $(sysctl kern.version)
+ EOF
+
+ distro="$distro $openbsd_ver"
+ ;;
+
+ (FreeBSD)
+ distro="$(echo $os | tr '[:upper:]' '[:lower:]')"
+ ;;
+
+ (*)
+ # Catch all to ensure '$distro' is never blank.
+ # This also handles the BSDs.
+ distro="$os $kernel"
+ ;;
+ esac
+}
+
+get_kernel() {
+ case $os in
+ # Don't print kernel output on some systems as the
+ # OS name includes it.
+ (*BSD*|Haiku|Minix)
+ return
+ ;;
+ esac
+
+ # '$kernel' is the cached output of 'uname -r'.
+ log kernel "$kernel" >&6
+}
+
+get_host() {
+ case $os in
+ (Linux*)
+ # Despite what these files are called, version doesn't
+ # always contain the version nor does name always contain
+ # the name.
+ read -r name < /sys/devices/virtual/dmi/id/product_name
+ read -r version < /sys/devices/virtual/dmi/id/product_version
+ read -r model < /sys/firmware/devicetree/base/model
+
+ host="$name $version $model"
+ ;;
+
+ (Darwin* | FreeBSD* | DragonFly*)
+ host=$(sysctl -n hw.model)
+ ;;
+
+ (NetBSD*)
+ host=$(sysctl -n machdep.dmi.system-vendor \
+ machdep.dmi.system-product)
+ ;;
+
+ (OpenBSD*)
+ host=$(sysctl -n hw.version)
+ ;;
+
+ (*BSD* | Minix)
+ host=$(sysctl -n hw.vendor hw.product)
+ ;;
+ esac
+
+ # Turn the host string into an argument list so we can iterate
+ # over it and remove OEM strings and other information which
+ # shouldn't be displayed.
+ #
+ # Disable the shellcheck warning for word-splitting
+ # as it's safe and intended ('set -f' disables globbing).
+ # shellcheck disable=2046,2086
+ {
+ set -f
+ set +f -- $host
+ host=
+ }
+
+ # Iterate over the host string word by word as a means of stripping
+ # unwanted and OEM information from the string as a whole.
+ #
+ # This could have been implemented using a long 'sed' command with
+ # a list of word replacements, however I want to show that something
+ # like this is possible in pure sh.
+ #
+ # This string reconstruction is needed as some OEMs either leave the
+ # identification information as "To be filled by OEM", "Default",
+ # "undefined" etc and we shouldn't print this to the screen.
+ for word do
+ # This works by reconstructing the string by excluding words
+ # found in the "blacklist" below. Only non-matches are appended
+ # to the final host string.
+ case $word in
+ (To | [Bb]e | [Ff]illed | [Bb]y | O.E.M. | OEM |\
+ Not | Applicable | Specified | System | Product | Name |\
+ Version | Undefined | Default | string | INVALID | � | os |\
+ Type1ProductConfigId )
+ continue
+ ;;
+ esac
+
+ host="$host$word "
+ done
+
+ # '$arch' is the cached output from 'uname -m'.
+ log host "${host:-$arch}" >&6
+}
+
+get_uptime() {
+ # Uptime works by retrieving the data in total seconds and then
+ # converting that data into days, hours and minutes using simple
+ # math.
+ case $os in
+ (Linux* | Minix* | SerenityOS*)
+ IFS=. read -r s _ < /proc/uptime
+ ;;
+
+ (Darwin* | *BSD* | DragonFly*)
+ s=$(sysctl -n kern.boottime)
+
+ # Extract the uptime in seconds from the following output:
+ # [...] { sec = 1271934886, usec = 667779 } Thu Apr 22 12:14:46 2010
+ s=${s#*=}
+ s=${s%,*}
+
+ # The uptime format from 'sysctl' needs to be subtracted from
+ # the current time in seconds.
+ s=$(($(date +%s) - s))
+ ;;
+
+ (Haiku)
+ # The boot time is returned in microseconds, convert it to
+ # regular seconds.
+ s=$(($(system_time) / 1000000))
+ ;;
+
+ (SunOS)
+ # Split the output of 'kstat' on '.' and any white-space
+ # which exists in the command output.
+ #
+ # The output is as follows:
+ # unix:0:system_misc:snaptime 14809.906993005
+ #
+ # The parser extracts: ^^^^^
+ IFS=' .' read -r _ s _ <<-EOF
+ $(kstat -p unix:0:system_misc:snaptime)
+ EOF
+ ;;
+
+ (IRIX)
+ # Grab the uptime in a pretty format. Usually,
+ # 00:00:00 from the 'ps' command.
+ t=$(LC_ALL=POSIX ps -o etime= -p 1)
+
+ # Split the pretty output into days or hours
+ # based on the uptime.
+ case $t in
+ (*-*) d=${t%%-*} t=${t#*-} ;;
+ (*:*:*) h=${t%%:*} t=${t#*:} ;;
+ esac
+
+ h=${h#0} t=${t#0}
+
+ # Convert the split pretty fields back into
+ # seconds so we may re-convert them to our format.
+ s=$((${d:-0}*86400 + ${h:-0}*3600 + ${t%%:*}*60 + ${t#*:}))
+ ;;
+ esac
+
+ # Convert the uptime from seconds into days, hours and minutes.
+ d=$((s / 60 / 60 / 24))
+ h=$((s / 60 / 60 % 24))
+ m=$((s / 60 % 60))
+
+ # Only append days, hours and minutes if they're non-zero.
+ case "$d" in ([!0]*) uptime="${uptime}${d}d "; esac
+ case "$h" in ([!0]*) uptime="${uptime}${h}h "; esac
+ case "$m" in ([!0]*) uptime="${uptime}${m}m "; esac
+
+ log uptime "${uptime:-0m}" >&6
+}
+
+get_pkgs() {
+ # This works by first checking for which package managers are
+ # installed and finally by printing each package manager's
+ # package list with each package one per line.
+ #
+ # The output from this is then piped to 'wc -l' to count each
+ # line, giving us the total package count of whatever package
+ # managers are installed.
+ packages=$(
+ case $os in
+ (Linux*)
+ # Commands which print packages one per line.
+ has bonsai && bonsai list
+ has crux && pkginfo -i
+ has pacman-key && pacman -Qq
+ has dpkg && dpkg-query -f '.\n' -W
+ has rpm && rpm -qa
+ has xbps-query && xbps-query -l
+ has apk && apk info
+ has guix && guix package --list-installed
+ has opkg && opkg list-installed
+
+ # Directories containing packages.
+ has kiss && printf '%s\n' /var/db/kiss/installed/*/
+ has cpt-list && printf '%s\n' /var/db/cpt/installed/*/
+ has brew && printf '%s\n' "$(brew --cellar)/"*
+ has emerge && printf '%s\n' /var/db/pkg/*/*/
+ has pkgtool && printf '%s\n' /var/log/packages/*
+ has eopkg && printf '%s\n' /var/lib/eopkg/package/*
+
+ # 'nix' requires two commands.
+ has nix-store && {
+ nix-store -q --requisites /run/current-system/sw
+ nix-store -q --requisites ~/.nix-profile
+ }
+ ;;
+
+ (Darwin*)
+ # Commands which print packages one per line.
+ has pkgin && pkgin list
+ has dpkg && dpkg-query -f '.\n' -W
+
+ # Directories containing packages.
+ has brew && printf '%s\n' /usr/local/Cellar/*
+
+ # 'port' prints a single line of output to 'stdout'
+ # when no packages are installed and exits with
+ # success causing a false-positive of 1 package
+ # installed.
+ #
+ # 'port' should really exit with a non-zero code
+ # in this case to allow scripts to cleanly handle
+ # this behavior.
+ has port && {
+ pkg_list=$(port installed)
+
+ case "$pkg_list" in
+ ("No ports are installed.")
+ # do nothing
+ ;;
+
+ (*)
+ printf '%s\n' "$pkg_list"
+ ;;
+ esac
+ }
+ ;;
+
+ (FreeBSD*|DragonFly*)
+ pkg info
+ ;;
+
+ (OpenBSD*)
+ printf '%s\n' /var/db/pkg/*/
+ ;;
+
+ (NetBSD*)
+ pkg_info
+ ;;
+
+ (Haiku)
+ printf '%s\n' /boot/system/package-links/*
+ ;;
+
+ (Minix)
+ printf '%s\n' /usr/pkg/var/db/pkg/*/
+ ;;
+
+ (SunOS)
+ has pkginfo && pkginfo -i
+ has pkg && pkg list
+ ;;
+
+ (IRIX)
+ versions -b
+ ;;
+
+ (SerenityOS)
+ while IFS=" " read -r type _; do
+ [ "$type" != dependency ] &&
+ printf "\n"
+ done < /usr/Ports/packages.db
+ ;;
+ esac | wc -l
+ )
+
+ # 'wc -l' can have leading and/or trailing whitespace
+ # depending on the implementation, so strip them.
+ # Procedure explained at https://github.com/dylanaraps/pure-sh-bible
+ # (trim-leading-and-trailing-white-space-from-string)
+ packages=${packages#"${packages%%[![:space:]]*}"}
+ packages=${packages%"${packages##*[![:space:]]}"}
+
+ case $os in
+ # IRIX's package manager adds 3 lines of extra
+ # output which we must account for here.
+ (IRIX)
+ packages=$((packages - 3))
+ ;;
+
+ # OpenBSD's wc prints whitespace before the output
+ # which needs to be stripped.
+ (OpenBSD)
+ packages=$((packages))
+ ;;
+ esac
+
+ case $packages in
+ (1?*|[2-9]*)
+ log pkgs "$packages" >&6
+ ;;
+ esac
+}
+
+get_memory() {
+ case $os in
+ # Used memory is calculated using the following "formula":
+ # MemUsed = MemTotal + Shmem - MemFree - Buffers - Cached - SReclaimable
+ # Source: https://github.com/KittyKatt/screenFetch/issues/386
+ (Linux*)
+ # Parse the '/proc/meminfo' file splitting on ':' and 'k'.
+ # The format of the file is 'key: 000kB' and an additional
+ # split is used on 'k' to filter out 'kB'.
+ while IFS=':k ' read -r key val _; do
+ case $key in
+ (MemTotal)
+ mem_used=$((mem_used + val))
+ mem_full=$val
+ ;;
+
+ (Shmem)
+ mem_used=$((mem_used + val))
+ ;;
+
+ (MemFree | Buffers | Cached | SReclaimable)
+ mem_used=$((mem_used - val))
+ ;;
+
+ # If detected this will be used over the above calculation
+ # for mem_used. Available since Linux 3.14rc.
+ # See kernel commit 34e431b0ae398fc54ea69ff85ec700722c9da773
+ (MemAvailable)
+ mem_avail=$val
+ ;;
+ esac
+ done < /proc/meminfo
+
+ case $mem_avail in
+ (*[0-9]*)
+ mem_used=$(((mem_full - mem_avail) / 1024))
+ ;;
+
+ *)
+ mem_used=$((mem_used / 1024))
+ ;;
+ esac
+
+ mem_full=$((mem_full / 1024))
+ ;;
+
+ # Used memory is calculated using the following "formula":
+ # (wired + active + occupied) * 4 / 1024
+ (Darwin*)
+ mem_full=$(($(sysctl -n hw.memsize) / 1024 / 1024))
+
+ # Parse the 'vmstat' file splitting on ':' and '.'.
+ # The format of the file is 'key: 000.' and an additional
+ # split is used on '.' to filter it out.
+ while IFS=:. read -r key val; do
+ case $key in
+ (*' wired'*|*' active'*|*' occupied'*)
+ mem_used=$((mem_used + ${val:-0}))
+ ;;
+ esac
+
+ # Using '<<-EOF' is the only way to loop over a command's
+ # output without the use of a pipe ('|').
+ # This ensures that any variables defined in the while loop
+ # are still accessible in the script.
+ done <<-EOF
+ $(vm_stat)
+ EOF
+
+ mem_used=$((mem_used * 4 / 1024))
+ ;;
+
+ (OpenBSD*)
+ mem_full=$(($(sysctl -n hw.physmem) / 1024 / 1024))
+
+ # This is a really simpler parser for 'vmstat' which grabs
+ # the used memory amount in a lazy way. 'vmstat' prints 3
+ # lines of output with the needed value being stored in the
+ # final line.
+ #
+ # This loop simply grabs the 3rd element of each line until
+ # the EOF is reached. Each line overwrites the value of the
+ # previous one so we're left with what we wanted. This isn't
+ # slow as only 3 lines are parsed.
+ while read -r _ _ line _; do
+ mem_used=${line%%M}
+
+ # Using '<<-EOF' is the only way to loop over a command's
+ # output without the use of a pipe ('|').
+ # This ensures that any variables defined in the while loop
+ # are still accessible in the script.
+ done <<-EOF
+ $(vmstat)
+ EOF
+ ;;
+
+ # Used memory is calculated using the following "formula":
+ # mem_full - ((inactive + free + cache) * page_size / 1024)
+ (FreeBSD*|DragonFly*)
+ mem_full=$(($(sysctl -n hw.physmem) / 1024 / 1024))
+
+ # Use 'set --' to store the output of the command in the
+ # argument list. POSIX sh has no arrays but this is close enough.
+ #
+ # Disable the shellcheck warning for word-splitting
+ # as it's safe and intended ('set -f' disables globbing).
+ # shellcheck disable=2046
+ {
+ set -f
+ set +f -- $(sysctl -n hw.pagesize \
+ vm.stats.vm.v_inactive_count \
+ vm.stats.vm.v_free_count \
+ vm.stats.vm.v_cache_count)
+ }
+
+ # Calculate the amount of used memory.
+ # $1: hw.pagesize
+ # $2: vm.stats.vm.v_inactive_count
+ # $3: vm.stats.vm.v_free_count
+ # $4: vm.stats.vm.v_cache_count
+ mem_used=$((mem_full - (($2 + $3 + $4) * $1 / 1024 / 1024)))
+ ;;
+
+ (NetBSD*)
+ mem_full=$(($(sysctl -n hw.physmem64) / 1024 / 1024))
+
+ # NetBSD implements a lot of the Linux '/proc' filesystem,
+ # this uses the same parser as the Linux memory detection.
+ while IFS=':k ' read -r key val _; do
+ case $key in
+ (MemFree)
+ mem_free=$((val / 1024))
+ break
+ ;;
+ esac
+ done < /proc/meminfo
+
+ mem_used=$((mem_full - mem_free))
+ ;;
+
+ (Haiku)
+ # Read the first line of 'sysinfo -mem' splitting on
+ # '(', ' ', and ')'. The needed information is then
+ # stored in the 5th and 7th elements. Using '_' "consumes"
+ # an element allowing us to proceed to the next one.
+ #
+ # The parsed format is as follows:
+ # 3501142016 bytes free (used/max 792645632 / 4293787648)
+ IFS='( )' read -r _ _ _ _ mem_used _ mem_full <<-EOF
+ $(sysinfo -mem)
+ EOF
+
+ mem_used=$((mem_used / 1024 / 1024))
+ mem_full=$((mem_full / 1024 / 1024))
+ ;;
+
+ (Minix)
+ # Minix includes the '/proc' filesystem though the format
+ # differs from Linux. The '/proc/meminfo' file is only a
+ # single line with space separated elements and elements
+ # 2 and 3 contain the total and free memory numbers.
+ read -r _ mem_full mem_free _ < /proc/meminfo
+
+ mem_used=$(((mem_full - mem_free) / 1024))
+ mem_full=$(( mem_full / 1024))
+ ;;
+
+ (SunOS)
+ hw_pagesize=$(pagesize)
+
+ # 'kstat' outputs memory in the following format:
+ # unix:0:system_pages:pagestotal 1046397
+ # unix:0:system_pages:pagesfree 885018
+ #
+ # This simply uses the first "element" (white-space
+ # separated) as the key and the second element as the
+ # value.
+ #
+ # A variable is then assigned based on the key.
+ while read -r key val; do
+ case $key in
+ (*total)
+ pages_full=$val
+ ;;
+
+ (*free)
+ pages_free=$val
+ ;;
+ esac
+ done <<-EOF
+ $(kstat -p unix:0:system_pages:pagestotal \
+ unix:0:system_pages:pagesfree)
+ EOF
+
+ mem_full=$((pages_full * hw_pagesize / 1024 / 1024))
+ mem_free=$((pages_free * hw_pagesize / 1024 / 1024))
+ mem_used=$((mem_full - mem_free))
+ ;;
+
+ (IRIX)
+ # Read the memory information from the 'top' command. Parse
+ # and split each line until we reach the line starting with
+ # "Memory".
+ #
+ # Example output: Memory: 160M max, 147M avail, .....
+ while IFS=' :' read -r label mem_full _ mem_free _; do
+ case $label in
+ (Memory)
+ mem_full=${mem_full%M}
+ mem_free=${mem_free%M}
+ break
+ ;;
+ esac
+ done <<-EOF
+ $(top -n)
+ EOF
+
+ mem_used=$((mem_full - mem_free))
+ ;;
+
+ (SerenityOS)
+ IFS='{}' read -r _ memstat _ < /proc/memstat
+
+ set -f -- "$IFS"
+ IFS=,
+
+ for pair in $memstat; do
+ case $pair in
+ (*user_physical_allocated*)
+ mem_used=${pair##*:}
+ ;;
+
+ (*user_physical_available*)
+ mem_free=${pair##*:}
+ ;;
+ esac
+ done
+
+ IFS=$1
+ set +f --
+
+ mem_used=$((mem_used * 4096 / 1024 / 1024))
+ mem_free=$((mem_free * 4096 / 1024 / 1024))
+
+ mem_full=$((mem_used + mem_free))
+ ;;
+ esac
+
+ log memory "${mem_used:-?}M / ${mem_full:-?}M" >&6
+}
+
+get_wm() {
+ case $os in
+ (Darwin*)
+ # Don't display window manager on macOS.
+ ;;
+
+ (*)
+ # xprop can be used to grab the window manager's properties
+ # which contains the window manager's name under '_NET_WM_NAME'.
+ #
+ # The upside to using 'xprop' is that you don't need to hardcode
+ # a list of known window manager names. The downside is that
+ # not all window managers conform to setting the '_NET_WM_NAME'
+ # atom..
+ #
+ # List of window managers which fail to set the name atom:
+ # catwm, fvwm, dwm, 2bwm, monster, wmaker and sowm [mine! ;)].
+ #
+ # The final downside to this approach is that it does _not_
+ # support Wayland environments. The only solution which supports
+ # Wayland is the 'ps' parsing mentioned below.
+ #
+ # A more naive implementation is to parse the last line of
+ # '~/.xinitrc' to extract the second white-space separated
+ # element.
+ #
+ # The issue with an approach like this is that this line data
+ # does not always equate to the name of the window manager and
+ # could in theory be _anything_.
+ #
+ # This also fails when the user launches xorg through a display
+ # manager or other means.
+ #
+ #
+ # Another naive solution is to parse 'ps' with a hardcoded list
+ # of window managers to detect the current window manager (based
+ # on what is running).
+ #
+ # The issue with this approach is the need to hardcode and
+ # maintain a list of known window managers.
+ #
+ # Another issue is that process names do not always equate to
+ # the name of the window manager. False-positives can happen too.
+ #
+ # This is the only solution which supports Wayland based
+ # environments sadly. It'd be nice if some kind of standard were
+ # established to identify Wayland environments.
+ #
+ # pfetch's goal is to remain _simple_, if you'd like a "full"
+ # implementation of window manager detection use 'neofetch'.
+ #
+ # Neofetch use a combination of 'xprop' and 'ps' parsing to
+ # support all window managers (including non-conforming and
+ # Wayland) though it's a lot more complicated!
+
+ # Don't display window manager if X isn't running.
+ [ "$DISPLAY" ] || return
+
+ # This is a two pass call to xprop. One call to get the window
+ # manager's ID and another to print its properties.
+ has xprop && {
+ # The output of the ID command is as follows:
+ # _NET_SUPPORTING_WM_CHECK: window id # 0x400000
+ #
+ # To extract the ID, everything before the last space
+ # is removed.
+ id=$(xprop -root -notype _NET_SUPPORTING_WM_CHECK)
+ id=${id##* }
+
+ # The output of the property command is as follows:
+ # _NAME 8t
+ # _NET_WM_PID = 252
+ # _NET_WM_NAME = "bspwm"
+ # _NET_SUPPORTING_WM_CHECK: window id # 0x400000
+ # WM_CLASS = "wm", "Bspwm"
+ #
+ # To extract the name, everything before '_NET_WM_NAME = \"'
+ # is removed and everything after the next '"' is removed.
+ wm=$(xprop -id "$id" -notype -len 7 -f _NET_WM_NAME 8t)
+ }
+
+ # Handle cases of a window manager _not_ populating the
+ # '_NET_WM_NAME' atom. Display nothing in this case.
+ case $wm in
+ (*'_NET_WM_NAME = '*)
+ wm=${wm##*_NET_WM_NAME = \"}
+ wm=${wm%%\"*}
+ ;;
+
+ (*)
+ # Fallback to checking the process list
+ # for the select few window managers which
+ # don't set '_NET_WM_NAME'.
+ while read -r ps_line; do
+ case $ps_line in
+ (*catwm*) wm=catwm ;;
+ (*fvwm*) wm=fvwm ;;
+ (*dwm*) wm=dwm ;;
+ (*2bwm*) wm=2bwm ;;
+ (*monsterwm*) wm=monsterwm ;;
+ (*wmaker*) wm='Window Maker' ;;
+ (*sowm*) wm=sowm ;;
+ (*penrose*) wm=penrose ;;
+ esac
+ done <<-EOF
+ $(ps x)
+ EOF
+ ;;
+ esac
+ ;;
+ esac
+
+ log wm "cwm" >&6
+}
+
+
+get_de() {
+ # This only supports Xorg related desktop environments though
+ # this is fine as knowing the desktop environment on Windows,
+ # macOS etc is useless (they'll always report the same value).
+ #
+ # Display the value of '$XDG_CURRENT_DESKTOP', if it's empty,
+ # display the value of '$DESKTOP_SESSION'.
+ log de "${XDG_CURRENT_DESKTOP:-$DESKTOP_SESSION}" >&6
+}
+
+get_shell() {
+ # Display the basename of the '$SHELL' environment variable.
+ log sh "${SHELL##*/}" >&6
+}
+
+get_editor() {
+ # Display the value of '$VISUAL', if it's empty, display the
+ # value of '$EDITOR'.
+ editor=${VISUAL:-"$EDITOR"}
+
+ log ed "${editor##*/}" >&6
+}
+
+get_palette() {
+ # Print the first 8 terminal colors. This uses the existing
+ # sequences to change text color with a sequence prepended
+ # to reverse the foreground and background colors.
+ #
+ # This allows us to save hardcoding a second set of sequences
+ # for background colors.
+ #
+ # False positive.
+ # shellcheck disable=2154
+ {
+ esc SGR 7
+ palette="$e$c1 $c1 $c2 $c2 $c3 $c3 $c4 $c4 $c5 $c5 $c6 $c6 "
+ esc SGR 0
+ palette="$palette$e"
+ }
+
+ # Print the palette with a new-line before and afterwards but no seperator.
+ printf '\n' >&6
+ log "$palette
+ " " " " " >&6
+}
+
+get_ascii() {
+ # This is a simple function to read the contents of
+ # an ascii file from 'stdin'. It allows for the use
+ # of '<<-EOF' to prevent the break in indentation in
+ # this source code.
+ #
+ # This function also sets the text colors according
+ # to the ascii color.
+ read_ascii() {
+ # 'PF_COL1': Set the info name color according to ascii color.
+ # 'PF_COL3': Set the title color to some other color. ¯\_(ツ)_/¯
+ PF_COL1=${PF_COL1:-${1:-7}}
+ PF_COL3=${PF_COL3:-$((${1:-7}%8+1))}
+
+ # POSIX sh has no 'var+=' so 'var=${var}append' is used. What's
+ # interesting is that 'var+=' _is_ supported inside '$(())'
+ # (arithmetic) though there's no support for 'var++/var--'.
+ #
+ # There is also no $'\n' to add a "literal"(?) newline to the
+ # string. The simplest workaround being to break the line inside
+ # the string (though this has the caveat of breaking indentation).
+ while IFS= read -r line; do
+ ascii="$ascii$line
+"
+ done
+ }
+
+ # This checks for ascii art in the following order:
+ # '$1': Argument given to 'get_ascii()' directly.
+ # '$PF_ASCII': Environment variable set by user.
+ # '$distro': The detected distribution name.
+ # '$os': The name of the operating system/kernel.
+ #
+ # NOTE: Each ascii art below is indented using tabs, this
+ # allows indentation to continue naturally despite
+ # the use of '<<-EOF'.
+ #
+ # False positive.
+ # shellcheck disable=2154
+ case ${1:-${PF_ASCII:-${distro:-$os}}} in
+ ([Aa]lpine*)
+ read_ascii 4 <<-EOF
+ ${c4} /\\ /\\
+ /${c7}/ ${c4}\\ \\
+ /${c7}/ ${c4}\\ \\
+ /${c7}// ${c4}\\ \\
+ ${c7}// ${c4}\\ \\
+ ${c4}\\
+ EOF
+ ;;
+
+ ([Aa]ndroid*)
+ read_ascii 2 <<-EOF
+ ${c2} ;, ,;
+ ${c2} ';,.-----.,;'
+ ${c2} ,' ',
+ ${c2} / O O \\
+ ${c2}| |
+ ${c2}'-----------------'
+ EOF
+ ;;
+
+ ([Aa]rch*)
+ read_ascii 4 <<-EOF
+ ${c6} /\\
+ ${c6} / \\
+ ${c6} /\\ \\
+ ${c4} / \\
+ ${c4} / ,, \\
+ ${c4} / | | -\\
+ ${c4} /_-'' ''-_\\
+ EOF
+ ;;
+
+ ([Aa]rco*)
+ read_ascii 4 <<-EOF
+ ${c4} /\\
+ ${c4} / \\
+ ${c4} / /\\ \\
+ ${c4} / / \\ \\
+ ${c4} / / \\ \\
+ ${c4} / / _____\\ \\
+ ${c4}/_/ \`----.\\_\\
+ EOF
+ ;;
+
+ ([Aa]rtix*)
+ read_ascii 6 <<-EOF
+ ${c4} /\\
+ ${c4} / \\
+ ${c4} /\`'.,\\
+ ${c4} / ',
+ ${c4} / ,\`\\
+ ${c4} / ,.'\`. \\
+ ${c4}/.,'\` \`'.\\
+ EOF
+ ;;
+
+ ([Bb]edrock*)
+ read_ascii 4 <<-EOF
+ ${c7}__
+ ${c7}\\ \\___
+ ${c7} \\ _ \\
+ ${c7} \\___/
+ EOF
+ ;;
+
+ ([Bb]uildroot*)
+ read_ascii 3 <<-EOF
+ ${c3} ___
+ ${c3} / \` \\
+ ${c3}| : :|
+ ${c3}-. _:__.-
+ ${c3} \` ---- \`
+ EOF
+ ;;
+
+ ([Cc]el[Oo][Ss]*)
+ read_ascii 5 0 <<-EOF
+ ${c5} .////\\\\\//\\.
+ ${c5} //_ \\\\
+ ${c5} /_ ${c7}##############
+ ${c5} // *\\
+ ${c7}############### ${c5}|#
+ ${c5} \/ */
+ ${c5} \* ${c7}##############
+ ${c5} */, .//
+ ${c5} '_///\\\\\//_'
+ EOF
+ ;;
+
+ ([Cc]ent[Oo][Ss]*)
+ read_ascii 5 <<-EOF
+ ${c2} ____${c3}^${c5}____
+ ${c2} |\\ ${c3}|${c5} /|
+ ${c2} | \\ ${c3}|${c5} / |
+ ${c5}<---- ${c4}---->
+ ${c4} | / ${c2}|${c3} \\ |
+ ${c4} |/__${c2}|${c3}__\\|
+ ${c2} v
+ EOF
+ ;;
+
+ ([Cc]rystal*[Ll]inux)
+ read_ascii 5 5 <<-EOF
+ ${c5} -//.
+ ${c5} -//.
+ ${c5} -//. .
+ ${c5} -//. '//-
+ ${c5} /+: :+/
+ ${c5} .//' .//.
+ ${c5} . .//.
+ ${c5} .//.
+ ${c5} .//.
+ EOF
+ ;;
+
+ ([Dd]ahlia*)
+ read_ascii 1 <<-EOF
+ ${c1} _
+ ${c1} ___/ \\___
+ ${c1} | _-_ |
+ ${c1} | / \ |
+ ${c1}/ | | \\
+ ${c1}\\ | | /
+ ${c1} | \ _ _ / |
+ ${c1} |___ - ___|
+ ${c1} \\_/
+ EOF
+ ;;
+
+ ([Dd]ebian*)
+ read_ascii 1 <<-EOF
+ ${c1} _____
+ ${c1} / __ \\
+ ${c1}| / |
+ ${c1}| \\___-
+ ${c1}-_
+ ${c1} --_
+ EOF
+ ;;
+
+ ([Dd]evuan*)
+ read_ascii 6 <<-EOF
+ ${c4} ..:::.
+ ${c4} ..-==-
+ ${c4} .+#:
+ ${c4} =@@
+ ${c4} :+%@#:
+ ${c4}.:=+#@@%*:
+ ${c4}#@@@#=:
+ EOF
+ ;;
+
+ ([Dd]ragon[Ff]ly*)
+ read_ascii 1 <<-EOF
+ ,${c1}_${c7},
+ ('-_${c1}|${c7}_-')
+ >--${c1}|${c7}--<
+ (_-'${c1}|${c7}'-_)
+ ${c1}|
+ ${c1}|
+ ${c1}|
+ EOF
+ ;;
+
+ ([Ee]lementary*)
+ read_ascii <<-EOF
+ ${c7} _______
+ ${c7} / ____ \\
+ ${c7}/ | / /\\
+ ${c7}|__\\ / / |
+ ${c7}\\ /__/ /
+ ${c7}\\_______/
+ EOF
+ ;;
+
+ ([Ee]ndeavour*)
+ read_ascii 4 <<-EOF
+ ${c1}/${c4}\\
+ ${c1}/${c4}/ \\${c6}\\
+ ${c1}/${c4}/ \\ ${c6}\\
+ ${c1}/ ${c4}/ _) ${c6})
+ ${c1}/_${c4}/___-- ${c6}__-
+ ${c6}/____--
+ EOF
+ ;;
+
+ ([Ff]edora*)
+ read_ascii 4 <<-EOF
+ ${c4},'''''.
+ ${c4}| ,. |
+ ${c4}| | '_'
+ ${c4} ,....| |..
+ ${c4}.' ,_;| ..'
+ ${c4}| | | |
+ ${c4}| ',_,' |
+ ${c4} '. ,'
+ ${c4}'''''
+ EOF
+ ;;
+
+ ([Ff]ree[Bb][Ss][Dd]*)
+ read_ascii 1 <<-EOF
+ ${c1}/\\,-'''''-,/\\
+ ${c1}\\_) (_/
+ ${c1}| |
+ ${c1}| |
+ ${c1}; ;
+ ${c1}'-_____-'
+ EOF
+ ;;
+
+ ([Gg]aruda*)
+ read_ascii 4 <<-EOF
+ ${c3} _______
+ ${c3} __/ \\_
+ ${c3} _/ / \\_
+ ${c7} _/ /_________\\
+ ${c7}_/ |
+ ${c2}\\ ____________
+ ${c2} \\_ __/
+ ${c2} \\__________/
+ EOF
+ ;;
+
+ ([Gg]entoo*)
+ read_ascii 5 <<-EOF
+ ${c5} _-----_
+ ${c5}( \\
+ ${c5}\\ 0 \\
+ ${c7} \\ )
+ ${c7} / _/
+ ${c7}( _-
+ ${c7}\\____-
+ EOF
+ ;;
+
+ ([Gg][Nn][Uu]*)
+ read_ascii 3 <<-EOF
+ ${c2} _-\`\`-, ,-\`\`-_
+ ${c2} .' _-_| |_-_ '.
+ ${c2}./ /_._ _._\\ \\.
+ ${c2}: _/_._\`:'_._\\_ :
+ ${c2}\\:._/ ,\` \\ \\ \\_.:/
+ ${c2} ,-';'.@) \\ @) \\
+ ${c2} ,'/' ..- .\\,-.|
+ ${c2} /'/' \\(( \\\` ./ )
+ ${c2} '/'' \\_,----'
+ ${c2} '/'' ,;/''
+ ${c2} \`\`;'
+ EOF
+ ;;
+
+ ([Gg]uix[Ss][Dd]*|[Gg]uix*)
+ read_ascii 3 <<-EOF
+ ${c3}|.__ __.|
+ ${c3}|__ \\ / __|
+ ${c3}\\ \\ / /
+ ${c3}\\ \\ / /
+ ${c3}\\ \\ / /
+ ${c3}\\ \\/ /
+ ${c3}\\__/
+ EOF
+ ;;
+
+ ([Hh]aiku*)
+ read_ascii 3 <<-EOF
+ ${c3} ,^,
+ ${c3} / \\
+ ${c3}*--_ ; ; _--*
+ ${c3}\\ '" "' /
+ ${c3}'. .'
+ ${c3}.-'" "'-.
+ ${c3}'-.__. .__.-'
+ ${c3}|_|
+ EOF
+ ;;
+
+ ([Hh]ydroOS*)
+ read_ascii 4 <<-EOF
+ ${c1}╔╗╔╗──╔╗───╔═╦══╗
+ ${c1}║╚╝╠╦╦╝╠╦╦═╣║║══╣
+ ${c1}║╔╗║║║╬║╔╣╬║║╠══║
+ ${c1}╚╝╚╬╗╠═╩╝╚═╩═╩══╝
+ ${c1}───╚═╝
+ EOF
+ ;;
+
+ ([Hh]yperbola*)
+ read_ascii <<-EOF
+ ${c7} |\`__.\`/
+ ${c7} \____/
+ ${c7} .--.
+ ${c7} / \\
+ ${c7} / ___ \\
+ ${c7}/ .\` \`.\\
+ ${c7}/.\` \`.\\
+ EOF
+ ;;
+
+ ([Ii]glunix*)
+ read_ascii <<-EOF
+ ${c0} |
+ ${c0} | |
+ ${c0} |
+ ${c0} | ________
+ ${c0} | /\\ | \\
+ ${c0} / \\ | \\ |
+ ${c0} / \\ \\ |
+ ${c0} / \\________\\
+ ${c0} \\ / /
+ ${c0} \\ / /
+ ${c0} \\ / /
+ ${c0} \\/________/
+ EOF
+ ;;
+
+ ([Ii]nstant[Oo][Ss]*)
+ read_ascii <<-EOF
+ ${c0} ,-''-,
+ ${c0}: .''. :
+ ${c0}: ',,' :
+ ${c0} '-____:__
+ ${c0} : \`.
+ ${c0} \`._.'
+ EOF
+ ;;
+
+ ([Ii][Rr][Ii][Xx]*)
+ read_ascii 1 <<-EOF
+ ${c1} __
+ ${c1} \\ \\ __
+ ${c1} \\ \\ / /
+ ${c1} \\ v /
+ ${c1} / . \\
+ ${c1} /_/ \\ \\
+ ${c1} \\_\\
+ EOF
+ ;;
+
+ ([Kk][Dd][Ee]*[Nn]eon*)
+ read_ascii 6 <<-EOF
+ ${c7} .${c6}__${c7}.${c6}__${c7}.
+ ${c6} / _${c7}.${c6}_ \\
+ ${c6} / / \\ \\
+ ${c7} . ${c6}| ${c7}O${c6} | ${c7}.
+ ${c6} \\ \\_${c7}.${c6}_/ /
+ ${c6} \\${c7}.${c6}__${c7}.${c6}__${c7}.${c6}/
+ EOF
+ ;;
+
+ ([Ll]inux*[Ll]ite*|[Ll]ite*)
+ read_ascii 3 <<-EOF
+ ${c3} /\\
+ ${c3} / \\
+ ${c3} / ${c7}/ ${c3}/
+ ${c3}> ${c7}/ ${c3}/
+ ${c3}\\ ${c7}\\ ${c3}\\
+ ${c3}\\_${c7}\\${c3}_\\
+ ${c7} \\
+ EOF
+ ;;
+
+ ([Ll]inux*[Mm]int*|[Mm]int)
+ read_ascii 2 <<-EOF
+ ${c2} ___________
+ ${c2}|_ \\
+ ${c2}| ${c7}| _____ ${c2}|
+ ${c2}| ${c7}| | | | ${c2}|
+ ${c2}| ${c7}| | | | ${c2}|
+ ${c2}| ${c7}\\__${c7}___/ ${c2}|
+ ${c2}\\_________/
+ EOF
+ ;;
+
+
+ ([Ll]inux*)
+ read_ascii 4 <<-EOF
+ ${c4} ___
+ ${c4}(${c7}.. ${c4}|
+ ${c4}(${c5}<> ${c4}|
+ ${c4}/ ${c7}__ ${c4}\\
+ ${c4}( ${c7}/ \\ ${c4}/|
+ ${c5}_${c4}/\\ ${c7}__)${c4}/${c5}_${c4})
+ ${c5}\/${c4}-____${c5}\/
+ EOF
+ ;;
+
+ ([Mm]ac[Oo][Ss]*|[Dd]arwin*)
+ read_ascii 1 <<-EOF
+ ${c2} .:'
+ ${c2} _ :'_
+ ${c3} .'\`_\`-'_\`\`.
+ ${c1}:________.-'
+ ${c1}:_______:
+ ${c4} :_______\`-;
+ ${c5} \`._.-._.'
+ EOF
+ ;;
+
+ ([Mm]ageia*)
+ read_ascii 2 <<-EOF
+ ${c6} *
+ ${c6} *
+ ${c6} **
+ ${c7} /\\__/\\
+ ${c7}/ \\
+ ${c7}\\ /
+ ${c7} \\____/
+ EOF
+ ;;
+
+ ([Mm]anjaro*)
+ read_ascii 2 <<-EOF
+ ${c2}||||||||| ||||
+ ${c2}||||||||| ||||
+ ${c2}|||| ||||
+ ${c2}|||| |||| ||||
+ ${c2}|||| |||| ||||
+ ${c2}|||| |||| ||||
+ ${c2}|||| |||| ||||
+ EOF
+ ;;
+
+ ([Mm]inix*)
+ read_ascii 4 <<-EOF
+ ${c4} ,, ,,
+ ${c4};${c7},${c4} ', ,' ${c7},${c4};
+ ${c4}; ${c7}',${c4} ',,' ${c7},'${c4} ;
+ ${c4}; ${c7}',${c4} ${c7},'${c4} ;
+ ${c4}; ${c7};, '' ,;${c4} ;
+ ${c4}; ${c7};${c4};${c7}',,'${c4};${c7};${c4} ;
+ ${c4}', ${c7};${c4};; ;;${c7};${c4} ,'
+ ${c4} '${c7};${c4}' '${c7};${c4}'
+ EOF
+ ;;
+
+ ([Mm][Xx]*)
+ read_ascii <<-EOF
+ ${c7} \\\\ /
+ ${c7} \\\\/
+ ${c7} \\\\
+ ${c7} /\\/ \\\\
+ ${c7} / \\ /\\
+ ${c7} / \\/ \\
+ ${c7}/__________\\
+ EOF
+ ;;
+
+ ([Nn]et[Bb][Ss][Dd]*)
+ read_ascii 3 <<-EOF
+ ${c7}\\${c3}\`-__,----__
+ ${c7} \\ ${c3}__\`_
+ ${c7} \\${c3}-__,----\`-
+ ${c7} \\
+ ${c7} \\
+ EOF
+ ;;
+
+ ([Nn]ix[Oo][Ss]*)
+ read_ascii 4 <<-EOF
+ ${c4} \\\\ \\\\ //
+ ${c4} ==\\\\__\\\\/ //
+ ${c4} // \\\\//
+ ${c4}==// //==
+ ${c4} //\\\\___//
+ ${c4}// /\\\\ \\\\==
+ ${c4} // \\\\ \\\\
+ EOF
+ ;;
+
+ ([Oo]pen[Bb][Ss][Dd]*)
+ read_ascii 3 <<-EOF
+ ${c3} _____
+ ${c3} \\- -/
+ ${c3} \\_/ \\
+ ${c3} | ${c7}O O${c3} |
+ ${c3} |_ < ) 3 )
+ ${c3} / \\ /
+ ${c3} /-_____-\\
+ EOF
+ ;;
+
+ ([Oo]pen[Ss][Uu][Ss][Ee]*[Tt]umbleweed*)
+ read_ascii 2 <<-EOF
+ ${c2} _____ ______
+ ${c2} / ____\\ / ____ \\
+ ${c2}/ / \`/ / \\ \\
+ ${c2}\\ \\____/ /,____/ /
+ ${c2} \\______/ \\_____/
+ EOF
+ ;;
+
+ ([Oo]pen[Ss][Uu][Ss][Ee]*|[Oo]pen*SUSE*|SUSE*|suse*)
+ read_ascii 2 <<-EOF
+ ${c2} _______
+ ${c2}__| __ \\
+ ${c2} / .\\ \\
+ ${c2} \\__/ |
+ ${c2} _______|
+ ${c2} \\_______
+ ${c2}__________/
+ EOF
+ ;;
+
+ ([Oo]pen[Ww]rt*)
+ read_ascii 1 <<-EOF
+ ${c1} _______
+ ${c1}| |.-----.-----.-----.
+ ${c1}| - || _ | -__| |
+ ${c1}|_______|| __|_____|__|__|
+ ${c1} ________|__| __
+ ${c1}| | | |.----.| |_
+ ${c1}| | | || _|| _|
+ ${c1}|________||__| |____|
+ EOF
+ ;;
+
+ ([Pp]arabola*)
+ read_ascii 5 <<-EOF
+ ${c5} __ __ __ _
+ ${c5}.\`_//_//_/ / \`.
+ ${c5} / .\`
+ ${c5} / .\`
+ ${c5} /.\`
+ ${c5} /\`
+ EOF
+ ;;
+
+ ([Pp]op!_[Oo][Ss]*)
+ read_ascii 6 <<-EOF
+ ${c6}______
+ ${c6}\\ _ \\ __
+ ${c6}\\ \\ \\ \\ / /
+ ${c6}\\ \\_\\ \\ / /
+ ${c6}\\ ___\\ /_/
+ ${c6} \\ \\ _
+ ${c6} __\\_\\__(_)_
+ ${c6}(___________)
+ EOF
+ ;;
+
+ ([Pp]ure[Oo][Ss]*)
+ read_ascii <<-EOF
+ ${c7} _____________
+ ${c7}| _________ |
+ ${c7}| | | |
+ ${c7}| | | |
+ ${c7}| |_________| |
+ ${c7}|_____________|
+ EOF
+ ;;
+
+ ([Rr]aspbian*)
+ read_ascii 1 <<-EOF
+ ${c2} __ __
+ ${c2} (_\\)(/_)
+ ${c1} (_(__)_)
+ ${c1}(_(_)(_)_)
+ ${c1} (_(__)_)
+ ${c1} (__)
+ EOF
+ ;;
+
+ ([Ss]erenity[Oo][Ss]*)
+ read_ascii 4 <<-EOF
+ ${c7} _____
+ ${c1} ,-${c7} -,
+ ${c1} ;${c7} ( ;
+ ${c1}| ${c7}. \_${c1}.,${c7} |
+ ${c1}| ${c7}o _${c1} ',${c7} |
+ ${c1} ; ${c7}(_)${c1} )${c7} ;
+ ${c1} '-_____-${c7}'
+ EOF
+ ;;
+
+ ([Ss]lackware*)
+ read_ascii 4 <<-EOF
+ ${c4} ________
+ ${c4} / ______|
+ ${c4} | |______
+ ${c4} \\______ \\
+ ${c4} ______| |
+ ${c4}| |________/
+ ${c4}|____________
+ EOF
+ ;;
+
+ ([Ss]olus*)
+ read_ascii 4 <<-EOF
+ ${c6}
+ ${c6} /|
+ ${c6} / |\\
+ ${c6} / | \\ _
+ ${c6} /___|__\\_\\
+ ${c6} \\ /
+ ${c6} \`-------´
+ EOF
+ ;;
+
+ ([Ss]un[Oo][Ss]|[Ss]olaris*)
+ read_ascii 3 <<-EOF
+ ${c3} . .; .
+ ${c3} . :; :: ;: .
+ ${c3} .;. .. .. .;.
+ ${c3}.. .. .. ..
+ ${c3} .;, ,;.
+ EOF
+ ;;
+
+ ([Uu]buntu*)
+ read_ascii 3 <<-EOF
+ ${c3} _
+ ${c3} ---(_)
+ ${c3} _/ --- \\
+ ${c3}(_) | |
+ ${c3} \\ --- _/
+ ${c3} ---(_)
+ EOF
+ ;;
+
+ ([Vv]oid*)
+ read_ascii 2 <<-EOF
+ ${c2} _______
+ ${c2} _ \\______ -
+ ${c2}| \\ ___ \\ |
+ ${c2}| | / \ | |
+ ${c2}| | \___/ | |
+ ${c2}| \\______ \\_|
+ ${c2} -_______\\
+ EOF
+ ;;
+
+ ([Xx]eonix*)
+ read_ascii 2 <<-EOF
+ ${c2} ___ ___
+ ${c2}___ \ \/ / ___
+ ${c2}\ \ \ / / /
+ ${c2} \ \/ \/ /
+ ${c2} \ /\ /
+ ${c2} \__/ \__/
+ EOF
+ ;;
+
+ (*)
+ # On no match of a distribution ascii art, this function calls
+ # itself again, this time to look for a more generic OS related
+ # ascii art (KISS Linux -> Linux).
+ [ "$1" ] || {
+ get_ascii "$os"
+ return
+ }
+
+ printf 'error: %s is not currently supported.\n' "$os" >&6
+ printf 'error: Open an issue for support to be added.\n' >&6
+ exit 1
+ ;;
+ esac
+
+ # Store the "width" (longest line) and "height" (number of lines)
+ # of the ascii art for positioning. This script prints to the screen
+ # *almost* like a TUI does. It uses escape sequences to allow dynamic
+ # printing of the information through user configuration.
+ #
+ # Iterate over each line of the ascii art to retrieve the above
+ # information. The 'sed' is used to strip '\033[3Xm' color codes from
+ # the ascii art so they don't affect the width variable.
+ while read -r line; do
+ ascii_height=$((${ascii_height:-0} + 1))
+
+ # This was a ternary operation but they aren't supported in
+ # Minix's shell.
+ [ "${#line}" -gt "${ascii_width:-0}" ] &&
+ ascii_width=${#line}
+
+ # Using '<<-EOF' is the only way to loop over a command's
+ # output without the use of a pipe ('|').
+ # This ensures that any variables defined in the while loop
+ # are still accessible in the script.
+ done <<-EOF
+ $(printf %s "$ascii" | sed 's/\[3.m//g')
+ EOF
+
+ # Add a gap between the ascii art and the information.
+ ascii_width=$((ascii_width + 4))
+
+ # Print the ascii art and position the cursor back where we
+ # started prior to printing it.
+ {
+ esc_p SGR 1
+ printf '%s' "$ascii"
+ esc_p SGR 0
+ esc_p CUU "$ascii_height"
+ } >&6
+}
+
+main() {
+ case $* in
+ -v)
+ printf '%s 0.7.0\n' "${0##*/}"
+ return 0
+ ;;
+
+ -d)
+ # Below exec is not run, stderr is shown.
+ ;;
+
+ '')
+ exec 2>/dev/null
+ ;;
+
+ *)
+ cat <<EOF
+${0##*/} show system information
+${0##*/} -d show stderr (debug mode)
+${0##*/} -v show version information
+EOF
+ return 0
+ ;;
+ esac
+
+ # Hide 'stdout' and selectively print to it using '>&6'.
+ # This gives full control over what it displayed on the screen.
+ exec 6>&1 >/dev/null
+
+ # Store raw escape sequence character for later reuse.
+ esc_c=$(printf '\033')
+
+ # Allow the user to execute their own script and modify or
+ # extend pfetch's behavior.
+ # shellcheck source=/dev/null
+ ! [ -f "$PF_SOURCE" ] || . "$PF_SOURCE"
+
+ # Ensure that the 'TMPDIR' is writable as heredocs use it and
+ # fail without the write permission. This was found to be the
+ # case on Android where the temporary directory requires root.
+ [ -w "${TMPDIR:-/tmp}" ] || export TMPDIR=~
+
+ # Generic color list.
+ # Disable warning about unused variables.
+ # shellcheck disable=2034
+ for _c in c1 c2 c3 c4 c5 c6 c7 c8; do
+ esc SGR "3${_c#?}" 0
+ export "$_c=$e"
+ done
+
+ # Disable line wrapping and catch the EXIT signal to enable it again
+ # on exit. Ideally you'd somehow query the current value and retain
+ # it but I'm yet to see this irk anyone.
+ esc_p DECAWM l >&6
+ trap 'esc_p DECAWM h >&6' EXIT
+
+ # Store the output of 'uname' to avoid calling it multiple times
+ # throughout the script. 'read <<EOF' is the simplest way of reading
+ # a command into a list of variables.
+ read -r os kernel arch <<-EOF
+ $(uname -srm)
+ EOF
+
+ # Always run 'get_os' for the purposes of detecting which ascii
+ # art to display.
+ get_os
+
+ # Allow the user to specify the order and inclusion of information
+ # functions through the 'PF_INFO' environment variable.
+ # shellcheck disable=2086
+ {
+ # Disable globbing and set the positional parameters to the
+ # contents of 'PF_INFO'.
+ set -f
+ set +f -- ${PF_INFO-ascii title os host kernel uptime pkgs memory}
+
+ # Iterate over the info functions to determine the lengths of the
+ # "info names" for output alignment. The option names and subtitles
+ # match 1:1 so this is thankfully simple.
+ for info do
+ command -v "get_$info" >/dev/null || continue
+
+ # This was a ternary operation but they aren't supported in
+ # Minix's shell.
+ [ "${#info}" -gt "${info_length:-0}" ] &&
+ info_length=${#info}
+ done
+
+ # Add an additional space of length to act as a gap.
+ info_length=$((info_length + 1))
+
+ # Iterate over the above list and run any existing "get_" functions.
+ for info do
+ "get_$info"
+ done
+ }
+
+ # Position the cursor below both the ascii art and information lines
+ # according to the height of both. If the information exceeds the ascii
+ # art in height, don't touch the cursor (0/unset), else move it down
+ # N lines.
+ #
+ # This was a ternary operation but they aren't supported in Minix's shell.
+ [ "${info_height:-0}" -lt "${ascii_height:-0}" ] &&
+ cursor_pos=$((ascii_height - info_height))
+
+ # Print '$cursor_pos' amount of newlines to correctly position the
+ # cursor. This used to be a 'printf $(seq X X)' however 'seq' is only
+ # typically available (by default) on GNU based systems!
+ while [ "${i:=0}" -le "${cursor_pos:-0}" ]; do
+ printf '\n'
+ i=$((i + 1))
+ done >&6
+}
+
+main "$@"