# Record Manager — Instructor Reference Solution (M1–M5) **PRIVATE — never ships to students.** This is the reference implementation for the Record Manager cumulative project (`assignments/projects/`), milestone by milestone, plus the graded-interface contract and grading notes. ## Layout ``` include/record.h M1 record_t + record_new/free + status_t include/buf.h M2 dynamic string buffer (+ readline) include/list.h M3 opaque list_t + append/find/remove (+ len/get/sort helpers) include/store.h M4 store_save / store_load (text file, '|' delimiter) src/record.c M1 deep-copy record lifecycle src/buf.c M2 growable buffer: append/appendc/cstr/reset/free/readline src/list.c M3 singly linked list; remove frees node + record src/store.c M4 one record per line; load appends + reports count src/main.c M5 menu (add/list/search/delete/save/load/help/quit) Makefile builds recman + recman_asan; `make test` runs unit + smoke tests/unit.c direct tests of buf/list/store (links library sources) tests/smoke.sh end-to-end CLI session ``` ## Build + verify ```bash make # recman make asan # recman_asan (ASan+UBSan) make test # unit + smoke, both under ASan/UBSan ``` Reference must be `-std=c17 -Wall -Wextra -Wpedantic -Werror` clean and report `0 bytes lost` under Valgrind. ## Graded interface (what hidden tests touch) `record_new`, `record_free`, `list_new`, `list_free`, `list_append`, `list_find`, `list_remove`, `store_save`, `store_load` — exact signatures in `assignments/projects/README.md`. This reference adds three non-graded helpers (`list_len`, `list_get`, `list_sort`) that the menu and store layer need; they are extra, not substitutes. ### Contract decisions (documented for graders) - **`list_remove`**: returns `0` on success, `STATUS_ERR_NOTFOUND` when no record matches, `STATUS_ERR_ARG` on NULL args. Frees the matching record + node. - **`store_load`**: returns the **number of records read** (≥ 0) on success; negative `status_t` on error. "0 on success" from the interface table is satisfied when the file is empty/blank. It **appends** into the existing list. - **Delimiter `|`**: none of the three flavors' fields (email/phone, assignment/ score, category/amount) can contain `|`; `name` may contain spaces — the delimiter is safe across all flavors. - **Empty fields**: saved as empty strings; loaded back as empty strings (never NULL from `store_load`). ## Milestone-by-milestone notes ### M2 — dynamic string buffer (week 7) Hidden tests likely: appends beyond initial capacity, `buf_cstr` NUL-termination after every append, reset-then-reuse, `buf_readline` on a long line (>256 chars), EOF/empty-line behavior. Sticky-OOM flag keeps the buffer usable after a failed realloc. **Common mistake:** forgetting the `+1` for the NUL in `buf_grow`, or realloc-ing to a shrinking capacity. ### M3 — linked list (week 11) Hidden tests likely: append to empty list, find across many nodes, remove head / remove middle / remove missing, `list_free` of a 3-node list under Valgrind (0 lost), NULL-arg safety. **Common mistakes:** freeing the record and then walking `next` (order matters — save `next` first); `list_remove` freeing the node but not the record (double-free later); storing `record_t *` by value instead of pointer. ### M4 — text file save/load (week 13) Hidden tests likely: save→load round-trip preserves order + all three fields; load into a non-empty list appends; file with blank lines; missing file returns error, not crash; fields containing spaces survive (the `|` delimiter). **Common mistakes:** assuming `fgets` succeeds; writing with `fprintf` and never checking `ferror`; not stripping `\n`; `strtok` mangles empty fields (use `strchr` splitting instead — empty field1/field2 must survive). ### M5 — refactor + search/sort + hardening (week 15) Hidden tests likely: multi-file build with one `make`; case-insensitive substring `search` across **all three** fields; `sort` alphabetical by name; ASan/UBSan-clean; Valgrind 0 lost. **Common mistakes:** search matching only `name`; sort comparing `record_t*` pointers instead of `->name`; `-Werror` violations from unused includes after the refactor. ## Grading sanity checks 1. `make` from a clean tree (no build/ artifacts committed). 2. `make asan && ./build/recman_asan` → add 3 records → delete middle → save → quit → reload → `list` shows the surviving two in order. 3. `valgrind --leak-check=full ./build/recman` with the same session → `0 bytes lost` (the menu leaks nothing). 4. Feed the `tests/` fixtures: long input line (buf), 10k-record list (list), save/load with spaces and empties (store).