Compare Pastes

Differences between the pastes #273175 (11.03.2024 13:27) and #274603 (23.05.2024 02:13).
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 <
1860
${0##*/}     show system information
1861
${0##*/} -d  show stderr (debug mode)
1862
${0##*/} -v  show version information
1863
EOF
1864
            return 0
1865
        ;;
1866
    esac
1867
1868
    # Hide 'stdout' and selectively print to it using '>&6'.
1869
    # This gives full control over what it displayed on the screen.
1870
    exec 6>&1 >/dev/null
1871
1872
    # Store raw escape sequence character for later reuse.
1873
    esc_c=$(printf '\033')
1874
1875
    # Allow the user to execute their own script and modify or
1876
    # extend pfetch's behavior.
1877
    # shellcheck source=/dev/null
1878
    ! [ -f "$PF_SOURCE" ] || . "$PF_SOURCE"
1879
1880
    # Ensure that the 'TMPDIR' is writable as heredocs use it and
1881
    # fail without the write permission. This was found to be the
1882
    # case on Android where the temporary directory requires root.
1883
    [ -w "${TMPDIR:-/tmp}" ] || export TMPDIR=~
1884
1885
    # Generic color list.
1886
    # Disable warning about unused variables.
1887
    # shellcheck disable=2034
1888
    for _c in c1 c2 c3 c4 c5 c6 c7 c8; do
1889
        esc SGR "3${_c#?}" 0
1890
        export "$_c=$e"
1891
    done
1892
1893
    # Disable line wrapping and catch the EXIT signal to enable it again
1894
    # on exit. Ideally you'd somehow query the current value and retain
1895
    # it but I'm yet to see this irk anyone.
1896
    esc_p DECAWM l >&6
1897
    trap 'esc_p DECAWM h >&6' EXIT
1898
1899
    # Store the output of 'uname' to avoid calling it multiple times
1900
    # throughout the script. 'read <
1901
    # a command into a list of variables.
1902
    read -r os kernel arch <<-EOF
1903
		$(uname -srm)
1904
	EOF
1905
1906
    # Always run 'get_os' for the purposes of detecting which ascii
1907
    # art to display.
1908
    get_os
1909
1910
    # Allow the user to specify the order and inclusion of information
1911
    # functions through the 'PF_INFO' environment variable.
1912
    # shellcheck disable=2086
1913
    {
1914
        # Disable globbing and set the positional parameters to the
1915
        # contents of 'PF_INFO'.
1916
        set -f
1917
        set +f -- ${PF_INFO-ascii title os host kernel uptime pkgs memory}
1918
1919
        # Iterate over the info functions to determine the lengths of the
1920
        # "info names" for output alignment. The option names and subtitles
1921
        # match 1:1 so this is thankfully simple.
1922
        for info do
1923
            command -v "get_$info" >/dev/null || continue
1924
1925
            # This was a ternary operation but they aren't supported in
1926
            # Minix's shell.
1927
            [ "${#info}" -gt "${info_length:-0}" ] &&
1928
                info_length=${#info}
1929
        done
1930
1931
        # Add an additional space of length to act as a gap.
1932
        info_length=$((info_length + 1))
1933
1934
        # Iterate over the above list and run any existing "get_" functions.
1935
        for info do
1936
            "get_$info"
1937
        done
1938
    }
1939
1940
    # Position the cursor below both the ascii art and information lines
1941
    # according to the height of both. If the information exceeds the ascii
1942
    # art in height, don't touch the cursor (0/unset), else move it down
1943
    # N lines.
1944
    #
1945
    # This was a ternary operation but they aren't supported in Minix's shell.
1946
    [ "${info_height:-0}" -lt "${ascii_height:-0}" ] &&
1947
        cursor_pos=$((ascii_height - info_height))
1948
1949
    # Print '$cursor_pos' amount of newlines to correctly position the
1950
    # cursor. This used to be a 'printf $(seq X X)' however 'seq' is only
1951
    # typically available (by default) on GNU based systems!
1952
    while [ "${i:=0}" -le "${cursor_pos:-0}" ]; do
1953
        printf '\n'
1954
        i=$((i + 1))
1955
    done >&6
1956
}
1957