Join GitHub today
GitHub is home to over 50 million developers working together to host and review code, manage projects, and build software together.
Sign up| #!/bin/sh | |
| # | |
| # pfetch - Simple POSIX sh fetch script. | |
| # 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 value of '$1' as we reset the argument list below. | |
| name=$1 | |
| # 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. | |
| printf '[%sC' "$ascii_width" | |
| # Print the info name and color the text. | |
| printf '[3%s;1m%s[m' "${PF_COL1-4}" "$name" | |
| # Print the info name and info data separator. | |
| 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. | |
| printf '[%sD[%sC' "${#name}" "${PF_ALIGN:-$info_length}" | |
| # Print the info data, color it and strip all leading whitespace | |
| # from the string. | |
| printf '[3%sm%s[m\n' "${PF_COL2-7}" "$info" | |
| # 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=SC2039 | |
| 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 | |
| log "[3${PF_COL3:-1}m${user}${c7}@[3${PF_COL3:-1}m${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)" | |
| else | |
| # 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 | |
| (PRETTY_NAME) distro=$val ;; | |
| esac | |
| done < /etc/os-release | |
| fi | |
| # 'os-release' and 'lsb_release' sometimes add quotes | |
| # around the distribution name, strip them. | |
| distro=${distro##[\"\']} | |
| distro=${distro%%[\"\']} | |
| # Special cases for (independent) distributions which | |
| # don't follow any os-release/lsb standards whatsoever. | |
| has crux && distro=$(crux) | |
| has guix && distro='Guix System' | |
| # 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 _ | |
| break | |
| ;; | |
| 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' ;; | |
| (*) distro='macOS' ;; | |
| 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" | |
| ;; | |
| (*) | |
| # 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 ) | |
| 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*) | |
| 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. | |
| # | |
| # Backticks are *required* here as '/bin/sh' on macOS is | |
| # 'bash 3.2' and it can't handle the following: | |
| # | |
| # var=$( | |
| # code here | |
| # ) | |
| # | |
| # shellcheck disable=2006 | |
| 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 | |
| # 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.") :;; (*) | |
| 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 | |
| ;; | |
| esac | wc -l | |
| ` | |
| case $os in | |
| # IRIX's package manager adds 3 lines of extra | |
| # output which we must account for here. | |
| (IRIX) packages=$((packages - 3)) ;; | |
| 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)) | |
| ;; | |
| esac | |
| log memory "${mem_used:-?}M / ${mem_full:-?}M" >&6 | |
| } | |
| get_wm() { | |
| case $os in | |
| # Don't display window manager on macOS. | |
| (Darwin*) ;; | |
| (*) | |
| # 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 25 -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 ;; | |
| esac | |
| done <<-EOF | |
| $(ps x) | |
| EOF | |
| ;; | |
| esac | |
| ;; | |
| esac | |
| log wm "$wm" >&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 shell "${SHELL##*/}" >&6 | |
| } | |
| get_editor() { | |
| # Display the value of '$VISUAL', if it's empty, display the | |
| # value of '$EDITOR'. | |
| log editor "${VISUAL:-$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. | |
| palette="[7m$c1 $c1 $c2 $c2 $c3 $c3 $c4 $c4 $c5 $c5 $c6 $c6 [m" | |
| # Print the palette with a new-line before and afterwards. | |
| 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'. | |
| 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]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 | |
| ;; | |
| ([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]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 | |
| ${c7} _____ | |
| / __)${c4}\\${c7} | |
| | / ${c4}\\ \\${c7} | |
| ${c4}__${c7}_| |_${c4}_/ /${c7} | |
| ${c4}/ ${c7}(_ _)${c4}_/${c7} | |
| ${c4}/ /${c7} | | | |
| ${c4}\\ \\${c7}__/ | | |
| ${c4}\\${c7}(_____/ | |
| EOF | |
| ;; | |
| ([Ff]ree[Bb][Ss][Dd]*) | |
| read_ascii 1 <<-EOF | |
| ${c1}/\\,-'''''-,/\\ | |
| ${c1}\\_) (_/ | |
| ${c1}| | | |
| ${c1}| | | |
| ${c1}; ; | |
| ${c1}'-_____-' | |
| 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]yperbola*) | |
| read_ascii <<-EOF | |
| ${c7} |\`__.\`/ | |
| ${c7} \____/ | |
| ${c7} .--. | |
| ${c7} / \\ | |
| ${c7} / ___ \\ | |
| ${c7}/ .\` \`.\\ | |
| ${c7}/.\` \`.\\ | |
| 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 | |
| ${c1} .:' | |
| ${c1} _ :'_ | |
| ${c2} .'\`_\`-'_\`\`. | |
| ${c2}:________.-' | |
| ${c3}:_______: | |
| ${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} \\\\${c3}-______,----\`- | |
| ${c7} \\\\ | |
| ${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]lackware*) | |
| read_ascii 4 <<-EOF | |
| ${c4} ________ | |
| ${c4} / ______| | |
| ${c4} | |______ | |
| ${c4} \\______ \\ | |
| ${c4} ______| | | |
| ${c4}| |________/ | |
| ${c4}|____________ | |
| 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 | |
| ;; | |
| (*) | |
| # 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 '[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. | |
| # '[1m': Print the ascii in bold. | |
| # '[m': Clear bold. | |
| # '[%sA': Move the cursor up '$ascii_height' amount of lines. | |
| printf '[1m%s[m[%sA' "$ascii" "$ascii_height" >&6 | |
| } | |
| main() { | |
| case "$1" in (--version) { | |
| printf 'pfetch 0.7.0\n' | |
| exit | |
| } | |
| # Hide 'stderr' unless the first argument is '-v'. This saves | |
| # polluting the script with '2>/dev/null'. | |
| ;;(-v) : | |
| ;;(*) exec 2>/dev/null | |
| ;;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 | |
| # Allow the user to execute their own script and modify or | |
| # extend pfetch's behavior. | |
| # shellcheck source=/dev/null | |
| . "${PF_SOURCE:-/dev/null}" ||: | |
| # 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 | |
| { | |
| c1='[31m'; c2='[32m' | |
| c3='[33m'; c4='[34m' | |
| c5='[35m'; c6='[36m' | |
| c7='[37m'; c8='[38m' | |
| } | |
| # Avoid text-wrapping from wrecking the program output. | |
| # | |
| # Some terminals don't support these sequences, nor do they | |
| # silently conceal them if they're printed resulting in | |
| # partial sequences being printed to the terminal! | |
| case "$TERM" in (dumb|minix|cons25) :;;(*) | |
| # Disable line-wrapping. | |
| printf '[?7l' >&6 | |
| # Enable line-wrapping again on exit. | |
| trap 'printf [?7h >&6' EXIT | |
| ;;esac | |
| # 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 "$@" |

Formed in 2009, the Archive Team (not to be confused with the archive.org Archive-It Team) is a rogue archivist collective dedicated to saving copies of rapidly dying or deleted websites for the sake of history and digital heritage. The group is 100% composed of volunteers and interested parties, and has expanded into a large amount of related projects for saving online and digital history.
