Make I/O errors fatal instead of mere warnings (#63)

An input/output error indicates a fatal condition, even if it
occurs when closing a file. Awk should not return success on I/O
error, but treat I/O errors as it already treats write errors.

Test case:

$ (trap '' PIPE; awk 'BEGIN { print "hi"; }'; echo "E $?" >&2) | :
awk: i/o error occurred while closing /dev/stdout
 source line number 1
E 2

The test case pipes a line into a dummy command that reads no
input, with SIGPIPE ignored so we rely on awk's own I/O checking.
No write error is detected, because the pipe is buffered; the
broken pipe is only detected as an I/O error on closing stdout.

Before this commit, "E 0" was printed (indicating status 0/success)
because an I/O error merely produced a warning. A shell script
was unable to detect the I/O error using the exit status.
This commit is contained in:
Martijn Dekker 2020-01-17 13:02:57 +01:00 committed by Arnold Robbins
parent 2976507cc1
commit fed1a562c3
3 changed files with 13 additions and 4 deletions

4
FIXES
View File

@ -25,6 +25,10 @@ THIS SOFTWARE.
This file lists all bug fixes, changes, etc., made since the AWK book This file lists all bug fixes, changes, etc., made since the AWK book
was sent to the printers in August, 1987. was sent to the printers in August, 1987.
January 9, 2020:
Input/output errors on closing files are now fatal instead of
mere warnings. Thanks to Martijn Dekker <martijn@inlv.org>.
January 5, 2020: January 5, 2020:
Fix a bug in the concatentation of two string constants into Fix a bug in the concatentation of two string constants into
one done in the grammar. Fixes GitHub issue #61. Thanks one done in the grammar. Fixes GitHub issue #61. Thanks

8
run.c
View File

@ -1769,13 +1769,13 @@ Cell *closefile(Node **a, int n)
for (i = 0; i < nfiles; i++) { for (i = 0; i < nfiles; i++) {
if (files[i].fname && strcmp(x->sval, files[i].fname) == 0) { if (files[i].fname && strcmp(x->sval, files[i].fname) == 0) {
if (ferror(files[i].fp)) if (ferror(files[i].fp))
WARNING( "i/o error occurred on %s", files[i].fname ); FATAL( "i/o error occurred on %s", files[i].fname );
if (files[i].mode == '|' || files[i].mode == LE) if (files[i].mode == '|' || files[i].mode == LE)
stat = pclose(files[i].fp); stat = pclose(files[i].fp);
else else
stat = fclose(files[i].fp); stat = fclose(files[i].fp);
if (stat == EOF) if (stat == EOF)
WARNING( "i/o error occurred closing %s", files[i].fname ); FATAL( "i/o error occurred closing %s", files[i].fname );
if (i > 2) /* don't do /dev/std... */ if (i > 2) /* don't do /dev/std... */
xfree(files[i].fname); xfree(files[i].fname);
files[i].fname = NULL; /* watch out for ref thru this */ files[i].fname = NULL; /* watch out for ref thru this */
@ -1795,13 +1795,13 @@ void closeall(void)
for (i = 0; i < FOPEN_MAX; i++) { for (i = 0; i < FOPEN_MAX; i++) {
if (files[i].fp) { if (files[i].fp) {
if (ferror(files[i].fp)) if (ferror(files[i].fp))
WARNING( "i/o error occurred on %s", files[i].fname ); FATAL( "i/o error occurred on %s", files[i].fname );
if (files[i].mode == '|' || files[i].mode == LE) if (files[i].mode == '|' || files[i].mode == LE)
stat = pclose(files[i].fp); stat = pclose(files[i].fp);
else else
stat = fclose(files[i].fp); stat = fclose(files[i].fp);
if (stat == EOF) if (stat == EOF)
WARNING( "i/o error occurred while closing %s", files[i].fname ); FATAL( "i/o error occurred while closing %s", files[i].fname );
} }
} }
} }

View File

@ -466,3 +466,8 @@ echo '' >foo0
$awk 'END { print NF, $0 }' foo0 >foo1 $awk 'END { print NF, $0 }' foo0 >foo1
awk '{ print NF, $0 }' foo0| tail -1 >foo2 awk '{ print NF, $0 }' foo0| tail -1 >foo2
cmp -s foo1 foo2 || echo 'BAD: T.misc END must preserve $0' cmp -s foo1 foo2 || echo 'BAD: T.misc END must preserve $0'
# Check for nonzero exit status on I/O error.
echo 'E 2' >foo1
(trap '' PIPE; "$awk" 'BEGIN { print "hi"; }' 2>/dev/null; echo "E $?" >foo2) | :
cmp -s foo1 foo2 || echo 'BAD: T.misc exit status on I/O error'