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,

        -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

Reply via email to