├── .editorconfig ├── .github └── workflows │ └── main.yml ├── LICENSE.md ├── Makefile ├── README.md └── pfetch /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | # Force GitHub to display tabs 4 | # mixed with [4] spaces properly. 5 | [pfetch] 6 | indent_style = tab 7 | indent_size = 4 8 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Shellcheck 2 | on: [push] 3 | jobs: 4 | build: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - uses: actions/checkout@v1 8 | - name: Run shellcheck. 9 | run: | 10 | shellcheck pfetch 11 | TERM=dumb sh pfetch 12 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016-2019 Dylan Araps 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | PREFIX ?= /usr 2 | 3 | all: 4 | @echo RUN \'make install\' to install pfetch 5 | 6 | install: 7 | @install -Dm755 pfetch $(DESTDIR)$(PREFIX)/bin/pfetch 8 | 9 | uninstall: 10 | @rm -f $(DESTDIR)$(PREFIX)/bin/pfetch 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 |

pfetch

3 |

A pretty system information tool written in POSIX sh


4 | 5 | 6 | 7 | The goal of this project is to implement a simple system 8 | information tool in POSIX `sh` using features built into 9 | the language itself (*where possible*). 10 | 11 | The source code is highly documented and I hope it will 12 | act as a learning resource for POSIX `sh` and simple 13 | information detection across various different operating 14 | systems. 15 | 16 | If anything in the source code is unclear or is lacking 17 | in its explanation, open an issue. Sometimes you get too 18 | close to something and you fail to see the "bigger 19 | picture"! 20 | 21 |
22 |
23 |
24 |
25 | 26 | ## OS support 27 | 28 | - **Linux** 29 | - Alpine Linux, Arch Linux, Arco Linux, Artix Linux, CentOS, Dahlia, Debian, Devuan, Elementary, EndeavourOS, Fedora, Garuda Linux, Gentoo, Guix, Hyperbola, instantOS, KISS Linux, Linux Lite, Linux Mint, Mageia, Manjaro, MX Linux, NixOS, OpenSUSE, Parabola, Pop!\_OS, PureOS, Slackware, Solus, Ubuntu and Void Linux. 30 | - All other distributions are supported with a generic penguin logo. 31 | - **Android** 32 | - **BSD** 33 | - DragonflyBSD, FreeBSD, NetBSD and OpenBSD. 34 | - **Windows** 35 | - Windows subsystem for Linux. 36 | - **Haiku** 37 | - **MacOS** 38 | - **Minix** 39 | - **Solaris** 40 | - **IRIX** 41 | - **SerenityOS** 42 | 43 | ## Configuration 44 | 45 | `pfetch` is configured through environment variables. 46 | 47 | ```sh 48 | # Which information to display. 49 | # NOTE: If 'ascii' will be used, it must come first. 50 | # Default: first example below 51 | # Valid: space separated string 52 | # 53 | # OFF by default: shell editor wm de palette 54 | PF_INFO="ascii title os host kernel uptime pkgs memory" 55 | 56 | # Example: Only ASCII. 57 | PF_INFO="ascii" 58 | 59 | # Example: Only Information. 60 | PF_INFO="title os host kernel uptime pkgs memory" 61 | 62 | # A file to source before running pfetch. 63 | # Default: unset 64 | # Valid: A shell script 65 | PF_SOURCE="" 66 | 67 | # Separator between info name and info data. 68 | # Default: unset 69 | # Valid: string 70 | PF_SEP=":" 71 | 72 | # Enable/Disable colors in output: 73 | # Default: 1 74 | # Valid: 1 (enabled), 0 (disabled) 75 | PF_COLOR=1 76 | 77 | # Color of info names: 78 | # Default: unset (auto) 79 | # Valid: 0-9 80 | PF_COL1=4 81 | 82 | # Color of info data: 83 | # Default: unset (auto) 84 | # Valid: 0-9 85 | PF_COL2=9 86 | 87 | # Color of title data: 88 | # Default: unset (auto) 89 | # Valid: 0-9 90 | PF_COL3=1 91 | 92 | # Alignment padding. 93 | # Default: unset (auto) 94 | # Valid: int 95 | PF_ALIGN="" 96 | 97 | # Which ascii art to use. 98 | # Default: unset (auto) 99 | # Valid: string 100 | PF_ASCII="openbsd" 101 | 102 | # The below environment variables control more 103 | # than just 'pfetch' and can be passed using 104 | # 'HOSTNAME=cool_pc pfetch' to restrict their 105 | # usage solely to 'pfetch'. 106 | 107 | # Which user to display. 108 | USER="" 109 | 110 | # Which hostname to display. 111 | HOSTNAME="" 112 | 113 | # Which editor to display. 114 | EDITOR="" 115 | 116 | # Which shell to display. 117 | SHELL="" 118 | 119 | # Which desktop environment to display. 120 | XDG_CURRENT_DESKTOP="" 121 | ``` 122 | 123 | ## Credit 124 | 125 | - [ufetch](https://gitlab.com/jschx/ufetch): Lots of ASCII logos. 126 | - Contrary to the belief of a certain youtuber, `pfetch` shares **zero** code with `ufetch`. Only some of the ASCII logos were used. 127 | -------------------------------------------------------------------------------- /pfetch: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # pfetch - Simple POSIX sh fetch script. 4 | 5 | # Wrapper around all escape sequences used by pfetch to allow for 6 | # greater control over which sequences are used (if any at all). 7 | esc() { 8 | case $1 in 9 | CUU) e="${esc_c}[${2}A" ;; # cursor up 10 | CUD) e="${esc_c}[${2}B" ;; # cursor down 11 | CUF) e="${esc_c}[${2}C" ;; # cursor right 12 | CUB) e="${esc_c}[${2}D" ;; # cursor left 13 | 14 | # text formatting 15 | SGR) 16 | case ${PF_COLOR:=1} in 17 | (1) 18 | e="${esc_c}[${2}m" 19 | ;; 20 | 21 | (0) 22 | # colors disabled 23 | e= 24 | ;; 25 | esac 26 | ;; 27 | 28 | # line wrap 29 | DECAWM) 30 | case $TERM in 31 | (dumb | minix | cons25) 32 | # not supported 33 | e= 34 | ;; 35 | 36 | (*) 37 | e="${esc_c}[?7${2}" 38 | ;; 39 | esac 40 | ;; 41 | esac 42 | } 43 | 44 | # Print a sequence to the terminal. 45 | esc_p() { 46 | esc "$@" 47 | printf '%s' "$e" 48 | } 49 | 50 | # This is just a simple wrapper around 'command -v' to avoid 51 | # spamming '>/dev/null' throughout this function. This also guards 52 | # against aliases and functions. 53 | has() { 54 | _cmd=$(command -v "$1") 2>/dev/null || return 1 55 | [ -x "$_cmd" ] || return 1 56 | } 57 | 58 | log() { 59 | # The 'log()' function handles the printing of information. 60 | # In 'pfetch' (and 'neofetch'!) the printing of the ascii art and info 61 | # happen independently of each other. 62 | # 63 | # The size of the ascii art is stored and the ascii is printed first. 64 | # Once the ascii is printed, the cursor is located right below the art 65 | # (See marker $[1]). 66 | # 67 | # Using the stored ascii size, the cursor is then moved to marker $[2]. 68 | # This is simply a cursor up escape sequence using the "height" of the 69 | # ascii art. 70 | # 71 | # 'log()' then moves the cursor to the right the "width" of the ascii art 72 | # with an additional amount of padding to add a gap between the art and 73 | # the information (See marker $[3]). 74 | # 75 | # When 'log()' has executed, the cursor is then located at marker $[4]. 76 | # When 'log()' is run a second time, the next line of information is 77 | # printed, moving the cursor to marker $[5]. 78 | # 79 | # Markers $[4] and $[5] repeat all the way down through the ascii art 80 | # until there is no more information left to print. 81 | # 82 | # Every time 'log()' is called the script keeps track of how many lines 83 | # were printed. When printing is complete the cursor is then manually 84 | # placed below the information and the art according to the "heights" 85 | # of both. 86 | # 87 | # The math is simple: move cursor down $((ascii_height - info_height)). 88 | # If the aim is to move the cursor from marker $[5] to marker $[6], 89 | # plus the ascii height is 8 while the info height is 2 it'd be a move 90 | # of 6 lines downwards. 91 | # 92 | # However, if the information printed is "taller" (takes up more lines) 93 | # than the ascii art, the cursor isn't moved at all! 94 | # 95 | # Once the cursor is at marker $[6], the script exits. This is the gist 96 | # of how this "dynamic" printing and layout works. 97 | # 98 | # This method allows ascii art to be stored without markers for info 99 | # and it allows for easy swapping of info order and amount. 100 | # 101 | # $[2] ___ $[3] goldie@KISS 102 | # $[4](.· | $[5] os KISS Linux 103 | # (<> | 104 | # / __ \ 105 | # ( / \ /| 106 | # _/\ __)/_) 107 | # \/-____\/ 108 | # $[1] 109 | # 110 | # $[6] /home/goldie $ 111 | 112 | # End here if no data was found. 113 | [ "$2" ] || return 114 | 115 | # Store the 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 | # ProductVersion 301 | # 10.14.6 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 <&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 </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 "$@" 1959 | --------------------------------------------------------------------------------