lib/getopt: printusage + printhelp
Port ref/hare/getopt/getopts.ha:202-314 (printusage / _printusage / containsh / printhelp): the two-pass io.empty width measurement + the >72-col indent rule. Table-driven tests. cstage-only (C-first); wwstage byte-id twin owed (#125). printsubcmds + parse() deferred (#127 / #126).
This commit is contained in:
@@ -22,6 +22,8 @@
|
||||
package getopt;
|
||||
|
||||
import getopt;
|
||||
import io;
|
||||
import memio;
|
||||
import strings;
|
||||
|
||||
// Direct exit(2) binding rather than `use os;` — os exports
|
||||
@@ -391,6 +393,103 @@ fn streq(a: str, b: str) bool = {
|
||||
};
|
||||
};
|
||||
|
||||
// ---- printusage_cases: table of width-measure paths --------------------
|
||||
//
|
||||
// ref/hare/getopt/getopts.ha:202-265.
|
||||
// Row 0: short line (≤72) — auto-[-h] + FLAG cluster, no wrap.
|
||||
// Row 1: long line (>72) — PARAM slots each prefixed with \n\t.
|
||||
//
|
||||
// Help entries are packed into a flat [7]getopt.help array; hoff[i] /
|
||||
// hcnt[i] mark each row's slice. Parallel [2]str arrays hold name and
|
||||
// expected output per row.
|
||||
@test fn printusage_cases() void = {
|
||||
let helps: [7]getopt.help;
|
||||
// Row 0: CMD + FLAG 'F' + FLAG 'a'
|
||||
getopt.cmdhelp(&helps[0], "list files");
|
||||
getopt.flaghelp(&helps[1], 'F': rune, "do F");
|
||||
getopt.flaghelp(&helps[2], 'a': rune, "do a");
|
||||
// Row 1: CMD + 3 PARAMs (no-indent measurement 77 > 72)
|
||||
getopt.cmdhelp(&helps[3], "process files");
|
||||
getopt.paramhelp(&helps[4], 'e': rune, "expression", "run expr");
|
||||
getopt.paramhelp(&helps[5], 'o': rune, "output-file", "output");
|
||||
getopt.paramhelp(&helps[6], 'i': rune, "input-path", "input");
|
||||
|
||||
let names: [2]str;
|
||||
let wants: [2]str;
|
||||
let hoff: [2]i32;
|
||||
let hcnt: [2]i32;
|
||||
|
||||
names[0] = "ls";
|
||||
wants[0] = "Usage: ls [-hFa]\n";
|
||||
hoff[0] = 0; hcnt[0] = 3;
|
||||
|
||||
names[1] = "myprogram";
|
||||
wants[1] = "Usage: myprogram [-h]\n\t [-e <expression>]\n\t [-o <output-file>]\n\t [-i <input-path>]\n";
|
||||
hoff[1] = 3; hcnt[1] = 4;
|
||||
|
||||
let buf: [512]u8;
|
||||
let i: i32 = 0;
|
||||
for (i < 2) {
|
||||
let ms: memio.stream = memio.fixed(buf[0:512]);
|
||||
let s: io.stream = &ms.vt;
|
||||
let h: io.handle = s: io.handle;
|
||||
let hs: []getopt.help;
|
||||
hs.ptr = &helps[hoff[i]];
|
||||
hs.len = hcnt[i];
|
||||
hs.cap = hcnt[i];
|
||||
match (getopt.printusage(h, names[i], hs)) {
|
||||
case void => {};
|
||||
case let e: io.error => fail();
|
||||
};
|
||||
let got: str = memio.string(&ms);
|
||||
if (!streq(got, wants[i])) { fail(); };
|
||||
i += 1;
|
||||
};
|
||||
};
|
||||
|
||||
// ---- printhelp_cases: table of help-output paths ----------------------
|
||||
//
|
||||
// ref/hare/getopt/getopts.ha:281-314.
|
||||
// Row 0: empty help slice → early return, no output.
|
||||
// Row 1: full help (CMD+FLAG+PARAM+CMD) → summary + usage + option list.
|
||||
//
|
||||
// help[0..4] holds the full-case entries; the empty-case uses a 0-len
|
||||
// slice from the same array (hcnt[0]=0).
|
||||
@test fn printhelp_cases() void = {
|
||||
let helps: [4]getopt.help;
|
||||
getopt.cmdhelp(&helps[0], "concatenate files");
|
||||
getopt.flaghelp(&helps[1], 'n': rune, "number lines");
|
||||
getopt.paramhelp(&helps[2], 'o': rune, "output", "output file");
|
||||
getopt.cmdhelp(&helps[3], "files...");
|
||||
|
||||
let names: [2]str;
|
||||
let wants: [2]str;
|
||||
let hcnt: [2]i32;
|
||||
|
||||
names[0] = "prog";
|
||||
wants[0] = "";
|
||||
hcnt[0] = 0;
|
||||
|
||||
names[1] = "cat";
|
||||
wants[1] = "cat: concatenate files\n\nUsage: cat [-hn] [-o <output>] files...\n\n-h: print this help text\n-n: number lines\n-o <output>: output file\n";
|
||||
hcnt[1] = 4;
|
||||
|
||||
let buf: [512]u8;
|
||||
let i: i32 = 0;
|
||||
for (i < 2) {
|
||||
let ms: memio.stream = memio.fixed(buf[0:512]);
|
||||
let s: io.stream = &ms.vt;
|
||||
let h: io.handle = s: io.handle;
|
||||
match (getopt.printhelp(h, names[i], helps[0:hcnt[i]])) {
|
||||
case void => {};
|
||||
case let e: io.error => fail();
|
||||
};
|
||||
let got: str = memio.string(&ms);
|
||||
if (!streq(got, wants[i])) { fail(); };
|
||||
i += 1;
|
||||
};
|
||||
};
|
||||
|
||||
export fn main() i32 = {
|
||||
signalled = 1; flagcluster();
|
||||
signalled = 2; paramflag();
|
||||
@@ -400,5 +499,7 @@ export fn main() i32 = {
|
||||
signalled = 6; errortable();
|
||||
signalled = 7; strerrortext();
|
||||
signalled = 8; nooptionsbare();
|
||||
signalled = 9; printusage_cases();
|
||||
signalled = 10; printhelp_cases();
|
||||
return 0;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user