From bf5837389a18a9636a428fa1cdc0e8395668a24f Mon Sep 17 00:00:00 2001 From: anihilis Date: Sun, 21 Dec 2025 10:31:31 -0800 Subject: [PATCH] switch to C --- qfetch.c | 267 +++++++++++++++++++++++++++++++++++++++++++ qfetchC-notes.txt | 284 ++++++++++++++++++++++++++++++++++++++++++++++ qfetchC.out | Bin 0 -> 13864 bytes 3 files changed, 551 insertions(+) create mode 100644 qfetch.c create mode 100644 qfetchC-notes.txt create mode 100755 qfetchC.out diff --git a/qfetch.c b/qfetch.c new file mode 100644 index 0000000..22ccbd9 --- /dev/null +++ b/qfetch.c @@ -0,0 +1,267 @@ +#define _POSIX_C_SOURCE 200809L + +/* + * qfetch.c — dynamic & portable system information fetcher + * + * Goals: + * - Pure C (C99) + * - Heap-allocated strings (caller owns memory) + * - Portable across Linux, macOS, and BSD + * - Graceful fallbacks when platform-specific features are unavailable + */ + +#include +#include +#include +#include +#include +#include +#include + +/* Platform-specific headers for macOS / BSD */ +#if defined(__APPLE__) || defined(__FreeBSD__) +#include +#include +#include +#include +#endif + +/* + * Utility: duplicate a string or return "unknown" if NULL/empty. + * Always returns heap-allocated memory. + */ +static char *dup_or_unknown(const char *s) { + if (!s || !*s) + return strdup("unknown"); + return strdup(s); +} + +/* + * Fetch system hostname using POSIX gethostname(). + * Buffer size is determined dynamically via sysconf(). + */ +char *get_hostname(void) { + long max = sysconf(_SC_HOST_NAME_MAX); + if (max < 0) + max = 256; /* fallback if limit is unavailable */ + + char *buf = calloc(max + 1, 1); + if (!buf) + return dup_or_unknown(NULL); + + if (gethostname(buf, max) != 0) { + free(buf); + return dup_or_unknown(NULL); + } + return buf; +} + +/* + * Fetch current username via getpwuid(). + */ +char *get_user(void) { + struct passwd *pw = getpwuid(getuid()); + return pw ? dup_or_unknown(pw->pw_name) : dup_or_unknown(NULL); +} + +/* + * Fetch login shell name (basename only, e.g. "bash"). + */ +char *get_shell(void) { + struct passwd *pw = getpwuid(getuid()); + if (!pw || !pw->pw_shell) + return dup_or_unknown(NULL); + + /* Extract basename from full path */ + const char *s = strrchr(pw->pw_shell, '/'); + return dup_or_unknown(s ? s + 1 : pw->pw_shell); +} + +/* + * Fetch kernel name and version using uname(). + */ +char *get_kernel(void) { + struct utsname u; + if (uname(&u) != 0) + return dup_or_unknown(NULL); + + size_t len = strlen(u.sysname) + strlen(u.release) + 2; + char *buf = malloc(len); + if (!buf) + return dup_or_unknown(NULL); + + snprintf(buf, len, "%s %s", u.sysname, u.release); + return buf; +} + +/* + * Fetch operating system / distribution name. + * Linux: /etc/os-release + * macOS/BSD: static identifier + */ +char *get_os(void) { +#if defined(__linux__) + FILE *f = fopen("/etc/os-release", "r"); + if (!f) + return dup_or_unknown("Linux"); + + char line[512]; + while (fgets(line, sizeof line, f)) { + if (strncmp(line, "PRETTY_NAME=", 12) == 0) { + char *v = line + 12; + v[strcspn(v, "\n")] = 0; /* strip newline */ + if (*v == '"') v++; + char *e = strrchr(v, '"'); + if (e) *e = 0; + fclose(f); + return strdup(v); + } + } + fclose(f); + return dup_or_unknown("Linux"); +#elif defined(__APPLE__) + return dup_or_unknown("macOS"); +#elif defined(__FreeBSD__) + return dup_or_unknown("FreeBSD"); +#else + return dup_or_unknown("Unix"); +#endif +} + +/* + * Fetch system uptime. + * Linux: /proc/uptime + * macOS/BSD: sysctl(KERN_BOOTTIME) + */ +char *get_uptime(void) { +#if defined(__linux__) + FILE *f = fopen("/proc/uptime", "r"); + if (!f) + return dup_or_unknown(NULL); + + double s; + if (fscanf(f, "%lf", &s) != 1) { + fclose(f); + return dup_or_unknown(NULL); + } + fclose(f); +#elif defined(__APPLE__) || defined(__FreeBSD__) + struct timeval boottime; + size_t len = sizeof(boottime); + int mib[2] = { CTL_KERN, KERN_BOOTTIME }; + + if (sysctl(mib, 2, &boottime, &len, NULL, 0) != 0) + return dup_or_unknown(NULL); + + time_t now = time(NULL); + double s = difftime(now, boottime.tv_sec); +#else + return dup_or_unknown("n/a"); +#endif + + /* Convert seconds to human-readable format */ + int d = s / 86400; + int h = ((int)s % 86400) / 3600; + int m = ((int)s % 3600) / 60; + + char buf[64]; + if (d) + snprintf(buf, sizeof buf, "%dd %dh %dm", d, h, m); + else if (h) + snprintf(buf, sizeof buf, "%dh %dm", h, m); + else + snprintf(buf, sizeof buf, "%dm", m); + + return strdup(buf); +} + +/* + * Fetch CPU model and core count. + * Linux: /proc/cpuinfo + * macOS/BSD: sysctl hw.model + hw.ncpu + */ +char *get_cpu(void) { +#if defined(__linux__) + FILE *f = fopen("/proc/cpuinfo", "r"); + if (!f) + return dup_or_unknown(NULL); + + char line[512]; + char model[256] = ""; + int cores = 0; + + while (fgets(line, sizeof line, f)) { + if (strncmp(line, "model name", 10) == 0) { + if (!*model) { + char *p = strchr(line, ':'); + if (p) { + p += 2; + p[strcspn(p, "\n")] = 0; + strncpy(model, p, sizeof model - 1); + } + } + cores++; + } + } + fclose(f); + + if (!*model) + return dup_or_unknown(NULL); + + char buf[512]; + snprintf(buf, sizeof buf, "%s (%d cores)", model, cores); + return strdup(buf); +#elif defined(__APPLE__) || defined(__FreeBSD__) + char model[256]; + size_t len = sizeof(model); + + if (sysctlbyname("hw.model", model, &len, NULL, 0) != 0) + strncpy(model, "unknown", sizeof model); + + int cores = 0; + len = sizeof(cores); + sysctlbyname("hw.ncpu", &cores, &len, NULL, 0); + + char buf[512]; + snprintf(buf, sizeof buf, "%s (%d cores)", model, cores); + return strdup(buf); +#else + return dup_or_unknown("unknown cpu"); +#endif +} + +/* + * Entry point. + * Fetches all information, prints it, then frees memory. + */ +int main(void) { + char *user = get_user(); + char *host = get_hostname(); + char *shell = get_shell(); + char *os = get_os(); + char *kernel = get_kernel(); + char *uptime = get_uptime(); + char *cpu = get_cpu(); + + printf("\n" + "\033[1m\033[37mwelcome, %s\033[1;31m ♥\033[0m\n\n" + " \033[1;37m• \033[1;37mhost\033[0m %s\n" + " \033[1;33m• \033[1;37mdistro\033[0m %s\n" + " \033[1;32m• \033[1;37mshell\033[0m %s\n" + " \033[1;36m• \033[1;37mcpu\033[0m %s\n" + " \033[1;34m• \033[1;37mkernel\033[0m %s\n" + " \033[1;35m• \033[1;37muptime\033[0m %s\n\n", + user, host, os, shell, cpu, kernel, uptime); + + /* Free all heap-allocated strings */ + free(user); + free(host); + free(shell); + free(os); + free(kernel); + free(uptime); + free(cpu); + + return 0; +} + diff --git a/qfetchC-notes.txt b/qfetchC-notes.txt new file mode 100644 index 0000000..1953db5 --- /dev/null +++ b/qfetchC-notes.txt @@ -0,0 +1,284 @@ +Perfect — let’s switch hats from *“just making it work”* to *“learning C properly”*. +I’ll explain **how to read and think about this file**, not just what it does. + +I’ll reference the structure you now have in the file and explain the *C concepts* behind each part. + +--- + +## 1️⃣ The mindset shift: C vs C++ + +In C: + +* ❌ No classes, no RAII, no `std::string` +* ✅ **You control memory explicitly** +* ✅ Functions are small, focused, and composable +* ✅ Portability comes from *conditional compilation* + +Your program follows a **very idiomatic C pattern**: + +> *“Each function returns ownership of dynamically allocated data, and the caller frees it.”* + +That’s a **huge** C concept to internalize. + +--- + +## 2️⃣ Feature macros (`_POSIX_C_SOURCE`) + +```c +#define _POSIX_C_SOURCE 200809L +``` + +### Why this exists + +C standard libraries hide some functions unless you *opt in*. + +This line tells the compiler: + +> “I want modern POSIX features like `strdup`, `gethostname`, `sysconf`.” + +Without it: + +* Some systems won’t expose those functions +* You’ll get mysterious warnings or missing symbols + +**Lesson:** +👉 In C, *what functions exist* depends on feature macros. + +--- + +## 3️⃣ Headers: what C teaches you here + +```c +#include // printf, FILE +#include // malloc, free +#include // strlen, strdup +#include // POSIX functions +``` + +In C: + +* Headers are **contracts** +* If it’s not included, the compiler assumes *nothing* + +**Rule of thumb:** + +> If you call a function, its header must be included — no exceptions. + +--- + +## 4️⃣ Conditional compilation (`#if defined(...)`) + +Example: + +```c +#if defined(__APPLE__) || defined(__FreeBSD__) +#include +#endif +``` + +### What this teaches you + +* C has **no runtime reflection** +* Portability happens at **compile time** +* The preprocessor literally removes code before compilation + +Think of it as: + +```text +“Only compile this code if the OS supports it.” +``` + +This is how *real* portable C software works (git, curl, openssh). + +--- + +## 5️⃣ The most important function in the file + +```c +static char *dup_or_unknown(const char *s) +``` + +### Why this exists + +This function enforces a **contract**: + +* Every getter: + + * returns a valid `char *` + * never returns `NULL` + * always returns heap memory + +### Why that matters + +It lets `main()` be *simple*: + +```c +char *user = get_user(); +/* ... */ +free(user); +``` + +No special cases. +No defensive `if (ptr)` checks. + +**Lesson:** +👉 In C, helper functions enforce invariants so callers stay clean. + +--- + +## 6️⃣ Dynamic allocation patterns (CRITICAL) + +Example: + +```c +char *buf = malloc(len); +snprintf(buf, len, "...", ...); +return buf; +``` + +### This is idiomatic C + +Rules being followed: + +1. Allocate **exactly what you need** +2. Initialize before use +3. Return ownership to the caller +4. Caller must `free()` + +Contrast this with C++: + +* No destructors +* No smart pointers +* No safety net + +**Lesson:** +👉 In C, *ownership is a social contract*, not a language feature. + +--- + +## 7️⃣ Why no global buffers? + +You *could* have done: + +```c +static char buf[256]; +``` + +But that would: + +* Break thread safety +* Break reentrancy +* Make functions non-composable + +Dynamic allocation makes your functions: + +* Reusable +* Testable +* Library-quality + +**Lesson:** +👉 Globals are easy — dynamic ownership is correct. + +--- + +## 8️⃣ Reading system information in C + +Each platform teaches a lesson: + +### Linux + +```c +/proc/cpuinfo +/proc/uptime +``` + +* Text parsing +* Line-by-line scanning +* Defensive string handling + +### macOS / BSD + +```c +sysctl() +``` + +* Structured kernel APIs +* Buffer-size negotiation +* Integer & struct-based data + +**Lesson:** +👉 C doesn’t abstract the OS — it exposes it. + +--- + +## 9️⃣ Time handling (classic C pain point) + +```c +time_t now = time(NULL); +difftime(now, boottime.tv_sec); +``` + +Why this matters: + +* `time_t` may not be an integer +* You *never* subtract times directly +* `difftime()` handles portability + +**Lesson:** +👉 In C, use the standard library *exactly as intended*. + +--- + +## 🔟 The `main()` function: clean by design + +```c +char *user = get_user(); +/* ... */ +free(user); +``` + +Notice: + +* No logic +* No parsing +* No platform checks +* No error handling clutter + +All complexity lives *outside* `main()`. + +**Lesson:** +👉 In C, `main()` should read like a script. + +--- + +## 1️⃣1️⃣ What this code teaches you overall + +You’ve learned: + +* ✅ How C handles memory *explicitly* +* ✅ How portability really works +* ✅ How to design safe APIs in unsafe languages +* ✅ How Unix exposes system information +* ✅ How real-world C programs are structured + +This is **not beginner C** — this is *professional-grade foundational C*. + +--- + +## If you want to go deeper next + +From a learning perspective, great next steps would be: + +* 🔹 Splitting into `.h` / `.c` +* 🔹 Writing your own `string_builder` +* 🔹 Adding error-return variants (`int get_x(char **out)`) +* 🔹 Making this a reusable library +* 🔹 Running it under `valgrind` and analyzing output + +If you want, I can: + +* Rewrite one function *line by line* explaining *every character* +* Or turn this into a **guided C learning project** + +Just tell me how deep you want to go — you’re asking exactly the right questions 👏 + diff --git a/qfetchC.out b/qfetchC.out new file mode 100755 index 0000000000000000000000000000000000000000..b4c58650d8acc3ce3932e612cc04c9f9154f8c5f GIT binary patch literal 13864 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