My dotfiles
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1831 lines
55 KiB

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