diff --git a/fec/src/diag.c b/fec/src/diag.c index c9d35b1..d0df0f4 100644 --- a/fec/src/diag.c +++ b/fec/src/diag.c @@ -1,5 +1,14 @@ #include "diag.h" +#include + +FILE *fe_diag_stream(void) +{ + static FILE *stream=0; + if(!stream) stream=getenv("FE_DIAG_STDOUT") ? stdout : stderr; + return stream; +} + static unsigned long digits(unsigned long n) { unsigned long count=1; @@ -27,21 +36,21 @@ static void excerpt(const FeDiags *d, FeLoc loc) end=start; while(endstart) fwrite(src+start,1,(size_t)(end-start),stderr); - fputc('\n',stderr); - fputs(" ",stderr); - for(i=0;istart) fwrite(src+start,1,(size_t)(end-start),fe_diag_stream()); + fputc('\n',fe_diag_stream()); + fputs(" ",fe_diag_stream()); + for(i=0;ierrors++; - fprintf(stderr, "%s:%lu:%lu: error: %s\n", loc.file ? loc.file : "", loc.line, loc.col, msg); + fprintf(fe_diag_stream(), "%s:%lu:%lu: error: %s\n", loc.file ? loc.file : "", loc.line, loc.col, msg); excerpt(d,loc); } void fe_diag_errorf(FeDiags *d, FeLoc loc, const char *msg, const char *arg) { d->errors++; - fprintf(stderr, "%s:%lu:%lu: error: ", loc.file ? loc.file : "", loc.line, loc.col); - fprintf(stderr, msg, arg); - fputc('\n', stderr); + fprintf(fe_diag_stream(), "%s:%lu:%lu: error: ", loc.file ? loc.file : "", loc.line, loc.col); + fprintf(fe_diag_stream(), msg, arg); + fputc('\n', fe_diag_stream()); excerpt(d,loc); } void fe_diag_note(FeLoc loc, const char *msg) { - fprintf(stderr, "%s:%lu:%lu: note: %s\n", loc.file ? loc.file : "", loc.line, loc.col, msg); + fprintf(fe_diag_stream(), "%s:%lu:%lu: note: %s\n", loc.file ? loc.file : "", loc.line, loc.col, msg); } void fe_diag_note_src(FeDiags *d, FeLoc loc, const char *msg) diff --git a/fec/src/diag.h b/fec/src/diag.h index 300f90f..df27869 100644 --- a/fec/src/diag.h +++ b/fec/src/diag.h @@ -16,6 +16,13 @@ typedef struct FeDiags { unsigned long source_len; } FeDiags; +/* Stream diagnostics are written to. Defaults to stderr; returns stdout when + FE_DIAG_STDOUT is set in the environment. DOS offers no way to redirect + handle 2 -- COMMAND.COM understands ">" and nothing else -- so under the test + runner every error message would otherwise be written straight to the screen + and lost. Interactive use is unaffected: both streams reach the console. */ +FILE *fe_diag_stream(void); + void fe_diags_init(FeDiags *d, const char *source, unsigned long source_len); void fe_diag_error(FeDiags *d, FeLoc loc, const char *msg); void fe_diag_errorf(FeDiags *d, FeLoc loc, const char *msg, const char *arg); diff --git a/fec/src/driver.c b/fec/src/driver.c index 3c3fc20..90903d5 100644 --- a/fec/src/driver.c +++ b/fec/src/driver.c @@ -8,7 +8,7 @@ static char *read_file(const char *name, unsigned long *size) { FILE *f; long n; char *p; - f=fopen(name,"rb"); if(!f){fprintf(stderr,"fec: cannot open %s\n",name);return 0;} + f=fopen(name,"rb"); if(!f){fprintf(fe_diag_stream(),"fec: cannot open %s\n",name);return 0;} if(fseek(f,0L,SEEK_END)!=0){fclose(f);return 0;} n=ftell(f); if(n<0){fclose(f);return 0;} rewind(f); p=(char *)malloc((unsigned long)n+1); if(!p){fclose(f);return 0;} if(n && fread(p,1,(size_t)n,f)!=(size_t)n){free(p);fclose(f);return 0;} fclose(f);p[n]='\0';*size=(unsigned long)n;return p; @@ -54,7 +54,7 @@ int main(int argc, char **argv) else if(strcmp(argv[i],"--dump-tokens")==0) dump_tok=1; else if(strcmp(argv[i],"--check")==0) check_only=1; else if(strcmp(argv[i],"--emit-c")==0) emit=1; - else if(strcmp(argv[i],"-o")==0){if(i+1>=argc){fprintf(stderr,"fec: -o needs a path\n");return 2;}outname=argv[++i];} + else if(strcmp(argv[i],"-o")==0){if(i+1>=argc){fprintf(fe_diag_stream(),"fec: -o needs a path\n");return 2;}outname=argv[++i];} else if(strncmp(argv[i],"-o",2)==0 && argv[i][2]) outname=argv[i]+2; else if(strncmp(argv[i],"--target=bits16",15)==0) pointer_bits=16; else if(strncmp(argv[i],"--target=bits32",15)==0) pointer_bits=32; @@ -62,13 +62,13 @@ int main(int argc, char **argv) else if(strncmp(argv[i],"--target=",9)==0 || strncmp(argv[i],"--model=",8)==0 || strcmp(argv[i],"--strip-error-names")==0) { } else if(argv[i][0]!='-') file=argv[i]; else if(strcmp(argv[i],"--help")==0){usage();return 0;} - else {fprintf(stderr,"fec: unknown option %s\n",argv[i]);return 2;} + else {fprintf(fe_diag_stream(),"fec: unknown option %s\n",argv[i]);return 2;} } if((dump?1:0)+(dump_tok?1:0)+(check_only?1:0)+(emit?1:0)>1){ - fprintf(stderr,"fec: choose only one output mode\n"); + fprintf(fe_diag_stream(),"fec: choose only one output mode\n"); return 2; } - if(!file){fprintf(stderr,"fec: no input file\n");return 2;} + if(!file){fprintf(fe_diag_stream(),"fec: no input file\n");return 2;} src=read_file(file,&n); if(!src)return 2; fe_diags_init(&d,src,n); @@ -98,7 +98,7 @@ int main(int argc, char **argv) return 0; } out=outname?fopen(outname,"w"):stdout; - if(!out){fprintf(stderr,"fec: cannot create %s\n",outname);fe_ast_destroy(&ast);free(src);return 2;} + if(!out){fprintf(fe_diag_stream(),"fec: cannot create %s\n",outname);fe_ast_destroy(&ast);free(src);return 2;} fe_emit_c_init(&emitter,out,&check,pointer_bits,no_checks); fe_emit_c_program(&emitter); if(outname)fclose(out); diff --git a/src/ferrolang_vm/dosboxx.py b/src/ferrolang_vm/dosboxx.py index ac94cfd..c968663 100644 --- a/src/ferrolang_vm/dosboxx.py +++ b/src/ferrolang_vm/dosboxx.py @@ -159,6 +159,9 @@ def _batch(cases: list[Case], *, show_dos: bool, trace_dos: bool) -> str: "@echo off", "if not exist RESULTS md RESULTS", "if not exist OUT md OUT", "set WATCOM=W:", "set INCLUDE=W:\\H", "set LIB=W:\\LIB286\\DOS;W:\\LIB286;W:\\LIB386\\DOS;W:\\LIB386", + # COMMAND.COM can only redirect handle 1, so fec diagnostics written to + # stderr never reach RESULTS\.LOG. Ask it for stdout instead. + "set FE_DIAG_STDOUT=1", build, "if not exist BUILD.OK goto BUILDFAIL", "echo PASS>RESULTS\\BUILD.RES", ]