add to testsuite and fix yet another sed corner case
authorDenis Vlasenko <vda.linux@googlemail.com>
Mon, 29 Jan 2007 17:10:19 +0000 (17:10 -0000)
committerDenis Vlasenko <vda.linux@googlemail.com>
Mon, 29 Jan 2007 17:10:19 +0000 (17:10 -0000)
editors/sed.c
testsuite/sed.tests

index bf40877e454d527f9dd7859506ba27d0b3616da5..695e5e9740cb09810afb6b00ee462926ca5c4a7c 100644 (file)
@@ -724,6 +724,7 @@ static void add_input_file(FILE *file)
  */
 enum {
        NO_EOL_CHAR = 1,
+       LAST_IS_NUL = 2,
 };
 static char *get_next_line(char *gets_char)
 {
@@ -737,17 +738,24 @@ static char *get_next_line(char *gets_char)
         * doesn't end with either '\n' or '\0' */
        gc = NO_EOL_CHAR;
        while (bbg.current_input_file < bbg.input_file_count) {
+               FILE *fp = bbg.input_file_list[bbg.current_input_file];
                /* Read line up to a newline or NUL byte, inclusive,
                 * return malloc'ed char[]. length of the chunk read
                 * is stored in len. NULL if EOF/error */
-               temp = bb_get_chunk_from_file(
-                       bbg.input_file_list[bbg.current_input_file], &len);
+               temp = bb_get_chunk_from_file(fp, &len);
                if (temp) {
                        /* len > 0 here, it's ok to do temp[len-1] */
                        char c = temp[len-1];
                        if (c == '\n' || c == '\0') {
                                temp[len-1] = '\0';
                                gc = c;
+                               if (c == '\0') {
+                                       int ch = fgetc(fp);
+                                       if (ch != EOF)
+                                               ungetc(ch, fp);
+                                       else
+                                               gc = LAST_IS_NUL;
+                               }
                        }
                        /* else we put NO_EOL_CHAR into *gets_char */
                        break;
@@ -761,7 +769,8 @@ static char *get_next_line(char *gets_char)
                 * (note: *no* newline after "b bang"!) */
                }
                /* Close this file and advance to next one */
-               fclose(bbg.input_file_list[bbg.current_input_file++]);
+               fclose(fp);
+               bbg.current_input_file++;
        }
        *gets_char = gc;
        return temp;
@@ -785,20 +794,29 @@ static void puts_maybe_newline(char *s, FILE *file, char *last_puts_char, char l
 {
        char lpc = *last_puts_char;
 
-       /* Is this a first line from new file
-        * and old file didn't end with '\n' or '\0'? */
+       /* Need to insert a '\n' between two files because first file's
+        * last line wasn't terminated? */
        if (lpc != '\n' && lpc != '\0') {
                fputc('\n', file);
                lpc = '\n';
        }
        fputs(s, file);
+
        /* 'x' - just something which is not '\n', '\0' or NO_EOL_CHAR */
        if (s[0])
                lpc = 'x';
-       if (last_gets_char != NO_EOL_CHAR) { /* had trailing '\n' or '\0'? */
+
+       /* had trailing '\0' and it was last char of file? */
+       if (last_gets_char == LAST_IS_NUL) {
+               fputc('\0', file);
+               lpc = 'x'; /* */
+       } else
+       /* had trailing '\n' or '\0'? */
+       if (last_gets_char != NO_EOL_CHAR) {
                fputc(last_gets_char, file);
                lpc = last_gets_char;
        }
+
        if (ferror(file)) {
                xfunc_error_retval = 4;  /* It's what gnu sed exits with... */
                bb_error_msg_and_die(bb_msg_write_error);
index cc200703d3f900b8fa6bc6df7a1e2f08d189d5e2..9576b6c4babbf87844aef99ec5108bfadd767dc3 100755 (executable)
@@ -143,6 +143,9 @@ testing "sed subst+write" \
        "sed -e 's/i/z/' -e 'woutputw' input -; echo -n X; cat outputw" \
        "thzngy\nagaznXthzngy\nagazn" "thingy" "again"
 rm outputw
+testing "sed trailing NUL" \
+       "sed 's/i/z/' input -" \
+       "a\0b\0\nc" "a\0b\0" "c"
 
 # Test end-of-file matching behavior