Adds rcfile_load(path, runner) to rcfile.c/rcfile.h: reads lines with getline() (no fixed-size buffer, so a long line like a multi-peer `set masters ...` can never be silently truncated or split), strips the trailing newline, invokes the caller-supplied runner once per line, and stops at the first nonzero runner return, propagating that value. A missing rc file is a silent no-op; any other open failure prints one stderr warning and returns 0. Found during GREEN: fopen() succeeds on a directory path on this platform, so the EISDIR failure only surfaces on the first getline() call (ferror(f) set) -- added a post-loop ferror() check to catch it (bug-1716). 5 new unit tests (11 total with task 1.1): ENOENT-silent, per-line-in-order with newline stripped, stop-on-nonzero with exact propagation, non-ENOENT open-failure warning, and a >4KB single line proving no truncation/split. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AgYRU3zDTqNGV5Jk8aiKLg
39 lines
1.9 KiB
C
39 lines
1.9 KiB
C
#pragma once
|
|
/*
|
|
* rcfile.h -- xce_test startup rc-file path resolution and line-loading
|
|
* (xce-test-rc-file spec, tasks 1.1/1.2).
|
|
*
|
|
* A standalone, dependency-light module following the same pattern as
|
|
* configcompare.c/h -- no dependency on test.c internals, linked into both
|
|
* the xce_test executable and controller_unit_test.
|
|
*
|
|
* Task 1.1 implemented path resolution (rcfile_resolve_path()). Task 1.2
|
|
* adds line-loading (rcfile_load()) with a pluggable runner callback.
|
|
* Wiring rcfile_load() into xce_test's own startup/main() is a later task
|
|
* in the same spec (2.2) and is out of scope here -- this module has no
|
|
* knowledge of xce_test commands, contexts, or client state.
|
|
*/
|
|
|
|
/* Resolves the rc-file path per the XDG Base Directory Specification:
|
|
* "$XDG_CONFIG_HOME/xce_test/rc" when XDG_CONFIG_HOME is set and non-empty,
|
|
* else "$HOME/.config/xce_test/rc" when HOME is set and non-empty. Returns
|
|
* a heap string the caller must free(), or NULL if neither variable yields
|
|
* a usable base directory. */
|
|
char* rcfile_resolve_path(void);
|
|
|
|
/* Opens `path` and calls `runner(line)` once per line (trailing newline
|
|
* stripped), stopping early if `runner` returns nonzero. Lines are read via
|
|
* getline() with no length cap -- a line is never truncated or split
|
|
* regardless of length.
|
|
*
|
|
* Returns:
|
|
* 0 - path is NULL, the file does not exist (ENOENT),
|
|
* the file could not be opened for another reason
|
|
* (a warning is printed to stderr; non-fatal), or
|
|
* every line ran and every runner call returned 0.
|
|
* nonzero (== runner's) - the runner requested an abort; no further lines
|
|
* are read.
|
|
*
|
|
* Preconditions: `runner` is non-NULL when `path` is non-NULL.
|
|
* Postconditions: the file handle is always closed before returning. */
|
|
int rcfile_load(const char* path, int (*runner)(char* line));
|