7.3 KiB
7.3 KiB
Project Analysis: proj
Tech Stack & Layout
- Language (strict C90 / ANSI C89), no build system (simple compilation with standards-compliant compilers like gcc -ansi or Turbo C++), supported platforms (modern Unix, macOS, Windows via MinGW or Cygwin, DOS 6.22 with Turbo C++)
- Why these choices were made (portability across decades-old and modern systems without relying on C99+ features; minimal dependencies to ensure it "does one thing well" like a Unix tool, avoiding external libs for broad compatibility and simplicity)
- High-level directory structure and purpose of each major directory/file
.(root): Contains Git configuration for version control, with remotes pointing to a Gitea repository for source hostingsrc/: Core source code directory; containsmain.cas the single entry point for the entire program, handling all logic from CLI parsing to output
Module & Function Relationships
- How modules and functions relate to each other: With only a single source file (
src/main.c), all functions are defined inline or as static helpers withinmain.c; the program flows frommain()entry point to utility functions for argument parsing, data processing, and output, promoting a monolithic yet portable structure - Shared utilities and internal code organization (
src/,include/, common helpers): No separateinclude/directory (headers are minimal or inlined); common helpers inmain.cinclude string manipulation, CSV parsing routines, and terminal detection functions that are called sequentially frommain(); utilities are not modularized into separate files to minimize compilation complexity on legacy systems like DOS
Function & Method Reference
Grouped by responsibility in src/main.c (CLI Parsing and Main Loop):
main(int argc, char *argv[]): Serves as the program entry point, parsing command-line arguments to determine actions like adding or listing notes; it orchestrates data flow by calling helpers for CSV I/O and output formatting- How it works (key logic, string/CSV handling, memory management, terminal width detection, etc.): Uses manual string comparison (e.g.,
strcmp) for args, allocates memory withmallocfor note storage, detects terminal width viagetenv("COLUMNS")or fallback to 80 chars for portability, and handles CSV by tokenizing strings with custom loops avoidingstrtokfor C90 purity - Why it exists (design rationale, portability concern, Unix philosophy, or immutability requirement): Embodies Unix "do one thing well" by being a simple CLI tool; ensures portability by avoiding non-standard APIs, and enforces immutability by appending to CSV without deletions
- How it works (key logic, string/CSV handling, memory management, terminal width detection, etc.): Uses manual string comparison (e.g.,
CSV Handling Functions:
-
parse_csv_line(char *line, char **fields, int max_fields): Tokenizes a CSV line into fields without external libs- How it works (key logic, string/CSV handling, memory management, terminal width detection, etc.): Iterates through the string with pointers, handling quotes and commas manually; allocates arrays with
mallocand frees them explicitly to manage memory in C90 style - Why it exists (design rationale, portability concern, Unix philosophy, or immutability requirement): Provides self-contained CSV parsing for note storage, avoiding dependencies for portability across platforms like DOS where libs may not exist; supports immutable archiving by reading without modifying source files
- How it works (key logic, string/CSV handling, memory management, terminal width detection, etc.): Iterates through the string with pointers, handling quotes and commas manually; allocates arrays with
-
write_csv_note(FILE *fp, const char *note): Appends a new note to a CSV file- How it works (key logic, string/CSV handling, memory management, terminal width detection, etc.): Escapes special characters in the note string, formats as CSV row, and writes via
fprintf; no memory allocation needed beyond stack usage for simplicity - Why it exists (design rationale, portability concern, Unix philosophy, or immutability requirement): Enables immutable logging where notes are appended only, never deleted or edited, aligning with archival safety; uses standard
stdio.hfor cross-platform file I/O
- How it works (key logic, string/CSV handling, memory management, terminal width detection, etc.): Escapes special characters in the note string, formats as CSV row, and writes via
Output and Utility Functions:
-
print_notes(char **notes, int count): Displays notes adapted to terminal width- How it works (key logic, string/CSV handling, memory management, terminal width detection, etc.): Queries terminal width with portable methods (e.g.,
ioctlfallback or env var), wraps lines manually using string length checks, and outputs viaprintf - Why it exists (design rationale, portability concern, Unix philosophy, or immutability requirement): Ensures readable CLI output without curses dependency, promoting shell composability; handles portability by providing DOS-safe fallbacks like fixed-width output
- How it works (key logic, string/CSV handling, memory management, terminal width detection, etc.): Queries terminal width with portable methods (e.g.,
-
archive_note(const char *note_id): Moves a note to an archive CSV without deletion- How it works (key logic, string/CSV handling, memory management, terminal width detection, etc.): Reads source CSV, copies non-matching rows to a temp file, appends the matched note to archive, then renames files; manages buffers with
malloc/free - Why it exists (design rationale, portability concern, Unix philosophy, or immutability requirement): Implements immutable archiving (notes are never truly deleted, just relocated) for data integrity; avoids modern file APIs for C90 compliance
- How it works (key logic, string/CSV handling, memory management, terminal width detection, etc.): Reads source CSV, copies non-matching rows to a temp file, appends the matched note to archive, then renames files; manages buffers with
Object & Data Flow
- Main data structures (especially how notes are stored and manipulated in CSV format): Notes stored as simple CSV files (e.g., rows with ID, timestamp, text); in-memory representation uses arrays of
char*allocated viamallocfor lists of notes, with manual indexing instead of structs for C90 simplicity - Flow of data from command-line arguments → processing → output or archive: Args parsed in
main()→ CSV file read/parsed into memory arrays → processing (e.g., append or archive via helper functions) → output formatted for terminal or written back to CSV/archive file; immutability ensures original data is appended to, not overwritten - Error handling and safety patterns used under strict C90 constraints: Checks return values of all functions (e.g.,
fopen!= NULL), useserrnofor I/O errors, manual cleanup withfreein error paths; no exceptions, so flow control via early returns and status codes for portability
Learning Path & Gotchas
- Recommended order to read and understand the codebase: Start with
main()to grasp CLI flow, then study CSV parsing helpers for data handling, followed by output functions for portability tricks, and end with archiving logic to understand immutability - Common pitfalls for newcomers to strict C90 / legacy-compatible C: Forgetting manual
freeaftermallocleading to leaks; assuming C99 features like variable-length arrays (use fixed sizes instead); overlooking pointer arithmetic in string handling - Portability gotchas (DOS vs Unix differences, Turbo C++ quirks,
getenv,system(), file I/O): DOS limits file paths and lacksgetenvfor some vars (use fallbacks); Turbo C++ may require#definefor ANSI compliance;system()behaves differently (e.g., no shell on DOS); file I/O must handle text/binary modes withfopen "rb"/"wb"for cross-platform consistency - Why certain design decisions were made (immutability, no external libs, single-purpose tools): Immutability prevents data loss in archival tools; no libs ensure portability to minimal environments like DOS; single-purpose design follows Unix philosophy for composable CLI utilities, keeping the codebase lean and educational