qfetch/qfetchC-notes.txt
2025-12-21 10:31:31 -08:00

284 lines
5.2 KiB
Text
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

Perfect — lets switch hats from *“just making it work”* to *“learning C properly”*.
Ill explain **how to read and think about this file**, not just what it does.
Ill 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.”*
Thats 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 wont expose those functions
* Youll 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 its 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 doesnt 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
Youve 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 — youre asking exactly the right questions 👏