From 3076e344eef92f2865a8a485e58f5d4c594722e5 Mon Sep 17 00:00:00 2001 From: TolaMironcenko Date: Tue, 5 Nov 2024 18:33:50 +0700 Subject: [PATCH] added scripts in bin --- bin/checkUpdates.sh | 44 + bin/dpmsdisablexorg | 5 + bin/fet.sh | 266 ++++++ bin/fff | 1145 ++++++++++++++++++++++ bin/linux-chroot.sh | 65 ++ bin/lisascreens | 12 + bin/lisascreenslayout.sh | 2 + bin/netdevlist | 4 + bin/pfetch | 1958 ++++++++++++++++++++++++++++++++++++++ bin/shfm | 428 +++++++++ bin/tux.sh | 53 ++ bin/updatepacmanrepo | 4 + 12 files changed, 3986 insertions(+) create mode 100755 bin/checkUpdates.sh create mode 100755 bin/dpmsdisablexorg create mode 100755 bin/fet.sh create mode 100755 bin/fff create mode 100755 bin/linux-chroot.sh create mode 100755 bin/lisascreens create mode 100755 bin/lisascreenslayout.sh create mode 100755 bin/netdevlist create mode 100755 bin/pfetch create mode 100755 bin/shfm create mode 100755 bin/tux.sh create mode 100755 bin/updatepacmanrepo diff --git a/bin/checkUpdates.sh b/bin/checkUpdates.sh new file mode 100755 index 0000000..39bf6ec --- /dev/null +++ b/bin/checkUpdates.sh @@ -0,0 +1,44 @@ +#!/bin/bash + +check_arch_updates() { + official_updates=0 + aur_updates=0 + + if [ "$1" = "-y" ]; then + aur_updates=$(yay -Qum 2>/dev/null | wc -l) + elif [ "$1" = "-p" ]; then + official_updates=$(checkupdates 2>/dev/null | wc -l) + else + official_updates=$(checkupdates 2>/dev/null | wc -l) + aur_updates=$(yay -Qum 2>/dev/null | wc -l) + fi + + total_updates=$((official_updates + aur_updates)) + + echo $total_updates +} + +check_ubuntu_updates() { + result=$(apt-get -s -o Debug::NoLocking=true upgrade | grep -c ^Inst) + echo "$result" +} + +check_fedora_updates() { + result=$(dnf check-update -q | grep -v '^Loaded plugins' | grep -v '^No match for' | wc -l) + echo "$result" +} + +case "$1" in +-arch) + check_arch_updates "$2" + ;; +-ubuntu) + check_ubuntu_updates + ;; +-fedora) + check_fedora_updates + ;; +*) + echo "0" + ;; +esac diff --git a/bin/dpmsdisablexorg b/bin/dpmsdisablexorg new file mode 100755 index 0000000..5315c5c --- /dev/null +++ b/bin/dpmsdisablexorg @@ -0,0 +1,5 @@ +#!/bin/sh + +xset -dpms +xset s off + diff --git a/bin/fet.sh b/bin/fet.sh new file mode 100755 index 0000000..1fb3c5a --- /dev/null +++ b/bin/fet.sh @@ -0,0 +1,266 @@ +#!/bin/sh +# +# fet.sh +# a fetch in pure POSIX shell +# + +# supress errors +exec 2>/dev/null +set -- +eq() { # equals | [ a = b ] with globbing + case $1 in + $2) ;; + *) return 1;; + esac +} + +## DE +wm=$XDG_CURRENT_DESKTOP +[ "$wm" ] || wm=$DESKTOP_SESSION + +## Distro +# freedesktop.org/software/systemd/man/os-release.html +# a common file that has variables about the distro +for os in /etc/os-release /usr/lib/os-release; do + # some POSIX shells exit when trying to source a file that doesn't exist + [ -f $os ] && . $os && break +done + +if [ -e /proc/$$/comm ]; then + ## Terminal + while [ ! "$term" ]; do + # loop over lines in /proc/pid/status until it reaches PPid + # then save that to a variable and exit the file + while read -r line; do + eq "$line" 'PPid*' && ppid=${line##*:?} && break + done < "/proc/${ppid:-$PPID}/status" + + # Make sure not to do an infinite loop + [ "$pppid" = "$ppid" ] && break + pppid=$ppid + + # get name of binary + read -r name < "/proc/$ppid/comm" + + case $name in + *sh|"${0##*/}") ;; # skip shells + *[Ll]ogin*|*init|*systemd*) break;; # exit when the top is reached + # anything else can be assumed to be the terminal + # this has the side affect of catching tmux, but tmux + # detaches from the terminal and therefore ignoring that + # will just make the init the term + *) term=$name + esac + done + + ## WM/DE + [ "$wm" ] || + # loop over all processes and check the binary name + for i in /proc/*/comm; do + read -r c < "$i" + case $c in + *bar*|*rc) ;; + awesome|xmonad*|qtile|sway|i3|[bfo]*box|*wm*) wm=${c%%-*}; break;; + esac + done + + ## Memory + # loop over lines in /proc/meminfo until it reaches MemTotal, + # then convert the amount (second word) from KB to MB + while read -r line; do + eq "$line" 'MemTotal*' && set -- $line && break + done < /proc/meminfo + mem="$(( $2 / 1000 ))MB" + + ## Processor + while read -r line; do + case $line in + vendor_id*) vendor="${line##*: } ";; + model\ name*) cpu=${line##*: }; break;; + esac + done < /proc/cpuinfo + + ## Uptime + # the simple math is shamefully stolen from aosync + IFS=. read -r uptime _ < /proc/uptime + d=$((uptime / 60 / 60 / 24)) + up=$(printf %02d:%02d $((uptime / 60 / 60 % 24)) $((uptime / 60 % 60))) + [ "$d" -gt 0 ] && up="${d}d $up" + + ## Kernel + read -r _ _ version _ < /proc/version + kernel=${version%%-*} + eq "$version" '*Microsoft*' && ID="fake $ID" + + ## Motherboard // laptop + read -r model < /sys/devices/virtual/dmi/id/product_name + # invalid model handling + case $model in + # alternate file with slightly different info + # on my laptop it has the device model (instead of 'hp notebook') + # on my desktop it has the extended motherboard model + 'System '*|'Default '*|'To Be Filled'*) + read -r model < /sys/devices/virtual/dmi/id/board_name + esac + + ## Packages + # clean environment, then make every file in the dir an argument, + # then save the argument count to $pkgs + set -- + # kiss, arch, debian, void, gentoo + for i in '/var/db/kiss/installed/*' '/var/lib/pacman/local/[0-9a-z]*' \ + '/var/lib/dpkg/info/*.list' '/var/db/xbps/.*' '/var/db/pkg/*/*'; do + set -- $i + [ $# -gt 1 ] && pkgs=$# && break + done + + read -r host < /proc/sys/kernel/hostname +elif [ -f /var/run/dmesg.boot ]; then + # Both OpenBSD and FreeBSD use this file, however they're formatted differently + read -r bsd < /var/run/dmesg.boot + case $bsd in + Open*) + ## OpenBSD cpu/mem/name + while read -r line; do + case $line in + 'real mem'*) + # use the pre-formatted value which is in brackets + mem=${line##*\(} + mem=${mem%\)*} + ;; + # set $cpu to everything before a comma and after the field name + cpu0:*) + cpu=${line#cpu0: } + # Remove excess info after the actual CPU name + cpu=${cpu%%,*} + # Set the CPU Manufacturer to the first word of the cpu + # variable [separated by '(' or ' '] + vendor=${cpu%%[\( ]*} + # We got all the info we want, stop reading + break + ;; + # First 2 words in the file are OpenBSD + *) [ "$ID" ] || { set -- $line; ID="$1 $2"; } + esac + done < /var/run/dmesg.boot + [ -d /var/db/pkg ] && set -- /var/db/pkg/* && pkgs=$# + read -r host < /etc/myname + host=${host%.*} + ;; + # Everything else, assume FreeBSD (first line is ---<> or something) + *) + # shellcheck source=/dev/null + . /etc/rc.conf + # shut shellcheck up without disabling the warning + host=${hostname:-} + + while read -r line; do + case $line in + # os version + FreeBSD*) + # If the OS is already set, no need to set it again + [ "$ID" ] && continue + ID=${line%%-R*} + ;; + + CPU:*) + cpu=${cpu#CPU: } + # Remove excess info from after the actual CPU name + cpu=${line%\(*} + ;; + *Origin=*) + # CPU Manufacturer + vendor=${line#*Origin=\"} + vendor="${vendor%%\"*} " + ;; + + 'real memory'*) + # Get the pre-formatted amount which is inside brackets + mem=${line##*\(} + mem=${mem%\)*} + # This appears to be the final thing we need from the file, + # no need to read it more. + break + esac + done < /var/run/dmesg.boot + ;; + esac +elif v=/System/Library/CoreServices/SystemVersion.plist; [ -f "$v" ]; then + ## Macos + # make sure this variable is empty as to not break the following loop + temp= + while read -r line; do + case $line in + # set a variable so the script knows it's on the correct line + # (the line after this one is the important one) + *ProductVersion*) temp=.;; + *) + # check if the script is reading the derired line, if not + # don't do anything + [ "$temp" ] || continue + # Remove everything before and including the first '>' + ID=${line#*>} + # Remove the other side of the XML tag, and insert the actual OS name + ID="MacOS ${ID%<*}" + # We got the info we want, end the loop. + break + esac + done < "$v" +fi + +eq "$0" '*fetish' && printf 'Step on me daddy\n' && exit + +# help i dont know if it's a capital consistently +eq "$wm" '*[Gg][Nn][Oo][Mm][Ee]*' && wm='foot DE' + +## GTK +while read -r line; do + eq "$line" 'gtk-theme*' && gtk=${line##*=} && break +done < "${XDG_CONFIG_HOME:=$HOME/.config}/gtk-3.0/settings.ini" + +# Shorten $cpu and $vendor +# this is so messy due to so many inconsistencies in the model names +vendor=${vendor##*Authentic} +vendor=${vendor##*Genuine} +cpu=${cpu##*) } +cpu=${cpu%% @*} +cpu=${cpu%% CPU} +cpu=${cpu##CPU } +cpu=${cpu##*AMD } +cpu=${cpu%% with*} +cpu=${cpu% *-Core*} + +col() { + printf ' ' + for i in 1 2 3 4 5 6; do + printf '\033[9%sm%s' "$i" "${colourblocks:-▅▅}" + done + printf '\033[0m\n' +} + +print() { + [ "$2" ] && printf '\033[9%sm%6s\033[0m%b%s\n' \ + "${accent:-4}" "$1" "${separator:- ~ }" "$2" +} + +# default value +: "${info:=n user os sh wm up gtk cpu mem host kern pkgs term col n}" + +for i in $info; do + case $i in + n) echo;; + os) print os "$ID";; + sh) print sh "${SHELL##*/}";; + wm) print wm "${wm##*/}";; + up) print up "$up";; + gtk) print gtk "${gtk# }";; + cpu) print cpu "$vendor$cpu";; + mem) print mem "$mem";; + host) print host "$model";; + kern) print kern "$kernel";; + pkgs) print pkgs "$pkgs";; + term) print term "$term";; + user) printf '%7s@%s\n' "$USER" "$host";; + col) col;; + esac +done diff --git a/bin/fff b/bin/fff new file mode 100755 index 0000000..16766e3 --- /dev/null +++ b/bin/fff @@ -0,0 +1,1145 @@ +#!/usr/bin/env bash +# +# fff - fucking fast file-manager. + +get_os() { + # Figure out the current operating system to set some specific variables. + # '$OSTYPE' typically stores the name of the OS kernel. + case $OSTYPE in + # Mac OS X / macOS. + darwin*) + opener=open + file_flags=bIL + ;; + + haiku) + opener=open + + [[ -z $FFF_TRASH_CMD ]] && + FFF_TRASH_CMD=trash + + [[ $FFF_TRASH_CMD == trash ]] && { + FFF_TRASH=$(finddir -v "$PWD" B_TRASH_DIRECTORY) + mkdir -p "$FFF_TRASH" + } + ;; + esac +} + +setup_terminal() { + # Setup the terminal for the TUI. + # '\e[?1049h': Use alternative screen buffer. + # '\e[?7l': Disable line wrapping. + # '\e[?25l': Hide the cursor. + # '\e[2J': Clear the screen. + # '\e[1;Nr': Limit scrolling to scrolling area. + # Also sets cursor to (0,0). + printf '\e[?1049h\e[?7l\e[?25l\e[2J\e[1;%sr' "$max_items" + + # Hide echoing of user input + stty -echo +} + +reset_terminal() { + # Reset the terminal to a useable state (undo all changes). + # '\e[?7h': Re-enable line wrapping. + # '\e[?25h': Unhide the cursor. + # '\e[2J': Clear the terminal. + # '\e[;r': Set the scroll region to its default value. + # Also sets cursor to (0,0). + # '\e[?1049l: Restore main screen buffer. + printf '\e[?7h\e[?25h\e[2J\e[;r\e[?1049l' + + # Show user input. + stty echo +} + +clear_screen() { + # Only clear the scrolling window (dir item list). + # '\e[%sH': Move cursor to bottom of scroll area. + # '\e[9999C': Move cursor to right edge of the terminal. + # '\e[1J': Clear screen to top left corner (from cursor up). + # '\e[2J': Clear screen fully (if using tmux) (fixes clear issues). + # '\e[1;%sr': Clearing the screen resets the scroll region(?). Re-set it. + # Also sets cursor to (0,0). + printf '\e[%sH\e[9999C\e[1J%b\e[1;%sr' \ + "$((LINES-2))" "${TMUX:+\e[2J}" "$max_items" +} + +setup_options() { + # Some options require some setup. + # This function is called once on open to parse + # select options so the operation isn't repeated + # multiple times in the code. + + # Format for normal files. + [[ $FFF_FILE_FORMAT == *%f* ]] && { + file_pre=${FFF_FILE_FORMAT/'%f'*} + file_post=${FFF_FILE_FORMAT/*'%f'} + } + + # Format for marked files. + # Use affixes provided by the user or use defaults, if necessary. + if [[ $FFF_MARK_FORMAT == *%f* ]]; then + mark_pre=${FFF_MARK_FORMAT/'%f'*} + mark_post=${FFF_MARK_FORMAT/*'%f'} + else + mark_pre=" " + mark_post="*" + fi + + # Find supported 'file' arguments. + file -I &>/dev/null || : "${file_flags:=biL}" +} + +get_term_size() { + # Get terminal size ('stty' is POSIX and always available). + # This can't be done reliably across all bash versions in pure bash. + read -r LINES COLUMNS < <(stty size) + + # Max list items that fit in the scroll area. + ((max_items=LINES-3)) +} + +get_ls_colors() { + # Parse the LS_COLORS variable and declare each file type + # as a separate variable. + # Format: ':.ext=0;0:*.jpg=0;0;0:*png=0;0;0;0:' + [[ -z $LS_COLORS ]] && { + FFF_LS_COLORS=0 + return + } + + # Turn $LS_COLORS into an array. + IFS=: read -ra ls_cols <<< "$LS_COLORS" + + for ((i=0;i<${#ls_cols[@]};i++)); { + # Separate patterns from file types. + [[ ${ls_cols[i]} =~ ^\*[^\.] ]] && + ls_patterns+="${ls_cols[i]/=*}|" + + # Prepend 'ls_' to all LS_COLORS items + # if they aren't types of files (symbolic links, block files etc.) + [[ ${ls_cols[i]} =~ ^(\*|\.) ]] && { + ls_cols[i]=${ls_cols[i]#\*} + ls_cols[i]=ls_${ls_cols[i]#.} + } + } + + # Strip non-ascii characters from the string as they're + # used as a key to color the dir items and variable + # names in bash must be '[a-zA-z0-9_]'. + ls_cols=("${ls_cols[@]//[^a-zA-Z0-9=\\;]/_}") + + # Store the patterns in a '|' separated string + # for use in a REGEX match later. + ls_patterns=${ls_patterns//\*} + ls_patterns=${ls_patterns%?} + + # Define the ls_ variables. + # 'declare' can't be used here as variables are scoped + # locally. 'declare -g' is not available in 'bash 3'. + # 'export' is a viable alternative. + export "${ls_cols[@]}" &>/dev/null +} + +get_w3m_path() { + # Find the path to the w3m-img library. + w3m_paths=(/usr/{pkg/,}{local/,}{bin,lib,libexec,lib64,libexec64}/w3m/w3mi*) + read -r w3m _ < <(type -p "$FFF_W3M_PATH" w3mimgdisplay "${w3m_paths[@]}") +} + +get_mime_type() { + # Get a file's mime_type. + mime_type=$(file "-${file_flags:-biL}" "$1" 2>/dev/null) +} + +status_line() { + # Status_line to print when files are marked for operation. + local mark_ui="[${#marked_files[@]}] selected (${file_program[*]}) [p] ->" + + # Escape the directory string. + # Remove all non-printable characters. + PWD_escaped=${PWD//[^[:print:]]/^[} + + # '\e7': Save cursor position. + # This is more widely supported than '\e[s'. + # '\e[%sH': Move cursor to bottom of the terminal. + # '\e[30;41m': Set foreground and background colors. + # '%*s': Insert enough spaces to fill the screen width. + # This sets the background color to the whole line + # and fixes issues in 'screen' where '\e[K' doesn't work. + # '\r': Move cursor back to column 0 (was at EOL due to above). + # '\e[m': Reset text formatting. + # '\e[H\e[K': Clear line below status_line. + # '\e8': Restore cursor position. + # This is more widely supported than '\e[u'. + printf '\e7\e[%sH\e[3%s;4%sm%*s\r%s %s%s\e[m\e[%sH\e[K\e8' \ + "$((LINES-1))" \ + "${FFF_COL5:-0}" \ + "${FFF_COL2:-1}" \ + "$COLUMNS" "" \ + "($((scroll+1))/$((list_total+1)))" \ + "${marked_files[*]:+${mark_ui}}" \ + "${1:-${PWD_escaped:-/}}" \ + "$LINES" +} + +read_dir() { + # Read a directory to an array and sort it directories first. + local dirs + local files + local item_index + + # Set window name. + printf '\e]2;fff: %s\e'\\ "$PWD" + + # If '$PWD' is '/', unset it to avoid '//'. + [[ $PWD == / ]] && PWD= + + for item in "$PWD"/*; do + if [[ -d $item ]]; then + dirs+=("$item") + + # Find the position of the child directory in the + # parent directory list. + [[ $item == "$OLDPWD" ]] && + ((previous_index=item_index)) + ((item_index++)) + else + files+=("$item") + fi + done + + list=("${dirs[@]}" "${files[@]}") + + # Indicate that the directory is empty. + [[ -z ${list[0]} ]] && + list[0]=empty + + ((list_total=${#list[@]}-1)) + + # Save the original dir in a second list as a backup. + cur_list=("${list[@]}") +} + +print_line() { + # Format the list item and print it. + local file_name=${list[$1]##*/} + local file_ext=${file_name##*.} + local format + local suffix + + # If the dir item doesn't exist, end here. + if [[ -z ${list[$1]} ]]; then + return + + # Directory. + elif [[ -d ${list[$1]} ]]; then + format+=\\e[${di:-1;3${FFF_COL1:-2}}m + suffix+=/ + + # Block special file. + elif [[ -b ${list[$1]} ]]; then + format+=\\e[${bd:-40;33;01}m + + # Character special file. + elif [[ -c ${list[$1]} ]]; then + format+=\\e[${cd:-40;33;01}m + + # Executable file. + elif [[ -x ${list[$1]} ]]; then + format+=\\e[${ex:-01;32}m + + # Symbolic Link (broken). + elif [[ -h ${list[$1]} && ! -e ${list[$1]} ]]; then + format+=\\e[${mi:-01;31;7}m + + # Symbolic Link. + elif [[ -h ${list[$1]} ]]; then + format+=\\e[${ln:-01;36}m + + # Fifo file. + elif [[ -p ${list[$1]} ]]; then + format+=\\e[${pi:-40;33}m + + # Socket file. + elif [[ -S ${list[$1]} ]]; then + format+=\\e[${so:-01;35}m + + # Color files that end in a pattern as defined in LS_COLORS. + # 'BASH_REMATCH' is an array that stores each REGEX match. + elif [[ $FFF_LS_COLORS == 1 && + $ls_patterns && + $file_name =~ ($ls_patterns)$ ]]; then + match=${BASH_REMATCH[0]} + file_ext=ls_${match//[^a-zA-Z0-9=\\;]/_} + format+=\\e[${!file_ext:-${fi:-37}}m + + # Color files based on file extension and LS_COLORS. + # Check if file extension adheres to POSIX naming + # standard before checking if it's a variable. + elif [[ $FFF_LS_COLORS == 1 && + $file_ext != "$file_name" && + $file_ext =~ ^[a-zA-Z0-9_]*$ ]]; then + file_ext=ls_${file_ext} + format+=\\e[${!file_ext:-${fi:-37}}m + + else + format+=\\e[${fi:-37}m + fi + + # If the list item is under the cursor. + (($1 == scroll)) && + format+="\\e[1;3${FFF_COL4:-6};7m" + + # If the list item is marked for operation. + [[ ${marked_files[$1]} == "${list[$1]:-null}" ]] && { + format+=\\e[3${FFF_COL3:-1}m${mark_pre} + suffix+=${mark_post} + } + + # Escape the directory string. + # Remove all non-printable characters. + file_name=${file_name//[^[:print:]]/^[} + + printf '\r%b%s\e[m\r' \ + "${file_pre}${format}" \ + "${file_name}${suffix}${file_post}" +} + +draw_dir() { + # Print the max directory items that fit in the scroll area. + local scroll_start=$scroll + local scroll_new_pos + local scroll_end + + # When going up the directory tree, place the cursor on the position + # of the previous directory. + ((find_previous == 1)) && { + ((scroll_start=previous_index)) + ((scroll=scroll_start)) + + # Clear the directory history. We're here now. + find_previous= + } + + # If current dir is near the top of the list, keep scroll position. + if ((list_total < max_items || scroll < max_items/2)); then + ((scroll_start=0)) + ((scroll_end=max_items)) + ((scroll_new_pos=scroll+1)) + + # If current dir is near the end of the list, keep scroll position. + elif ((list_total - scroll < max_items/2)); then + ((scroll_start=list_total-max_items+1)) + ((scroll_new_pos=max_items-(list_total-scroll))) + ((scroll_end=list_total+1)) + + # If current dir is somewhere in the middle, center scroll position. + else + ((scroll_start=scroll-max_items/2)) + ((scroll_end=scroll_start+max_items)) + ((scroll_new_pos=max_items/2+1)) + fi + + # Reset cursor position. + printf '\e[H' + + for ((i=scroll_start;i scroll_start)) && + printf '\n' + + print_line "$i" + } + + # Move the cursor to its new position if it changed. + # If the variable 'scroll_new_pos' is empty, the cursor + # is moved to line '0'. + printf '\e[%sH' "$scroll_new_pos" + ((y=scroll_new_pos)) +} + +draw_img() { + # Draw an image file on the screen using w3m-img. + # We can use the framebuffer; set win_info_cmd as appropriate. + [[ $(tty) == /dev/tty[0-9]* && -w /dev/fb0 ]] && + win_info_cmd=fbset + + # X isn't running and we can't use the framebuffer, do nothing. + [[ -z $DISPLAY && $win_info_cmd != fbset ]] && + return + + # File isn't an image file, do nothing. + get_mime_type "${list[scroll]}" + [[ $mime_type != image/* ]] && + return + + # w3m-img isn't installed, do nothing. + type -p "$w3m" &>/dev/null || { + cmd_line "error: Couldn't find 'w3m-img', is it installed?" + return + } + + # $win_info_cmd isn't installed, do nothing. + type -p "${win_info_cmd:=xdotool}" &>/dev/null || { + cmd_line "error: Couldn't find '$win_info_cmd', is it installed?" + return + } + + # Get terminal window size in pixels and set it to WIDTH and HEIGHT. + if [[ $win_info_cmd == xdotool ]]; then + IFS=$'\n' read -d "" -ra win_info \ + < <(xdotool getactivewindow getwindowgeometry --shell) + + declare "${win_info[@]}" &>/dev/null || { + cmd_line "error: Failed to retrieve window size." + return + } + else + [[ $(fbset --show) =~ .*\"([0-9]+x[0-9]+)\".* ]] + IFS=x read -r WIDTH HEIGHT <<< "${BASH_REMATCH[1]}" + fi + + # Get the image size in pixels. + read -r img_width img_height < <("$w3m" <<< "5;${list[scroll]}") + + # Subtract the status_line area from the image size. + ((HEIGHT=HEIGHT-HEIGHT*5/LINES)) + + ((img_width > WIDTH)) && { + ((img_height=img_height*WIDTH/img_width)) + ((img_width=WIDTH)) + } + + ((img_height > HEIGHT)) && { + ((img_width=img_width*HEIGHT/img_height)) + ((img_height=HEIGHT)) + } + + clear_screen + status_line "${list[scroll]}" + + # Add a small delay to fix issues in VTE terminals. + ((BASH_VERSINFO[0] > 3)) && + read "${read_flags[@]}" -srn 1 + + # Display the image. + printf '0;1;%s;%s;%s;%s;;;;;%s\n3;\n4\n' \ + "${FFF_W3M_XOFFSET:-0}" \ + "${FFF_W3M_YOFFSET:-0}" \ + "$img_width" \ + "$img_height" \ + "${list[scroll]}" | "$w3m" &>/dev/null + + # Wait for user input. + read -ern 1 + + # Clear the image. + printf '6;%s;%s;%s;%s\n3;' \ + "${FFF_W3M_XOFFSET:-0}" \ + "${FFF_W3M_YOFFSET:-0}" \ + "$WIDTH" \ + "$HEIGHT" | "$w3m" &>/dev/null + + redraw +} + +redraw() { + # Redraw the current window. + # If 'full' is passed, re-fetch the directory list. + [[ $1 == full ]] && { + read_dir + scroll=0 + } + + clear_screen + draw_dir + status_line +} + +mark() { + # Mark file for operation. + # If an item is marked in a second directory, + # clear the marked files. + [[ $PWD != "$mark_dir" ]] && + marked_files=() + + # Don't allow the user to mark the empty directory list item. + [[ ${list[0]} == empty && -z ${list[1]} ]] && + return + + if [[ $1 == all ]]; then + if ((${#marked_files[@]} != ${#list[@]})); then + marked_files=("${list[@]}") + mark_dir=$PWD + else + marked_files=() + fi + + redraw + else + if [[ ${marked_files[$1]} == "${list[$1]}" ]]; then + unset 'marked_files[scroll]' + + else + marked_files[$1]="${list[$1]}" + mark_dir=$PWD + fi + + # Clear line before changing it. + printf '\e[K' + print_line "$1" + fi + + # Find the program to use. + case "$2" in + ${FFF_KEY_YANK:=y}|${FFF_KEY_YANK_ALL:=Y}) file_program=(cp -iR) ;; + ${FFF_KEY_MOVE:=m}|${FFF_KEY_MOVE_ALL:=M}) file_program=(mv -i) ;; + ${FFF_KEY_LINK:=s}|${FFF_KEY_LINK_ALL:=S}) file_program=(ln -s) ;; + + # These are 'fff' functions. + ${FFF_KEY_TRASH:=d}|${FFF_KEY_TRASH_ALL:=D}) + file_program=(trash) + ;; + + ${FFF_KEY_BULK_RENAME:=b}|${FFF_KEY_BULK_RENAME_ALL:=B}) + file_program=(bulk_rename) + ;; + esac + + status_line +} + +trash() { + # Trash a file. + cmd_line "trash [${#marked_files[@]}] items? [y/n]: " y n + + [[ $cmd_reply != y ]] && + return + + if [[ $FFF_TRASH_CMD ]]; then + # Pass all but the last argument to the user's + # custom script. command is used to prevent this function + # from conflicting with commands named "trash". + command "$FFF_TRASH_CMD" "${@:1:$#-1}" + + else + cd "$FFF_TRASH" || cmd_line "error: Can't cd to trash directory." + + if cp -alf "$@" &>/dev/null; then + rm -r "${@:1:$#-1}" + else + mv -f "$@" + fi + + # Go back to where we were. + cd "$OLDPWD" ||: + fi +} + +bulk_rename() { + # Bulk rename files using '$EDITOR'. + rename_file=${XDG_CACHE_HOME:=${HOME}/.cache}/fff/bulk_rename + marked_files=("${@:1:$#-1}") + + # Save marked files to a file and open them for editing. + printf '%s\n' "${marked_files[@]##*/}" > "$rename_file" + "${EDITOR:-vi}" "$rename_file" + + # Read the renamed files to an array. + IFS=$'\n' read -d "" -ra changed_files < "$rename_file" + + # If the user deleted a line, stop here. + ((${#marked_files[@]} != ${#changed_files[@]})) && { + rm "$rename_file" + cmd_line "error: Line mismatch in rename file. Doing nothing." + return + } + + printf '%s\n%s\n' \ + "# This file will be executed when the editor is closed." \ + "# Clear the file to abort." > "$rename_file" + + # Construct the rename commands. + for ((i=0;i<${#marked_files[@]};i++)); { + [[ ${marked_files[i]} != "${PWD}/${changed_files[i]}" ]] && { + printf 'mv -i -- %q %q\n' \ + "${marked_files[i]}" "${PWD}/${changed_files[i]}" + local renamed=1 + } + } >> "$rename_file" + + # Let the user double-check the commands and execute them. + ((renamed == 1)) && { + "${EDITOR:-vi}" "$rename_file" + + source "$rename_file" + rm "$rename_file" + } + + # Fix terminal settings after '$EDITOR'. + setup_terminal +} + +open() { + # Open directories and files. + if [[ -d $1/ ]]; then + search= + search_end_early= + cd "${1:-/}" ||: + redraw full + + elif [[ -f $1 ]]; then + # Figure out what kind of file we're working with. + get_mime_type "$1" + + # Open all text-based files in '$EDITOR'. + # Everything else goes through 'xdg-open'/'open'. + case "$mime_type" in + text/*|*x-empty*|*json*) + # If 'fff' was opened as a file picker, save the opened + # file in a file called 'opened_file'. + ((file_picker == 1)) && { + printf '%s\n' "$1" > \ + "${XDG_CACHE_HOME:=${HOME}/.cache}/fff/opened_file" + exit + } + + clear_screen + reset_terminal + "${VISUAL:-${EDITOR:-vi}}" "$1" + setup_terminal + redraw + ;; + + *) + # 'nohup': Make the process immune to hangups. + # '&': Send it to the background. + # 'disown': Detach it from the shell. + nohup "${FFF_OPENER:-${opener:-xdg-open}}" "$1" &>/dev/null & + disown + ;; + esac + fi +} + +cmd_line() { + # Write to the command_line (under status_line). + cmd_reply= + + # '\e7': Save cursor position. + # '\e[?25h': Unhide the cursor. + # '\e[%sH': Move cursor to bottom (cmd_line). + printf '\e7\e[%sH\e[?25h' "$LINES" + + # '\r\e[K': Redraw the read prompt on every keypress. + # This is mimicking what happens normally. + while IFS= read -rsn 1 -p $'\r\e[K'"${1}${cmd_reply}" read_reply; do + case $read_reply in + # Backspace. + $'\177'|$'\b') + cmd_reply=${cmd_reply%?} + + # Clear tab-completion. + unset comp c + ;; + + # Tab. + $'\t') + comp_glob="$cmd_reply*" + + # Pass the argument dirs to limit completion to directories. + [[ $2 == dirs ]] && + comp_glob="$cmd_reply*/" + + # Generate a completion list once. + [[ -z ${comp[0]} ]] && + IFS=$'\n' read -d "" -ra comp < <(compgen -G "$comp_glob") + + # On each tab press, cycle through the completion list. + [[ -n ${comp[c]} ]] && { + cmd_reply=${comp[c]} + ((c=c >= ${#comp[@]}-1 ? 0 : ++c)) + } + ;; + + # Escape / Custom 'no' value (used as a replacement for '-n 1'). + $'\e'|${3:-null}) + read "${read_flags[@]}" -rsn 2 + cmd_reply= + break + ;; + + # Enter/Return. + "") + # If there's only one search result and its a directory, + # enter it on one enter keypress. + [[ $2 == search && -d ${list[0]} ]] && ((list_total == 0)) && { + # '\e[?25l': Hide the cursor. + printf '\e[?25l' + + open "${list[0]}" + search_end_early=1 + + # Unset tab completion variables since we're done. + unset comp c + return + } + + break + ;; + + # Custom 'yes' value (used as a replacement for '-n 1'). + ${2:-null}) + cmd_reply=$read_reply + break + ;; + + # Replace '~' with '$HOME'. + "~") + cmd_reply+=$HOME + ;; + + # Anything else, add it to read reply. + *) + cmd_reply+=$read_reply + + # Clear tab-completion. + unset comp c + ;; + esac + + # Search on keypress if search passed as an argument. + [[ $2 == search ]] && { + # '\e[?25l': Hide the cursor. + printf '\e[?25l' + + # Use a greedy glob to search. + list=("$PWD"/*"$cmd_reply"*) + ((list_total=${#list[@]}-1)) + + # Draw the search results on screen. + scroll=0 + redraw + + # '\e[%sH': Move cursor back to cmd-line. + # '\e[?25h': Unhide the cursor. + printf '\e[%sH\e[?25h' "$LINES" + } + done + + # Unset tab completion variables since we're done. + unset comp c + + # '\e[2K': Clear the entire cmd_line on finish. + # '\e[?25l': Hide the cursor. + # '\e8': Restore cursor position. + printf '\e[2K\e[?25l\e8' +} + +key() { + # Handle special key presses. + [[ $1 == $'\e' ]] && { + read "${read_flags[@]}" -rsn 2 + + # Handle a normal escape key press. + [[ ${1}${REPLY} == $'\e\e['* ]] && + read "${read_flags[@]}" -rsn 1 _ + + local special_key=${1}${REPLY} + } + + case ${special_key:-$1} in + # Open list item. + # 'C' is what bash sees when the right arrow is pressed + # ('\e[C' or '\eOC'). + # '' is what bash sees when the enter/return key is pressed. + ${FFF_KEY_CHILD1:=l}|\ + ${FFF_KEY_CHILD2:=$'\e[C'}|\ + ${FFF_KEY_CHILD3:=""}|\ + ${FFF_KEY_CHILD4:=$'\eOC'}) + open "${list[scroll]}" + ;; + + # Go to the parent directory. + # 'D' is what bash sees when the left arrow is pressed + # ('\e[D' or '\eOD'). + # '\177' and '\b' are what bash sometimes sees when the backspace + # key is pressed. + ${FFF_KEY_PARENT1:=h}|\ + ${FFF_KEY_PARENT2:=$'\e[D'}|\ + ${FFF_KEY_PARENT3:=$'\177'}|\ + ${FFF_KEY_PARENT4:=$'\b'}|\ + ${FFF_KEY_PARENT5:=$'\eOD'}) + # If a search was done, clear the results and open the current dir. + if ((search == 1 && search_end_early != 1)); then + open "$PWD" + + # If '$PWD' is '/', do nothing. + elif [[ $PWD && $PWD != / ]]; then + find_previous=1 + open "${PWD%/*}" + fi + ;; + + # Scroll down. + # 'B' is what bash sees when the down arrow is pressed + # ('\e[B' or '\eOB'). + ${FFF_KEY_SCROLL_DOWN1:=j}|\ + ${FFF_KEY_SCROLL_DOWN2:=$'\e[B'}|\ + ${FFF_KEY_SCROLL_DOWN3:=$'\eOB'}) + ((scroll < list_total)) && { + ((scroll++)) + ((y < max_items)) && ((y++)) + + print_line "$((scroll-1))" + printf '\n' + print_line "$scroll" + status_line + } + ;; + + # Scroll up. + # 'A' is what bash sees when the up arrow is pressed + # ('\e[A' or '\eOA'). + ${FFF_KEY_SCROLL_UP1:=k}|\ + ${FFF_KEY_SCROLL_UP2:=$'\e[A'}|\ + ${FFF_KEY_SCROLL_UP3:=$'\eOA'}) + # '\e[1L': Insert a line above the cursor. + # '\e[A': Move cursor up a line. + ((scroll > 0)) && { + ((scroll--)) + + print_line "$((scroll+1))" + + if ((y < 2)); then + printf '\e[L' + else + printf '\e[A' + ((y--)) + fi + + print_line "$scroll" + status_line + } + ;; + + # Go to top. + ${FFF_KEY_TO_TOP:=g}) + ((scroll != 0)) && { + scroll=0 + redraw + } + ;; + + # Go to bottom. + ${FFF_KEY_TO_BOTTOM:=G}) + ((scroll != list_total)) && { + ((scroll=list_total)) + redraw + } + ;; + + # Show hidden files. + ${FFF_KEY_HIDDEN:=.}) + # 'a=a>0?0:++a': Toggle between both values of 'shopt_flags'. + # This also works for '3' or more values with + # some modification. + shopt_flags=(u s) + shopt -"${shopt_flags[((a=${a:=$FFF_HIDDEN}>0?0:++a))]}" dotglob + redraw full + ;; + + # Search. + ${FFF_KEY_SEARCH:=/}) + cmd_line "/" "search" + + # If the search came up empty, redraw the current dir. + if [[ -z ${list[*]} ]]; then + list=("${cur_list[@]}") + ((list_total=${#list[@]}-1)) + redraw + search= + else + search=1 + fi + ;; + + # Spawn a shell. + ${FFF_KEY_SHELL:=!}) + reset_terminal + + # Make fff aware of how many times it is nested. + export FFF_LEVEL + ((FFF_LEVEL++)) + + cd "$PWD" && "$SHELL" + setup_terminal + redraw full + ;; + + # Mark files for operation. + ${FFF_KEY_YANK:=y}|\ + ${FFF_KEY_MOVE:=m}|\ + ${FFF_KEY_TRASH:=d}|\ + ${FFF_KEY_LINK:=s}|\ + ${FFF_KEY_BULK_RENAME:=b}) + mark "$scroll" "$1" + ;; + + # Mark all files for operation. + ${FFF_KEY_YANK_ALL:=Y}|\ + ${FFF_KEY_MOVE_ALL:=M}|\ + ${FFF_KEY_TRASH_ALL:=D}|\ + ${FFF_KEY_LINK_ALL:=S}|\ + ${FFF_KEY_BULK_RENAME_ALL:=B}) + mark all "$1" + ;; + + # Do the file operation. + ${FFF_KEY_PASTE:=p}) + [[ ${marked_files[*]} ]] && { + [[ ! -w $PWD ]] && { + cmd_line "warn: no write access to dir." + return + } + + # Clear the screen to make room for a prompt if needed. + clear_screen + reset_terminal + + stty echo + printf '\e[1mfff\e[m: %s\n' "Running ${file_program[0]}" + "${file_program[@]}" "${marked_files[@]}" . + stty -echo + + marked_files=() + setup_terminal + redraw full + } + ;; + + # Clear all marked files. + ${FFF_KEY_CLEAR:=c}) + [[ ${marked_files[*]} ]] && { + marked_files=() + redraw + } + ;; + + # Rename list item. + ${FFF_KEY_RENAME:=r}) + [[ ! -e ${list[scroll]} ]] && + return + + cmd_line "rename ${list[scroll]##*/}: " + + [[ $cmd_reply ]] && + if [[ -e $cmd_reply ]]; then + cmd_line "warn: '$cmd_reply' already exists." + + elif [[ -w ${list[scroll]} ]]; then + mv "${list[scroll]}" "${PWD}/${cmd_reply}" + redraw full + + else + cmd_line "warn: no write access to file." + fi + ;; + + # Create a directory. + ${FFF_KEY_MKDIR:=n}) + cmd_line "mkdir: " "dirs" + + [[ $cmd_reply ]] && + if [[ -e $cmd_reply ]]; then + cmd_line "warn: '$cmd_reply' already exists." + + elif [[ -w $PWD ]]; then + mkdir -p "${PWD}/${cmd_reply}" + redraw full + + else + cmd_line "warn: no write access to dir." + fi + ;; + + # Create a file. + ${FFF_KEY_MKFILE:=f}) + cmd_line "mkfile: " + + [[ $cmd_reply ]] && + if [[ -e $cmd_reply ]]; then + cmd_line "warn: '$cmd_reply' already exists." + + elif [[ -w $PWD ]]; then + : > "$cmd_reply" + redraw full + + else + cmd_line "warn: no write access to dir." + fi + ;; + + # Show file attributes. + ${FFF_KEY_ATTRIBUTES:=x}) + [[ -e "${list[scroll]}" ]] && { + clear_screen + status_line "${list[scroll]}" + "${FFF_STAT_CMD:-stat}" "${list[scroll]}" + read -ern 1 + redraw + } + ;; + + # Toggle executable flag. + ${FFF_KEY_EXECUTABLE:=X}) + [[ -f ${list[scroll]} && -w ${list[scroll]} ]] && { + if [[ -x ${list[scroll]} ]]; then + chmod -x "${list[scroll]}" + status_line "Unset executable." + else + chmod +x "${list[scroll]}" + status_line "Set executable." + fi + } + ;; + + # Show image in terminal. + ${FFF_KEY_IMAGE:=i}) + draw_img + ;; + + # Go to dir. + ${FFF_KEY_GO_DIR:=:}) + cmd_line "go to dir: " "dirs" + + # Let 'cd' know about the current directory. + cd "$PWD" &>/dev/null ||: + + [[ $cmd_reply ]] && + cd "${cmd_reply/\~/$HOME}" &>/dev/null && + open "$PWD" + ;; + + # Go to '$HOME'. + ${FFF_KEY_GO_HOME:='~'}) + open ~ + ;; + + # Go to trash. + ${FFF_KEY_GO_TRASH:=t}) + get_os + open "$FFF_TRASH" + ;; + + # Go to previous dir. + ${FFF_KEY_PREVIOUS:=-}) + open "$OLDPWD" + ;; + + # Refresh current dir. + ${FFF_KEY_REFRESH:=e}) + open "$PWD" + ;; + + # Directory favourites. + [1-9]) + favourite="FFF_FAV${1}" + favourite="${!favourite}" + + [[ $favourite ]] && + open "$favourite" + ;; + + # Quit and store current directory in a file for CD on exit. + # Don't allow user to redefine 'q' so a bad keybinding doesn't + # remove the option to quit. + q) + : "${FFF_CD_FILE:=${XDG_CACHE_HOME:=${HOME}/.cache}/fff/.fff_d}" + + [[ -w $FFF_CD_FILE ]] && + rm "$FFF_CD_FILE" + + [[ ${FFF_CD_ON_EXIT:=1} == 1 ]] && + printf '%s\n' "$PWD" > "$FFF_CD_FILE" + + exit + ;; + esac +} + +main() { + # Handle a directory as the first argument. + # 'cd' is a cheap way of finding the full path to a directory. + # It updates the '$PWD' variable on successful execution. + # It handles relative paths as well as '../../../'. + # + # '||:': Do nothing if 'cd' fails. We don't care. + cd "${2:-$1}" &>/dev/null ||: + + [[ $1 == -v ]] && { + printf '%s\n' "fff 2.2" + exit + } + + [[ $1 == -h ]] && { + man fff + exit + } + + # Store file name in a file on open instead of using 'FFF_OPENER'. + # Used in 'fff.vim'. + [[ $1 == -p ]] && + file_picker=1 + + # bash 5 and some versions of bash 4 don't allow SIGWINCH to interrupt + # a 'read' command and instead wait for it to complete. In this case it + # causes the window to not redraw on resize until the user has pressed + # a key (causing the read to finish). This sets a read timeout on the + # affected versions of bash. + # NOTE: This shouldn't affect idle performance as the loop doesn't do + # anything until a key is pressed. + # SEE: https://github.com/dylanaraps/fff/issues/48 + ((BASH_VERSINFO[0] > 3)) && + read_flags=(-t 0.05) + + ((${FFF_LS_COLORS:=1} == 1)) && + get_ls_colors + + ((${FFF_HIDDEN:=0} == 1)) && + shopt -s dotglob + + # Create the trash and cache directory if they don't exist. + mkdir -p "${XDG_CACHE_HOME:=${HOME}/.cache}/fff" \ + "${FFF_TRASH:=${XDG_DATA_HOME:=${HOME}/.local/share}/fff/trash}" + + # 'nocaseglob': Glob case insensitively (Used for case insensitive search). + # 'nullglob': Don't expand non-matching globs to themselves. + shopt -s nocaseglob nullglob + + # Trap the exit signal (we need to reset the terminal to a useable state.) + trap 'reset_terminal' EXIT + + # Trap the window resize signal (handle window resize events). + trap 'get_term_size; redraw' WINCH + + get_os + get_term_size + get_w3m_path + setup_options + setup_terminal + redraw full + + # Vintage infinite loop. + for ((;;)); { + read "${read_flags[@]}" -srn 1 && key "$REPLY" + + # Exit if there is no longer a terminal attached. + [[ -t 1 ]] || exit 1 + } +} + +main "$@" diff --git a/bin/linux-chroot.sh b/bin/linux-chroot.sh new file mode 100755 index 0000000..f11516d --- /dev/null +++ b/bin/linux-chroot.sh @@ -0,0 +1,65 @@ +#!/bin/sh + +PATH=$1 +SHELL=$2 + +#------- Check root func ------------------- +check_root() { + if [ $(/usr/bin/id -u) -ne 0 ]; then + printf "** You must be root\n" + exit 1 + fi +} +#------------------------------------------- + +#----------- checking or create dirs ------- +check_or_mkdir() { + [ ! -d $PATH/dev ] && mkdir -pv $PATH/dev + [ ! -d $PATH/proc ] && mkdir -pv $PATH/proc + [ ! -d $PATH/sys ] && mkdir -pv $PATH/sys + [ ! -d $PATH/run ] && mkdir -pv $PATH/run +} +#------------------------------------------- + +#-------- mount virtual filesystems -------- +mount_virtual() { + /usr/bin/mount -v --bind /dev $PATH/dev + /usr/bin/mount -vt devpts devpts -o gid=5,mode=0620 $PATH/dev/pts + /usr/bin/mount -vt proc proc $PATH/proc + /usr/bin/mount -vt sysfs sysfs $PATH/sys + /usr/bin/mount -vt tmpfs tmpfs $PATH/run + if [ -h $PATH/dev/shm ]; then + /usr/bin/install -v -d -m 1777 $PATH$(/usr/bin/realpath /dev/shm) + else + /usr/bin/mount -vt tmpfs -o nosuid,nodev tmpfs $PATH/dev/shm + fi + /usr/bin/cp -v /etc/resolv.conf $PATH/etc/resolv.conf +} +#------------------------------------------- + +#------ umount virtual filesystems --------- +umount_virtual() { + /usr/bin/mountpoint -q $PATH/dev/shm && /usr/bin/umount $PATH/dev/shm + /usr/bin/umount $PATH/dev/pts + /usr/bin/umount $PATH/{sys,proc,run,dev} +} +#------------------------------------------- + +#------ get shell path func ---------------- +get_shell() { + if [[ "$SHELL" != "" ]]; then + echo $SHELL + fi + echo "/bin/sh" +} +#------------------------------------------- + +check_root +check_or_mkdir +mount_virtual +/usr/bin/chroot \ + $PATH \ + /usr/bin/env -i \ + PATH=/bin:/usr/bin:/sbin:/usr/sbin:/usr/local/bin \ + $(get_shell) +umount_virtual diff --git a/bin/lisascreens b/bin/lisascreens new file mode 100755 index 0000000..90fefe7 --- /dev/null +++ b/bin/lisascreens @@ -0,0 +1,12 @@ +#!/bin/sh + +xrandr --output HDMI-0 --mode 1680x1050 + +cvt 1920 1200 60 +xrandr --newmode "1920x1200_60.00" 193.25 1920 2056 2256 2592 1200 1203 1209 1245 -hsync +vsync + +xrandr --addmode DVI-0 1920x1200_60.00 +xrandr --output DVI-0 --mode 1920x1200_60.00 + +~/dotfiles/bin/lisascreenslayout.sh + diff --git a/bin/lisascreenslayout.sh b/bin/lisascreenslayout.sh new file mode 100755 index 0000000..ecf724f --- /dev/null +++ b/bin/lisascreenslayout.sh @@ -0,0 +1,2 @@ +#!/bin/sh +xrandr --output DisplayPort-0 --off --output HDMI-0 --mode 1680x1050 --pos 0x0 --rotate normal --output DVI-0 --mode 1920x1200_60.00 --pos 1680x0 --rotate normal --output DVI-1 --off diff --git a/bin/netdevlist b/bin/netdevlist new file mode 100755 index 0000000..1b1fae2 --- /dev/null +++ b/bin/netdevlist @@ -0,0 +1,4 @@ +#!/bin/sh + +ls --color /sys/class/net + diff --git a/bin/pfetch b/bin/pfetch new file mode 100755 index 0000000..d47b878 --- /dev/null +++ b/bin/pfetch @@ -0,0 +1,1958 @@ +#!/bin/sh +# +# pfetch - Simple POSIX sh fetch script. + +# Wrapper around all escape sequences used by pfetch to allow for +# greater control over which sequences are used (if any at all). +esc() { + case $1 in + CUU) e="${esc_c}[${2}A" ;; # cursor up + CUD) e="${esc_c}[${2}B" ;; # cursor down + CUF) e="${esc_c}[${2}C" ;; # cursor right + CUB) e="${esc_c}[${2}D" ;; # cursor left + + # text formatting + SGR) + case ${PF_COLOR:=1} in + (1) + e="${esc_c}[${2}m" + ;; + + (0) + # colors disabled + e= + ;; + esac + ;; + + # line wrap + DECAWM) + case $TERM in + (dumb | minix | cons25) + # not supported + e= + ;; + + (*) + e="${esc_c}[?7${2}" + ;; + esac + ;; + esac +} + +# Print a sequence to the terminal. +esc_p() { + esc "$@" + printf '%s' "$e" +} + +# This is just a simple wrapper around 'command -v' to avoid +# spamming '>/dev/null' throughout this function. This also guards +# against aliases and functions. +has() { + _cmd=$(command -v "$1") 2>/dev/null || return 1 + [ -x "$_cmd" ] || return 1 +} + +log() { + # The 'log()' function handles the printing of information. + # In 'pfetch' (and 'neofetch'!) the printing of the ascii art and info + # happen independently of each other. + # + # The size of the ascii art is stored and the ascii is printed first. + # Once the ascii is printed, the cursor is located right below the art + # (See marker $[1]). + # + # Using the stored ascii size, the cursor is then moved to marker $[2]. + # This is simply a cursor up escape sequence using the "height" of the + # ascii art. + # + # 'log()' then moves the cursor to the right the "width" of the ascii art + # with an additional amount of padding to add a gap between the art and + # the information (See marker $[3]). + # + # When 'log()' has executed, the cursor is then located at marker $[4]. + # When 'log()' is run a second time, the next line of information is + # printed, moving the cursor to marker $[5]. + # + # Markers $[4] and $[5] repeat all the way down through the ascii art + # until there is no more information left to print. + # + # Every time 'log()' is called the script keeps track of how many lines + # were printed. When printing is complete the cursor is then manually + # placed below the information and the art according to the "heights" + # of both. + # + # The math is simple: move cursor down $((ascii_height - info_height)). + # If the aim is to move the cursor from marker $[5] to marker $[6], + # plus the ascii height is 8 while the info height is 2 it'd be a move + # of 6 lines downwards. + # + # However, if the information printed is "taller" (takes up more lines) + # than the ascii art, the cursor isn't moved at all! + # + # Once the cursor is at marker $[6], the script exits. This is the gist + # of how this "dynamic" printing and layout works. + # + # This method allows ascii art to be stored without markers for info + # and it allows for easy swapping of info order and amount. + # + # $[2] ___ $[3] goldie@KISS + # $[4](.· | $[5] os KISS Linux + # (<> | + # / __ \ + # ( / \ /| + # _/\ __)/_) + # \/-____\/ + # $[1] + # + # $[6] /home/goldie $ + + # End here if no data was found. + [ "$2" ] || return + + # Store the values of '$1' and '$3' as we reset the argument list below. + name=$1 + use_seperator=$3 + + # Use 'set --' as a means of stripping all leading and trailing + # white-space from the info string. This also normalizes all + # white-space inside of the string. + # + # Disable the shellcheck warning for word-splitting + # as it's safe and intended ('set -f' disables globbing). + # shellcheck disable=2046,2086 + { + set -f + set +f -- $2 + info=$* + } + + # Move the cursor to the right, the width of the ascii art with an + # additional gap for text spacing. + esc_p CUF "$ascii_width" + + # Print the info name and color the text. + esc_p SGR "3${PF_COL1-4}"; + esc_p SGR 1 + printf '%s' "$name" + esc_p SGR 0 + + # Print the info name and info data separator, if applicable. + [ "$use_seperator" ] || printf %s "$PF_SEP" + + # Move the cursor backward the length of the *current* info name and + # then move it forwards the length of the *longest* info name. This + # aligns each info data line. + esc_p CUB "${#name}" + esc_p CUF "${PF_ALIGN:-$info_length}" + + # Print the info data, color it and strip all leading whitespace + # from the string. + esc_p SGR "3${PF_COL2-9}" + printf '%s' "$info" + esc_p SGR 0 + printf '\n' + + # Keep track of the number of times 'log()' has been run. + info_height=$((${info_height:-0} + 1)) +} + +get_title() { + # Username is retrieved by first checking '$USER' with a fallback + # to the 'id -un' command. + user=${USER:-$(id -un)} + + # Hostname is retrieved by first checking '$HOSTNAME' with a fallback + # to the 'hostname' command. + # + # Disable the warning about '$HOSTNAME' being undefined in POSIX sh as + # the intention for using it is allowing the user to overwrite the + # value on invocation. + # shellcheck disable=3028,2039 + hostname=${HOSTNAME:-${hostname:-$(hostname)}} + + # If the hostname is still not found, fallback to the contents of the + # /etc/hostname file. + [ "$hostname" ] || read -r hostname < /etc/hostname + + # Add escape sequences for coloring to user and host name. As we embed + # them directly in the arguments passed to log(), we cannot use esc_p(). + esc SGR 1 + user=$e$user + esc SGR "3${PF_COL3:-1}" + user=$e$user + esc SGR 1 + user=$user$e + esc SGR 1 + hostname=$e$hostname + esc SGR "3${PF_COL3:-1}" + hostname=$e$hostname + + log "${user}@${hostname}" " " " " >&6 +} + +get_os() { + # This function is called twice, once to detect the distribution name + # for the purposes of picking an ascii art early and secondly to display + # the distribution name in the info output (if enabled). + # + # On first run, this function displays _nothing_, only on the second + # invocation is 'log()' called. + [ "$distro" ] && { + log os "$distro" >&6 + return + } + + case $os in + (Linux*) + # Some Linux distributions (which are based on others) + # fail to identify as they **do not** change the upstream + # distribution's identification packages or files. + # + # It is senseless to add a special case in the code for + # each and every distribution (which _is_ technically no + # different from what it is based on) as they're either too + # lazy to modify upstream's identification files or they + # don't have the know-how (or means) to ship their own + # lsb-release package. + # + # This causes users to think there's a bug in system detection + # tools like neofetch or pfetch when they technically *do* + # function correctly. + # + # Exceptions are made for distributions which are independent, + # not based on another distribution or follow different + # standards. + # + # This applies only to distributions which follow the standard + # by shipping unmodified identification files and packages + # from their respective upstreams. + if has lsb_release; then + distro=$(lsb_release -sd) + + # Android detection works by checking for the existence of + # the follow two directories. I don't think there's a simpler + # method than this. + elif [ -d /system/app ] && [ -d /system/priv-app ]; then + distro="Android $(getprop ro.build.version.release)" + + elif [ -f /etc/os-release ]; then + # This used to be a simple '. /etc/os-release' but I believe + # this is insecure as we blindly executed whatever was in the + # file. This parser instead simply handles 'key=val', treating + # the file contents as plain-text. + while IFS='=' read -r key val; do + case $key in + (PRETTY_NAME) + distro=$val + ;; + esac + done < /etc/os-release + + else + # Special cases for (independent) distributions which + # don't follow any os-release/lsb standards whatsoever. + has crux && distro=$(crux) + has guix && distro='Guix System' + fi + + # 'os-release' and 'lsb_release' sometimes add quotes + # around the distribution name, strip them. + distro=${distro##[\"\']} + distro=${distro%%[\"\']} + + # Check to see if we're running Bedrock Linux which is + # very unique. This simply checks to see if the user's + # PATH contains a Bedrock specific value. + case $PATH in + (*/bedrock/cross/*) + distro='Bedrock Linux' + ;; + esac + + # Check to see if Linux is running in Windows 10 under + # WSL1 (Windows subsystem for Linux [version 1]) and + # append a string accordingly. + # + # If the kernel version string ends in "-Microsoft", + # we're very likely running under Windows 10 in WSL1. + if [ "$WSLENV" ]; then + distro="${distro}${WSLENV+ on Windows 10 [WSL2]}" + + # Check to see if Linux is running in Windows 10 under + # WSL2 (Windows subsystem for Linux [version 2]) and + # append a string accordingly. + # + # This checks to see if '$WSLENV' is defined. This + # appends the Windows 10 string even if '$WSLENV' is + # empty. We only need to check that is has been _exported_. + elif [ -z "${kernel%%*-Microsoft}" ]; then + distro="$distro on Windows 10 [WSL1]" + fi + ;; + + (Darwin*) + # Parse the SystemVersion.plist file to grab the macOS + # version. The file is in the following format: + # + # ProductVersion + # 10.14.6 + # + # 'IFS' is set to '<>' to enable splitting between the + # keys and a second 'read' is used to operate on the + # next line directly after a match. + # + # '_' is used to nullify a field. '_ _ line _' basically + # says "populate $line with the third field's contents". + while IFS='<>' read -r _ _ line _; do + case $line in + # Match 'ProductVersion' and read the next line + # directly as it contains the key's value. + ProductVersion) + IFS='<>' read -r _ _ mac_version _ + continue + ;; + + ProductName) + IFS='<>' read -r _ _ mac_product _ + continue + ;; + esac + done < /System/Library/CoreServices/SystemVersion.plist + + # Use the ProductVersion to determine which macOS/OS X codename + # the system has. As far as I'm aware there's no "dynamic" way + # of grabbing this information. + case $mac_version in + (10.4*) distro='Mac OS X Tiger' ;; + (10.5*) distro='Mac OS X Leopard' ;; + (10.6*) distro='Mac OS X Snow Leopard' ;; + (10.7*) distro='Mac OS X Lion' ;; + (10.8*) distro='OS X Mountain Lion' ;; + (10.9*) distro='OS X Mavericks' ;; + (10.10*) distro='OS X Yosemite' ;; + (10.11*) distro='OS X El Capitan' ;; + (10.12*) distro='macOS Sierra' ;; + (10.13*) distro='macOS High Sierra' ;; + (10.14*) distro='macOS Mojave' ;; + (10.15*) distro='macOS Catalina' ;; + (11*) distro='macOS Big Sur' ;; + (12*) distro='macOS Monterey' ;; + (*) distro='macOS' ;; + esac + + # Use the ProductName to determine if we're running in iOS. + case $mac_product in + (iP*) distro='iOS' ;; + esac + + distro="$distro $mac_version" + ;; + + (Haiku) + # Haiku uses 'uname -v' for version information + # instead of 'uname -r' which only prints '1'. + distro=$(uname -sv) + ;; + + (Minix|DragonFly) + distro="$os $kernel" + + # Minix and DragonFly don't support the escape + # sequences used, clear the exit trap. + trap '' EXIT + ;; + + (SunOS) + # Grab the first line of the '/etc/release' file + # discarding everything after '('. + IFS='(' read -r distro _ < /etc/release + ;; + + (OpenBSD*) + # Show the OpenBSD version type (current if present). + # kern.version=OpenBSD 6.6-current (GENERIC.MP) ... + IFS=' =' read -r _ distro openbsd_ver _ <<-EOF + $(sysctl kern.version) + EOF + + distro="$distro $openbsd_ver" + ;; + + (FreeBSD) + distro="$os $(freebsd-version)" + ;; + + (*) + # Catch all to ensure '$distro' is never blank. + # This also handles the BSDs. + distro="$os $kernel" + ;; + esac +} + +get_kernel() { + case $os in + # Don't print kernel output on some systems as the + # OS name includes it. + (*BSD*|Haiku|Minix) + return + ;; + esac + + # '$kernel' is the cached output of 'uname -r'. + log kernel "$kernel" >&6 +} + +get_host() { + case $os in + (Linux*) + # Despite what these files are called, version doesn't + # always contain the version nor does name always contain + # the name. + read -r name < /sys/devices/virtual/dmi/id/product_name + read -r version < /sys/devices/virtual/dmi/id/product_version + read -r model < /sys/firmware/devicetree/base/model + + host="$name $version $model" + ;; + + (Darwin* | FreeBSD* | DragonFly*) + host=$(sysctl -n hw.model) + ;; + + (NetBSD*) + host=$(sysctl -n machdep.dmi.system-vendor \ + machdep.dmi.system-product) + ;; + + (OpenBSD*) + host=$(sysctl -n hw.version) + ;; + + (*BSD* | Minix) + host=$(sysctl -n hw.vendor hw.product) + ;; + esac + + # Turn the host string into an argument list so we can iterate + # over it and remove OEM strings and other information which + # shouldn't be displayed. + # + # Disable the shellcheck warning for word-splitting + # as it's safe and intended ('set -f' disables globbing). + # shellcheck disable=2046,2086 + { + set -f + set +f -- $host + host= + } + + # Iterate over the host string word by word as a means of stripping + # unwanted and OEM information from the string as a whole. + # + # This could have been implemented using a long 'sed' command with + # a list of word replacements, however I want to show that something + # like this is possible in pure sh. + # + # This string reconstruction is needed as some OEMs either leave the + # identification information as "To be filled by OEM", "Default", + # "undefined" etc and we shouldn't print this to the screen. + for word do + # This works by reconstructing the string by excluding words + # found in the "blacklist" below. Only non-matches are appended + # to the final host string. + case $word in + (To | [Bb]e | [Ff]illed | [Bb]y | O.E.M. | OEM |\ + Not | Applicable | Specified | System | Product | Name |\ + Version | Undefined | Default | string | INVALID | � | os |\ + Type1ProductConfigId ) + continue + ;; + esac + + host="$host$word " + done + + # '$arch' is the cached output from 'uname -m'. + log host "${host:-$arch}" >&6 +} + +get_uptime() { + # Uptime works by retrieving the data in total seconds and then + # converting that data into days, hours and minutes using simple + # math. + case $os in + (Linux* | Minix* | SerenityOS*) + IFS=. read -r s _ < /proc/uptime + ;; + + (Darwin* | *BSD* | DragonFly*) + s=$(sysctl -n kern.boottime) + + # Extract the uptime in seconds from the following output: + # [...] { sec = 1271934886, usec = 667779 } Thu Apr 22 12:14:46 2010 + s=${s#*=} + s=${s%,*} + + # The uptime format from 'sysctl' needs to be subtracted from + # the current time in seconds. + s=$(($(date +%s) - s)) + ;; + + (Haiku) + # The boot time is returned in microseconds, convert it to + # regular seconds. + s=$(($(system_time) / 1000000)) + ;; + + (SunOS) + # Split the output of 'kstat' on '.' and any white-space + # which exists in the command output. + # + # The output is as follows: + # unix:0:system_misc:snaptime 14809.906993005 + # + # The parser extracts: ^^^^^ + IFS=' .' read -r _ s _ <<-EOF + $(kstat -p unix:0:system_misc:snaptime) + EOF + ;; + + (IRIX) + # Grab the uptime in a pretty format. Usually, + # 00:00:00 from the 'ps' command. + t=$(LC_ALL=POSIX ps -o etime= -p 1) + + # Split the pretty output into days or hours + # based on the uptime. + case $t in + (*-*) d=${t%%-*} t=${t#*-} ;; + (*:*:*) h=${t%%:*} t=${t#*:} ;; + esac + + h=${h#0} t=${t#0} + + # Convert the split pretty fields back into + # seconds so we may re-convert them to our format. + s=$((${d:-0}*86400 + ${h:-0}*3600 + ${t%%:*}*60 + ${t#*:})) + ;; + esac + + # Convert the uptime from seconds into days, hours and minutes. + d=$((s / 60 / 60 / 24)) + h=$((s / 60 / 60 % 24)) + m=$((s / 60 % 60)) + + # Only append days, hours and minutes if they're non-zero. + case "$d" in ([!0]*) uptime="${uptime}${d}d "; esac + case "$h" in ([!0]*) uptime="${uptime}${h}h "; esac + case "$m" in ([!0]*) uptime="${uptime}${m}m "; esac + + log uptime "${uptime:-0m}" >&6 +} + +get_pkgs() { + # This works by first checking for which package managers are + # installed and finally by printing each package manager's + # package list with each package one per line. + # + # The output from this is then piped to 'wc -l' to count each + # line, giving us the total package count of whatever package + # managers are installed. + packages=$( + case $os in + (Linux*) + # Commands which print packages one per line. + has bonsai && bonsai list + has crux && pkginfo -i + has pacman-key && pacman -Qq + has dpkg && dpkg-query -f '.\n' -W + has rpm && rpm -qa + has xbps-query && xbps-query -l + has apk && apk info + has guix && guix package --list-installed + has opkg && opkg list-installed + + # Directories containing packages. + has kiss && printf '%s\n' /var/db/kiss/installed/*/ + has cpt-list && printf '%s\n' /var/db/cpt/installed/*/ + has brew && printf '%s\n' "$(brew --cellar)/"* + has emerge && printf '%s\n' /var/db/pkg/*/*/ + has pkgtool && printf '%s\n' /var/log/packages/* + has eopkg && printf '%s\n' /var/lib/eopkg/package/* + + # 'nix' requires two commands. + has nix-store && { + nix-store -q --requisites /run/current-system/sw + nix-store -q --requisites ~/.nix-profile + } + ;; + + (Darwin*) + # Commands which print packages one per line. + has pkgin && pkgin list + has dpkg && dpkg-query -f '.\n' -W + + # Directories containing packages. + has brew && printf '%s\n' /usr/local/Cellar/* + + # 'port' prints a single line of output to 'stdout' + # when no packages are installed and exits with + # success causing a false-positive of 1 package + # installed. + # + # 'port' should really exit with a non-zero code + # in this case to allow scripts to cleanly handle + # this behavior. + has port && { + pkg_list=$(port installed) + + case "$pkg_list" in + ("No ports are installed.") + # do nothing + ;; + + (*) + printf '%s\n' "$pkg_list" + ;; + esac + } + ;; + + (FreeBSD*|DragonFly*) + pkg info + ;; + + (OpenBSD*) + printf '%s\n' /var/db/pkg/*/ + ;; + + (NetBSD*) + pkg_info + ;; + + (Haiku) + printf '%s\n' /boot/system/package-links/* + ;; + + (Minix) + printf '%s\n' /usr/pkg/var/db/pkg/*/ + ;; + + (SunOS) + has pkginfo && pkginfo -i + has pkg && pkg list + ;; + + (IRIX) + versions -b + ;; + + (SerenityOS) + while IFS=" " read -r type _; do + [ "$type" != dependency ] && + printf "\n" + done < /usr/Ports/packages.db + ;; + esac | wc -l + ) + + # 'wc -l' can have leading and/or trailing whitespace + # depending on the implementation, so strip them. + # Procedure explained at https://github.com/dylanaraps/pure-sh-bible + # (trim-leading-and-trailing-white-space-from-string) + packages=${packages#"${packages%%[![:space:]]*}"} + packages=${packages%"${packages##*[![:space:]]}"} + + case $os in + # IRIX's package manager adds 3 lines of extra + # output which we must account for here. + (IRIX) + packages=$((packages - 3)) + ;; + + # OpenBSD's wc prints whitespace before the output + # which needs to be stripped. + (OpenBSD) + packages=$((packages)) + ;; + esac + + case $packages in + (1?*|[2-9]*) + log pkgs "$packages" >&6 + ;; + esac +} + +get_memory() { + case $os in + # Used memory is calculated using the following "formula": + # MemUsed = MemTotal + Shmem - MemFree - Buffers - Cached - SReclaimable + # Source: https://github.com/KittyKatt/screenFetch/issues/386 + (Linux*) + # Parse the '/proc/meminfo' file splitting on ':' and 'k'. + # The format of the file is 'key: 000kB' and an additional + # split is used on 'k' to filter out 'kB'. + while IFS=':k ' read -r key val _; do + case $key in + (MemTotal) + mem_used=$((mem_used + val)) + mem_full=$val + ;; + + (Shmem) + mem_used=$((mem_used + val)) + ;; + + (MemFree | Buffers | Cached | SReclaimable) + mem_used=$((mem_used - val)) + ;; + + # If detected this will be used over the above calculation + # for mem_used. Available since Linux 3.14rc. + # See kernel commit 34e431b0ae398fc54ea69ff85ec700722c9da773 + (MemAvailable) + mem_avail=$val + ;; + esac + done < /proc/meminfo + + case $mem_avail in + (*[0-9]*) + mem_used=$(((mem_full - mem_avail) / 1024)) + ;; + + *) + mem_used=$((mem_used / 1024)) + ;; + esac + + mem_full=$((mem_full / 1024)) + ;; + + # Used memory is calculated using the following "formula": + # (wired + active + occupied) * 4 / 1024 + (Darwin*) + mem_full=$(($(sysctl -n hw.memsize) / 1024 / 1024)) + + # Parse the 'vmstat' file splitting on ':' and '.'. + # The format of the file is 'key: 000.' and an additional + # split is used on '.' to filter it out. + while IFS=:. read -r key val; do + case $key in + (*' wired'*|*' active'*|*' occupied'*) + mem_used=$((mem_used + ${val:-0})) + ;; + esac + + # Using '<<-EOF' is the only way to loop over a command's + # output without the use of a pipe ('|'). + # This ensures that any variables defined in the while loop + # are still accessible in the script. + done <<-EOF + $(vm_stat) + EOF + + mem_used=$((mem_used * 4 / 1024)) + ;; + + (OpenBSD*) + mem_full=$(($(sysctl -n hw.physmem) / 1024 / 1024)) + + # This is a really simpler parser for 'vmstat' which grabs + # the used memory amount in a lazy way. 'vmstat' prints 3 + # lines of output with the needed value being stored in the + # final line. + # + # This loop simply grabs the 3rd element of each line until + # the EOF is reached. Each line overwrites the value of the + # previous one so we're left with what we wanted. This isn't + # slow as only 3 lines are parsed. + while read -r _ _ line _; do + mem_used=${line%%M} + + # Using '<<-EOF' is the only way to loop over a command's + # output without the use of a pipe ('|'). + # This ensures that any variables defined in the while loop + # are still accessible in the script. + done <<-EOF + $(vmstat) + EOF + ;; + + # Used memory is calculated using the following "formula": + # mem_full - ((inactive + free + cache) * page_size / 1024) + (FreeBSD*|DragonFly*) + mem_full=$(($(sysctl -n hw.physmem) / 1024 / 1024)) + + # Use 'set --' to store the output of the command in the + # argument list. POSIX sh has no arrays but this is close enough. + # + # Disable the shellcheck warning for word-splitting + # as it's safe and intended ('set -f' disables globbing). + # shellcheck disable=2046 + { + set -f + set +f -- $(sysctl -n hw.pagesize \ + vm.stats.vm.v_inactive_count \ + vm.stats.vm.v_free_count \ + vm.stats.vm.v_cache_count) + } + + # Calculate the amount of used memory. + # $1: hw.pagesize + # $2: vm.stats.vm.v_inactive_count + # $3: vm.stats.vm.v_free_count + # $4: vm.stats.vm.v_cache_count + mem_used=$((mem_full - (($2 + $3 + $4) * $1 / 1024 / 1024))) + ;; + + (NetBSD*) + mem_full=$(($(sysctl -n hw.physmem64) / 1024 / 1024)) + + # NetBSD implements a lot of the Linux '/proc' filesystem, + # this uses the same parser as the Linux memory detection. + while IFS=':k ' read -r key val _; do + case $key in + (MemFree) + mem_free=$((val / 1024)) + break + ;; + esac + done < /proc/meminfo + + mem_used=$((mem_full - mem_free)) + ;; + + (Haiku) + # Read the first line of 'sysinfo -mem' splitting on + # '(', ' ', and ')'. The needed information is then + # stored in the 5th and 7th elements. Using '_' "consumes" + # an element allowing us to proceed to the next one. + # + # The parsed format is as follows: + # 3501142016 bytes free (used/max 792645632 / 4293787648) + IFS='( )' read -r _ _ _ _ mem_used _ mem_full <<-EOF + $(sysinfo -mem) + EOF + + mem_used=$((mem_used / 1024 / 1024)) + mem_full=$((mem_full / 1024 / 1024)) + ;; + + (Minix) + # Minix includes the '/proc' filesystem though the format + # differs from Linux. The '/proc/meminfo' file is only a + # single line with space separated elements and elements + # 2 and 3 contain the total and free memory numbers. + read -r _ mem_full mem_free _ < /proc/meminfo + + mem_used=$(((mem_full - mem_free) / 1024)) + mem_full=$(( mem_full / 1024)) + ;; + + (SunOS) + hw_pagesize=$(pagesize) + + # 'kstat' outputs memory in the following format: + # unix:0:system_pages:pagestotal 1046397 + # unix:0:system_pages:pagesfree 885018 + # + # This simply uses the first "element" (white-space + # separated) as the key and the second element as the + # value. + # + # A variable is then assigned based on the key. + while read -r key val; do + case $key in + (*total) + pages_full=$val + ;; + + (*free) + pages_free=$val + ;; + esac + done <<-EOF + $(kstat -p unix:0:system_pages:pagestotal \ + unix:0:system_pages:pagesfree) + EOF + + mem_full=$((pages_full * hw_pagesize / 1024 / 1024)) + mem_free=$((pages_free * hw_pagesize / 1024 / 1024)) + mem_used=$((mem_full - mem_free)) + ;; + + (IRIX) + # Read the memory information from the 'top' command. Parse + # and split each line until we reach the line starting with + # "Memory". + # + # Example output: Memory: 160M max, 147M avail, ..... + while IFS=' :' read -r label mem_full _ mem_free _; do + case $label in + (Memory) + mem_full=${mem_full%M} + mem_free=${mem_free%M} + break + ;; + esac + done <<-EOF + $(top -n) + EOF + + mem_used=$((mem_full - mem_free)) + ;; + + (SerenityOS) + IFS='{}' read -r _ memstat _ < /proc/memstat + + set -f -- "$IFS" + IFS=, + + for pair in $memstat; do + case $pair in + (*user_physical_allocated*) + mem_used=${pair##*:} + ;; + + (*user_physical_available*) + mem_free=${pair##*:} + ;; + esac + done + + IFS=$1 + set +f -- + + mem_used=$((mem_used * 4096 / 1024 / 1024)) + mem_free=$((mem_free * 4096 / 1024 / 1024)) + + mem_full=$((mem_used + mem_free)) + ;; + esac + + log memory "${mem_used:-?}M / ${mem_full:-?}M" >&6 +} + +get_wm() { + case $os in + (Darwin*) + # Don't display window manager on macOS. + ;; + + (*) + # xprop can be used to grab the window manager's properties + # which contains the window manager's name under '_NET_WM_NAME'. + # + # The upside to using 'xprop' is that you don't need to hardcode + # a list of known window manager names. The downside is that + # not all window managers conform to setting the '_NET_WM_NAME' + # atom.. + # + # List of window managers which fail to set the name atom: + # catwm, fvwm, dwm, 2bwm, monster, wmaker and sowm [mine! ;)]. + # + # The final downside to this approach is that it does _not_ + # support Wayland environments. The only solution which supports + # Wayland is the 'ps' parsing mentioned below. + # + # A more naive implementation is to parse the last line of + # '~/.xinitrc' to extract the second white-space separated + # element. + # + # The issue with an approach like this is that this line data + # does not always equate to the name of the window manager and + # could in theory be _anything_. + # + # This also fails when the user launches xorg through a display + # manager or other means. + # + # + # Another naive solution is to parse 'ps' with a hardcoded list + # of window managers to detect the current window manager (based + # on what is running). + # + # The issue with this approach is the need to hardcode and + # maintain a list of known window managers. + # + # Another issue is that process names do not always equate to + # the name of the window manager. False-positives can happen too. + # + # This is the only solution which supports Wayland based + # environments sadly. It'd be nice if some kind of standard were + # established to identify Wayland environments. + # + # pfetch's goal is to remain _simple_, if you'd like a "full" + # implementation of window manager detection use 'neofetch'. + # + # Neofetch use a combination of 'xprop' and 'ps' parsing to + # support all window managers (including non-conforming and + # Wayland) though it's a lot more complicated! + + # Don't display window manager if X isn't running. + [ "$DISPLAY" ] || return + + # This is a two pass call to xprop. One call to get the window + # manager's ID and another to print its properties. + has xprop && { + # The output of the ID command is as follows: + # _NET_SUPPORTING_WM_CHECK: window id # 0x400000 + # + # To extract the ID, everything before the last space + # is removed. + id=$(xprop -root -notype _NET_SUPPORTING_WM_CHECK) + id=${id##* } + + # The output of the property command is as follows: + # _NAME 8t + # _NET_WM_PID = 252 + # _NET_WM_NAME = "bspwm" + # _NET_SUPPORTING_WM_CHECK: window id # 0x400000 + # WM_CLASS = "wm", "Bspwm" + # + # To extract the name, everything before '_NET_WM_NAME = \"' + # is removed and everything after the next '"' is removed. + wm=$(xprop -id "$id" -notype -len 25 -f _NET_WM_NAME 8t) + } + + # Handle cases of a window manager _not_ populating the + # '_NET_WM_NAME' atom. Display nothing in this case. + case $wm in + (*'_NET_WM_NAME = '*) + wm=${wm##*_NET_WM_NAME = \"} + wm=${wm%%\"*} + ;; + + (*) + # Fallback to checking the process list + # for the select few window managers which + # don't set '_NET_WM_NAME'. + while read -r ps_line; do + case $ps_line in + (*catwm*) wm=catwm ;; + (*fvwm*) wm=fvwm ;; + (*dwm*) wm=dwm ;; + (*2bwm*) wm=2bwm ;; + (*monsterwm*) wm=monsterwm ;; + (*wmaker*) wm='Window Maker' ;; + (*sowm*) wm=sowm ;; + (*penrose*) wm=penrose ;; + esac + done <<-EOF + $(ps x) + EOF + ;; + esac + ;; + esac + + log wm "$wm" >&6 +} + + +get_de() { + # This only supports Xorg related desktop environments though + # this is fine as knowing the desktop environment on Windows, + # macOS etc is useless (they'll always report the same value). + # + # Display the value of '$XDG_CURRENT_DESKTOP', if it's empty, + # display the value of '$DESKTOP_SESSION'. + log de "${XDG_CURRENT_DESKTOP:-$DESKTOP_SESSION}" >&6 +} + +get_shell() { + # Display the basename of the '$SHELL' environment variable. + log shell "${SHELL##*/}" >&6 +} + +get_editor() { + # Display the value of '$VISUAL', if it's empty, display the + # value of '$EDITOR'. + editor=${VISUAL:-"$EDITOR"} + + log editor "${editor##*/}" >&6 +} + +get_palette() { + # Print the first 8 terminal colors. This uses the existing + # sequences to change text color with a sequence prepended + # to reverse the foreground and background colors. + # + # This allows us to save hardcoding a second set of sequences + # for background colors. + # + # False positive. + # shellcheck disable=2154 + { + esc SGR 7 + palette="$e$c1 $c1 $c2 $c2 $c3 $c3 $c4 $c4 $c5 $c5 $c6 $c6 " + esc SGR 0 + palette="$palette$e" + } + + # Print the palette with a new-line before and afterwards but no seperator. + printf '\n' >&6 + log "$palette + " " " " " >&6 +} + +get_ascii() { + # This is a simple function to read the contents of + # an ascii file from 'stdin'. It allows for the use + # of '<<-EOF' to prevent the break in indentation in + # this source code. + # + # This function also sets the text colors according + # to the ascii color. + read_ascii() { + # 'PF_COL1': Set the info name color according to ascii color. + # 'PF_COL3': Set the title color to some other color. ¯\_(ツ)_/¯ + PF_COL1=${PF_COL1:-${1:-7}} + PF_COL3=${PF_COL3:-$((${1:-7}%8+1))} + + # POSIX sh has no 'var+=' so 'var=${var}append' is used. What's + # interesting is that 'var+=' _is_ supported inside '$(())' + # (arithmetic) though there's no support for 'var++/var--'. + # + # There is also no $'\n' to add a "literal"(?) newline to the + # string. The simplest workaround being to break the line inside + # the string (though this has the caveat of breaking indentation). + while IFS= read -r line; do + ascii="$ascii$line +" + done + } + + # This checks for ascii art in the following order: + # '$1': Argument given to 'get_ascii()' directly. + # '$PF_ASCII': Environment variable set by user. + # '$distro': The detected distribution name. + # '$os': The name of the operating system/kernel. + # + # NOTE: Each ascii art below is indented using tabs, this + # allows indentation to continue naturally despite + # the use of '<<-EOF'. + # + # False positive. + # shellcheck disable=2154 + case ${1:-${PF_ASCII:-${distro:-$os}}} in + ([Aa]lpine*) + read_ascii 4 <<-EOF + ${c4} /\\ /\\ + /${c7}/ ${c4}\\ \\ + /${c7}/ ${c4}\\ \\ + /${c7}// ${c4}\\ \\ + ${c7}// ${c4}\\ \\ + ${c4}\\ + EOF + ;; + + ([Aa]ndroid*) + read_ascii 2 <<-EOF + ${c2} ;, ,; + ${c2} ';,.-----.,;' + ${c2} ,' ', + ${c2} / O O \\ + ${c2}| | + ${c2}'-----------------' + EOF + ;; + + ([Aa]rch*) + read_ascii 4 <<-EOF + ${c6} /\\ + ${c6} / \\ + ${c6} /\\ \\ + ${c4} / \\ + ${c4} / ,, \\ + ${c4} / | | -\\ + ${c4} /_-'' ''-_\\ + EOF + ;; + + ([Aa]rco*) + read_ascii 4 <<-EOF + ${c4} /\\ + ${c4} / \\ + ${c4} / /\\ \\ + ${c4} / / \\ \\ + ${c4} / / \\ \\ + ${c4} / / _____\\ \\ + ${c4}/_/ \`----.\\_\\ + EOF + ;; + + ([Aa]rtix*) + read_ascii 6 <<-EOF + ${c4} /\\ + ${c4} / \\ + ${c4} /\`'.,\\ + ${c4} / ', + ${c4} / ,\`\\ + ${c4} / ,.'\`. \\ + ${c4}/.,'\` \`'.\\ + EOF + ;; + + ([Bb]edrock*) + read_ascii 4 <<-EOF + ${c7}__ + ${c7}\\ \\___ + ${c7} \\ _ \\ + ${c7} \\___/ + EOF + ;; + + ([Bb]uildroot*) + read_ascii 3 <<-EOF + ${c3} ___ + ${c3} / \` \\ + ${c3}| : :| + ${c3}-. _:__.- + ${c3} \` ---- \` + EOF + ;; + + ([Cc]el[Oo][Ss]*) + read_ascii 5 0 <<-EOF + ${c5} .////\\\\\//\\. + ${c5} //_ \\\\ + ${c5} /_ ${c7}############## + ${c5} // *\\ + ${c7}############### ${c5}|# + ${c5} \/ */ + ${c5} \* ${c7}############## + ${c5} */, .// + ${c5} '_///\\\\\//_' + EOF + ;; + + ([Cc]ent[Oo][Ss]*) + read_ascii 5 <<-EOF + ${c2} ____${c3}^${c5}____ + ${c2} |\\ ${c3}|${c5} /| + ${c2} | \\ ${c3}|${c5} / | + ${c5}<---- ${c4}----> + ${c4} | / ${c2}|${c3} \\ | + ${c4} |/__${c2}|${c3}__\\| + ${c2} v + EOF + ;; + + ([Cc]rystal*[Ll]inux) + read_ascii 5 5 <<-EOF + ${c5} -//. + ${c5} -//. + ${c5} -//. . + ${c5} -//. '//- + ${c5} /+: :+/ + ${c5} .//' .//. + ${c5} . .//. + ${c5} .//. + ${c5} .//. + EOF + ;; + + ([Dd]ahlia*) + read_ascii 1 <<-EOF + ${c1} _ + ${c1} ___/ \\___ + ${c1} | _-_ | + ${c1} | / \ | + ${c1}/ | | \\ + ${c1}\\ | | / + ${c1} | \ _ _ / | + ${c1} |___ - ___| + ${c1} \\_/ + EOF + ;; + + ([Dd]ebian*) + read_ascii 1 <<-EOF + ${c1} _____ + ${c1} / __ \\ + ${c1}| / | + ${c1}| \\___- + ${c1}-_ + ${c1} --_ + EOF + ;; + + ([Dd]evuan*) + read_ascii 6 <<-EOF + ${c4} ..:::. + ${c4} ..-==- + ${c4} .+#: + ${c4} =@@ + ${c4} :+%@#: + ${c4}.:=+#@@%*: + ${c4}#@@@#=: + EOF + ;; + + ([Dd]ragon[Ff]ly*) + read_ascii 1 <<-EOF + ,${c1}_${c7}, + ('-_${c1}|${c7}_-') + >--${c1}|${c7}--< + (_-'${c1}|${c7}'-_) + ${c1}| + ${c1}| + ${c1}| + EOF + ;; + + ([Ee]lementary*) + read_ascii <<-EOF + ${c7} _______ + ${c7} / ____ \\ + ${c7}/ | / /\\ + ${c7}|__\\ / / | + ${c7}\\ /__/ / + ${c7}\\_______/ + EOF + ;; + + ([Ee]ndeavour*) + read_ascii 4 <<-EOF + ${c1}/${c4}\\ + ${c1}/${c4}/ \\${c6}\\ + ${c1}/${c4}/ \\ ${c6}\\ + ${c1}/ ${c4}/ _) ${c6}) + ${c1}/_${c4}/___-- ${c6}__- + ${c6}/____-- + EOF + ;; + + ([Ff]edora*) + read_ascii 4 <<-EOF + ${c4},'''''. + ${c4}| ,. | + ${c4}| | '_' + ${c4} ,....| |.. + ${c4}.' ,_;| ..' + ${c4}| | | | + ${c4}| ',_,' | + ${c4} '. ,' + ${c4}''''' + EOF + ;; + + ([Ff]ree[Bb][Ss][Dd]*) + read_ascii 1 <<-EOF + ${c1}/\\,-'''''-,/\\ + ${c1}\\_) (_/ + ${c1}| | + ${c1}| | + ${c1}; ; + ${c1}'-_____-' + EOF + ;; + + ([Gg]aruda*) + read_ascii 4 <<-EOF + ${c3} _______ + ${c3} __/ \\_ + ${c3} _/ / \\_ + ${c7} _/ /_________\\ + ${c7}_/ | + ${c2}\\ ____________ + ${c2} \\_ __/ + ${c2} \\__________/ + EOF + ;; + + ([Gg]entoo*) + read_ascii 5 <<-EOF + ${c5} _-----_ + ${c5}( \\ + ${c5}\\ 0 \\ + ${c7} \\ ) + ${c7} / _/ + ${c7}( _- + ${c7}\\____- + EOF + ;; + + ([Gg][Nn][Uu]*) + read_ascii 3 <<-EOF + ${c2} _-\`\`-, ,-\`\`-_ + ${c2} .' _-_| |_-_ '. + ${c2}./ /_._ _._\\ \\. + ${c2}: _/_._\`:'_._\\_ : + ${c2}\\:._/ ,\` \\ \\ \\_.:/ + ${c2} ,-';'.@) \\ @) \\ + ${c2} ,'/' ..- .\\,-.| + ${c2} /'/' \\(( \\\` ./ ) + ${c2} '/'' \\_,----' + ${c2} '/'' ,;/'' + ${c2} \`\`;' + EOF + ;; + + ([Gg]uix[Ss][Dd]*|[Gg]uix*) + read_ascii 3 <<-EOF + ${c3}|.__ __.| + ${c3}|__ \\ / __| + ${c3}\\ \\ / / + ${c3}\\ \\ / / + ${c3}\\ \\ / / + ${c3}\\ \\/ / + ${c3}\\__/ + EOF + ;; + + ([Hh]aiku*) + read_ascii 3 <<-EOF + ${c3} ,^, + ${c3} / \\ + ${c3}*--_ ; ; _--* + ${c3}\\ '" "' / + ${c3}'. .' + ${c3}.-'" "'-. + ${c3}'-.__. .__.-' + ${c3}|_| + EOF + ;; + + ([Hh]ydroOS*) + read_ascii 4 <<-EOF + ${c1}╔╗╔╗──╔╗───╔═╦══╗ + ${c1}║╚╝╠╦╦╝╠╦╦═╣║║══╣ + ${c1}║╔╗║║║╬║╔╣╬║║╠══║ + ${c1}╚╝╚╬╗╠═╩╝╚═╩═╩══╝ + ${c1}───╚═╝ + EOF + ;; + + ([Hh]yperbola*) + read_ascii <<-EOF + ${c7} |\`__.\`/ + ${c7} \____/ + ${c7} .--. + ${c7} / \\ + ${c7} / ___ \\ + ${c7}/ .\` \`.\\ + ${c7}/.\` \`.\\ + EOF + ;; + + ([Ii]glunix*) + read_ascii <<-EOF + ${c0} | + ${c0} | | + ${c0} | + ${c0} | ________ + ${c0} | /\\ | \\ + ${c0} / \\ | \\ | + ${c0} / \\ \\ | + ${c0} / \\________\\ + ${c0} \\ / / + ${c0} \\ / / + ${c0} \\ / / + ${c0} \\/________/ + EOF + ;; + + ([Ii]nstant[Oo][Ss]*) + read_ascii <<-EOF + ${c0} ,-''-, + ${c0}: .''. : + ${c0}: ',,' : + ${c0} '-____:__ + ${c0} : \`. + ${c0} \`._.' + EOF + ;; + + ([Ii][Rr][Ii][Xx]*) + read_ascii 1 <<-EOF + ${c1} __ + ${c1} \\ \\ __ + ${c1} \\ \\ / / + ${c1} \\ v / + ${c1} / . \\ + ${c1} /_/ \\ \\ + ${c1} \\_\\ + EOF + ;; + + ([Kk][Dd][Ee]*[Nn]eon*) + read_ascii 6 <<-EOF + ${c7} .${c6}__${c7}.${c6}__${c7}. + ${c6} / _${c7}.${c6}_ \\ + ${c6} / / \\ \\ + ${c7} . ${c6}| ${c7}O${c6} | ${c7}. + ${c6} \\ \\_${c7}.${c6}_/ / + ${c6} \\${c7}.${c6}__${c7}.${c6}__${c7}.${c6}/ + EOF + ;; + + ([Ll]inux*[Ll]ite*|[Ll]ite*) + read_ascii 3 <<-EOF + ${c3} /\\ + ${c3} / \\ + ${c3} / ${c7}/ ${c3}/ + ${c3}> ${c7}/ ${c3}/ + ${c3}\\ ${c7}\\ ${c3}\\ + ${c3}\\_${c7}\\${c3}_\\ + ${c7} \\ + EOF + ;; + + ([Ll]inux*[Mm]int*|[Mm]int) + read_ascii 2 <<-EOF + ${c2} ___________ + ${c2}|_ \\ + ${c2}| ${c7}| _____ ${c2}| + ${c2}| ${c7}| | | | ${c2}| + ${c2}| ${c7}| | | | ${c2}| + ${c2}| ${c7}\\__${c7}___/ ${c2}| + ${c2}\\_________/ + EOF + ;; + + + ([Ll]inux*) + read_ascii 4 <<-EOF + ${c4} ___ + ${c4}(${c7}.. ${c4}| + ${c4}(${c5}<> ${c4}| + ${c4}/ ${c7}__ ${c4}\\ + ${c4}( ${c7}/ \\ ${c4}/| + ${c5}_${c4}/\\ ${c7}__)${c4}/${c5}_${c4}) + ${c5}\/${c4}-____${c5}\/ + EOF + ;; + + ([Mm]ac[Oo][Ss]*|[Dd]arwin*) + read_ascii 1 <<-EOF + ${c2} .:' + ${c2} _ :'_ + ${c3} .'\`_\`-'_\`\`. + ${c1}:________.-' + ${c1}:_______: + ${c4} :_______\`-; + ${c5} \`._.-._.' + EOF + ;; + + ([Mm]ageia*) + read_ascii 2 <<-EOF + ${c6} * + ${c6} * + ${c6} ** + ${c7} /\\__/\\ + ${c7}/ \\ + ${c7}\\ / + ${c7} \\____/ + EOF + ;; + + ([Mm]anjaro*) + read_ascii 2 <<-EOF + ${c2}||||||||| |||| + ${c2}||||||||| |||| + ${c2}|||| |||| + ${c2}|||| |||| |||| + ${c2}|||| |||| |||| + ${c2}|||| |||| |||| + ${c2}|||| |||| |||| + EOF + ;; + + ([Mm]inix*) + read_ascii 4 <<-EOF + ${c4} ,, ,, + ${c4};${c7},${c4} ', ,' ${c7},${c4}; + ${c4}; ${c7}',${c4} ',,' ${c7},'${c4} ; + ${c4}; ${c7}',${c4} ${c7},'${c4} ; + ${c4}; ${c7};, '' ,;${c4} ; + ${c4}; ${c7};${c4};${c7}',,'${c4};${c7};${c4} ; + ${c4}', ${c7};${c4};; ;;${c7};${c4} ,' + ${c4} '${c7};${c4}' '${c7};${c4}' + EOF + ;; + + ([Mm][Xx]*) + read_ascii <<-EOF + ${c7} \\\\ / + ${c7} \\\\/ + ${c7} \\\\ + ${c7} /\\/ \\\\ + ${c7} / \\ /\\ + ${c7} / \\/ \\ + ${c7}/__________\\ + EOF + ;; + + ([Nn]et[Bb][Ss][Dd]*) + read_ascii 3 <<-EOF + ${c7}\\\\${c3}\`-______,----__ + ${c7} \\\\ ${c3}__,---\`_ + ${c7} \\\\ ${c3}\`.____ + ${c7} \\\\${c3}-______,----\`- + ${c7} \\\\ + ${c7} \\\\ + ${c7} \\\\ + EOF + ;; + + ([Nn]ix[Oo][Ss]*) + read_ascii 4 <<-EOF + ${c4} \\\\ \\\\ // + ${c4} ==\\\\__\\\\/ // + ${c4} // \\\\// + ${c4}==// //== + ${c4} //\\\\___// + ${c4}// /\\\\ \\\\== + ${c4} // \\\\ \\\\ + EOF + ;; + + ([Oo]pen[Bb][Ss][Dd]*) + read_ascii 3 <<-EOF + ${c3} _____ + ${c3} \\- -/ + ${c3} \\_/ \\ + ${c3} | ${c7}O O${c3} | + ${c3} |_ < ) 3 ) + ${c3} / \\ / + ${c3} /-_____-\\ + EOF + ;; + + ([Oo]pen[Ss][Uu][Ss][Ee]*[Tt]umbleweed*) + read_ascii 2 <<-EOF + ${c2} _____ ______ + ${c2} / ____\\ / ____ \\ + ${c2}/ / \`/ / \\ \\ + ${c2}\\ \\____/ /,____/ / + ${c2} \\______/ \\_____/ + EOF + ;; + + ([Oo]pen[Ss][Uu][Ss][Ee]*|[Oo]pen*SUSE*|SUSE*|suse*) + read_ascii 2 <<-EOF + ${c2} _______ + ${c2}__| __ \\ + ${c2} / .\\ \\ + ${c2} \\__/ | + ${c2} _______| + ${c2} \\_______ + ${c2}__________/ + EOF + ;; + + ([Oo]pen[Ww]rt*) + read_ascii 1 <<-EOF + ${c1} _______ + ${c1}| |.-----.-----.-----. + ${c1}| - || _ | -__| | + ${c1}|_______|| __|_____|__|__| + ${c1} ________|__| __ + ${c1}| | | |.----.| |_ + ${c1}| | | || _|| _| + ${c1}|________||__| |____| + EOF + ;; + + ([Pp]arabola*) + read_ascii 5 <<-EOF + ${c5} __ __ __ _ + ${c5}.\`_//_//_/ / \`. + ${c5} / .\` + ${c5} / .\` + ${c5} /.\` + ${c5} /\` + EOF + ;; + + ([Pp]op!_[Oo][Ss]*) + read_ascii 6 <<-EOF + ${c6}______ + ${c6}\\ _ \\ __ + ${c6}\\ \\ \\ \\ / / + ${c6}\\ \\_\\ \\ / / + ${c6}\\ ___\\ /_/ + ${c6} \\ \\ _ + ${c6} __\\_\\__(_)_ + ${c6}(___________) + EOF + ;; + + ([Pp]ure[Oo][Ss]*) + read_ascii <<-EOF + ${c7} _____________ + ${c7}| _________ | + ${c7}| | | | + ${c7}| | | | + ${c7}| |_________| | + ${c7}|_____________| + EOF + ;; + + ([Rr]aspbian*) + read_ascii 1 <<-EOF + ${c2} __ __ + ${c2} (_\\)(/_) + ${c1} (_(__)_) + ${c1}(_(_)(_)_) + ${c1} (_(__)_) + ${c1} (__) + EOF + ;; + + ([Ss]erenity[Oo][Ss]*) + read_ascii 4 <<-EOF + ${c7} _____ + ${c1} ,-${c7} -, + ${c1} ;${c7} ( ; + ${c1}| ${c7}. \_${c1}.,${c7} | + ${c1}| ${c7}o _${c1} ',${c7} | + ${c1} ; ${c7}(_)${c1} )${c7} ; + ${c1} '-_____-${c7}' + EOF + ;; + + ([Ss]lackware*) + read_ascii 4 <<-EOF + ${c4} ________ + ${c4} / ______| + ${c4} | |______ + ${c4} \\______ \\ + ${c4} ______| | + ${c4}| |________/ + ${c4}|____________ + EOF + ;; + + ([Ss]olus*) + read_ascii 4 <<-EOF + ${c6} + ${c6} /| + ${c6} / |\\ + ${c6} / | \\ _ + ${c6} /___|__\\_\\ + ${c6} \\ / + ${c6} \`-------´ + EOF + ;; + + ([Ss]un[Oo][Ss]|[Ss]olaris*) + read_ascii 3 <<-EOF + ${c3} . .; . + ${c3} . :; :: ;: . + ${c3} .;. .. .. .;. + ${c3}.. .. .. .. + ${c3} .;, ,;. + EOF + ;; + + ([Uu]buntu*) + read_ascii 3 <<-EOF + ${c3} _ + ${c3} ---(_) + ${c3} _/ --- \\ + ${c3}(_) | | + ${c3} \\ --- _/ + ${c3} ---(_) + EOF + ;; + + ([Vv]oid*) + read_ascii 2 <<-EOF + ${c2} _______ + ${c2} _ \\______ - + ${c2}| \\ ___ \\ | + ${c2}| | / \ | | + ${c2}| | \___/ | | + ${c2}| \\______ \\_| + ${c2} -_______\\ + EOF + ;; + + ([Xx]eonix*) + read_ascii 2 <<-EOF + ${c2} ___ ___ + ${c2}___ \ \/ / ___ + ${c2}\ \ \ / / / + ${c2} \ \/ \/ / + ${c2} \ /\ / + ${c2} \__/ \__/ + EOF + ;; + + (*) + # On no match of a distribution ascii art, this function calls + # itself again, this time to look for a more generic OS related + # ascii art (KISS Linux -> Linux). + [ "$1" ] || { + get_ascii "$os" + return + } + + printf 'error: %s is not currently supported.\n' "$os" >&6 + printf 'error: Open an issue for support to be added.\n' >&6 + exit 1 + ;; + esac + + # Store the "width" (longest line) and "height" (number of lines) + # of the ascii art for positioning. This script prints to the screen + # *almost* like a TUI does. It uses escape sequences to allow dynamic + # printing of the information through user configuration. + # + # Iterate over each line of the ascii art to retrieve the above + # information. The 'sed' is used to strip '\033[3Xm' color codes from + # the ascii art so they don't affect the width variable. + while read -r line; do + ascii_height=$((${ascii_height:-0} + 1)) + + # This was a ternary operation but they aren't supported in + # Minix's shell. + [ "${#line}" -gt "${ascii_width:-0}" ] && + ascii_width=${#line} + + # Using '<<-EOF' is the only way to loop over a command's + # output without the use of a pipe ('|'). + # This ensures that any variables defined in the while loop + # are still accessible in the script. + done <<-EOF + $(printf %s "$ascii" | sed 's/\[3.m//g') + EOF + + # Add a gap between the ascii art and the information. + ascii_width=$((ascii_width + 4)) + + # Print the ascii art and position the cursor back where we + # started prior to printing it. + { + esc_p SGR 1 + printf '%s' "$ascii" + esc_p SGR 0 + esc_p CUU "$ascii_height" + } >&6 +} + +main() { + case $* in + -v) + printf '%s 0.7.0\n' "${0##*/}" + return 0 + ;; + + -d) + # Below exec is not run, stderr is shown. + ;; + + '') + exec 2>/dev/null + ;; + + *) + cat <&6'. + # This gives full control over what it displayed on the screen. + exec 6>&1 >/dev/null + + # Store raw escape sequence character for later reuse. + esc_c=$(printf '\033') + + # Allow the user to execute their own script and modify or + # extend pfetch's behavior. + # shellcheck source=/dev/null + ! [ -f "$PF_SOURCE" ] || . "$PF_SOURCE" + + # Ensure that the 'TMPDIR' is writable as heredocs use it and + # fail without the write permission. This was found to be the + # case on Android where the temporary directory requires root. + [ -w "${TMPDIR:-/tmp}" ] || export TMPDIR=~ + + # Generic color list. + # Disable warning about unused variables. + # shellcheck disable=2034 + for _c in c1 c2 c3 c4 c5 c6 c7 c8; do + esc SGR "3${_c#?}" 0 + export "$_c=$e" + done + + # Disable line wrapping and catch the EXIT signal to enable it again + # on exit. Ideally you'd somehow query the current value and retain + # it but I'm yet to see this irk anyone. + esc_p DECAWM l >&6 + trap 'esc_p DECAWM h >&6' EXIT + + # Store the output of 'uname' to avoid calling it multiple times + # throughout the script. 'read </dev/null || continue + + # This was a ternary operation but they aren't supported in + # Minix's shell. + [ "${#info}" -gt "${info_length:-0}" ] && + info_length=${#info} + done + + # Add an additional space of length to act as a gap. + info_length=$((info_length + 1)) + + # Iterate over the above list and run any existing "get_" functions. + for info do + "get_$info" + done + } + + # Position the cursor below both the ascii art and information lines + # according to the height of both. If the information exceeds the ascii + # art in height, don't touch the cursor (0/unset), else move it down + # N lines. + # + # This was a ternary operation but they aren't supported in Minix's shell. + [ "${info_height:-0}" -lt "${ascii_height:-0}" ] && + cursor_pos=$((ascii_height - info_height)) + + # Print '$cursor_pos' amount of newlines to correctly position the + # cursor. This used to be a 'printf $(seq X X)' however 'seq' is only + # typically available (by default) on GNU based systems! + while [ "${i:=0}" -le "${cursor_pos:-0}" ]; do + printf '\n' + i=$((i + 1)) + done >&6 +} + +main "$@" diff --git a/bin/shfm b/bin/shfm new file mode 100755 index 0000000..aee611e --- /dev/null +++ b/bin/shfm @@ -0,0 +1,428 @@ +#!/bin/sh + +esc() { + case $1 in + # vt100 (IL is vt102) (DECTCEM is vt520) + CUD) printf '%s[%sB' "$esc_c" "$2" ;; # cursor down + CUP) printf '%s[%s;%sH' "$esc_c" "$2" "$3" ;; # cursor home + CUU) printf '%s[%sA' "$esc_c" "$2" ;; # cursor up + DECAWM) printf '%s[?7%s' "$esc_c" "$2" ;; # line wrap + DECRC) printf '%s8' "$esc_c" ;; # cursor restore + DECSC) printf '%s7' "$esc_c" ;; # cursor save + DECSTBM) printf '%s[%s;%sr' "$esc_c" "$2" "$3" ;; # scroll region + DECTCEM) printf '%s[?25%s' "$esc_c" "$2" ;; # cursor visible + ED[0-2]) printf '%s[%sJ' "$esc_c" "${1#ED}" ;; # clear screen + EL[0-2]) printf '%s[%sK' "$esc_c" "${1#EL}" ;; # clear line + IL) printf '%s[%sL' "$esc_c" "$2" ;; # insert line + SGR) printf '%s[%s;%sm' "$esc_c" "$2" "$3" ;; # colors + + # xterm (since 1988, supported widely) + screen_alt) printf '%s[?1049%s' "$esc_c" "$2" ;; # alternate buffer + esac +} + +term_setup() { + stty=$(stty -g) + stty -icanon -echo + esc screen_alt h + esc DECAWM l + esc DECTCEM l + esc ED2 + esc DECSTBM 1 "$((LINES - 2))" +} + +term_reset() { + esc DECAWM h >&2 + esc DECTCEM h >&2 + esc ED2 >&2 + esc DECSTBM >&2 + esc screen_alt l >&2 + stty "$stty" + + # needed for cd-on-exit + printf '%s\n' "$PWD" >&1 +} + +term_resize() { + # false-positive, behavior intentional, globbing is disabled. + # shellcheck disable=2046 + { + set -f + set +f -- $(stty size) + } + + LINES=$1 COLUMNS=$2 + + # space for status_line + bottom=$((LINES - 2)) +} + +term_scroll_down() { + case $((y - $#)) in + [0-9]*) return + esac + + y=$((y + 1)) + y2=$((y2 + 1 < bottom ? y2 + 1 : bottom)) + + line_print "$((y - 1))" "$@" + printf '\n' + line_print "$y" "$@" + status_line "$#" +} + +term_scroll_up() { + case $y in + -*|0|1) return + esac + + y=$((y - 1)) + + line_print "$((y + 1))" "$@" + + case $y2 in + 1) esc IL ;; + *) esc CUU; y2=$((y2 > 1 ? y2 - 1 : 1)) + esac + + line_print "$y" "$@" + status_line "$#" +} + +cmd_run() { + stty "$stty" + esc DECTCEM h + esc DECSTBM + esc ED2 + "$@" ||: + esc DECSTBM 1 "$((LINES - 2))" + esc DECTCEM l + stty -icanon -echo + hist=2 +} + +file_escape() { + tmp=$1 safe= + + # loop over string char by char + while c=${tmp%"${tmp#?}"}; do + case $c in + '') return ;; + [[:cntrl:]]) safe=$safe\? ;; + *) safe=$safe$c ;; + esac + + tmp=${tmp#?} + done +} + +hist_search() { + hist=0 j=1 + + for file do + case ${PWD%%/}/$file in + "$old_pwd") y=$j y2=$((j >= bottom ? mid : j)) cur=$file + esac + + j=$((j + 1)) + done +} + +list_print() { + esc ED2 + esc CUP + + i=1 + end=$((bottom + 1)) + mid=$((bottom / 4 < 5 ? 1 : bottom / 4)) + + case $# in + 1) [ -e "$1" ] || [ "$1" = 'no results' ] || set -- empty + esac + + case $hist in + 2) # redraw after cmd run + shift "$((y > y2 ? y - y2 : 0))" + ;; + + 1) # redraw after go-to-parent + hist_search "$@" + shift "$((y >= bottom ? y - mid : 0))" + ;; + + *) # everything else + shift "$((y >= bottom ? y - bottom : 0))" + ;; + esac + + for file do + case $i in + "$y2") esc SGR 0 7 + esac + + case $((i - end)) in + -*) + line_format "$file" + esc CUD + ;; + esac + + i=$((i + 1)) + done + + esc CUP "$((y > y2 ? y2 : y))" +} + +redraw() { + list_print "$@" + status_line "$#" +} + +status_line() { + esc DECSC + esc CUP "$LINES" + + case $USER in + root) esc SGR 31 7 ;; + *) esc SGR 34 7 ;; + esac + + printf '%*s\r%s ' "$COLUMNS" "" "($y/$1)" + + case $ltype in + '') printf %s "$PWD" ;; + *) printf %s "$ltype" + esac + + esc SGR 0 0 + esc DECRC +} + +prompt() { + esc DECSC + esc CUP "$LINES" + printf %s "$1" + esc DECTCEM h + esc EL0 + + case $2 in + r) + stty icanon echo + read -r ans ||: + stty -icanon -echo + ;; + esac + + esc DECRC + esc DECTCEM l + status_line "($y/$#) $PWD" +} + +line_print() { + offset=$1 + + case $offset in + "$y") esc SGR 0 7 + esac + + shift "$offset" + + case $offset in + "$y") cur=$1 + esac + + line_format "$1" +} + +line_format() { + file_escape "$1" + [ -d "$1" ] && esc SGR 1 31 + printf %s "$safe" + [ -d "$1" ] && printf / + esc SGR 0 0 + esc EL0 + printf '\r' +} + +main() { + set -e + + case $1 in + -h|--help) + printf 'shfm -[hv] \n' + exit 0 + ;; + + -v|--version) + printf 'shfm 0.4.2\n' + exit 0 + ;; + + *) + cd -- "${1:-"$PWD"}" + ;; + esac + + esc_c=$(printf '\033') + bs_char=$(printf '\177') + + set -- * + cur=$1 + + term_resize + term_setup + + trap 'term_reset' EXIT INT + trap 'term_resize; term_setup; y=1 y2=1; redraw "$@"' WINCH + + y=1 y2=1 + redraw "$@" + + while key=$(dd ibs=1 count=1 2>/dev/null); do + case $key${esc:=0} in + k?|A2) + term_scroll_up "$@" + ;; + + j?|B2) + term_scroll_down "$@" + ;; + + l?|C2|"$esc") # ARROW RIGHT + if [ -d "$cur" ] && cd -- "$cur" >/dev/null 2>&1; then + set -- * + y=1 y2=1 cur=$1 ltype= + redraw "$@" + + elif [ -e "$cur" ]; then + cmd_run "${SHFM_OPENER:="${EDITOR:=vi}"}" "$cur" + redraw "$@" + fi + ;; + + h?|D2|"$bs_char"?) # ARROW LEFT + old_pwd=$PWD + + case $ltype in + '') cd .. || continue ;; + *) ltype= ;; + esac + + set -- * + y=1 y2=1 cur=$1 hist=1 + redraw "$@" + ;; + + g?) + case $y in + 1) continue + esac + + y=1 y2=1 cur=$1 + redraw "$@" + ;; + + G?) + y=$# + y2=$(($# < bottom ? $# : bottom)) + line_print "$y" "$@" + redraw "$@" + ;; + + .?) + case ${hidden:=1} in + 1) hidden=0; set -- .* ;; + 0) hidden=1; set -- * + esac + + y=1 y2=1 cur=$1 + redraw "$@" + ;; + + :?) + prompt "cd: " r + + # false positive, behavior intentional + # shellcheck disable=2088 + case $ans in + '~') ans=$HOME ;; + '~/'*) ans=$HOME/${ans#"~/"} + esac + + cd -- "${ans:="$0"}" >/dev/null 2>&1|| continue + set -- * + y=1 y2=1 cur=$1 + redraw "$@" + ;; + + /?) + prompt / r + + IFS= + # globbing intentional, word splitting is disabled. + # shellcheck disable=2086 + set -- $ans* + unset IFS + + case $1$# in + "$ans*1") set -- 'no results' + esac + + y=1 y2=1 cur=$1 ltype="search $PWD/$ans*" + redraw "$@" + status_line "$#" + ;; + + -?) + cd -- "$OLDPWD" >/dev/null 2>&1|| continue + set -- * + y=1 y2=1 cur=$1 + redraw "$@" + ;; + + \~?) + cd || continue + set -- * + y=1 y2=1 cur=$1 + redraw "$@" + ;; + + \!?) + export SHFM_LEVEL + SHFM_LEVEL=$((SHFM_LEVEL + 1)) + cmd_run "${SHELL:=/bin/sh}" + redraw "$@" + ;; + + \??) + set -- 'j - down' \ + 'k - up' \ + 'l - open file or directory' \ + 'h - go up level' \ + 'g - go to top' \ + 'G - go to bottom' \ + 'q - quit' \ + ': - cd to ' \ + '/ - search current directory *' \ + '- - go to last directory' \ + '~ - go home' \ + '! - spawn shell' \ + '. - toggle hidden files' \ + '? - show keybinds' + + y=1 y2=1 cur=$1 ltype=keybinds + redraw "$@" + status_line "$#" + ;; + + q?) exit 0 ;; + + # handle keys which emit escape sequences + "$esc_c"*) esc=1 ;; + '[1') esc=2 ;; + *) esc=0 ;; + esac + done +} + +main "$@" >/dev/tty diff --git a/bin/tux.sh b/bin/tux.sh new file mode 100755 index 0000000..344d1a5 --- /dev/null +++ b/bin/tux.sh @@ -0,0 +1,53 @@ +#!/bin/sh + +print_little_tux() { + echo " .--." + echo " |o_o |" + echo " |:_/ |" + echo " // \ \ " + echo " (| | )" + echo "/'\_ _/\`\ " + echo "\___)=(___/" +} + +print_big_tux() { + echo " .88888888:." + echo " 88888888.88888." + echo " .8888888888888888." + echo " 888888888888888888" + echo " 88' _\`88'_ \`88888" + echo " 88 88 88 88 88888" + echo " 88_88_::_88_:88888" + echo " 88:::,::,:::::8888" + echo " 88\`:::::::::'\`8888" + echo " .88 \`::::' 8:88." + echo " 8888 \`8:888." + echo " .8888' \`888888." + echo " .8888:.. .::. ...:'8888888:." + echo " .8888.' :' \`'::\`88:88888" + echo " .8888 ' \`.888:8888." + echo " 888:8 . 888:88888" + echo " .888:88 .: 888:88888:" + echo " 8888888. :: 88:888888" + echo " \`.::.888. :: .88888888" + echo " .::::::.888. :: :::\`8888'.:." + echo " ::::::::::.888 ' .::::::::::::" + echo " ::::::::::::.8 ' .:8::::::::::::." + echo " .::::::::::::::. .:888:::::::::::::" + echo " :::::::::::::::88:.__..:88888:::::::::::'" + echo " \`'.:::::::::::88888888888.88:::::::::'" + echo " \`':::_:' -- '' -'-' \`':_::::'\`" +} + +case "$1" in + big|b|-b) + print_big_tux + ;; + little|l|-l) + print_little_tux + ;; + *) + print_little_tux + ;; +esac + diff --git a/bin/updatepacmanrepo b/bin/updatepacmanrepo new file mode 100755 index 0000000..953b5bc --- /dev/null +++ b/bin/updatepacmanrepo @@ -0,0 +1,4 @@ +#!/bin/sh + +sudo reflector --protocol https --latest 5 --age 24 --sort rate --save /etc/pacman.d/mirrorlist +