X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=editors%2Fsed.c;h=9f3af33ed303e57e587ab4bc4b42c9b12e6371c8;hb=d35c0efddbb7602512f439f74f0e984331740464;hp=45cf7a7ad65ccce746958ad7968afdd1eec9b08c;hpb=5d2edbf16d598c002e64572f9bb45647f88532ed;p=oweals%2Fbusybox.git diff --git a/editors/sed.c b/editors/sed.c index 45cf7a7ad..9f3af33ed 100644 --- a/editors/sed.c +++ b/editors/sed.c @@ -8,20 +8,9 @@ * Copyright (C) 2003 by Glenn McGrath * Copyright (C) 2003,2004 by Rob Landley * - * 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 - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * MAINTAINER: Rob Landley * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. */ /* Code overview. @@ -32,9 +21,12 @@ add_cmd() is called on each line of sed command text (from a file or from the command line). It calls get_address() and parse_cmd_args(). The resulting sed_cmd_t structures are appended to a linked list - (sed_cmd_head/sed_cmd_tail). + (bbg.sed_cmd_head/bbg.sed_cmd_tail). + + add_input_file() adds a FILE * to the list of input files. We need to + know all input sources ahead of time to find the last line for the $ match. - process_file() does actual sedding, reading data lines from an input FILE * + process_files() does actual sedding, reading data lines from each input FILE * (which could be stdin) and applying the sed command list (sed_cmd_head) to each of the resulting lines. @@ -50,39 +42,26 @@ - commands: (p)rint, (d)elete, (s)ubstitue (with g & I flags) - edit commands: (a)ppend, (i)nsert, (c)hange - file commands: (r)ead - - backreferences in substitution expressions (\1, \2...\9) + - backreferences in substitution expressions (\0, \1, \2...\9) - grouped commands: {cmd1;cmd2} - transliteration (y/source-chars/dest-chars/) - pattern space hold space storing / swapping (g, h, x) - - labels / branching (: label, b, t) + - labels / branching (: label, b, t, T) (Note: Specifying an address (range) to match is *optional*; commands default to the whole pattern space if no specific address match was requested.) - Unsupported features: - - - GNU extensions - - and more. - Todo: - - Create a wrapper around regex to make libc's regex conform with sed - - Fix bugs - Reference http://www.opengroup.org/onlinepubs/007904975/utilities/sed.html */ -#include -#include /* for getopt() */ -#include -#include /* for strdup() */ -#include -#include /* for isspace() */ -#include #include "busybox.h" +#include "xregex.h" +/* Each sed command turns into one of these structures. */ typedef struct sed_cmd_s { /* Ordered by alignment requirements: currently 36 bytes on x86 */ @@ -93,7 +72,7 @@ typedef struct sed_cmd_s { int beg_line; /* 'sed 1p' 0 == apply commands to all lines */ int end_line; /* 'sed 1,3p' 0 == one line only. -1 = last line ($) */ - FILE *file; /* File (sr) command writes to, -1 for none. */ + FILE *file; /* File (sw) command writes to, -1 for none. */ char *string; /* Data string for (saicytb) commands. */ unsigned short which_match; /* (s) Which match to replace (0 for all) */ @@ -101,57 +80,59 @@ typedef struct sed_cmd_s { /* Bitfields (gcc won't group them if we don't) */ unsigned int invert:1; /* the '!' after the address */ unsigned int in_match:1; /* Next line also included in match? */ - unsigned int no_newline:1; /* Last line written by (sr) had no '\n' */ + unsigned int no_newline:1; /* Last line written by (sw) had no '\n' */ unsigned int sub_p:1; /* (s) print option */ - /* GENERAL FIELDS */ char cmd; /* The command char: abcdDgGhHilnNpPqrstwxy:={} */ struct sed_cmd_s *next; /* Next command (linked list, NULL terminated) */ } sed_cmd_t; -/* globals */ -/* options */ -static int be_quiet = 0, in_place=0; -FILE *nonstdout; -char *outname; +static const char *const semicolon_whitespace = "; \n\r\t\v"; + +struct sed_globals +{ + /* options */ + int be_quiet, in_place, regex_type; + FILE *nonstdout; + char *outname, *hold_space; + + /* List of input files */ + int input_file_count,current_input_file; + FILE **input_file_list; + regmatch_t regmatch[10]; + regex_t *previous_regex_ptr; + + /* linked list of sed commands */ + sed_cmd_t sed_cmd_head, *sed_cmd_tail; -static const char bad_format_in_subst[] = - "bad format in substitution expression"; -const char *const semicolon_whitespace = "; \n\r\t\v"; + /* Linked list of append lines */ + llist_t *append_head; -regmatch_t regmatch[10]; -static regex_t *previous_regex_ptr = NULL; + char *add_cmd_line; -/* linked list of sed commands */ -static sed_cmd_t sed_cmd_head; -static sed_cmd_t *sed_cmd_tail = &sed_cmd_head; + struct pipeline { + char *buf; /* Space to hold string */ + int idx; /* Space used */ + int len; /* Space allocated */ + } pipeline; +} bbg; -/* Linked list of append lines */ -struct append_list { - char *string; - struct append_list *next; -}; -struct append_list *append_head=NULL, *append_tail=NULL; -#ifdef CONFIG_FEATURE_CLEAN_UP -static void free_and_close_stuff(void) +void sed_free_and_close_stuff(void); +#if ENABLE_FEATURE_CLEAN_UP +void sed_free_and_close_stuff(void) { - sed_cmd_t *sed_cmd = sed_cmd_head.next; + sed_cmd_t *sed_cmd = bbg.sed_cmd_head.next; - while(append_head) { - append_tail=append_head->next; - free(append_head->string); - free(append_head); - append_head=append_tail; - } + llist_free(bbg.append_head, free); while (sed_cmd) { sed_cmd_t *sed_cmd_next = sed_cmd->next; if(sed_cmd->file) - bb_xprint_and_close_file(sed_cmd->file); + xprint_and_close_file(sed_cmd->file); if (sed_cmd->beg_match) { regfree(sed_cmd->beg_match); @@ -169,6 +150,11 @@ static void free_and_close_stuff(void) free(sed_cmd); sed_cmd = sed_cmd_next; } + + if(bbg.hold_space) free(bbg.hold_space); + + while(bbg.current_input_file= 0) { if (ch == ']' && !(bracket == idx - 1 || (bracket == idx - 2 && str[idx - 1] == '^'))) bracket = -1; @@ -230,44 +220,39 @@ static int index_of_next_unescaped_regexp_delim(const char delimiter, escaped = 0; else if (ch == '\\') escaped = 1; - else if (ch == '[') + else if (bracket == -1 && ch == '[') bracket = idx; else if (ch == delimiter) return idx; } /* if we make it to here, we've hit the end of the string */ - return -1; + bb_error_msg_and_die("unmatched '%c'",delimiter); } /* * Returns the index of the third delimiter */ -static int parse_regex_delim(const char *cmdstr, char **match, char **replace) +static int parse_regex_delim(char *cmdstr, char **match, char **replace) { - const char *cmdstr_ptr = cmdstr; + char *cmdstr_ptr = cmdstr; char delimiter; int idx = 0; /* verify that the 's' or 'y' is followed by something. That something * (typically a 'slash') is now our regexp delimiter... */ - if (*cmdstr == '\0') bb_error_msg_and_die(bad_format_in_subst); + if (*cmdstr == '\0') + bb_error_msg_and_die("bad format in substitution expression"); delimiter = *(cmdstr_ptr++); /* save the match string */ idx = index_of_next_unescaped_regexp_delim(delimiter, cmdstr_ptr); - if (idx == -1) { - bb_error_msg_and_die(bad_format_in_subst); - } - *match = copy_parsing_slashn(cmdstr_ptr, idx); + *match = copy_parsing_escapes(cmdstr_ptr, idx); /* save the replacement string */ cmdstr_ptr += idx + 1; - idx = index_of_next_unescaped_regexp_delim(delimiter, cmdstr_ptr); - if (idx == -1) { - bb_error_msg_and_die(bad_format_in_subst); - } - *replace = copy_parsing_slashn(cmdstr_ptr, idx); + idx = index_of_next_unescaped_regexp_delim(-delimiter, cmdstr_ptr); + *replace = copy_parsing_escapes(cmdstr_ptr, idx); return ((cmdstr_ptr - cmdstr) + idx); } @@ -293,21 +278,18 @@ static int get_address(char *my_str, int *linenum, regex_t ** regex) if (*my_str == '\\') delimiter = *(++pos); else delimiter = '/'; next = index_of_next_unescaped_regexp_delim(delimiter, ++pos); - if (next == -1) - bb_error_msg_and_die("unterminated match expression"); - - temp=copy_parsing_slashn(pos,next); + temp = copy_parsing_escapes(pos,next); *regex = (regex_t *) xmalloc(sizeof(regex_t)); - xregcomp(*regex, temp, REG_NEWLINE); + xregcomp(*regex, temp, bbg.regex_type|REG_NEWLINE); free(temp); /* Move position to next character after last delimiter */ - pos+=(next+1); + pos += (next+1); } return pos - my_str; } /* Grab a filename. Whitespace at start is skipped, then goes to EOL. */ -static int parse_file_cmd(sed_cmd_t * sed_cmd, const char *filecmdstr, char **retval) +static int parse_file_cmd(sed_cmd_t *sed_cmd, char *filecmdstr, char **retval) { int start = 0, idx, hack=0; @@ -318,15 +300,15 @@ static int parse_file_cmd(sed_cmd_t * sed_cmd, const char *filecmdstr, char **re /* If lines glued together, put backslash back. */ if(filecmdstr[idx]=='\n') hack=1; if(idx==start) bb_error_msg_and_die("Empty filename"); - *retval = bb_xstrndup(filecmdstr+start, idx-start+hack+1); + *retval = xstrndup(filecmdstr+start, idx-start+hack+1); if(hack) *(idx+*retval)='\\'; return idx; } -static int parse_subst_cmd(sed_cmd_t * const sed_cmd, char *substr) +static int parse_subst_cmd(sed_cmd_t *sed_cmd, char *substr) { - int cflags = 0; + int cflags = bbg.regex_type; char *match; int idx = 0; @@ -369,6 +351,7 @@ static int parse_subst_cmd(sed_cmd_t * const sed_cmd, char *substr) case 'p': sed_cmd->sub_p = 1; break; + /* Write to file */ case 'w': { char *temp; @@ -380,6 +363,11 @@ static int parse_subst_cmd(sed_cmd_t * const sed_cmd, char *substr) case 'I': cflags |= REG_ICASE; break; + /* Comment */ + case '#': + while(substr[++idx]); + /* Fall through */ + /* End of command */ case ';': case '}': goto out; @@ -418,7 +406,7 @@ static char *parse_cmd_args(sed_cmd_t *sed_cmd, char *cmdstr) } else if(isspace(*cmdstr)) cmdstr++; else break; } - sed_cmd->string = bb_xstrdup(cmdstr); + sed_cmd->string = xstrdup(cmdstr); parse_escapes(sed_cmd->string,sed_cmd->string,strlen(cmdstr),0,0); cmdstr += strlen(cmdstr); /* handle file cmds: (r)ead */ @@ -427,15 +415,15 @@ static char *parse_cmd_args(sed_cmd_t *sed_cmd, char *cmdstr) bb_error_msg_and_die("Command only uses one address"); cmdstr += parse_file_cmd(sed_cmd, cmdstr, &sed_cmd->string); if(sed_cmd->cmd=='w') - sed_cmd->file=bb_xfopen(sed_cmd->string,"w"); + sed_cmd->file=xfopen(sed_cmd->string,"w"); /* handle branch commands */ - } else if (strchr(":bt", sed_cmd->cmd)) { + } else if (strchr(":btT", sed_cmd->cmd)) { int length; while(isspace(*cmdstr)) cmdstr++; length = strcspn(cmdstr, semicolon_whitespace); if (length) { - sed_cmd->string = strndup(cmdstr, length); + sed_cmd->string = xstrndup(cmdstr, length); cmdstr += length; } } @@ -449,7 +437,7 @@ static char *parse_cmd_args(sed_cmd_t *sed_cmd, char *cmdstr) parse_escapes(match,match,strlen(match),i,i); parse_escapes(replace,replace,strlen(replace),i,i); - sed_cmd->string = xcalloc(1, (strlen(match) + 1) * 2); + sed_cmd->string = xzalloc((strlen(match) + 1) * 2); for (i = 0; match[i] && replace[i]; i++) { sed_cmd->string[i * 2] = match[i]; sed_cmd->string[(i * 2) + 1] = replace[i]; @@ -471,29 +459,23 @@ static char *parse_cmd_args(sed_cmd_t *sed_cmd, char *cmdstr) /* Parse address+command sets, skipping comment lines. */ -void add_cmd(char *cmdstr) +static void add_cmd(char *cmdstr) { - static char *add_cmd_line=NULL; sed_cmd_t *sed_cmd; int temp; /* Append this line to any unfinished line from last time. */ - if(add_cmd_line) { - int lastlen=strlen(add_cmd_line); - char *tmp=xmalloc(lastlen+strlen(cmdstr)+2); - - memcpy(tmp,add_cmd_line,lastlen); - tmp[lastlen]='\n'; - strcpy(tmp+lastlen+1,cmdstr); - free(add_cmd_line); - cmdstr=add_cmd_line=tmp; - } else add_cmd_line=NULL; + if (bbg.add_cmd_line) { + cmdstr = xasprintf("%s\n%s", bbg.add_cmd_line, cmdstr); + free(bbg.add_cmd_line); + bbg.add_cmd_line = cmdstr; + } /* If this line ends with backslash, request next line. */ temp=strlen(cmdstr); if(temp && cmdstr[temp-1]=='\\') { - if(!add_cmd_line) add_cmd_line=strdup(cmdstr); - add_cmd_line[temp-1]=0; + if (!bbg.add_cmd_line) bbg.add_cmd_line = xstrdup(cmdstr); + bbg.add_cmd_line[temp-1] = 0; return; } @@ -508,7 +490,7 @@ void add_cmd(char *cmdstr) /* if this is a comment, jump past it and keep going */ if (*cmdstr == '#') { /* "#n" is the same as using -n on the command line */ - if (cmdstr[1] == 'n') be_quiet++; + if (cmdstr[1] == 'n') bbg.be_quiet++; if(!(cmdstr=strpbrk(cmdstr, "\n\r"))) break; continue; } @@ -519,7 +501,7 @@ void add_cmd(char *cmdstr) * part1 part2 part3 */ - sed_cmd = xcalloc(1, sizeof(sed_cmd_t)); + sed_cmd = xzalloc(sizeof(sed_cmd_t)); /* first part (if present) is an address: either a '$', a number or a /regex/ */ cmdstr += get_address(cmdstr, &sed_cmd->beg_line, &sed_cmd->beg_match); @@ -530,7 +512,7 @@ void add_cmd(char *cmdstr) cmdstr++; idx = get_address(cmdstr, &sed_cmd->end_line, &sed_cmd->end_match); - if (!idx) bb_error_msg_and_die("get_address: no address found in string\n"); + if (!idx) bb_error_msg_and_die("no address after comma"); cmdstr += idx; } @@ -552,48 +534,43 @@ void add_cmd(char *cmdstr) cmdstr = parse_cmd_args(sed_cmd, cmdstr); /* Add the command to the command array */ - sed_cmd_tail->next = sed_cmd; - sed_cmd_tail = sed_cmd_tail->next; + bbg.sed_cmd_tail->next = sed_cmd; + bbg.sed_cmd_tail = bbg.sed_cmd_tail->next; } /* If we glued multiple lines together, free the memory. */ - if(add_cmd_line) { - free(add_cmd_line); - add_cmd_line=NULL; - } + free(bbg.add_cmd_line); + bbg.add_cmd_line = NULL; } -struct pipeline { - char *buf; /* Space to hold string */ - int idx; /* Space used */ - int len; /* Space allocated */ -} pipeline; +/* Append to a string, reallocating memory as necessary. */ #define PIPE_GROW 64 -void pipe_putc(char c) +static void pipe_putc(char c) { - if(pipeline.idx==pipeline.len) { - pipeline.buf = xrealloc(pipeline.buf, pipeline.len + PIPE_GROW); - pipeline.len+=PIPE_GROW; + if(bbg.pipeline.idx==bbg.pipeline.len) { + bbg.pipeline.buf = xrealloc(bbg.pipeline.buf, + bbg.pipeline.len + PIPE_GROW); + bbg.pipeline.len+=PIPE_GROW; } - pipeline.buf[pipeline.idx++] = (c); + bbg.pipeline.buf[bbg.pipeline.idx++] = c; } -static void do_subst_w_backrefs(const char *line, const char *replace) +static void do_subst_w_backrefs(char *line, char *replace) { int i,j; /* go through the replacement string */ for (i = 0; replace[i]; i++) { /* if we find a backreference (\1, \2, etc.) print the backref'ed * text */ - if (replace[i] == '\\' && replace[i+1]>'0' && replace[i+1]<='9') { + if (replace[i] == '\\' && replace[i+1]>='0' && replace[i+1]<='9') { int backref=replace[++i]-'0'; - /* print out the text held in regmatch[backref] */ - if(regmatch[backref].rm_so != -1) - for (j = regmatch[backref].rm_so; j < regmatch[backref].rm_eo; j++) - pipe_putc(line[j]); + /* print out the text held in bbg.regmatch[backref] */ + if(bbg.regmatch[backref].rm_so != -1) + for (j = bbg.regmatch[backref].rm_so; + j < bbg.regmatch[backref].rm_eo; j++) pipe_putc(line[j]); } /* if we find a backslash escaped character, print the character */ @@ -601,14 +578,14 @@ static void do_subst_w_backrefs(const char *line, const char *replace) /* if we find an unescaped '&' print out the whole matched text. */ else if (replace[i] == '&') - for (j = regmatch[0].rm_so; j < regmatch[0].rm_eo; j++) + for (j = bbg.regmatch[0].rm_so; j < bbg.regmatch[0].rm_eo; j++) pipe_putc(line[j]); /* Otherwise just output the character. */ else pipe_putc(replace[i]); } } -static int do_subst_command(sed_cmd_t * sed_cmd, char **line) +static int do_subst_command(sed_cmd_t *sed_cmd, char **line) { char *oldline = *line; int altered = 0; @@ -617,19 +594,19 @@ static int do_subst_command(sed_cmd_t * sed_cmd, char **line) /* Handle empty regex. */ if (sed_cmd->sub_match == NULL) { - current_regex = previous_regex_ptr; + current_regex = bbg.previous_regex_ptr; if(!current_regex) bb_error_msg_and_die("No previous regexp."); - } else previous_regex_ptr = current_regex = sed_cmd->sub_match; + } else bbg.previous_regex_ptr = current_regex = sed_cmd->sub_match; /* Find the first match */ - if(REG_NOMATCH==regexec(current_regex, oldline, 10, regmatch, 0)) + if(REG_NOMATCH==regexec(current_regex, oldline, 10, bbg.regmatch, 0)) return 0; /* Initialize temporary output buffer. */ - pipeline.buf=xmalloc(PIPE_GROW); - pipeline.len=PIPE_GROW; - pipeline.idx=0; + bbg.pipeline.buf=xmalloc(PIPE_GROW); + bbg.pipeline.len=PIPE_GROW; + bbg.pipeline.idx=0; /* Now loop through, substituting for matches */ do { @@ -639,7 +616,7 @@ static int do_subst_command(sed_cmd_t * sed_cmd, char **line) echo " a.b" | busybox sed 's [^ .]* x g' The match_count check is so not to break echo "hi" | busybox sed 's/^/!/g' */ - if(!regmatch[0].rm_so && !regmatch[0].rm_eo && match_count) { + if(!bbg.regmatch[0].rm_so && !bbg.regmatch[0].rm_eo && match_count) { pipe_putc(*(oldline++)); continue; } @@ -649,25 +626,25 @@ static int do_subst_command(sed_cmd_t * sed_cmd, char **line) /* If we aren't interested in this match, output old line to end of match and continue */ if(sed_cmd->which_match && sed_cmd->which_match!=match_count) { - for(i=0;istring); /* advance past the match */ - oldline += regmatch[0].rm_eo; + oldline += bbg.regmatch[0].rm_eo; /* flag that something has changed */ altered++; /* if we're not doing this globally, get out now */ if (sed_cmd->which_match) break; - } while (*oldline && (regexec(current_regex, oldline, 10, regmatch, 0) != REG_NOMATCH)); + } while (*oldline && (regexec(current_regex, oldline, 10, bbg.regmatch, 0) != REG_NOMATCH)); /* Copy rest of string into output pipeline */ @@ -675,16 +652,16 @@ static int do_subst_command(sed_cmd_t * sed_cmd, char **line) pipe_putc(0); free(*line); - *line = pipeline.buf; + *line = bbg.pipeline.buf; return altered; } /* Set command pointer to point to this label. (Does not handle null label.) */ -static sed_cmd_t *branch_to(const char *label) +static sed_cmd_t *branch_to(char *label) { sed_cmd_t *sed_cmd; - for (sed_cmd = sed_cmd_head.next; sed_cmd; sed_cmd = sed_cmd->next) { + for (sed_cmd = bbg.sed_cmd_head.next; sed_cmd; sed_cmd = sed_cmd->next) { if ((sed_cmd->cmd == ':') && (sed_cmd->string) && (strcmp(sed_cmd->string, label) == 0)) { return (sed_cmd); } @@ -692,44 +669,46 @@ static sed_cmd_t *branch_to(const char *label) bb_error_msg_and_die("Can't find label for jump to `%s'", label); } -/* Append copy of string to append buffer */ static void append(char *s) { - struct append_list *temp=calloc(1,sizeof(struct append_list)); - - if(append_head) - append_tail=(append_tail->next=temp); - else append_head=append_tail=temp; - temp->string=strdup(s); + llist_add_to_end(&bbg.append_head, xstrdup(s)); } static void flush_append(void) { + char *data; + /* Output appended lines. */ - while(append_head) { - fprintf(nonstdout,"%s\n",append_head->string); - append_tail=append_head->next; - free(append_head->string); - free(append_head); - append_head=append_tail; + while((data = (char *)llist_pop(&bbg.append_head))) { + fprintf(bbg.nonstdout,"%s\n",data); + free(data); } - append_head=append_tail=NULL; } -/* Get next line of input, flushing append buffer and noting if we hit EOF - * without a newline on the last line. +static void add_input_file(FILE *file) +{ + bbg.input_file_list=xrealloc(bbg.input_file_list, + (bbg.input_file_count + 1) * sizeof(FILE *)); + bbg.input_file_list[bbg.input_file_count++] = file; +} + +/* Get next line of input from bbg.input_file_list, flushing append buffer and + * noting if we ran out of files without a newline on the last line we read. */ -static char *get_next_line(FILE * file, int *no_newline) +static char *get_next_line(int *no_newline) { - char *temp; + char *temp=NULL; int len; flush_append(); - temp=bb_get_line_from_file(file); - if(temp) { - len=strlen(temp); - if(len && temp[len-1]=='\n') temp[len-1]=0; - else *no_newline=1; + while (bbg.current_input_filenext) { + for (sed_cmd = bbg.sed_cmd_head.next; sed_cmd; sed_cmd = sed_cmd->next) + { int old_matched, matched; old_matched = sed_cmd->in_match; @@ -833,7 +817,7 @@ restart: if (sed_cmd->invert ? !matched : matched) { /* Update last used regex in case a blank substitute BRE is found */ if (sed_cmd->beg_match) { - previous_regex_ptr = sed_cmd->beg_match; + bbg.previous_regex_ptr = sed_cmd->beg_match; } /* actual sedding */ @@ -841,7 +825,7 @@ restart: /* Print line number */ case '=': - fprintf(nonstdout,"%d\n", linenum); + fprintf(bbg.nonstdout,"%d\n", linenum); break; /* Write the current pattern space up to the first newline */ @@ -868,7 +852,7 @@ restart: char *tmp = strchr(pattern_space,'\n'); if(tmp) { - tmp=bb_xstrdup(tmp+1); + tmp=xstrdup(tmp+1); free(pattern_space); pattern_space=tmp; goto restart; @@ -908,22 +892,22 @@ restart: /* Cut and paste text (replace) */ case 'c': /* Only triggers on last line of a matching range. */ - if (!sed_cmd->in_match) sed_puts(sed_cmd->string,1); + if (!sed_cmd->in_match) sed_puts(sed_cmd->string,0); goto discard_line; /* Read file, append contents to output */ case 'r': { - FILE *outfile; + FILE *rfile; - outfile = fopen(sed_cmd->string, "r"); - if (outfile) { + rfile = fopen(sed_cmd->string, "r"); + if (rfile) { char *line; - while ((line = bb_get_chomped_line_from_file(outfile)) + while ((line = bb_get_chomped_line_from_file(rfile)) != NULL) append(line); - bb_xprint_and_close_file(outfile); + xprint_and_close_file(rfile); } break; @@ -936,13 +920,13 @@ restart: /* Read next line from input */ case 'n': - if (!be_quiet) + if (!bbg.be_quiet) sed_puts(pattern_space,no_newline); if (next_line) { free(pattern_space); pattern_space = next_line; no_newline=next_no_newline; - next_line = get_next_line(file,&next_no_newline); + next_line = get_next_line(&next_no_newline); linenum++; break; } @@ -972,17 +956,21 @@ restart: pattern_space[len]='\n'; strcpy(pattern_space+len+1, next_line); no_newline=next_no_newline; - next_line = get_next_line(file,&next_no_newline); + next_line = get_next_line(&next_no_newline); linenum++; } break; } - /* Test if substition worked, branch if so. */ + /* Test/branch if substitution occurred */ case 't': - if (!substituted) break; + if(!substituted) break; substituted=0; - /* Fall through */ + /* Fall through */ + /* Test/branch if substitution didn't occur */ + case 'T': + if (substituted) break; + /* Fall through */ /* Branch to label */ case 'b': if (!sed_cmd->string) goto discard_commands; @@ -999,6 +987,7 @@ restart: for (j = 0; sed_cmd->string[j]; j += 2) { if (pattern_space[i] == sed_cmd->string[j]) { pattern_space[i] = sed_cmd->string[j + 1]; + break; } } } @@ -1007,10 +996,7 @@ restart: } case 'g': /* Replace pattern space with hold space */ free(pattern_space); - if (hold_space) { - pattern_space = strdup(hold_space); - no_newline=0; - } + pattern_space = xstrdup(bbg.hold_space ? bbg.hold_space : ""); break; case 'G': /* Append newline and hold space to pattern space */ { @@ -1019,42 +1005,46 @@ restart: if (pattern_space) pattern_space_size += strlen(pattern_space); - if (hold_space) hold_space_size = strlen(hold_space); - pattern_space = xrealloc(pattern_space, pattern_space_size + hold_space_size); + if (bbg.hold_space) + hold_space_size = strlen(bbg.hold_space); + pattern_space = xrealloc(pattern_space, + pattern_space_size + hold_space_size); if (pattern_space_size == 2) pattern_space[0]=0; strcat(pattern_space, "\n"); - if (hold_space) strcat(pattern_space, hold_space); + if (bbg.hold_space) + strcat(pattern_space, bbg.hold_space); no_newline=0; break; } case 'h': /* Replace hold space with pattern space */ - free(hold_space); - hold_space = strdup(pattern_space); + free(bbg.hold_space); + bbg.hold_space = xstrdup(pattern_space); break; case 'H': /* Append newline and pattern space to hold space */ { int hold_space_size = 2; int pattern_space_size = 0; - if (hold_space) hold_space_size += strlen(hold_space); + if (bbg.hold_space) + hold_space_size += strlen(bbg.hold_space); if (pattern_space) pattern_space_size = strlen(pattern_space); - hold_space = xrealloc(hold_space, + bbg.hold_space = xrealloc(bbg.hold_space, hold_space_size + pattern_space_size); - if (hold_space_size == 2) hold_space[0]=0; - strcat(hold_space, "\n"); - if (pattern_space) strcat(hold_space, pattern_space); + if (hold_space_size == 2) *bbg.hold_space=0; + strcat(bbg.hold_space, "\n"); + if (pattern_space) strcat(bbg.hold_space, pattern_space); break; } case 'x': /* Exchange hold and pattern space */ { char *tmp = pattern_space; - pattern_space = hold_space; + pattern_space = bbg.hold_space ? : xzalloc(1); no_newline=0; - hold_space = tmp; + bbg.hold_space = tmp; break; } } @@ -1067,7 +1057,7 @@ restart: discard_commands: /* we will print the line unless we were told to be quiet ('-n') or if the line was suppressed (ala 'd'elete) */ - if (!be_quiet) sed_puts(pattern_space,no_newline); + if (!bbg.be_quiet) sed_puts(pattern_space,no_newline); /* Delete and such jump here. */ discard_line: @@ -1082,7 +1072,7 @@ discard_line: static void add_cmd_block(char *cmdstr) { int go=1; - char *temp=bb_xstrdup(cmdstr),*temp2=temp; + char *temp=xstrdup(cmdstr),*temp2=temp; while(go) { int len=strcspn(temp2,"\n"); @@ -1094,35 +1084,33 @@ static void add_cmd_block(char *cmdstr) free(temp); } -extern int sed_main(int argc, char **argv) +int sed_main(int argc, char **argv) { - int opt; - char getpat=1; - char status = EXIT_SUCCESS; + int status = EXIT_SUCCESS, opt, getpat = 1; + + bbg.sed_cmd_tail=&bbg.sed_cmd_head; -#ifdef CONFIG_FEATURE_CLEAN_UP /* destroy command strings on exit */ - if (atexit(free_and_close_stuff) == -1) - bb_perror_msg_and_die("atexit"); -#endif + if (ENABLE_FEATURE_CLEAN_UP) atexit(sed_free_and_close_stuff); -#define LIE_TO_AUTOCONF -#ifdef LIE_TO_AUTOCONF + /* Lie to autoconf when it starts asking stupid questions. */ if(argc==2 && !strcmp(argv[1],"--version")) { printf("This is not GNU sed version 4.0\n"); exit(0); } -#endif /* do normal option parsing */ - while ((opt = getopt(argc, argv, "ine:f:")) > 0) { + while ((opt = getopt(argc, argv, "irne:f:")) > 0) { switch (opt) { case 'i': - in_place++; + bbg.in_place++; atexit(cleanup_outname); break; + case 'r': + bbg.regex_type|=REG_EXTENDED; + break; case 'n': - be_quiet++; + bbg.be_quiet++; break; case 'e': add_cmd_block(optarg); @@ -1133,15 +1121,14 @@ extern int sed_main(int argc, char **argv) FILE *cmdfile; char *line; - cmdfile = bb_xfopen(optarg, "r"); + cmdfile = xfopen(optarg, "r"); - while ((line = bb_get_chomped_line_from_file(cmdfile)) - != NULL) { + while ((line = bb_get_chomped_line_from_file(cmdfile)) != NULL) { add_cmd(line); getpat=0; free(line); } - bb_xprint_and_close_file(cmdfile); + xprint_and_close_file(cmdfile); break; } @@ -1150,8 +1137,7 @@ extern int sed_main(int argc, char **argv) } } - /* if we didn't get a pattern from a -e and no command file was specified, - * argv[optind] should be the pattern. no pattern, no worky */ + /* if we didn't get a pattern from -e or -f, use argv[optind] */ if(getpat) { if (argv[optind] == NULL) bb_show_usage(); @@ -1162,55 +1148,56 @@ extern int sed_main(int argc, char **argv) add_cmd(""); /* By default, we write to stdout */ - nonstdout=stdout; + bbg.nonstdout=stdout; /* argv[(optind)..(argc-1)] should be names of file to process. If no * files were specified or '-' was specified, take input from stdin. * Otherwise, we process all the files specified. */ if (argv[optind] == NULL) { - if(in_place) { - fprintf(stderr,"sed: Filename required for -i\n"); - exit(1); - } - process_file(stdin); + if(bbg.in_place) bb_error_msg_and_die(bb_msg_requires_arg, "-i"); + add_input_file(stdin); + process_files(); } else { int i; FILE *file; for (i = optind; i < argc; i++) { - if(!strcmp(argv[i], "-") && !in_place) { - process_file(stdin); + if(!strcmp(argv[i], "-") && !bbg.in_place) { + add_input_file(stdin); + process_files(); } else { file = bb_wfopen(argv[i], "r"); if (file) { - if(in_place) { + if(bbg.in_place) { struct stat statbuf; - outname=bb_xstrndup(argv[i],strlen(argv[i])+6); - strcat(outname,"XXXXXX"); - /* Set permissions of output file */ - fstat(fileno(file),&statbuf); - mkstemp(outname); - nonstdout=bb_wfopen(outname,"w"); + int nonstdoutfd; + + bbg.outname=xstrndup(argv[i],strlen(argv[i])+6); + strcat(bbg.outname,"XXXXXX"); + if(-1==(nonstdoutfd=mkstemp(bbg.outname))) + bb_error_msg_and_die("no temp file"); + bbg.nonstdout=fdopen(nonstdoutfd,"w"); + /* Set permissions of output file */ + fstat(fileno(file),&statbuf); - fchmod(fileno(nonstdout),statbuf.st_mode); - atexit(cleanup_outname); - } - process_file(file); - fclose(file); - if(in_place) { - fclose(nonstdout); - nonstdout=stdout; + fchmod(nonstdoutfd,statbuf.st_mode); + add_input_file(file); + process_files(); + fclose(bbg.nonstdout); + + bbg.nonstdout=stdout; unlink(argv[i]); - rename(outname,argv[i]); - free(outname); - outname=0; - } + rename(bbg.outname,argv[i]); + free(bbg.outname); + bbg.outname=0; + } else add_input_file(file); } else { status = EXIT_FAILURE; } } } + if(bbg.input_file_count>bbg.current_input_file) process_files(); } return status;