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