switch to C
This commit is contained in:
parent
b081820659
commit
bf5837389a
3 changed files with 551 additions and 0 deletions
267
qfetch.c
Normal file
267
qfetch.c
Normal file
|
|
@ -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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <pwd.h>
|
||||
#include <sys/utsname.h>
|
||||
#include <errno.h>
|
||||
|
||||
/* Platform-specific headers for macOS / BSD */
|
||||
#if defined(__APPLE__) || defined(__FreeBSD__)
|
||||
#include <sys/types.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
#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;
|
||||
}
|
||||
|
||||
284
qfetchC-notes.txt
Normal file
284
qfetchC-notes.txt
Normal file
|
|
@ -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 <stdio.h> // printf, FILE
|
||||
#include <stdlib.h> // malloc, free
|
||||
#include <string.h> // strlen, strdup
|
||||
#include <unistd.h> // 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 <sys/sysctl.h>
|
||||
#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 👏
|
||||
|
||||
BIN
qfetchC.out
Executable file
BIN
qfetchC.out
Executable file
Binary file not shown.
Loading…
Add table
Add a link
Reference in a new issue