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 0000000..b4c5865 Binary files /dev/null and b/qfetchC.out differ