On Tue, Feb 21, 2017 at 07:00:34AM +0100, Otto Moerbeek wrote: > On Tue, Feb 21, 2017 at 04:08:57AM +0100, Martijn Dekker wrote: > > > Upon encountering a parsing error, bc(1) passes an error message on to > > dc(1), which writes the error message to standard output along with the > > normal output. > > > > That is a bug. Error messages should go to standard error instead, as > > POSIX specifies: > > http://pubs.opengroup.org/onlinepubs/9699919799/utilities/bc.html#tag_20_09_10 > > > > GNU 'bc' and Solaris 'bc' act like POSIX says and write error messages > > to standard error. > > > > Bizarrely, the exit status of bc(1) is left unspecified: > > http://pubs.opengroup.org/onlinepubs/9699919799/utilities/bc.html#tag_20_09_18 > > And indeed, all versions of 'bc' exit with status 0 if there is an input > > error such as a parsing error, so the exit status cannot be used to > > catch it. That leaves examining standard error as the only method for a > > program calling bc(1), such as a shell script, to distinguish between an > > error state and normal operation. That is, with this bug, there is no > > way at all. > > > > The following example shell function transparently hardens bc(1) by > > intercepting standard error and exiting the program or subshell if an > > error was produced. > > > > bc() { > > _bc_err=$(command -p bc "$@" 1>&3 2>&1) > > [ -z "${_bc_err}" ] && return > > printf '%s\n' "$0: bc(1) caught errors:" "${_bc_err}" 1>&2 > > exit 125 > > } 3>&1 > > > > The patch below fixes bc(1) so error messages are written directly to > > standard error and the above shell function works as expected. As a side > > effect, yyerror() is simplified. > > > > Another side effect is that bc(1) error messages are no longer neatly > > included in the generated dc(1) source when debugging it using 'bc -c'. > > But I don't think that is actually a problem; they are just printed to > > standard error instead. In fact, the patch makes 'bc -c' act like > > Solaris. If others find this problematic, the patch could be extended to > > restore the old behaviour only if '-c' is given. > > > > The manual page does not document error message behaviour one way or > > another. Since the patch implements standard behaviour, no change seems > > necessary there. > > > > Thanks, > > > > - M. > > > > Thanks for the diff. I am now wondering why I wrote it this way.... > Likely beacuse the original bc had a similar approach. > Anyway, I'll try to look at this the coming days,
Indeed, 4.4BSD bc does this: yyerror( s ) char *s; { if(ifile > sargc)ss="teletype"; printf("c[%s on line %d, %s]pc\n", s ,ln+1,ss); fflush(stdout); cp = cary; crs = rcrs; bindx = 0; lev = 0; b_sp_nxt = &b_space[0]; } My original goal was to make a bc that produced the same dc commands as the reference implementation I used. You can now see that your diff skips the 'c' commands an in that changes behaviour. Pondering if introducing a way to write to stderr in dc(1) would be better... -Otto > > > Index: bc.y > > =================================================================== > > RCS file: /cvs/src/usr.bin/bc/bc.y,v > > retrieving revision 1.49 > > diff -u -p -r1.49 bc.y > > --- bc.y 23 Nov 2015 09:58:55 -0000 1.49 > > +++ bc.y 21 Feb 2017 02:09:08 -0000 > > @@ -942,35 +942,25 @@ add_local(ssize_t n) > > void > > yyerror(char *s) > > { > > - char *str, *p; > > int n; > > > > if (yyin != NULL && feof(yyin)) > > - n = asprintf(&str, "%s: %s:%d: %s: unexpected EOF", > > + n = fprintf(stderr, "%s: %s:%d: %s: unexpected EOF\n", > > __progname, filename, lineno, s); > > else if (yytext[0] == '\n') > > - n = asprintf(&str, > > - "%s: %s:%d: %s: newline unexpected", > > + n = fprintf(stderr, > > + "%s: %s:%d: %s: newline unexpected\n", > > __progname, filename, lineno, s); > > else if (isspace((unsigned char)yytext[0]) || > > !isprint((unsigned char)yytext[0])) > > - n = asprintf(&str, > > - "%s: %s:%d: %s: ascii char 0x%02x unexpected", > > + n = fprintf(stderr, > > + "%s: %s:%d: %s: ascii char 0x%02x unexpected\n", > > __progname, filename, lineno, s, yytext[0] & 0xff); > > else > > - n = asprintf(&str, "%s: %s:%d: %s: %s unexpected", > > + n = fprintf(stderr, "%s: %s:%d: %s: %s unexpected\n", > > __progname, filename, lineno, s, yytext); > > if (n == -1) > > err(1, NULL); > > - > > - fputs("c[", stdout); > > - for (p = str; *p != '\0'; p++) { > > - if (*p == '[' || *p == ']' || *p =='\\') > > - putchar('\\'); > > - putchar(*p); > > - } > > - fputs("]pc\n", stdout); > > - free(str); > > } > > > > void