#!/bin/sh
#
# Copyright 2025  r1w1s1
# All rights reserved.
#
# Redistribution and use of this script, with or without modification, is
# permitted provided that the following conditions are met:
#
# 1. Redistributions of this script must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO
# EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
# OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
# slackfetch - minimal, truthful system info for Slackware
#
# Based on ufetch by J. Schillinger.
# Window manager detection uses EWMH via xprop only (no guessing).
# Non-EWMH window managers may set SLACKFETCH_WM manually.
#
# Usage:
#   slackfetch
#   slackfetch -m             # minimal output
#   NO_COLOR=1 slackfetch   # disable color output (https://no-color.org)

minimal=0
case "${1-}" in
    "") ;;
    -m) minimal=1 ;;
    *)
        printf 'usage: %s [-m]\n' "${0##*/}" >&2
        exit 1
        ;;
esac

## BASIC INFO

host="$(uname -n)"; host="${host%%.*}"
kernel="$(uname -sr) ($(uname -m))"
cpu="$(awk -F ': ' '
    /^model name/ {
        cpu = $2
        gsub(/\(R\)|\(TM\)/, "", cpu)
        sub(/^[0-9]+(st|nd|rd|th) Gen /, "", cpu)
        sub(/( CPU)? @.*/, "", cpu)
        gsub(/[[:space:]]+/, " ", cpu)
        sub(/^ /, "", cpu)
        print cpu
        exit
    }
' /proc/cpuinfo 2>/dev/null)"
memory="$(awk '/^MemTotal:/ { printf "%.1f GiB", $2 / 1048576; exit }' \
    /proc/meminfo 2>/dev/null)"
hardware="$cpu"
[ -n "$memory" ] && hardware="$hardware (mem: $memory)"
uptime="$(uptime -p 2>/dev/null | sed 's/^up //')"
case "$uptime" in
    *day*,*\ minute*)
        uptime="${uptime%, *}"
        ;;
esac

libc=""
for libc_file in /lib64/libc.so.6 /lib/libc.so.6; do
    if [ -x "$libc_file" ]; then
        libc_ver="$("$libc_file" 2>/dev/null \
            | sed -n 's/^GNU C Library.* version \([0-9][0-9.]*[0-9]\).*/\1/p')"
        if [ -n "$libc_ver" ]; then
            libc="glibc $libc_ver"
            break
        fi
    fi
done

init_ver=""
if [ -x /sbin/init ]; then
    init_ver="$(/sbin/init --version 2>/dev/null \
        | sed -n 's/^SysV init version: /sysvinit /p')"
fi
[ -n "$libc" ] && [ -n "$init_ver" ] && libc="$libc ($init_ver)"

## OS

os="$(cat /etc/slackware-version 2>/dev/null)"

if [ -r /etc/os-release ]; then
    codename="$(sed -n 's/^VERSION_CODENAME=//p' /etc/os-release)"
    [ "$codename" = "current" ] && os="$os (-current)"
fi

## SHELL (pretty name)

shell="${SHELL##*/}"
case "$shell" in
    bash) [ -n "$BASH_VERSION" ] && shell="$shell ${BASH_VERSION%%(*}" ;;
    zsh)  shell="zsh"
          [ -n "$ZSH_VERSION" ] && shell="$shell $ZSH_VERSION" ;;
esac

## MULTIPLEXER
[ -n "$TMUX" ] && shell="$shell (tmux)"
[ -n "$STY"  ] && shell="$shell (screen)"
[ -n "$MTM"  ] && shell="$shell (mtm)"

## DESKTOP MANAGER

de="${XDG_CURRENT_DESKTOP##*:}"
case "$de" in
    KDE)
        if command -v plasmashell >/dev/null 2>&1; then
            ver="$(plasmashell --version 2>/dev/null | sed -n 's/.* \([0-9][0-9.]*\).*/\1/p')"
            [ -n "$ver" ] && de="$de $ver"
        fi ;;
    XFCE)
        if command -v xfce4-session >/dev/null 2>&1; then
            ver="$(xfce4-session --version 2>/dev/null | sed -n '1s/^xfce4-session \([0-9.]*\).*/\1/p')"
            [ -n "$ver" ] && de="$de $ver"
        fi ;;
esac

[ -n "$de" ] && de="$(printf '%s\n' "$de" | tr '[:upper:]' '[:lower:]')"

## WINDOW MANAGER

wm=""

# User override (for twm, mwm, etc.)
if [ -n "$SLACKFETCH_WM" ]; then
    wm="$SLACKFETCH_WM"

# EWMH via xprop (X11 only)
elif [ -n "$DISPLAY" ] && command -v xprop >/dev/null 2>&1; then
    wm_id="$(xprop -root _NET_SUPPORTING_WM_CHECK 2>/dev/null \
        | awk '{print $5}')"
    if [ -n "$wm_id" ] && [ "$wm_id" != "not" ]; then
        wm="$(xprop -id "$wm_id" _NET_WM_NAME 2>/dev/null \
            | sed -n 's/.*= "\(.*\)"/\1/p')"
    fi
fi

[ -n "$wm" ] && wm="$(printf '%s\n' "$wm" | tr '[:upper:]' '[:lower:]')"

## DISPLAY SERVER

display=""
if [ -n "$WAYLAND_DISPLAY" ]; then
    display="Wayland"
elif [ -n "$DISPLAY" ]; then
    xver="$(/usr/libexec/Xorg -version 2>&1)"
    ver="$(printf '%s\n' "$xver" \
        | sed -n 's/.*X Server \([0-9][0-9.]*\).*/\1/p')"
    case "$xver" in
        *XLibre*) display="XLibre" ;;
        *X.Org*)  display="X.Org"  ;;
        *)        display="X11"    ;;
    esac
    [ -n "$ver" ] && display="$display $ver"
fi

## OS AGE (days since Slackware installation)

os_age=""
package_dir="${SLACKFETCH_PACKAGE_DIR:-/var/lib/pkgtools/packages}"
root_dir="${SLACKFETCH_ROOT:-/}"
pkg_birth="$(find "$package_dir" -maxdepth 1 -type f \
    -printf '%T@\n' 2>/dev/null | sort -n \
    | sed -n '1s/\..*//p')"
# Compare both sources and keep the oldest valid timestamp.  The package
# database survives migration to a new filesystem, while the root birth time
# covers systems without a usable package database.  Do not use aaa_base
# itself: it is upgraded regularly.
root_birth="$(stat -c %W "$root_dir" 2>/dev/null)"
birth="$pkg_birth"
if [ -z "$birth" ] || [ "$birth" -le 0 ] 2>/dev/null; then
    birth="$root_birth"
elif [ -n "$root_birth" ] && [ "$root_birth" -gt 0 ] 2>/dev/null \
    && [ "$root_birth" -lt "$birth" ]; then
    birth="$root_birth"
fi
if [ -n "$birth" ] && [ "$birth" -gt 0 ] 2>/dev/null; then
    os_age="$(( ($(date +%s) - birth) / 86400 ))d"
fi
[ -n "$os_age" ] && uptime="$uptime (age $os_age)"
uptime_minimal="$uptime"

## Machine Model

if [ -n "$SLACKFETCH_MODEL" ]; then
    model="$SLACKFETCH_MODEL"
else
    sys_vendor="$(cat /sys/devices/virtual/dmi/id/sys_vendor 2>/dev/null)"
    product_name="$(cat /sys/devices/virtual/dmi/id/product_name 2>/dev/null)"
    product_version="$(cat /sys/devices/virtual/dmi/id/product_version 2>/dev/null)"
    case "$sys_vendor" in
        LENOVO) model="$product_version" ;;
        Dell*)
            case "$product_name" in
                Dell*) model="$product_name" ;;
                *)     model="Dell $product_name" ;;
            esac ;;
        *)      model="$product_name" ;;
    esac
    [ -n "$model" ] || model="$product_version"
fi

## PACKAGE COUNT

packages=""
if [ -d /var/log/packages ]; then
    package_extra=""
    flatpak=""
    total="$(find -L /var/log/packages -mindepth 1 -maxdepth 1 -printf '.' | wc -c)"
    sbo="$(find -L /var/log/packages -mindepth 1 -maxdepth 1 -printf '%f\n' |
        grep -c '_SBo$')"
    unofficial="$(find -L /var/log/packages -mindepth 1 -maxdepth 1 -printf '%f\n' |
        grep -cvE '(-[0-9]+|_SBo)$')"
    if command -v flatpak >/dev/null 2>&1; then
        flatpak="$(flatpak list --app 2>/dev/null | wc -l | tr -d ' ')"
    fi
    [ -n "$total" ] && packages="$total"
    [ -n "$sbo" ] && [ "$sbo" -gt 0 ] 2>/dev/null && package_extra="$sbo SBo"
    if [ -n "$unofficial" ] && [ "$unofficial" -gt 0 ] 2>/dev/null; then
        [ -n "$package_extra" ] && package_extra="$package_extra, "
        package_extra="${package_extra}${unofficial} unofficial"
    fi
    if [ -n "$flatpak" ] && [ "$flatpak" -gt 0 ] 2>/dev/null; then
        [ -n "$package_extra" ] && package_extra="$package_extra, "
        package_extra="${package_extra}${flatpak} Flatpak"
    fi
    [ -n "$package_extra" ] && packages="$packages ($package_extra)"
fi

## SESSION AND FONT
font="$SLACKFETCH_FONT"
if [ -z "$font" ] && [ -n "$DISPLAY" ] && command -v xrdb >/dev/null 2>&1; then
    font_name="$(xrdb -query 2>/dev/null \
        | sed -n 's/^XTerm\*faceName:[[:space:]]*//p' | head -n 1)"
    font_size="$(xrdb -query 2>/dev/null \
        | sed -n 's/^XTerm\*faceSize:[[:space:]]*//p' | head -n 1)"
    [ -n "$font_name" ] && font="$font_name"
    [ -n "$font" ] && [ -n "$font_size" ] && font="$font $font_size"
fi

tty_name=""
tty_path="$(tty 2>/dev/null)"
case "$tty_path" in
    /dev/tty[0-9]|/dev/tty[0-9][0-9]) tty_name="${tty_path##*/}" ;;
esac

session_kind=""
session_font=""
if [ -n "$tty_name" ] && [ -z "$SSH_CONNECTION" ] &&
   [ -z "$SSH_CLIENT" ] && [ -z "$SSH_TTY" ]; then
    session_kind="console $tty_name"
    session_font="$font"
    if [ -z "$session_font" ] && [ -r /etc/default/rc.font ]; then
        session_font="$(sed -n \
            's/^[[:space:]]*FONT[[:space:]]*=[[:space:]]*//p' \
            /etc/default/rc.font | head -n 1)"
        session_font="${session_font#\"}"
        session_font="${session_font%\"}"
        session_font="${session_font#\'}"
        session_font="${session_font%\'}"
    fi
elif [ -n "$SSH_CONNECTION" ] || [ -n "$SSH_CLIENT" ] || [ -n "$SSH_TTY" ]; then
    session_kind="ssh"
    case "$tty_path" in
        /dev/*) session_kind="$session_kind ${tty_path#/dev/}" ;;
        *)
            case "$SSH_TTY" in
                /dev/*) session_kind="$session_kind ${SSH_TTY#/dev/}" ;;
            esac
            ;;
    esac
elif [ -n "$wm" ]; then
    session_kind="$wm"
elif [ -n "$display" ]; then
    session_kind="$display"
fi

## COLORS

if [ -z "$NO_COLOR" ] && command -v tput >/dev/null 2>&1; then
    bold="$(tput bold)"
    blue="$(tput setaf 4)"
    reset="$(tput sgr0)"
fi
lc="${reset}${bold}${blue}"     # labels
nc="${reset}${bold}"            # header
ic="${reset}"                   # values
c0="${reset}${bold}${blue}"     # logo

## OUTPUT

session=""
session_minimal=""
session_extra=""
wm_label="$wm"
wm_details="${display}"
[ -n "$font" ] && [ -n "$wm_details" ] && wm_details="$wm_details, font: $font"
[ -n "$font" ] && [ -z "$wm_details" ] && wm_details="font: $font"
[ -n "$wm_details" ] && wm_label="$wm ($wm_details)"
session_value="$session_kind"
if [ -n "$wm" ] && [ -z "$SSH_CONNECTION" ] &&
   [ -z "$SSH_CLIENT" ] && [ -z "$SSH_TTY" ]; then
    session_value="$wm_label"
fi
if [ -n "$de" ]; then
    session="${lc}de:        ${ic}${de}${reset}"
    session_minimal="${lc}de: ${ic}${de}${reset}"
    [ -n "$session_value" ] && session_extra="
${c0}                 ${lc}session:   ${ic}${session_value}${session_font:+ (font: $session_font)}${reset}"
elif [ -n "$session_value" ]; then
    session="${lc}session:   ${ic}${session_value}${session_font:+ (font: $session_font)}${reset}"
    session_minimal="${lc}session: ${ic}${session_value}${session_font:+ (font: $session_font)}${reset}"
fi

header="${USER}@${host} (${model})"
rule="$(printf '%*s' "${#header}" '' | tr ' ' '-')"

if [ "$minimal" -eq 1 ]; then
    header="${USER}@${host}"
    rule="$(printf '%*s' "${#header}" '' | tr ' ' '-')"
    cat <<EOF

${nc}${header}${reset}
${c0}${rule}${reset}
${lc}os:      ${ic}${os}${reset}
${kernel:+${lc}kernel:  ${ic}${kernel}${reset}
}${lc}uptime:  ${ic}${uptime_minimal}${reset}
${lc}shell:   ${ic}${shell}${reset}
${session_minimal}

EOF
else
    cat <<EOF

${c0}                 ${nc}${header}${reset}
${c0}                 ${rule}${reset}
${c0}     ________    ${lc}os:        ${ic}${os}${reset}
${c0}    /  ______|   ${lc}kernel:    ${ic}${kernel}${reset}
${c0}    | |______    ${hardware:+${lc}hardware:  ${ic}${hardware}${reset}}
${c0}    \\______  \\   ${libc:+${lc}libc:      ${ic}${libc}${reset}}
${c0}     ______| |   ${lc}uptime:    ${ic}${uptime}${reset}
${c0}  | |________/   ${lc}shell:     ${ic}${shell}${reset}
${c0}  |____________  ${packages:+${lc}pkgs:      ${ic}${packages}${reset}}
${c0}                 ${session}${session_extra}

EOF
fi

# Ensure terminal colors are fully reset
[ -n "$reset" ] && printf '%s' "$reset"

# Color palette aligned with labels
if [ -z "$NO_COLOR" ] && [ "$minimal" -eq 0 ]; then
    printf '               '
    for i in 0 1 2 3 4 5 6 7; do
        printf '\033[4%dm \033[0m' "$i"
    done
    printf '\n\n'
fi
