diff options
Diffstat (limited to 'sed/execute.c')
-rw-r--r-- | sed/execute.c | 1464 |
1 files changed, 707 insertions, 757 deletions
diff --git a/sed/execute.c b/sed/execute.c index cb84192..009d188 100644 --- a/sed/execute.c +++ b/sed/execute.c @@ -1,6 +1,5 @@ /* GNU SED, a batch stream editor. - Copyright (C) 1989,90,91,92,93,94,95,98,99,2002,2003,2004,2005,2006,2008,2009 - Free Software Foundation, Inc. + Copyright (C) 1989-2016 Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -36,6 +35,7 @@ #include <selinux/selinux.h> #include <selinux/context.h> #include "acl.h" +#include "ignore-value.h" #ifdef __GNUC__ # if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__-0 >= 7) @@ -47,7 +47,11 @@ # define UNUSED #endif - +/* The number of extra bytes that must be allocated/usable, beyond + the declared "end" of each line buffer that may be passed to + match_regex. This is imposed by its use of dfaexec. */ +#define DFA_SLOP 1 + /* Sed operates a line at a time. */ struct line { char *text; /* Pointer to line allocated by malloc. */ @@ -79,10 +83,10 @@ struct input { char **file_list; /* Count of files we failed to open. */ - countT bad_count; + countT bad_count; /* Current input line number (over all files). */ - countT line_number; + countT line_number; /* True if we'll reset line numbers and addresses before starting to process the next (possibly the first) file. */ @@ -132,11 +136,8 @@ static struct append_queue *append_tail = NULL; /* increase a struct line's length, making some attempt at keeping realloc() calls under control by padding for future growth. */ -static void resize_line (struct line *, size_t); static void -resize_line(lb, len) - struct line *lb; - size_t len; +resize_line (struct line *lb, size_t len) { int inactive; inactive = lb->active - lb->text; @@ -151,7 +152,7 @@ resize_line(lb, len) inactive = 0; if (lb->alloc > len) - return; + return; } lb->alloc *= 2; @@ -159,18 +160,14 @@ resize_line(lb, len) lb->alloc = len; if (lb->alloc < INITIAL_BUFFER_SIZE) lb->alloc = INITIAL_BUFFER_SIZE; - - lb->text = REALLOC(lb->text, inactive + lb->alloc, char); + + lb->text = REALLOC(lb->text, inactive + lb->alloc + DFA_SLOP, char); lb->active = lb->text + inactive; } -/* Append `length' bytes from `string' to the line `to'. */ -static void str_append (struct line *, const char *, size_t); +/* Append LENGTH bytes from STRING to the line, TO. */ static void -str_append(to, string, length) - struct line *to; - const char *string; - size_t length; +str_append(struct line *to, const char *string, size_t length) { size_t new_length = to->length + length; @@ -184,26 +181,25 @@ str_append(to, string, length) { size_t n = MBRLEN (string, length, &to->mbstate); - /* An invalid or imcomplete sequence is treated like a singlebyte character. */ + /* Treat an invalid or incomplete sequence like a + single-byte character. */ if (n == (size_t) -1 || n == (size_t) -2) - { - memset (&to->mbstate, 0, sizeof (to->mbstate)); - n = 1; - } - - if (n > 0) - { - string += n; - length -= n; - } - else - break; + { + memset (&to->mbstate, 0, sizeof (to->mbstate)); + n = 1; + } + + if (n == 0) + break; + + string += n; + length -= n; } } static void str_append_modified(struct line *to, const char *string, size_t length, - enum replacement_types type) + enum replacement_types type) { mbstate_t from_stat; @@ -220,42 +216,58 @@ str_append_modified(struct line *to, const char *string, size_t length, while (length) { wchar_t wc; - int n = MBRTOWC (&wc, string, length, &from_stat); + size_t n = MBRTOWC (&wc, string, length, &from_stat); - /* An invalid sequence is treated like a singlebyte character. */ - if (n == -1) + /* Treat an invalid sequence like a single-byte character. */ + if (n == (size_t) -1) { + type &= ~(REPL_LOWERCASE_FIRST | REPL_UPPERCASE_FIRST); + if (type == REPL_ASIS) + { + str_append(to, string, length); + return; + } + + str_append (to, string, 1); memset (&to->mbstate, 0, sizeof (from_stat)); n = 1; + string += n, length -= n; + continue; } - if (n > 0) - string += n, length -= n; - else - { - /* Incomplete sequence, copy it manually. */ - str_append(to, string, length); - return; - } + if (n == 0 || n == (size_t) -2) + { + /* L'\0' or an incomplete sequence: copy it manually. */ + str_append(to, string, length); + return; + } + + string += n, length -= n; /* Convert the first character specially... */ if (type & (REPL_UPPERCASE_FIRST | REPL_LOWERCASE_FIRST)) - { + { if (type & REPL_UPPERCASE_FIRST) wc = towupper(wc); else wc = towlower(wc); type &= ~(REPL_LOWERCASE_FIRST | REPL_UPPERCASE_FIRST); - if (type == REPL_ASIS) - { - n = WCRTOMB (to->active + to->length, wc, &to->mbstate); - to->length += n; - str_append(to, string, length); - return; - } + if (type == REPL_ASIS) + { + /* Copy the new wide character to the end of the string. */ + n = WCRTOMB (to->active + to->length, wc, &to->mbstate); + to->length += n; + if (n == (size_t) -1 || n == (size_t) -2) + { + fprintf (stderr, + _("case conversion produced an invalid character")); + abort (); + } + str_append(to, string, length); + return; + } } - else if (type & REPL_UPPERCASE) wc = towupper(wc); else @@ -265,23 +277,19 @@ str_append_modified(struct line *to, const char *string, size_t length, n = WCRTOMB (to->active + to->length, wc, &to->mbstate); to->length += n; if (n == -1 || n == -2) - { - fprintf (stderr, "Case conversion produced an invalid character!"); - abort (); - } + { + fprintf (stderr, _("case conversion produced an invalid character")); + abort (); + } } } /* Initialize a "struct line" buffer. Copy multibyte state from `state' if not null. */ -static void line_init (struct line *, struct line *, size_t initial_size); static void -line_init(buf, state, initial_size) - struct line *buf; - struct line *state; - size_t initial_size; +line_init(struct line *buf, struct line *state, size_t initial_size) { - buf->text = MALLOC(initial_size, char); + buf->text = MALLOC(initial_size + DFA_SLOP, char); buf->active = buf->text; buf->alloc = initial_size; buf->length = 0; @@ -295,10 +303,8 @@ line_init(buf, state, initial_size) /* Reset a "struct line" buffer to length zero. Copy multibyte state from `state' if not null. */ -static void line_reset (struct line *, struct line *); static void -line_reset(buf, state) - struct line *buf, *state; +line_reset(struct line *buf, struct line *state) { if (buf->alloc == 0) line_init(buf, state, INITIAL_BUFFER_SIZE); @@ -315,12 +321,8 @@ line_reset(buf, state) /* Copy the contents of the line `from' into the line `to'. This destroys the old contents of `to'. Copy the multibyte state if `state' is true. */ -static void line_copy (struct line *from, struct line *to, int state); static void -line_copy(from, to, state) - struct line *from; - struct line *to; - int state; +line_copy(struct line *from, struct line *to, int state) { /* Remove the inactive portion in the destination buffer. */ to->alloc += to->active - to->text; @@ -329,13 +331,13 @@ line_copy(from, to, state) { to->alloc *= 2; if (to->alloc < from->length) - to->alloc = from->length; + to->alloc = from->length; if (to->alloc < INITIAL_BUFFER_SIZE) - to->alloc = INITIAL_BUFFER_SIZE; + to->alloc = INITIAL_BUFFER_SIZE; /* Use free()+MALLOC() instead of REALLOC() to - avoid unnecessary copying of old text. */ + avoid unnecessary copying of old text. */ free(to->text); - to->text = MALLOC(to->alloc, char); + to->text = MALLOC(to->alloc + DFA_SLOP, char); } to->active = to->text; @@ -349,12 +351,8 @@ line_copy(from, to, state) /* Append the contents of the line `from' to the line `to'. Copy the multibyte state if `state' is true. */ -static void line_append (struct line *from, struct line *to, int state); static void -line_append(from, to, state) - struct line *from; - struct line *to; - int state; +line_append (struct line *from, struct line *to, int state) { str_append(to, &buffer_delimiter, 1); str_append(to, from->active, from->length); @@ -366,12 +364,8 @@ line_append(from, to, state) /* Exchange two "struct line" buffers. Copy the multibyte state if `state' is true. */ -static void line_exchange (struct line *a, struct line *b, int state); static void -line_exchange(a, b, state) - struct line *a; - struct line *b; - int state; +line_exchange (struct line *a, struct line *b, int state) { struct line t; @@ -391,18 +385,14 @@ line_exchange(a, b, state) /* dummy function to simplify read_pattern_space() */ -static bool read_always_fail (struct input *); static bool -read_always_fail(input) - struct input *input UNUSED; +read_always_fail(struct input *input UNUSED) { return false; } -static bool read_file_line (struct input *); static bool -read_file_line(input) - struct input *input; +read_file_line(struct input *input) { static char *b; static size_t blen; @@ -422,10 +412,8 @@ read_file_line(input) } -static inline void output_missing_newline (struct output *); static inline void -output_missing_newline(outf) - struct output *outf; +output_missing_newline(struct output *outf) { if (outf->missing_newline) { @@ -434,22 +422,15 @@ output_missing_newline(outf) } } -static inline void flush_output (FILE *); static inline void -flush_output(fp) - FILE *fp; +flush_output(FILE *fp) { if (fp != stdout || unbuffered) ck_fflush(fp); } -static void output_line (const char *, size_t, int, struct output *); static void -output_line(text, length, nl, outf) - const char *text; - size_t length; - int nl; - struct output *outf; +output_line(const char *text, size_t length, int nl, struct output *outf) { if (!text) return; @@ -465,9 +446,8 @@ output_line(text, length, nl, outf) flush_output(outf->fp); } -static struct append_queue *next_append_slot (void); static struct append_queue * -next_append_slot() +next_append_slot(void) { struct append_queue *n = MALLOC(1, struct append_queue); @@ -484,9 +464,8 @@ next_append_slot() return append_tail = n; } -static void release_append_queue (void); static void -release_append_queue() +release_append_queue(void) { struct append_queue *p, *q; @@ -501,9 +480,8 @@ release_append_queue() append_head = append_tail = NULL; } -static void dump_append_queue (void); static void -dump_append_queue() +dump_append_queue(void) { struct append_queue *p; @@ -514,23 +492,23 @@ dump_append_queue() ck_fwrite(p->text, 1, p->textlen, output_file.fp); if (p->fname) - { - char buf[FREAD_BUFFER_SIZE]; - size_t cnt; - FILE *fp; - - /* "If _fname_ does not exist or cannot be read, it shall - be treated as if it were an empty file, causing no error - condition." IEEE Std 1003.2-1992 - So, don't fail. */ - fp = ck_fopen(p->fname, read_mode, false); - if (fp) - { - while ((cnt = ck_fread(buf, 1, sizeof buf, fp)) > 0) - ck_fwrite(buf, 1, cnt, output_file.fp); - ck_fclose(fp); - } - } + { + char buf[FREAD_BUFFER_SIZE]; + size_t cnt; + FILE *fp; + + /* "If _fname_ does not exist or cannot be read, it shall + be treated as if it were an empty file, causing no error + condition." IEEE Std 1003.2-1992 + So, don't fail. */ + fp = ck_fopen(p->fname, read_mode, false); + if (fp) + { + while ((cnt = ck_fread(buf, 1, sizeof buf, fp)) > 0) + ck_fwrite(buf, 1, cnt, output_file.fp); + ck_fclose(fp); + } + } } flush_output(output_file.fp); @@ -539,10 +517,8 @@ dump_append_queue() /* Compute the name of the backup file for in-place editing */ -static char *get_backup_file_name (const char *); static char * -get_backup_file_name(name) - const char *name; +get_backup_file_name(const char *name) { char *old_asterisk, *asterisk, *backup, *p; int name_length = strlen(name), backup_length = strlen(in_place_extension); @@ -572,39 +548,39 @@ get_backup_file_name(name) } /* Initialize a struct input for the named file. */ -static void open_next_file (const char *name, struct input *); static void -open_next_file(name, input) - const char *name; - struct input *input; +open_next_file(const char *name, struct input *input) { buffer.length = 0; + input->in_file_name = name; if (name[0] == '-' && name[1] == '\0' && !in_place_extension) { clearerr(stdin); /* clear any stale EOF indication */ -#if defined(WIN32) || defined(_WIN32) || defined(__CYGWIN__) || defined(MSDOS) || defined(__EMX__) +#if defined WIN32 || defined _WIN32 || defined __CYGWIN__ \ + || defined MSDOS || defined __EMX__ input->fp = ck_fdopen (fileno (stdin), "stdin", read_mode, false); #else input->fp = stdin; #endif } - else if ( ! (input->fp = ck_fopen(name, read_mode, false)) ) + else { - const char *ptr = strerror(errno); - fprintf(stderr, _("%s: can't read %s: %s\n"), myname, name, ptr); - input->read_fn = read_always_fail; /* a redundancy */ - ++input->bad_count; - return; + if (follow_symlinks) + input->in_file_name = follow_symlink (name); + + if ( ! (input->fp = ck_fopen (name, read_mode, false)) ) + { + const char *ptr = strerror (errno); + fprintf (stderr, _("%s: can't read %s: %s\n"), myname, name, ptr); + input->read_fn = read_always_fail; /* a redundancy */ + ++input->bad_count; + return; + } } input->read_fn = read_file_line; - if (follow_symlinks) - input->in_file_name = follow_symlink (name); - else - input->in_file_name = name; - if (in_place_extension) { int input_fd; @@ -616,9 +592,9 @@ open_next_file(name, input) /* get the base name */ tmpdir = ck_strdup(input->in_file_name); if ((p = strrchr(tmpdir, '/'))) - *p = 0; + *p = 0; else - strcpy(tmpdir, "."); + strcpy(tmpdir, "."); if (isatty (fileno (input->fp))) panic(_("couldn't edit %s: is a terminal"), input->in_file_name); @@ -629,39 +605,43 @@ open_next_file(name, input) panic(_("couldn't edit %s: not a regular file"), input->in_file_name); if (is_selinux_enabled () > 0) - { + { security_context_t con; - if (getfilecon (input->in_file_name, &con) != -1) - { - /* Save and restore the old context for the sake of w and W - commands. */ - reset_fscreatecon = getfscreatecon (&old_fscreatecon) >= 0; - if (setfscreatecon (con) < 0) - fprintf (stderr, _("%s: warning: failed to set default file creation context to %s: %s"), - myname, con, strerror (errno)); - freecon (con); - } - else - { - if (errno != ENOSYS) - fprintf (stderr, _("%s: warning: failed to get security context of %s: %s"), - myname, input->in_file_name, strerror (errno)); - } - } + if (getfilecon (input->in_file_name, &con) != -1) + { + /* Save and restore the old context for the sake of w and W + commands. */ + reset_fscreatecon = getfscreatecon (&old_fscreatecon) >= 0; + if (setfscreatecon (con) < 0) + fprintf (stderr, _("%s: warning: failed to set default" \ + " file creation context to %s: %s"), + myname, con, strerror (errno)); + freecon (con); + } + else + { + if (errno != ENOSYS) + fprintf (stderr, _("%s: warning: failed to get" \ + " security context of %s: %s"), + myname, input->in_file_name, strerror (errno)); + } + } output_file.fp = ck_mkstemp (&input->out_file_name, tmpdir, "sed", - write_mode); + write_mode); + register_cleanup_file (input->out_file_name); output_file.missing_newline = false; free (tmpdir); if (reset_fscreatecon) - { - setfscreatecon (old_fscreatecon); - freecon (old_fscreatecon); - } + { + setfscreatecon (old_fscreatecon); + freecon (old_fscreatecon); + } if (!output_file.fp) - panic(_("couldn't open temporary file %s: %s"), input->out_file_name, strerror(errno)); + panic(_("couldn't open temporary file %s: %s"), input->out_file_name, + strerror(errno)); } else { @@ -673,10 +653,8 @@ open_next_file(name, input) /* Clean up an input stream that we are done with. */ -static void closedown (struct input *); static void -closedown(input) - struct input *input; +closedown(struct input *input) { input->read_fn = read_always_fail; if (!input->fp) @@ -691,23 +669,26 @@ closedown(input) input_fd = fileno (input->fp); output_fd = fileno (output_file.fp); #ifdef HAVE_FCHOWN + /* Try to set both UID and GID, but if that fails, + try to set only the GID. Ignore failure. */ if (fchown (output_fd, input->st.st_uid, input->st.st_gid) == -1) - fchown (output_fd, -1, input->st.st_gid); + ignore_value (fchown (output_fd, -1, input->st.st_gid)); #endif copy_acl (input->in_file_name, input_fd, - input->out_file_name, output_fd, - input->st.st_mode); + input->out_file_name, output_fd, + input->st.st_mode); ck_fclose (input->fp); ck_fclose (output_file.fp); if (strcmp(in_place_extension, "*") != 0) { char *backup_file_name = get_backup_file_name(target_name); - ck_rename (target_name, backup_file_name, input->out_file_name); + ck_rename (target_name, backup_file_name, input->out_file_name); free (backup_file_name); - } + } ck_rename (input->out_file_name, target_name, input->out_file_name); + cancel_cleanup (); free (input->out_file_name); } else @@ -717,18 +698,16 @@ closedown(input) } /* Reset range commands so that they are marked as non-matching */ -static void reset_addresses (struct vector *); static void -reset_addresses(vec) - struct vector *vec; +reset_addresses(struct vector *vec) { struct sed_cmd *cur_cmd; int n; for (cur_cmd = vec->v, n = vec->v_length; n--; cur_cmd++) if (cur_cmd->a1 - && cur_cmd->a1->addr_type == ADDR_IS_NUM - && cur_cmd->a1->addr_number == 0) + && cur_cmd->a1->addr_type == ADDR_IS_NUM + && cur_cmd->a1->addr_number == 0) cur_cmd->range_state = RANGE_ACTIVE; else cur_cmd->range_state = RANGE_INACTIVE; @@ -736,12 +715,8 @@ reset_addresses(vec) /* Read in the next line of input, and store it in the pattern space. Return zero if there is nothing left to input. */ -static bool read_pattern_space (struct input *, struct vector *, int); static bool -read_pattern_space(input, the_program, append) - struct input *input; - struct vector *the_program; - int append; +read_pattern_space(struct input *input, struct vector *the_program, int append) { if (append_head) /* redundant test to optimize for common case */ dump_append_queue(); @@ -755,23 +730,23 @@ read_pattern_space(input, the_program, append) closedown(input); if (!*input->file_list) - return false; + return false; if (input->reset_at_next_file) - { - input->line_number = 0; - hold.length = 0; - reset_addresses (the_program); - rewind_read_files (); - - /* If doing in-place editing, we will never append the - new-line to this file; but if the output goes to stdout, - we might still have to output the missing new-line. */ - if (in_place_extension) - output_file.missing_newline = false; - - input->reset_at_next_file = separate_files; - } + { + input->line_number = 0; + hold.length = 0; + reset_addresses (the_program); + rewind_read_files (); + + /* If doing in-place editing, we will never append the + new-line to this file; but if the output goes to stdout, + we might still have to output the missing new-line. */ + if (in_place_extension) + output_file.missing_newline = false; + + input->reset_at_next_file = separate_files; + } open_next_file (*input->file_list++, input); } @@ -781,10 +756,8 @@ read_pattern_space(input, the_program, append) } -static bool last_file_with_data_p (struct input *); static bool -last_file_with_data_p(input) - struct input *input; +last_file_with_data_p(struct input *input) { for (;;) { @@ -792,24 +765,22 @@ last_file_with_data_p(input) closedown(input); if (!*input->file_list) - return true; + return true; open_next_file(*input->file_list++, input); if (input->fp) - { - if ((ch = getc(input->fp)) != EOF) - { - ungetc(ch, input->fp); - return false; - } - } + { + if ((ch = getc(input->fp)) != EOF) + { + ungetc(ch, input->fp); + return false; + } + } } } /* Determine if we match the `$' address. */ -static bool test_eof (struct input *); static bool -test_eof(input) - struct input *input; +test_eof(struct input *input) { int ch; @@ -827,11 +798,8 @@ test_eof(input) /* Return non-zero if the current line matches the address pointed to by `addr'. */ -static bool match_an_address_p (struct addr *, struct input *); static bool -match_an_address_p(addr, input) - struct addr *addr; - struct input *input; +match_an_address_p(struct addr *addr, struct input *input) { switch (addr->addr_type) { @@ -839,11 +807,13 @@ match_an_address_p(addr, input) return true; case ADDR_IS_REGEX: - return match_regex(addr->addr_regex, line.active, line.length, 0, NULL, 0); + return match_regex(addr->addr_regex, line.active, line.length, 0, + NULL, 0); case ADDR_IS_NUM_MOD: return (input->line_number >= addr->addr_number - && ((input->line_number - addr->addr_number) % addr->addr_step) == 0); + && ((input->line_number - addr->addr_number) + % addr->addr_step) == 0); case ADDR_IS_STEP: case ADDR_IS_STEP_MOD: @@ -855,8 +825,10 @@ match_an_address_p(addr, input) case ADDR_IS_LAST: return test_eof(input); - /* ADDR_IS_NUM is handled in match_address_p. */ case ADDR_IS_NUM: + /* reminder: these are only meaningful for a1 addresses */ + return (addr->addr_number == input->line_number); + default: panic("INTERNAL ERROR: bad address type"); } @@ -865,59 +837,54 @@ match_an_address_p(addr, input) } /* return non-zero if current address is valid for cmd */ -static bool match_address_p (struct sed_cmd *, struct input *); static bool -match_address_p(cmd, input) - struct sed_cmd *cmd; - struct input *input; +match_address_p(struct sed_cmd *cmd, struct input *input) { if (!cmd->a1) return true; if (cmd->range_state != RANGE_ACTIVE) { + if (!cmd->a2) + return match_an_address_p(cmd->a1, input); + /* Find if we are going to activate a range. Handle ADDR_IS_NUM - specially: it represent an "absolute" state, it should not - be computed like regexes. */ + specially: it represent an "absolute" state, it should not + be computed like regexes. */ if (cmd->a1->addr_type == ADDR_IS_NUM) - { - if (!cmd->a2) - return (input->line_number == cmd->a1->addr_number); - - if (cmd->range_state == RANGE_CLOSED - || input->line_number < cmd->a1->addr_number) - return false; - } + { + if (cmd->range_state == RANGE_CLOSED + || input->line_number < cmd->a1->addr_number) + return false; + } else - { - if (!cmd->a2) - return match_an_address_p(cmd->a1, input); - - if (!match_an_address_p(cmd->a1, input)) + { + if (!match_an_address_p(cmd->a1, input)) return false; - } + } /* Ok, start a new range. */ cmd->range_state = RANGE_ACTIVE; switch (cmd->a2->addr_type) - { - case ADDR_IS_REGEX: - /* Always include at least two lines. */ - return true; - case ADDR_IS_NUM: - /* Same handling as below, but always include at least one line. */ + { + case ADDR_IS_REGEX: + /* Always include at least two lines. */ + return true; + case ADDR_IS_NUM: + /* Same handling as below, but always include at least one line. */ if (input->line_number >= cmd->a2->addr_number) - cmd->range_state = RANGE_CLOSED; + cmd->range_state = RANGE_CLOSED; + return (input->line_number <= cmd->a2->addr_number + || match_an_address_p(cmd->a1, input)); + case ADDR_IS_STEP: + cmd->a2->addr_number = input->line_number + cmd->a2->addr_step; + return true; + case ADDR_IS_STEP_MOD: + cmd->a2->addr_number = input->line_number + cmd->a2->addr_step + - (input->line_number%cmd->a2->addr_step); return true; - case ADDR_IS_STEP: - cmd->a2->addr_number = input->line_number + cmd->a2->addr_step; - return true; - case ADDR_IS_STEP_MOD: - cmd->a2->addr_number = input->line_number + cmd->a2->addr_step - - (input->line_number%cmd->a2->addr_step); - return true; - default: - break; + default: + break; } } @@ -928,10 +895,10 @@ match_address_p(cmd, input) { /* If the second address is a line number, and if we got past that line, fail to match (it can happen when you jump - over such addresses with `b' and `t'. Use RANGE_CLOSED + over such addresses with `b' and `t'. Use RANGE_CLOSED so that the range is not re-enabled anymore. */ if (input->line_number >= cmd->a2->addr_number) - cmd->range_state = RANGE_CLOSED; + cmd->range_state = RANGE_CLOSED; return (input->line_number <= cmd->a2->addr_number); } @@ -944,10 +911,8 @@ match_address_p(cmd, input) } -static void do_list (int line_len); static void -do_list(line_len) - int line_len; +do_list(int line_len) { unsigned char *p = (unsigned char *)line.active; countT len = line.length; @@ -960,53 +925,55 @@ do_list(line_len) output_missing_newline(&output_file); for (; len--; ++p) { o = obuf; - + /* Some locales define 8-bit characters as printable. This makes the - testsuite fail at 8to7.sed because the `l' command in fact will not - convert the 8-bit characters. */ + testsuite fail at 8to7.sed because the `l' command in fact will not + convert the 8-bit characters. */ #if defined isascii || defined HAVE_ISASCII if (isascii(*p) && ISPRINT(*p)) { #else if (ISPRINT(*p)) { #endif - *o++ = *p; - if (*p == '\\') - *o++ = '\\'; + *o++ = *p; + if (*p == '\\') + *o++ = '\\'; } else { - *o++ = '\\'; - switch (*p) { + *o++ = '\\'; + switch (*p) { #if defined __STDC__ && __STDC__-0 - case '\a': *o++ = 'a'; break; + case '\a': *o++ = 'a'; break; #else /* Not STDC; we'll just assume ASCII */ - case 007: *o++ = 'a'; break; + case 007: *o++ = 'a'; break; #endif - case '\b': *o++ = 'b'; break; - case '\f': *o++ = 'f'; break; - case '\n': *o++ = 'n'; break; - case '\r': *o++ = 'r'; break; - case '\t': *o++ = 't'; break; - case '\v': *o++ = 'v'; break; - default: - sprintf(o, "%03o", *p); - o += strlen(o); - break; - } + case '\b': *o++ = 'b'; break; + case '\f': *o++ = 'f'; break; + case '\n': *o++ = 'n'; break; + case '\r': *o++ = 'r'; break; + case '\t': *o++ = 't'; break; + case '\v': *o++ = 'v'; break; + default: + sprintf(o, "%03o", *p); + o += strlen(o); + break; + } } olen = o - obuf; if (width+olen >= line_len && line_len > 0) { - ck_fwrite("\\\n", 1, 2, fp); - width = 0; + ck_fwrite("\\", 1, 1, fp); + ck_fwrite(&buffer_delimiter, 1, 1, fp); + width = 0; } ck_fwrite(obuf, 1, olen, fp); width += olen; } - ck_fwrite("$\n", 1, 2, fp); + ck_fwrite("$", 1, 1, fp); + ck_fwrite(&buffer_delimiter, 1, 1, fp); flush_output (fp); } static void append_replacement (struct line *buf, struct replacement *p, - struct re_registers *regs) + struct re_registers *regs) { enum replacement_types repl_mod = 0; @@ -1026,30 +993,28 @@ static void append_replacement (struct line *buf, struct replacement *p, if (p->prefix_length) { str_append_modified(buf, p->prefix, p->prefix_length, - curr_type); + curr_type); curr_type &= ~REPL_MODIFIERS; } if (0 <= i) - { + { if (regs->end[i] == regs->start[i] && p->repl_type & REPL_MODIFIERS) /* Save this modifier, we shall apply it later. - e.g. in s/()([a-z])/\u\1\2/ - the \u modifier is applied to \2, not \1 */ - repl_mod = curr_type & REPL_MODIFIERS; - - else if (regs->end[i] != regs->start[i]) - str_append_modified(buf, line.active + regs->start[i], - (size_t)(regs->end[i] - regs->start[i]), - curr_type); - } + e.g. in s/()([a-z])/\u\1\2/ + the \u modifier is applied to \2, not \1 */ + repl_mod = curr_type & REPL_MODIFIERS; + + else if (regs->end[i] != regs->start[i]) + str_append_modified(buf, line.active + regs->start[i], + (size_t)(regs->end[i] - regs->start[i]), + curr_type); + } } } -static void do_subst (struct subst *); static void -do_subst(sub) - struct subst *sub; +do_subst(struct subst *sub) { size_t start = 0; /* where to start scan for (next) match in LINE */ size_t last_end = 0; /* where did the last successful match end in LINE */ @@ -1063,28 +1028,28 @@ do_subst(sub) /* The first part of the loop optimizes s/xxx// when xxx is at the start, and s/xxx$// */ if (!match_regex(sub->regx, line.active, line.length, start, - ®s, sub->max_id + 1)) + ®s, sub->max_id + 1)) return; - + if (!sub->replacement && sub->numb <= 1) { if (regs.start[0] == 0 && !sub->global) { - /* We found a match, set the `replaced' flag. */ - replaced = true; + /* We found a match, set the `replaced' flag. */ + replaced = true; - line.active += regs.end[0]; - line.length -= regs.end[0]; - line.alloc -= regs.end[0]; - goto post_subst; + line.active += regs.end[0]; + line.length -= regs.end[0]; + line.alloc -= regs.end[0]; + goto post_subst; } else if (regs.end[0] == line.length) { - /* We found a match, set the `replaced' flag. */ - replaced = true; + /* We found a match, set the `replaced' flag. */ + replaced = true; - line.length = regs.start[0]; - goto post_subst; + line.length = regs.start[0]; + goto post_subst; } } @@ -1095,11 +1060,11 @@ do_subst(sub) /* Copy stuff to the left of this match into the output string. */ if (start < offset) - str_append(&s_accum, line.active + start, offset - start); + str_append(&s_accum, line.active + start, offset - start); /* If we're counting up to the Nth match, are we there yet? And even if we are there, there is another case we have to - skip: are we matching an empty string immediately following + skip: are we matching an empty string immediately following another match? This latter case avoids that baaaac, when passed through @@ -1107,41 +1072,41 @@ do_subst(sub) unacceptable because it is not consistently applied (for example, `baaaa' gives `xbx', not `xbxx'). */ if ((matched > 0 || count == 0 || offset > last_end) - && ++count >= sub->numb) + && ++count >= sub->numb) { /* We found a match, set the `replaced' flag. */ replaced = true; /* Now expand the replacement string into the output string. */ append_replacement (&s_accum, sub->replacement, ®s); - again = sub->global; + again = sub->global; } else - { + { /* The match was not replaced. Copy the text until its end; if it was vacuous, skip over one character and - add that character to the output. */ - if (matched == 0) - { - if (start < line.length) - matched = 1; - else - break; - } - - str_append(&s_accum, line.active + offset, matched); + add that character to the output. */ + if (matched == 0) + { + if (start < line.length) + matched = 1; + else + break; + } + + str_append(&s_accum, line.active + offset, matched); } /* Start after the match. last_end is the real end of the matched - substring, excluding characters that were skipped in case the RE - matched the empty string. */ + substring, excluding characters that were skipped in case the RE + matched the empty string. */ start = offset + matched; last_end = regs.end[0]; } while (again - && start <= line.length - && match_regex(sub->regx, line.active, line.length, start, - ®s, sub->max_id + 1)); + && start <= line.length + && match_regex(sub->regx, line.active, line.length, start, + ®s, sub->max_id + 1)); /* Copy stuff to the right of the last match into the output string. */ if (start < line.length) @@ -1159,43 +1124,43 @@ do_subst(sub) post_subst: if (sub->print & 1) output_line(line.active, line.length, line.chomped, &output_file); - - if (sub->eval) + + if (sub->eval) { #ifdef HAVE_POPEN FILE *pipe_fp; line_reset(&s_accum, NULL); - + str_append (&line, "", 1); pipe_fp = popen(line.active, "r"); - - if (pipe_fp != NULL) - { - while (!feof (pipe_fp)) - { - char buf[4096]; - int n = fread (buf, sizeof(char), 4096, pipe_fp); - if (n > 0) - str_append(&s_accum, buf, n); - } - - pclose (pipe_fp); - - /* Exchange line and s_accum. This can be much cheaper than copying - s_accum.active into line.text (for huge lines). See comment above - for 'g' as to while the third argument is incorrect anyway. */ - line_exchange(&line, &s_accum, true); - if (line.length && - line.active[line.length - 1] == buffer_delimiter) - line.length--; - } + + if (pipe_fp != NULL) + { + while (!feof (pipe_fp)) + { + char buf[4096]; + int n = fread (buf, sizeof(char), 4096, pipe_fp); + if (n > 0) + str_append(&s_accum, buf, n); + } + + pclose (pipe_fp); + + /* Exchange line and s_accum. This can be much cheaper than copying + s_accum.active into line.text (for huge lines). See comment above + for 'g' as to while the third argument is incorrect anyway. */ + line_exchange(&line, &s_accum, true); + if (line.length && + line.active[line.length - 1] == buffer_delimiter) + line.length--; + } else - panic(_("error in subprocess")); + panic(_("error in subprocess")); #else panic(_("option `e' not supported")); #endif - } - + } + if (sub->print & 2) output_line(line.active, line.length, line.chomped, &output_file); if (sub->outf) @@ -1207,7 +1172,6 @@ do_subst(sub) static countT branches; -static countT count_branches (struct vector *); static countT count_branches(program) struct vector *program; @@ -1219,18 +1183,17 @@ count_branches(program) while (isn_cnt-- > 0) { switch (cur_cmd->cmd) - { - case 'b': - case 't': - case 'T': - case '{': - ++cnt; - } + { + case 'b': + case 't': + case 'T': + case '{': + ++cnt; + } } return cnt; } -static struct sed_cmd *shrink_program (struct vector *, struct sed_cmd *); static struct sed_cmd * shrink_program(vec, cur_cmd) struct vector *vec; @@ -1255,13 +1218,74 @@ shrink_program(vec, cur_cmd) } #endif /*EXPERIMENTAL_DASH_N_OPTIMIZATION*/ +/* Translate the global input LINE via TRANS. + This function handles the multi-byte case. */ +static void +translate_mb (char *const *trans) +{ + size_t idx; /* index in the input line. */ + mbstate_t mbstate = { 0, }; + for (idx = 0; idx < line.length;) + { + unsigned int i; + size_t mbclen = MBRLEN (line.active + idx, + line.length - idx, &mbstate); + /* An invalid sequence, or a truncated multibyte + character. Treat it as a single-byte character. */ + if (mbclen == (size_t) -1 || mbclen == (size_t) -2 || mbclen == 0) + mbclen = 1; + + /* `i' indicate i-th translate pair. */ + for (i = 0; trans[2*i] != NULL; i++) + { + if (strncmp(line.active + idx, trans[2*i], mbclen) == 0) + { + bool move_remain_buffer = false; + const char *tr = trans[2*i+1]; + size_t trans_len = *tr == '\0' ? 1 : strlen (tr); + + if (mbclen < trans_len) + { + size_t new_len = (line.length + 1 + + trans_len - mbclen); + /* We must extend the line buffer. */ + if (line.alloc < new_len) + { + /* And we must resize the buffer. */ + resize_line(&line, new_len); + } + move_remain_buffer = true; + } + else if (mbclen > trans_len) + { + /* We must truncate the line buffer. */ + move_remain_buffer = true; + } + size_t prev_idx = idx; + if (move_remain_buffer) + { + /* Move the remaining with \0. */ + char const *move_from = (line.active + idx + mbclen); + char *move_to = line.active + idx + trans_len; + size_t move_len = line.length + 1 - idx - mbclen; + size_t move_offset = trans_len - mbclen; + memmove(move_to, move_from, move_len); + line.length += move_offset; + idx += move_offset; + } + memcpy(line.active + prev_idx, trans[2*i+1], + trans_len); + break; + } + } + idx += mbclen; + } +} + /* Execute the program `vec' on the current input line. Return exit status if caller should quit, -1 otherwise. */ -static int execute_program (struct vector *, struct input *); static int -execute_program(vec, input) - struct vector *vec; - struct input *input; +execute_program(struct vector *vec, struct input *input) { struct sed_cmd *cur_cmd; struct sed_cmd *end_cmd; @@ -1271,174 +1295,164 @@ execute_program(vec, input) while (cur_cmd < end_cmd) { if (match_address_p(cur_cmd, input) != cur_cmd->addr_bang) - { - switch (cur_cmd->cmd) - { - case 'a': - { - struct append_queue *aq = next_append_slot(); - aq->text = cur_cmd->x.cmd_txt.text; - aq->textlen = cur_cmd->x.cmd_txt.text_length; - } - break; - - case '{': - case 'b': - cur_cmd = vec->v + cur_cmd->x.jump_index; - continue; - - case '}': - case '#': - case ':': - /* Executing labels and block-ends are easy. */ - break; - - case 'c': - if (cur_cmd->range_state != RANGE_ACTIVE) - output_line(cur_cmd->x.cmd_txt.text, - cur_cmd->x.cmd_txt.text_length - 1, true, - &output_file); - /* POSIX.2 is silent about c starting a new cycle, - but it seems to be expected (and make sense). */ - /* Fall Through */ - case 'd': - return -1; - - case 'D': - { - char *p = memchr(line.active, buffer_delimiter, line.length); - if (!p) - return -1; - - ++p; - line.alloc -= p - line.active; - line.length -= p - line.active; - line.active += p - line.active; - - /* reset to start next cycle without reading a new line: */ - cur_cmd = vec->v; - continue; - } - - case 'e': { -#ifdef HAVE_POPEN - FILE *pipe_fp; - int cmd_length = cur_cmd->x.cmd_txt.text_length; - line_reset(&s_accum, NULL); - - if (!cmd_length) - { - str_append (&line, "", 1); - pipe_fp = popen(line.active, "r"); - } - else - { - cur_cmd->x.cmd_txt.text[cmd_length - 1] = 0; - pipe_fp = popen(cur_cmd->x.cmd_txt.text, "r"); - output_missing_newline(&output_file); - } - - if (pipe_fp != NULL) - { - char buf[4096]; - int n; - while (!feof (pipe_fp)) - if ((n = fread (buf, sizeof(char), 4096, pipe_fp)) > 0) - { - if (!cmd_length) - str_append(&s_accum, buf, n); - else - ck_fwrite(buf, 1, n, output_file.fp); - } - - pclose (pipe_fp); - if (!cmd_length) - { - /* Store into pattern space for plain `e' commands */ - if (s_accum.length && - s_accum.active[s_accum.length - 1] == buffer_delimiter) - s_accum.length--; - - /* Exchange line and s_accum. This can be much - cheaper than copying s_accum.active into line.text - (for huge lines). See comment above for 'g' as - to while the third argument is incorrect anyway. */ - line_exchange(&line, &s_accum, true); - } - else - flush_output(output_file.fp); - - } - else - panic(_("error in subprocess")); + { + switch (cur_cmd->cmd) + { + case 'a': + { + struct append_queue *aq = next_append_slot(); + aq->text = cur_cmd->x.cmd_txt.text; + aq->textlen = cur_cmd->x.cmd_txt.text_length; + } + break; + + case '{': + case 'b': + cur_cmd = vec->v + cur_cmd->x.jump_index; + continue; + + case '}': + case '#': + case ':': + /* Executing labels and block-ends are easy. */ + break; + + case 'c': + if (cur_cmd->range_state != RANGE_ACTIVE) + output_line(cur_cmd->x.cmd_txt.text, + cur_cmd->x.cmd_txt.text_length - 1, true, + &output_file); + /* POSIX.2 is silent about c starting a new cycle, + but it seems to be expected (and make sense). */ + /* Fall Through */ + case 'd': + return -1; + + case 'D': + { + char *p = memchr(line.active, buffer_delimiter, line.length); + if (!p) + return -1; + + ++p; + line.alloc -= p - line.active; + line.length -= p - line.active; + line.active += p - line.active; + + /* reset to start next cycle without reading a new line: */ + cur_cmd = vec->v; + continue; + } + + case 'e': { +#ifndef HAVE_POPEN + panic(_("`e' command not supported")); #else - panic(_("`e' command not supported")); + FILE *pipe_fp; + int cmd_length = cur_cmd->x.cmd_txt.text_length; + line_reset(&s_accum, NULL); + + if (!cmd_length) + { + str_append (&line, "", 1); + pipe_fp = popen(line.active, "r"); + } + else + { + cur_cmd->x.cmd_txt.text[cmd_length - 1] = 0; + pipe_fp = popen(cur_cmd->x.cmd_txt.text, "r"); + output_missing_newline(&output_file); + } + + if (pipe_fp == NULL) + panic(_("error in subprocess")); + + { + char buf[4096]; + int n; + while (!feof (pipe_fp)) + if ((n = fread (buf, sizeof(char), 4096, pipe_fp)) > 0) + { + if (!cmd_length) + str_append(&s_accum, buf, n); + else + ck_fwrite(buf, 1, n, output_file.fp); + } + + pclose (pipe_fp); + if (!cmd_length) + { + /* Store into pattern space for plain `e' commands */ + if (s_accum.length && + s_accum.active[s_accum.length - 1] == buffer_delimiter) + s_accum.length--; + + /* Exchange line and s_accum. This can be much + cheaper than copying s_accum.active into line.text + (for huge lines). See comment above for 'g' as + to while the third argument is incorrect anyway. */ + line_exchange(&line, &s_accum, true); + } + else + flush_output(output_file.fp); + } #endif - break; - } - - case 'g': - /* We do not have a really good choice for the third parameter. - The problem is that hold space and the input file might as - well have different states; copying it from hold space means - that subsequent input might be read incorrectly, while - keeping it as in pattern space means that commands operating - on the moved buffer might consider a wrong character set. - We keep it true because it's what sed <= 4.1.5 did. */ - line_copy(&hold, &line, true); - break; - - case 'G': - /* We do not have a really good choice for the third parameter. - The problem is that hold space and pattern space might as - well have different states. So, true is as wrong as false. - We keep it true because it's what sed <= 4.1.5 did, but - we could consider having line_ap. */ - line_append(&hold, &line, true); - break; - - case 'h': - /* Here, it is ok to have true. */ - line_copy(&line, &hold, true); - break; - - case 'H': - /* See comment above for 'G' regarding the third parameter. */ - line_append(&line, &hold, true); - break; - - case 'i': - output_line(cur_cmd->x.cmd_txt.text, - cur_cmd->x.cmd_txt.text_length - 1, - true, &output_file); - break; - - case 'l': - do_list(cur_cmd->x.int_arg == -1 - ? lcmd_out_line_len - : cur_cmd->x.int_arg); - break; - - case 'L': - output_missing_newline(&output_file); - fmt(line.active, line.active + line.length, - cur_cmd->x.int_arg == -1 - ? lcmd_out_line_len - : cur_cmd->x.int_arg, - output_file.fp); - flush_output(output_file.fp); - break; - - case 'n': - if (!no_default_output) - output_line(line.active, line.length, line.chomped, &output_file); - if (test_eof(input) || !read_pattern_space(input, vec, false)) - return -1; - break; - - case 'N': - str_append(&line, &buffer_delimiter, 1); - + break; + } + + case 'g': + /* We do not have a really good choice for the third parameter. + The problem is that hold space and the input file might as + well have different states; copying it from hold space means + that subsequent input might be read incorrectly, while + keeping it as in pattern space means that commands operating + on the moved buffer might consider a wrong character set. + We keep it true because it's what sed <= 4.1.5 did. */ + line_copy(&hold, &line, true); + break; + + case 'G': + /* We do not have a really good choice for the third parameter. + The problem is that hold space and pattern space might as + well have different states. So, true is as wrong as false. + We keep it true because it's what sed <= 4.1.5 did, but + we could consider having line_ap. */ + line_append(&hold, &line, true); + break; + + case 'h': + /* Here, it is ok to have true. */ + line_copy(&line, &hold, true); + break; + + case 'H': + /* See comment above for 'G' regarding the third parameter. */ + line_append(&line, &hold, true); + break; + + case 'i': + output_line(cur_cmd->x.cmd_txt.text, + cur_cmd->x.cmd_txt.text_length - 1, + true, &output_file); + break; + + case 'l': + do_list(cur_cmd->x.int_arg == -1 + ? lcmd_out_line_len + : cur_cmd->x.int_arg); + break; + + case 'n': + if (!no_default_output) + output_line(line.active, line.length, line.chomped, + &output_file); + if (test_eof(input) || !read_pattern_space(input, vec, false)) + return -1; + break; + + case 'N': + str_append(&line, &buffer_delimiter, 1); + if (test_eof(input) || !read_pattern_space(input, vec, true)) { line.length--; @@ -1447,199 +1461,137 @@ execute_program(vec, input) &output_file); return -1; } - break; + break; - case 'p': - output_line(line.active, line.length, line.chomped, &output_file); - break; + case 'p': + output_line(line.active, line.length, line.chomped, &output_file); + break; - case 'P': - { - char *p = memchr(line.active, buffer_delimiter, line.length); - output_line(line.active, p ? p - line.active : line.length, - p ? true : line.chomped, &output_file); - } - break; + case 'P': + { + char *p = memchr(line.active, buffer_delimiter, line.length); + output_line(line.active, p ? p - line.active : line.length, + p ? true : line.chomped, &output_file); + } + break; case 'q': if (!no_default_output) - output_line(line.active, line.length, line.chomped, &output_file); - dump_append_queue(); - - case 'Q': - return cur_cmd->x.int_arg == -1 ? 0 : cur_cmd->x.int_arg; - - case 'r': - if (cur_cmd->x.fname) - { - struct append_queue *aq = next_append_slot(); - aq->fname = cur_cmd->x.fname; - } - break; - - case 'R': - if (cur_cmd->x.fp && !feof (cur_cmd->x.fp)) - { - struct append_queue *aq; - size_t buflen; - char *text = NULL; - int result; - - result = ck_getdelim (&text, &buflen, buffer_delimiter, - cur_cmd->x.fp); - if (result != EOF) - { - aq = next_append_slot(); - aq->free = true; - aq->text = text; - aq->textlen = result; - } - } - break; - - case 's': - do_subst(cur_cmd->x.cmd_subst); - break; - - case 't': - if (replaced) - { - replaced = false; - cur_cmd = vec->v + cur_cmd->x.jump_index; - continue; - } - break; - - case 'T': - if (!replaced) - { - cur_cmd = vec->v + cur_cmd->x.jump_index; - continue; - } - else - replaced = false; - break; - - case 'w': - if (cur_cmd->x.fp) - output_line(line.active, line.length, - line.chomped, cur_cmd->x.outf); - break; - - case 'W': - if (cur_cmd->x.fp) - { - char *p = memchr(line.active, buffer_delimiter, line.length); - output_line(line.active, p ? p - line.active : line.length, - p ? true : line.chomped, cur_cmd->x.outf); - } - break; - - case 'x': - /* See comment above for 'g' regarding the third parameter. */ - line_exchange(&line, &hold, false); - break; - - case 'y': - { - if (mb_cur_max > 1) - { - int idx, prev_idx; /* index in the input line. */ - char **trans; - mbstate_t mbstate; - memset(&mbstate, 0, sizeof(mbstate_t)); - for (idx = 0; idx < line.length;) - { - int mbclen, i; - mbclen = MBRLEN (line.active + idx, line.length - idx, - &mbstate); - /* An invalid sequence, or a truncated multibyte - character. We treat it as a singlebyte character. - */ - if (mbclen == (size_t) -1 || mbclen == (size_t) -2 - || mbclen == 0) - mbclen = 1; - - trans = cur_cmd->x.translatemb; - /* `i' indicate i-th translate pair. */ - for (i = 0; trans[2*i] != NULL; i++) - { - if (strncmp(line.active + idx, trans[2*i], mbclen) == 0) - { - bool move_remain_buffer = false; - int trans_len = strlen(trans[2*i+1]); - - if (mbclen < trans_len) - { - int new_len; - new_len = line.length + 1 + trans_len - mbclen; - /* We must extend the line buffer. */ - if (line.alloc < new_len) - { - /* And we must resize the buffer. */ - resize_line(&line, new_len); - } - move_remain_buffer = true; - } - else if (mbclen > trans_len) - { - /* We must truncate the line buffer. */ - move_remain_buffer = true; - } - prev_idx = idx; - if (move_remain_buffer) - { - int move_len, move_offset; - char *move_from, *move_to; - /* Move the remaining with \0. */ - move_from = line.active + idx + mbclen; - move_to = line.active + idx + trans_len; - move_len = line.length + 1 - idx - mbclen; - move_offset = trans_len - mbclen; - memmove(move_to, move_from, move_len); - line.length += move_offset; - idx += move_offset; - } - strncpy(line.active + prev_idx, trans[2*i+1], - trans_len); - break; - } - } - idx += mbclen; - } - } - else - { - unsigned char *p, *e; - p = (unsigned char *)line.active; - for (e=p+line.length; p<e; ++p) - *p = cur_cmd->x.translate[*p]; - } - } - break; - - case 'z': - line.length = 0; - break; - - case '=': + output_line(line.active, line.length, line.chomped, + &output_file); + dump_append_queue(); + /* FALLTHROUGH */ + + case 'Q': + return cur_cmd->x.int_arg == -1 ? 0 : cur_cmd->x.int_arg; + + case 'r': + if (cur_cmd->x.fname) + { + struct append_queue *aq = next_append_slot(); + aq->fname = cur_cmd->x.fname; + } + break; + + case 'R': + if (cur_cmd->x.fp && !feof (cur_cmd->x.fp)) + { + struct append_queue *aq; + size_t buflen; + char *text = NULL; + int result; + + result = ck_getdelim (&text, &buflen, buffer_delimiter, + cur_cmd->x.fp); + if (result != EOF) + { + aq = next_append_slot(); + aq->free = true; + aq->text = text; + aq->textlen = result; + } + } + break; + + case 's': + do_subst(cur_cmd->x.cmd_subst); + break; + + case 't': + if (replaced) + { + replaced = false; + cur_cmd = vec->v + cur_cmd->x.jump_index; + continue; + } + break; + + case 'T': + if (!replaced) + { + cur_cmd = vec->v + cur_cmd->x.jump_index; + continue; + } + else + replaced = false; + break; + + case 'w': + if (cur_cmd->x.fp) + output_line(line.active, line.length, + line.chomped, cur_cmd->x.outf); + break; + + case 'W': + if (cur_cmd->x.fp) + { + char *p = memchr(line.active, buffer_delimiter, line.length); + output_line(line.active, p ? p - line.active : line.length, + p ? true : line.chomped, cur_cmd->x.outf); + } + break; + + case 'x': + /* See comment above for 'g' regarding the third parameter. */ + line_exchange(&line, &hold, false); + break; + + case 'y': + if (mb_cur_max > 1) + translate_mb (cur_cmd->x.translatemb); + else + { + unsigned char *p, *e; + p = (unsigned char *)line.active; + for (e=p+line.length; p<e; ++p) + *p = cur_cmd->x.translate[*p]; + } + break; + + case 'z': + line.length = 0; + break; + + case '=': output_missing_newline(&output_file); - fprintf(output_file.fp, "%lu\n", - (unsigned long)input->line_number); + fprintf(output_file.fp, "%lu%c", + (unsigned long)input->line_number, + buffer_delimiter); flush_output(output_file.fp); break; case 'F': output_missing_newline(&output_file); - fprintf(output_file.fp, "%s\n", - input->in_file_name); + fprintf(output_file.fp, "%s%c", + input->in_file_name, + buffer_delimiter); flush_output(output_file.fp); break; default: panic("INTERNAL ERROR: Bad cmd %c", cur_cmd->cmd); } - } + } #ifdef EXPERIMENTAL_DASH_N_OPTIMIZATION /* If our top-level program consists solely of commands with @@ -1651,42 +1603,42 @@ execute_program(vec, input) compared against how much time is saved. One semantic difference (which I think is an improvement) is that *this* version will terminate after printing line two in the script - "yes | sed -n 2p". - + "yes | sed -n 2p". + Don't use this when in-place editing is active, because line numbers restart each time then. */ else if (!separate_files) - { - if (cur_cmd->a1->addr_type == ADDR_IS_NUM - && (cur_cmd->a2 - ? cur_cmd->range_state == RANGE_CLOSED - : cur_cmd->a1->addr_number < input->line_number)) - { - /* Skip this address next time */ - cur_cmd->addr_bang = !cur_cmd->addr_bang; - cur_cmd->a1->addr_type = ADDR_IS_NULL; - if (cur_cmd->a2) - cur_cmd->a2->addr_type = ADDR_IS_NULL; - - /* can we make an optimization? */ - if (cur_cmd->addr_bang) - { - if (cur_cmd->cmd == 'b' || cur_cmd->cmd == 't' - || cur_cmd->cmd == 'T' || cur_cmd->cmd == '}') - branches--; - - cur_cmd->cmd = '#'; /* replace with no-op */ - if (branches == 0) - cur_cmd = shrink_program(vec, cur_cmd); - if (!cur_cmd && no_default_output) - return 0; - end_cmd = vec->v + vec->v_length; - if (!cur_cmd) - cur_cmd = end_cmd; - continue; - } - } - } + { + if (cur_cmd->a1->addr_type == ADDR_IS_NUM + && (cur_cmd->a2 + ? cur_cmd->range_state == RANGE_CLOSED + : cur_cmd->a1->addr_number < input->line_number)) + { + /* Skip this address next time */ + cur_cmd->addr_bang = !cur_cmd->addr_bang; + cur_cmd->a1->addr_type = ADDR_IS_NULL; + if (cur_cmd->a2) + cur_cmd->a2->addr_type = ADDR_IS_NULL; + + /* can we make an optimization? */ + if (cur_cmd->addr_bang) + { + if (cur_cmd->cmd == 'b' || cur_cmd->cmd == 't' + || cur_cmd->cmd == 'T' || cur_cmd->cmd == '}') + branches--; + + cur_cmd->cmd = '#'; /* replace with no-op */ + if (branches == 0) + cur_cmd = shrink_program(vec, cur_cmd); + if (!cur_cmd && no_default_output) + return 0; + end_cmd = vec->v + vec->v_length; + if (!cur_cmd) + cur_cmd = end_cmd; + continue; + } + } + } #endif /*EXPERIMENTAL_DASH_N_OPTIMIZATION*/ /* this is buried down here so that a "continue" statement can skip it */ @@ -1702,9 +1654,7 @@ execute_program(vec, input) /* Apply the compiled script to all the named files. */ int -process_files(the_program, argv) - struct vector *the_program; - char **argv; +process_files(struct vector *the_program, char **argv) { static char dash[] = "-"; static char *stdin_argv[2] = { dash, NULL }; @@ -1736,9 +1686,9 @@ process_files(the_program, argv) { status = execute_program(the_program, &input); if (status == -1) - status = EXIT_SUCCESS; + status = EXIT_SUCCESS; else - break; + break; } closedown(&input); @@ -1756,7 +1706,7 @@ process_files(the_program, argv) #endif /*DEBUG_LEAKS*/ if (input.bad_count) - status = 2; + status = EXIT_BAD_INPUT; return status; } |