feat(rtl): LIST/DISPLAY TO FILE — text output redirection

Wire up TO FILE for both LIST and DISPLAY: __dbList grows a 9th
parameter cFile, opens it (truncating any prior content) when non-
empty, and writes the formatted rows there via fmt.Fprintln. Default
behavior (no TO FILE) still goes to stdout.

std.ch gets two new rules placed *before* the regular LIST/DISPLAY
patterns so they win when TO FILE is present:

  LIST    [<v,...>] TO FILE <(f)> [OFF] [FOR] [WHILE] [NEXT] ...
  DISPLAY [<v,...>] TO FILE <(f)> [OFF] [FOR] [WHILE] [NEXT] ...

Open failure raises a clear *HbError ("LIST/DISPLAY TO FILE: cannot
create <path> — <syscall reason>") so callers know exactly what went
wrong instead of getting partial-or-empty output.

TO PRINTER stays rejected via __dbNotImpl — Five doesn't drive a
printer port. Test coverage: tests/std_ch/test_list_to_file.prg
exercises four shapes (full LIST, single-row DISPLAY, OFF + FOR with
explicit fields, and confirms TO PRINTER still raises). Wired into
the std.ch runner so the regression suite now stands at 14/14.

Gates green:
  go test ./...      : PASS
  FiveSql2 SQL:1999  : 43/43
  Harbour compat     : 56/56
  std.ch suite       : 14/14

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-01 08:15:32 +09:00
parent 3a7f1dea72
commit 412351b67d
4 changed files with 124 additions and 18 deletions

View File

@@ -101,18 +101,25 @@
/* --- console output ---
LIST emits every record matching the filter; DISPLAY without ALL
shows just the current record. Both share __dbList — lAll
distinguishes them. TO PRINTER / TO FILE redirection is not yet
implemented; the stub rules below surface a clear error rather
than silently sending output to stdout when a printer/file was
requested. Order matters: more specific rules first. */
distinguishes them. TO FILE redirects to a freshly-truncated text
file; TO PRINTER is rejected at PP-time (Five doesn't drive a
printer port). Order matters: more specific rules first. */
#command LIST [<v,...>] TO PRINTER [<*tail*>] => ;
__dbNotImpl("LIST ... TO PRINTER")
#command LIST [<v,...>] TO FILE <(f)> [<*tail*>] => ;
__dbNotImpl("LIST ... TO FILE")
#command DISPLAY [<v,...>] TO PRINTER [<*tail*>] => ;
__dbNotImpl("DISPLAY ... TO PRINTER")
#command DISPLAY [<v,...>] TO FILE <(f)> [<*tail*>] => ;
__dbNotImpl("DISPLAY ... TO FILE")
#command LIST [<v,...>] TO FILE <(f)> [<off:OFF>] ;
[FOR <for>] [WHILE <while>] [NEXT <next>] ;
[RECORD <rec>] [<rest:REST>] [ALL] => ;
__dbList( <.off.>, { <{v}> }, .T., ;
<{for}>, <{while}>, <next>, <rec>, <.rest.>, <(f)> )
#command DISPLAY [<v,...>] TO FILE <(f)> [<off:OFF>] ;
[FOR <for>] [WHILE <while>] [NEXT <next>] ;
[RECORD <rec>] [<rest:REST>] [<all:ALL>] => ;
__dbList( <.off.>, { <{v}> }, <.all.>, ;
<{for}>, <{while}>, <next>, <rec>, <.rest.>, <(f)> )
#command LIST [<v,...>] [<off:OFF>] ;
[FOR <for>] [WHILE <while>] [NEXT <next>] ;