ssf
init
a3fa3b5aea23127bf8d23b434d4ac690363fc3a9
IIIlllIIIllI <seb.michalk@gmail.com>
2026-01-18 12:49:20 +0000
LICENSE | 2 +- Makefile | 39 +++++ ascii | 248 ++++++++++++++++++++++++++ config.mk | 12 ++ readme.txt | 38 ++++ ssf.1 | 63 +++++++ ssf.c | 582 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 983 insertions(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index a4bf177..c24c4a2 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2026 Sebastian +Copyright (c) 2026 Sebastian Michalk Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..cca41b1 --- /dev/null +++ b/Makefile @@ -0,0 +1,39 @@ +# ssf - simple system fetcher + +include config.mk + +SRC = ssf.c +OBJ = ${SRC:.c=.o} +BIN = ssf +MAN1 = ssf.1 +MANDIR = ${MANPREFIX} + +all: options ${BIN} + +options: + @echo "ssf build options:" + @echo "CC = ${CC}" + @echo "CPPFLAGS = ${CPPFLAGS}" + @echo "CFLAGS = ${CFLAGS}" + @echo "LDFLAGS = ${LDFLAGS}" + +${BIN}: ${OBJ} + ${CC} ${OBJ} ${LDFLAGS} -o $@ + +%.o: %.c ascii + ${CC} ${CPPFLAGS} ${CFLAGS} -c $< -o $@ + +clean: + rm -f ${BIN} ${OBJ} + +install: all + mkdir -p ${DESTDIR}${BINDIR} + install -m 755 ${BIN} ${DESTDIR}${BINDIR}/${BIN} + mkdir -p ${DESTDIR}${MANDIR}/man1 + install -m 644 ${MAN1} ${DESTDIR}${MANDIR}/man1/${MAN1} + +uninstall: + rm -f ${DESTDIR}${BINDIR}/${BIN} + rm -f ${DESTDIR}${MANDIR}/man1/${MAN1} + +.PHONY: all options clean install uninstall diff --git a/ascii b/ascii new file mode 100644 index 0000000..4c4c09d --- /dev/null +++ b/ascii @@ -0,0 +1,248 @@ +static const char *ascii_alpine[] = { + " /\\ /\\", + " /./ \\ \\", + " /./ \\ \\", + "/./ \\ \\", + "// \\ \\", + " \\", + NULL +}; + +static const char *ascii_android[] = { + " ;, ,;", + " ';,.-----.,;'", + " ,' ',", + " / O O \\", + "| |", + "'-----------------'", + NULL +}; + +static const char *ascii_arch[] = { + " /\\", + " / \\", + " /\\ \\", + " / \\", + " / ,, \\", + " / | | -\\", + " /_-'' ''-_\\", + NULL +}; + +static const char *ascii_arco[] = { + " /\\", + " / \\", + " / /\\ \\", + " / / \\ \\", + " / / \\ \\", + " / / _____\\ \\", + "/_/ `----.\\_\\", + NULL +}; + +static const char *ascii_artix[] = { + " /\\", + " / \\", + " /`'.,\\", + " / ',", + " / ,`\\", + " / ,.'`. \\", + "/.,`' `'.\\", + NULL +}; + +static const char *ascii_centos[] = { + " ____^____", + " |\\ | /|", + " | \\ | / |", + "<---- ---->", + " | / | \\ |", + " |/__| __\\|", + " v", + NULL +}; + +static const char *ascii_debian[] = { + " _____", + " / __ \\", + "| / |", + "| \\___-", + "-_", + " --_", + NULL +}; + +static const char *ascii_endeavour[] = { + " /\\", + " // \\\\", + " // \\ \\", + " / // _) )", + "/_/___-- __-", + " /____--", + NULL +}; + +static const char *ascii_fedora[] = { + " ,'''''. ", + " | ,. | ", + " | | '_' ", + " ,....| |.. ", + ".' ,_;| ..' ", + "| | | | ", + "| ',_,' | ", + " '. ,' ", + " ''''' ", + NULL +}; + +static const char *ascii_freebsd[] = { + "/\\,-'''''-,/\\", + "\\_) (_/", + "| |", + "| |", + " ; ;", + " '-_____-'", + NULL +}; + +static const char *ascii_gentoo[] = { + " _-----_", + "( \\", + "\\ 0 \\", + " \\ )", + " / _/", + "( _-", + "\\____-", + NULL +}; + +static const char *ascii_linux[] = { + " ___", + " (.. |", + " (<> |", + " / __ \\", + " ( / \\ /|", + "_/\\ __)/_)", + "\\/----\\/", + NULL +}; + +static const char *ascii_linux_mint[] = { + " ___________", + "|_ \\", + " | | _____ |", + " | | | | | |", + " | | | | | |", + " | \\__ ___/ |", + " \\_________/", + NULL +}; + +static const char *ascii_macos[] = { + " .:'", + " _ :'_", + " .'`_`-'_`'.", + ":________.-'", + ":_______:", + " :_______`-;", + " `._.-._.'", + NULL +}; + +static const char *ascii_manjaro[] = { + "||||||||| ||||", + "||||||||| ||||", + "|||| ||||", + "|||| |||| ||||", + "|||| |||| ||||", + "|||| |||| ||||", + "|||| |||| ||||", + NULL +}; + +static const char *ascii_nixos[] = { + " \\\\ \\\\ //", + " ==\\\\__\\\\/ //", + " // \\\\//", + "==// //==", + " //\\\\___//", + "// /\\\\ \\\\==", + " // \\\\ \\\\", + NULL +}; + +static const char *ascii_openbsd[] = { + " _____", + " \\- -/", + " \\_/ \\_/", + " | O O |", + " |_ < ) 3 )", + " / \\ /", + " /-_____-\\", + NULL +}; + +static const char *ascii_opensuse[] = { + " _______", + "__| __ \\", + " / .\\ \\", + " \\__/ |", + " _______|", + " \\_______", + "__________/", + NULL +}; + +static const char *ascii_pop_os[] = { + "______", + "\\ _ \\ __", + " \\ \\ \\ \\ / /", + " \\ \\_\\ \\ / /", + " \\ ___\\ /_/", + " \\ \\ _", + " __\\_\\__(_)_", + " (___________)", + NULL +}; + +static const char *ascii_slackware[] = { + " ________", + " / ______|", + " | |______", + " \\______ \\", + " ______| |", + "| |________/", + "|____________", + NULL +}; + +static const char *ascii_solus[] = { + " /|", + " / |\\", + " / | \\ _", + " /___|__\\_\\", + " \\ /", + " `-------ยด", + NULL +}; + +static const char *ascii_ubuntu[] = { + " _", + " ---(_)", + " _/ --- \\", + "(_) | |", + " \\ --- _/", + " ---(_)", + NULL +}; + +static const char *ascii_void[] = { + " _______", + " _ \\______ -", + "| \\ ___ \\ |", + "| | / \\ | |", + "| | \\___/ | |", + "| \\______ \\_|", + " -_______\\", + NULL +}; diff --git a/config.mk b/config.mk new file mode 100644 index 0000000..adea8f1 --- /dev/null +++ b/config.mk @@ -0,0 +1,12 @@ +# ssf - simple system fetcher + +VERSION = 1.0 + +PREFIX = /usr/local +BINDIR = ${PREFIX}/bin +MANPREFIX = ${PREFIX}/share/man + +CC = cc +CPPFLAGS = -DVERSION=\"${VERSION}\" +CFLAGS = -std=c99 -pedantic -Wall -Wextra -O2 +LDFLAGS = diff --git a/readme.txt b/readme.txt new file mode 100644 index 0000000..595344c --- /dev/null +++ b/readme.txt @@ -0,0 +1,38 @@ +ssf - simple system fetcher +=============================== + +ssf prints a small set of system facts next to a coloured ASCII logo to stdout. +The code keeps to plain POSIX interfaces so it runs on Linux and OpenBSD. + +Requirements +------------ + +A POSIX environment with a C99 compiler and make(1). + +Installation +------------ + +Edit config.mk to match your local setup (ssf installs into /usr/local +by default). Afterwards run: + + make + make install + +Usage +----- + +Simply run ssf with no arguments for the standard display. Optional +flags mimic the original interface: + + -m print values without labels + -s print a short subset of fields + -k print only the value for a specific key + -h show usage help + -v show the version string + +Customisation +------------- + +ASCII definitions live in the ascii file and can be edited to taste. +Feature toggles, compiler flags, and install paths are configured through +config.mk. diff --git a/ssf.1 b/ssf.1 new file mode 100644 index 0000000..5360def --- /dev/null +++ b/ssf.1 @@ -0,0 +1,63 @@ +.Dd 2025-12-31 +.Dt SSF 1 +.Os +.Sh NAME +.Nm ssf +.Nd simple system fetcher +.Sh SYNOPSIS +.Nm +.Op Fl m +.Op Fl s +.Op Fl k Ar key +.Op Fl h +.Op Fl v +.Sh DESCRIPTION +.Nm +prints a small set of system facts next to a colored ASCII logo. +It uses plain POSIX interfaces and supports Linux and OpenBSD. +Output includes ANSI color escape sequences. +.Sh OPTIONS +.Bl -tag -width Ds +.It Fl m +Use a pipe separator ("|") between field names and values for easier parsing. +.It Fl s +Show a short subset of fields: hostname, kernel, uptime, memory. +.It Fl k Ar key +Print only the value for the named field and exit. +Known keys are: +user, hostname, distro, kernel, uptime, shell, terminal, cpu, memory, load. +If the key is unknown, nothing is printed. +.It Fl h +Show usage help and exit. +.It Fl v +Print the version string and exit. +.El +.Sh ENVIRONMENT +.Bl -tag -width Ds +.It Ev SHELL +Used to derive the shell name. +.It Ev TERM +Used to display the terminal type. +.El +.Sh FILES +.Bl -tag -width Ds +.It Pa /etc/os-release +Distribution name (PRETTY_NAME). +.It Pa /etc/issue +Fallback distribution name. +.It Pa /proc/cpuinfo +CPU model and core count (Linux). +.It Pa /proc/meminfo +Memory totals (Linux). +.El +.Sh NOTES +On OpenBSD, CPU and memory are read via +.Xr sysctl 3 . +If a value cannot be determined, it is printed as "unknown". +The ASCII logo is selected by substring match on the distro name; +if no match is found, a generic Linux logo is used. +.Sh SEE ALSO +.Xr uname 1 , +.Xr sysctl 3 +.Sh AUTHORS +Sebastian Michalk <sebastian.michalk@pm.me> diff --git a/ssf.c b/ssf.c new file mode 100644 index 0000000..da617fe --- /dev/null +++ b/ssf.c @@ -0,0 +1,582 @@ +#ifdef __OpenBSD__ +#define _BSD_SOURCE +#else +#define _POSIX_C_SOURCE 200809L +#endif + +#include <pwd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <strings.h> +#include <sys/types.h> +#include <sys/utsname.h> +#include <time.h> +#include <unistd.h> + +#ifdef __OpenBSD__ +#include <getopt.h> +#endif + +#ifdef __linux__ +#include <sys/sysinfo.h> +#endif + +#ifdef __OpenBSD__ +#include <sys/param.h> +#include <sys/sysctl.h> +#include <sys/time.h> +#include <uvm/uvm_extern.h> +#endif + +#include "ascii" + +/* sizes */ +#define MAXLINE 256 +#define BUFSIZE 512 + +#define CLR_RESET "\033[0m" +#define CLR_BLUE "\033[1;34m" +#define CLR_CYAN "\033[1;36m" +#define CLR_GREEN "\033[1;32m" +#define CLR_MAGENTA "\033[1;35m" +#define CLR_RED "\033[1;31m" +#define CLR_WHITE "\033[1;37m" +#define CLR_YELLOW "\033[1;33m" + +#define LEN(x) (sizeof(x) / sizeof(*(x))) +#define MAXIMUM(a,b) ((a) > (b) ? (a) : (b)) +#define UNKNOWN(buf) sset((buf), sizeof(buf), "unknown") +#define APPEND(buf, fmt, ...) \ + do { \ + size_t __off = strlen(buf); \ + if (__off < sizeof(buf)) \ + snprintf((buf) + __off, sizeof(buf) - __off, fmt, __VA_ARGS__); \ + } while (0) + +#ifdef __OpenBSD__ +static int sysctl_hw (const char *, void *, size_t *); +#endif + +struct Info +{ + char hostname[MAXLINE], kernel[MAXLINE], uptime[MAXLINE]; + char cpu[MAXLINE], memory[MAXLINE], load[MAXLINE]; + char distro[MAXLINE], shell[MAXLINE], terminal[MAXLINE], user[MAXLINE]; +}; + +struct AsciiEntry +{ + const char *needle; + const char **art; + const char *color; +}; + +static void sset (char *, size_t, const char *); +static void usage (void); +static void hostname (struct Info *); +static void kernel (struct Info *); +static void uptime (struct Info *); +static void cpu (struct Info *); +static void memory (struct Info *); +static void load (struct Info *); +static void distro (struct Info *); +static void shell (struct Info *); +static void terminal (struct Info *); +static void user (struct Info *); +static void collect (struct Info *); +static void display (struct Info *, int, int, char *); +static const char **asciisel (const char *, const char **); +static int asciimatch (const char *, const char *); + +static char *argv0; + +static const struct AsciiEntry ascii_entries[] = { + {"alpine", ascii_alpine, CLR_CYAN}, + {"android", ascii_android, CLR_GREEN}, + {"arch", ascii_arch, CLR_CYAN}, + {"arco", ascii_arco, CLR_BLUE}, + {"artix", ascii_artix, CLR_CYAN}, + {"centos", ascii_centos, CLR_MAGENTA}, + {"debian", ascii_debian, CLR_RED}, + {"endeavour", ascii_endeavour, CLR_MAGENTA}, + {"fedora", ascii_fedora, CLR_BLUE}, + {"freebsd", ascii_freebsd, CLR_RED}, + {"gentoo", ascii_gentoo, CLR_MAGENTA}, + {"linux mint", ascii_linux_mint, CLR_GREEN}, + {"macos", ascii_macos, CLR_WHITE}, + {"mac os", ascii_macos, CLR_WHITE}, + {"manjaro", ascii_manjaro, CLR_GREEN}, + {"nixos", ascii_nixos, CLR_BLUE}, + {"openbsd", ascii_openbsd, CLR_YELLOW}, + {"opensuse", ascii_opensuse, CLR_GREEN}, + {"pop!_os", ascii_pop_os, CLR_CYAN}, + {"pop os", ascii_pop_os, CLR_CYAN}, + {"slackware", ascii_slackware, CLR_BLUE}, + {"solus", ascii_solus, CLR_BLUE}, + {"ubuntu", ascii_ubuntu, CLR_MAGENTA}, + {"void", ascii_void, CLR_GREEN}, + {"linux", ascii_linux, CLR_WHITE} +}; + +static void +sset (char *dst, size_t dstsz, const char *src) +{ + if (!dstsz) + return; +#ifdef __OpenBSD__ + strlcpy (dst, src, dstsz); +#else + snprintf (dst, dstsz, "%s", src); +#endif +} + +#ifdef __OpenBSD__ +static int +sysctl_hw (const char *name, void *out, size_t *lenp) +{ + int mib[2]; + int miblen = 2; + + if (!strcmp (name, "hw.model")) { + mib[0] = CTL_HW; + mib[1] = HW_MODEL; + } + else if (!strcmp (name, "hw.ncpu")) { + mib[0] = CTL_HW; + mib[1] = HW_NCPU; + } + else if (!strcmp (name, "hw.physmem64")) { + mib[0] = CTL_HW; + mib[1] = HW_PHYSMEM64; + } + else { + return -1; + } + return sysctl (mib, (u_int) miblen, out, lenp, NULL, 0); +} + +#endif + +static void +usage (void) +{ + fprintf (stderr, "usage: %s [-m] [-s] [-k key] [-h] [-v]\n", argv0); + exit (1); +} + +static void +hostname (struct Info *i) +{ + if (gethostname (i->hostname, sizeof (i->hostname)) < 0) + UNKNOWN (i->hostname); +} + +static void +kernel (struct Info *i) +{ + struct utsname u; + if (uname (&u) < 0 || !*u.sysname) + UNKNOWN (i->kernel); + else + snprintf (i->kernel, sizeof (i->kernel), "%s %s", u.sysname, u.release); +} + +static void +uptime (struct Info *i) +{ +#if defined(__linux__) + struct sysinfo s; + int d, h, m; + if (sysinfo (&s) < 0) { + UNKNOWN (i->uptime); + return; + } + d = s.uptime / 86400; + h = (s.uptime % 86400) / 3600; + m = (s.uptime % 3600) / 60; +#elif defined(__OpenBSD__) + struct timeval boottime; + time_t now, up; + int mib[2] = { CTL_KERN, KERN_BOOTTIME }; + size_t size = sizeof (boottime); + int d, h, m; + + if (sysctl (mib, 2, &boottime, &size, NULL, 0) < 0 || !boottime.tv_sec) { + UNKNOWN (i->uptime); + return; + } + if ((now = time (NULL)) == (time_t) - 1) { + UNKNOWN (i->uptime); + return; + } + up = now - boottime.tv_sec; + if (up < 0) + up = 0; + d = up / 86400; + h = (up % 86400) / 3600; + m = (up % 3600) / 60; +#else + UNKNOWN (i->uptime); + return; +#endif + if (d) + snprintf (i->uptime, sizeof (i->uptime), "%dd %dh %dm", d, h, m); + else if (h) + snprintf (i->uptime, sizeof (i->uptime), "%dh %dm", h, m); + else + snprintf (i->uptime, sizeof (i->uptime), "%dm", m); +} + +static void +cpu (struct Info *i) +{ +#if defined(__linux__) + FILE *f; + char buf[BUFSIZE], model[MAXLINE] = "unknown"; + int cores = 0; + + if (!(f = fopen ("/proc/cpuinfo", "r"))) { + UNKNOWN (i->cpu); + return; + } + + while (fgets (buf, sizeof (buf), f)) + if (!strncmp (buf, "model name", 10)) { + if ((buf[strcspn (buf, "\n")] = 0, strchr (buf, ':'))) + sset (model, sizeof (model), strchr (buf, ':') + 2); + } + else if (!strncmp (buf, "processor", 9)) + cores++; + + fclose (f); + sset (i->cpu, sizeof (i->cpu), model); + if (cores) + APPEND (i->cpu, " (%d cores)", cores); + +#elif defined(__OpenBSD__) + char model[MAXLINE] = "unknown"; + int cores = 0; + size_t len; + + len = sizeof (model); + if (sysctl_hw ("hw.model", model, &len) == -1 || !*model) { + UNKNOWN (i->cpu); + return; + } + len = sizeof (cores); + if (sysctl_hw ("hw.ncpu", &cores, &len) == -1) + cores = 0; + sset (i->cpu, sizeof (i->cpu), model); + if (cores > 0) + APPEND (i->cpu, " (%d cores)", cores); +#else + UNKNOWN (i->cpu); +#endif +} + +static void +memory (struct Info *i) +{ +#if defined(__linux__) + FILE *f; + char buf[BUFSIZE]; + long total = 0, avail = 0; + + if (!(f = fopen ("/proc/meminfo", "r"))) { + UNKNOWN (i->memory); + return; + } + + while (fgets (buf, sizeof (buf), f) && (!total || !avail)) + if (!strncmp (buf, "MemTotal:", 9)) + sscanf (buf, "MemTotal: %ld", &total); + else if (!strncmp (buf, "MemAvailable:", 13)) + sscanf (buf, "MemAvailable: %ld", &avail); + + fclose (f); + if (total && avail) { + long used = (total - avail) / 1024; + long tmb = total / 1024; + snprintf (i->memory, sizeof (i->memory), "%ld/%ld MB", used, tmb); + } + else { + UNKNOWN (i->memory); + } +#elif defined(__OpenBSD__) + int mib[2]; + struct uvmexp uvmexp; + unsigned long long total, used; + size_t len; + long pagesize; + + mib[0] = CTL_HW; + mib[1] = HW_PHYSMEM64; + len = sizeof (total); + if (sysctl (mib, 2, &total, &len, NULL, 0) == -1) { + UNKNOWN (i->memory); + return; + } + mib[0] = CTL_VM; + mib[1] = VM_UVMEXP; + len = sizeof (uvmexp); + if (sysctl (mib, 2, &uvmexp, &len, NULL, 0) == -1) { + UNKNOWN (i->memory); + return; + } + if ((pagesize = sysconf (_SC_PAGESIZE)) <= 0) { + UNKNOWN (i->memory); + return; + } + used = + (unsigned long long) (uvmexp.active + + uvmexp.wired) * (unsigned long long) pagesize; + snprintf (i->memory, sizeof (i->memory), "%llu/%llu MiB", + used / (1024ULL * 1024ULL), total / (1024ULL * 1024ULL)); +#else + UNKNOWN (i->memory); +#endif +} + +static void +load (struct Info *i) +{ +#if defined(__linux__) + struct sysinfo s; + if (sysinfo (&s) < 0) { + UNKNOWN (i->load); + return; + } + snprintf (i->load, sizeof (i->load), "%.2f %.2f %.2f", + s.loads[0] / 65536.0, s.loads[1] / 65536.0, s.loads[2] / 65536.0); +#elif defined(__OpenBSD__) + double loadavg[3]; + + if (getloadavg (loadavg, 3) == -1) { + UNKNOWN (i->load); + return; + } + snprintf (i->load, sizeof (i->load), "%.2f %.2f %.2f", + loadavg[0], loadavg[1], loadavg[2]); +#else + UNKNOWN (i->load); +#endif +} + +static void +distro (struct Info *i) +{ + FILE *f; + char buf[BUFSIZE], *p, *q; + + *i->distro = 0; + if ((f = fopen ("/etc/os-release", "r"))) { + while (fgets (buf, sizeof (buf), f)) + if (!strncmp (buf, "PRETTY_NAME=", 12) && (p = strchr (buf, '"'))) { + if ((q = strchr (++p, '"'))) { + *q = 0; + sset (i->distro, sizeof (i->distro), p); + break; + } + } + fclose (f); + } + if (!*i->distro && (f = fopen ("/etc/issue", "r"))) { + if (fgets (buf, sizeof (buf), f)) { + buf[strcspn (buf, "\n\\\\")] = 0; + sset (i->distro, sizeof (i->distro), buf); + } + fclose (f); + } + if (!*i->distro) { + struct utsname u; + if (uname (&u) == 0 && *u.sysname) + sset (i->distro, sizeof (i->distro), u.sysname); + else + UNKNOWN (i->distro); + } +} + +static void +shell (struct Info *i) +{ + char *s, *b; + if (!(s = getenv ("SHELL"))) { + UNKNOWN (i->shell); + return; + } + sset (i->shell, sizeof (i->shell), (b = strrchr (s, '/')) ? b + 1 : s); +} + +static void +terminal (struct Info *i) +{ + char *t = getenv ("TERM"); + if (!t) { + UNKNOWN (i->terminal); + return; + } + sset (i->terminal, sizeof (i->terminal), t); +} + +static void +user (struct Info *i) +{ + struct passwd *p = getpwuid (getuid ()); + if (!p || !p->pw_name) { + UNKNOWN (i->user); + return; + } + sset (i->user, sizeof (i->user), p->pw_name); +} + +static void +collect (struct Info *i) +{ + hostname (i); + kernel (i); + uptime (i); + cpu (i); + memory (i); + load (i); + distro (i); + shell (i); + terminal (i); + user (i); +} + +static void +display (struct Info *i, int m, int s, char *k) +{ + char *sep; + struct + { + char *name, *val; + } fields[] = { + {"user", i->user}, {"hostname", i->hostname}, {"distro", i->distro}, + {"kernel", i->kernel}, {"uptime", i->uptime}, {"shell", i->shell}, + {"terminal", i->terminal}, {"cpu", i->cpu}, {"memory", i->memory}, + {"load", i->load} + }, short_fields[] = { + {"hostname", i->hostname}, {"kernel", i->kernel}, {"uptime", i->uptime}, + {"memory", i->memory} + }; + const char **ascii; + const char *color, *line; + int n, j, ascii_lines, ascii_width, max_lines, len, pad; + int total, short_total; + + sep = m ? "|" : ": "; + total = LEN (fields); + short_total = LEN (short_fields); + if (k) { + for (j = 0; j < total; j++) + if (!strcmp (k, fields[j].name)) { + printf ("%s\n", fields[j].val); + return; + } + return; + } + + ascii = asciisel (i->distro, &color); + ascii_lines = 0; + ascii_width = 0; + if (ascii) { + for (j = 0; ascii[j]; j++) { + len = (int) strlen (ascii[j]); + if (len > ascii_width) + ascii_width = len; + } + ascii_lines = j; + } + + n = s ? short_total : total; + max_lines = MAXIMUM (ascii_lines, n); + for (j = 0; j < max_lines; j++) { + len = 0; + pad = ascii_width; + if (j < ascii_lines) { + line = ascii[j]; + len = (int) strlen (line); + pad = ascii_width - len; + if (pad < 0) + pad = 0; + printf ("%s%s%s", color, line, CLR_RESET); + if (pad) + printf ("%*s", pad, ""); + } + else if (ascii_width) { + printf ("%*s", ascii_width, ""); + } + printf (" "); + if (j < n) + printf ("%s%s%s", (s ? short_fields : fields)[j].name, sep, + (s ? short_fields : fields)[j].val); + putchar ('\n'); + } +} + +static const char ** +asciisel (const char *distro, const char **color) +{ + int idx, count; + + count = (int) LEN (ascii_entries); + for (idx = 0; idx < count; idx++) + if (asciimatch (distro, ascii_entries[idx].needle)) { + *color = ascii_entries[idx].color; + return ascii_entries[idx].art; + } + *color = CLR_WHITE; + return ascii_linux; +} + +static int +asciimatch (const char *distro, const char *needle) +{ + size_t len; + const char *p; + + if (!distro || !needle) + return 0; + len = strlen (needle); + if (!len) + return 0; + for (p = distro; *p; p++) + if (!strncasecmp (p, needle, len)) + return 1; + return 0; +} + +int +main (int argc, char **argv) +{ + struct Info info = { 0 }; + int m = 0, s = 0, c; + char *k = NULL; + + for (argv0 = *argv; (c = getopt (argc, argv, "msk:hv")) != -1;) + switch (c) { + case 'm': + m = 1; + break; + case 's': + s = 1; + break; + case 'k': + k = optarg; + break; + case 'h': + usage (); + break; + case 'v': + puts ("ssf 1.0"); + exit (0); + default: + usage (); + } + + collect (&info); + display (&info, m, s, k); + return 0; +}