Magellan Linux

Diff of /trunk/mkinitrd-magellan/busybox/editors/sed.c

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 532 by niro, Sat Sep 1 22:45:15 2007 UTC revision 816 by niro, Fri Apr 24 18:33:46 2009 UTC
# Line 5  Line 5 
5   * Copyright (C) 1999,2000,2001 by Lineo, inc. and Mark Whitley   * Copyright (C) 1999,2000,2001 by Lineo, inc. and Mark Whitley
6   * Copyright (C) 1999,2000,2001 by Mark Whitley <markw@codepoet.org>   * Copyright (C) 1999,2000,2001 by Mark Whitley <markw@codepoet.org>
7   * Copyright (C) 2002  Matt Kraai   * Copyright (C) 2002  Matt Kraai
8   * Copyright (C) 2003 by Glenn McGrath <bug1@iinet.net.au>   * Copyright (C) 2003 by Glenn McGrath
9   * Copyright (C) 2003,2004 by Rob Landley <rob@landley.net>   * Copyright (C) 2003,2004 by Rob Landley <rob@landley.net>
10   *   *
11   * MAINTAINER: Rob Landley <rob@landley.net>   * MAINTAINER: Rob Landley <rob@landley.net>
# Line 21  Line 21 
21    add_cmd() is called on each line of sed command text (from a file or from    add_cmd() is called on each line of sed command text (from a file or from
22    the command line).  It calls get_address() and parse_cmd_args().  The    the command line).  It calls get_address() and parse_cmd_args().  The
23    resulting sed_cmd_t structures are appended to a linked list    resulting sed_cmd_t structures are appended to a linked list
24    (bbg.sed_cmd_head/bbg.sed_cmd_tail).    (G.sed_cmd_head/G.sed_cmd_tail).
25    
26    add_input_file() adds a FILE * to the list of input files.  We need to    add_input_file() adds a FILE* to the list of input files.  We need to
27    know all input sources ahead of time to find the last line for the $ match.    know all input sources ahead of time to find the last line for the $ match.
28    
29    process_files() does actual sedding, reading data lines from each input FILE *    process_files() does actual sedding, reading data lines from each input FILE *
# Line 58  Line 58 
58   Reference http://www.opengroup.org/onlinepubs/007904975/utilities/sed.html   Reference http://www.opengroup.org/onlinepubs/007904975/utilities/sed.html
59  */  */
60    
61  #include "busybox.h"  #include "libbb.h"
62  #include "xregex.h"  #include "xregex.h"
63    
64  /* Each sed command turns into one of these structures. */  /* Each sed command turns into one of these structures. */
65  typedef struct sed_cmd_s {  typedef struct sed_cmd_s {
66   /* Ordered by alignment requirements: currently 36 bytes on x86 */   /* Ordered by alignment requirements: currently 36 bytes on x86 */
67     struct sed_cmd_s *next; /* Next command (linked list, NULL terminated) */
68    
69   /* address storage */   /* address storage */
70   regex_t *beg_match;     /* sed -e '/match/cmd' */   regex_t *beg_match;     /* sed -e '/match/cmd' */
# Line 72  typedef struct sed_cmd_s { Line 73  typedef struct sed_cmd_s {
73   int beg_line;           /* 'sed 1p'   0 == apply commands to all lines */   int beg_line;           /* 'sed 1p'   0 == apply commands to all lines */
74   int end_line;           /* 'sed 1,3p' 0 == one line only. -1 = last line ($) */   int end_line;           /* 'sed 1,3p' 0 == one line only. -1 = last line ($) */
75    
76   FILE *file;             /* File (sw) command writes to, -1 for none. */   FILE *sw_file;          /* File (sw) command writes to, -1 for none. */
77   char *string;           /* Data string for (saicytb) commands. */   char *string;           /* Data string for (saicytb) commands. */
78    
79   unsigned short which_match;     /* (s) Which match to replace (0 for all) */   unsigned which_match;   /* (s) Which match to replace (0 for all) */
80    
81   /* Bitfields (gcc won't group them if we don't) */   /* Bitfields (gcc won't group them if we don't) */
82   unsigned int invert:1;          /* the '!' after the address */   unsigned invert:1;      /* the '!' after the address */
83   unsigned int in_match:1;        /* Next line also included in match? */   unsigned in_match:1;    /* Next line also included in match? */
84   unsigned int sub_p:1;           /* (s) print option */   unsigned sub_p:1;       /* (s) print option */
85    
86   int last_char;                  /* Last line written by (sw) had no '\n' */   char sw_last_char;      /* Last line written by (sw) had no '\n' */
87    
88   /* GENERAL FIELDS */   /* GENERAL FIELDS */
89   char cmd;               /* The command char: abcdDgGhHilnNpPqrstwxy:={} */   char cmd;               /* The command char: abcdDgGhHilnNpPqrstwxy:={} */
  struct sed_cmd_s *next; /* Next command (linked list, NULL terminated) */  
90  } sed_cmd_t;  } sed_cmd_t;
91    
92  static const char *const semicolon_whitespace = "; \n\r\t\v";  static const char semicolon_whitespace[] ALIGN1 = "; \n\r\t\v";
93    
94  struct sed_globals {  struct globals {
95   /* options */   /* options */
96   int be_quiet, regex_type;   int be_quiet, regex_type;
97   FILE *nonstdout;   FILE *nonstdout;
# Line 117  struct sed_globals { Line 117  struct sed_globals {
117   int idx; /* Space used */   int idx; /* Space used */
118   int len; /* Space allocated */   int len; /* Space allocated */
119   } pipeline;   } pipeline;
120  } bbg;  };
121    #define G (*(struct globals*)&bb_common_bufsiz1)
122    void BUG_sed_globals_too_big(void);
123    #define INIT_G() do { \
124     if (sizeof(struct globals) > COMMON_BUFSIZE) \
125     BUG_sed_globals_too_big(); \
126     G.sed_cmd_tail = &G.sed_cmd_head; \
127    } while (0)
128    
129    
130  #if ENABLE_FEATURE_CLEAN_UP  #if ENABLE_FEATURE_CLEAN_UP
131  static void sed_free_and_close_stuff(void)  static void sed_free_and_close_stuff(void)
132  {  {
133   sed_cmd_t *sed_cmd = bbg.sed_cmd_head.next;   sed_cmd_t *sed_cmd = G.sed_cmd_head.next;
134    
135   llist_free(bbg.append_head, free);   llist_free(G.append_head, free);
136    
137   while (sed_cmd) {   while (sed_cmd) {
138   sed_cmd_t *sed_cmd_next = sed_cmd->next;   sed_cmd_t *sed_cmd_next = sed_cmd->next;
139    
140   if (sed_cmd->file)   if (sed_cmd->sw_file)
141   xprint_and_close_file(sed_cmd->file);   xprint_and_close_file(sed_cmd->sw_file);
142    
143   if (sed_cmd->beg_match) {   if (sed_cmd->beg_match) {
144   regfree(sed_cmd->beg_match);   regfree(sed_cmd->beg_match);
# Line 150  static void sed_free_and_close_stuff(voi Line 157  static void sed_free_and_close_stuff(voi
157   sed_cmd = sed_cmd_next;   sed_cmd = sed_cmd_next;
158   }   }
159    
160   if (bbg.hold_space) free(bbg.hold_space);   free(G.hold_space);
161    
162   while (bbg.current_input_file < bbg.input_file_count)   while (G.current_input_file < G.input_file_count)
163   fclose(bbg.input_file_list[bbg.current_input_file++]);   fclose(G.input_file_list[G.current_input_file++]);
164  }  }
165  #else  #else
166  void sed_free_and_close_stuff(void);  void sed_free_and_close_stuff(void);
# Line 163  void sed_free_and_close_stuff(void); Line 170  void sed_free_and_close_stuff(void);
170    
171  static void cleanup_outname(void)  static void cleanup_outname(void)
172  {  {
173   if (bbg.outname) unlink(bbg.outname);   if (G.outname) unlink(G.outname);
174  }  }
175    
176  /* strdup, replacing "\n" with '\n', and "\delimiter" with 'delimiter' */  /* strcpy, replacing "\from" with 'to'. If to is NUL, replacing "\any" with 'any' */
177    
178  static void parse_escapes(char *dest, char *string, int len, char from, char to)  static void parse_escapes(char *dest, const char *string, int len, char from, char to)
179  {  {
180   int i = 0;   int i = 0;
181    
# Line 181  static void parse_escapes(char *dest, ch Line 188  static void parse_escapes(char *dest, ch
188   }   }
189   *dest++ = string[i++];   *dest++ = string[i++];
190   }   }
191     /* TODO: is it safe wrt a string with trailing '\\' ? */
192   *dest++ = string[i++];   *dest++ = string[i++];
193   }   }
194   *dest = 0;   *dest = '\0';
195  }  }
196    
197  static char *copy_parsing_escapes(char *string, int len)  static char *copy_parsing_escapes(const char *string, int len)
198  {  {
199   char *dest = xmalloc(len + 1);   char *dest = xmalloc(len + 1);
200    
201   parse_escapes(dest, string, len, 'n', '\n');   parse_escapes(dest, string, len, 'n', '\n');
202     /* GNU sed also recognizes \t */
203     parse_escapes(dest, dest, strlen(dest), 't', '\t');
204   return dest;   return dest;
205  }  }
206    
# Line 198  static char *copy_parsing_escapes(char * Line 208  static char *copy_parsing_escapes(char *
208  /*  /*
209   * index_of_next_unescaped_regexp_delim - walks left to right through a string   * index_of_next_unescaped_regexp_delim - walks left to right through a string
210   * beginning at a specified index and returns the index of the next regular   * beginning at a specified index and returns the index of the next regular
211   * expression delimiter (typically a forward * slash ('/')) not preceded by   * expression delimiter (typically a forward slash ('/')) not preceded by
212   * a backslash ('\').  A negative delimiter disables square bracket checking.   * a backslash ('\').  A negative delimiter disables square bracket checking.
213   */   */
214  static int index_of_next_unescaped_regexp_delim(int delimiter, char *str)  static int index_of_next_unescaped_regexp_delim(int delimiter, const char *str)
215  {  {
216   int bracket = -1;   int bracket = -1;
217   int escaped = 0;   int escaped = 0;
# Line 235  static int index_of_next_unescaped_regex Line 245  static int index_of_next_unescaped_regex
245  /*  /*
246   *  Returns the index of the third delimiter   *  Returns the index of the third delimiter
247   */   */
248  static int parse_regex_delim(char *cmdstr, char **match, char **replace)  static int parse_regex_delim(const char *cmdstr, char **match, char **replace)
249  {  {
250   char *cmdstr_ptr = cmdstr;   const char *cmdstr_ptr = cmdstr;
251   char delimiter;   char delimiter;
252   int idx = 0;   int idx = 0;
253    
# Line 262  static int parse_regex_delim(char *cmdst Line 272  static int parse_regex_delim(char *cmdst
272  /*  /*
273   * returns the index in the string just past where the address ends.   * returns the index in the string just past where the address ends.
274   */   */
275  static int get_address(char *my_str, int *linenum, regex_t ** regex)  static int get_address(const char *my_str, int *linenum, regex_t ** regex)
276  {  {
277   char *pos = my_str;   const char *pos = my_str;
278    
279   if (isdigit(*my_str)) {   if (isdigit(*my_str)) {
280   *linenum = strtol(my_str, &pos, 10);   *linenum = strtol(my_str, (char**)&pos, 10);
281   /* endstr shouldnt ever equal NULL */   /* endstr shouldnt ever equal NULL */
282   } else if (*my_str == '$') {   } else if (*my_str == '$') {
283   *linenum = -1;   *linenum = -1;
# Line 282  static int get_address(char *my_str, int Line 292  static int get_address(char *my_str, int
292   next = index_of_next_unescaped_regexp_delim(delimiter, ++pos);   next = index_of_next_unescaped_regexp_delim(delimiter, ++pos);
293   temp = copy_parsing_escapes(pos, next);   temp = copy_parsing_escapes(pos, next);
294   *regex = xmalloc(sizeof(regex_t));   *regex = xmalloc(sizeof(regex_t));
295   xregcomp(*regex, temp, bbg.regex_type|REG_NEWLINE);   xregcomp(*regex, temp, G.regex_type|REG_NEWLINE);
296   free(temp);   free(temp);
297   /* Move position to next character after last delimiter */   /* Move position to next character after last delimiter */
298   pos += (next+1);   pos += (next+1);
# Line 291  static int get_address(char *my_str, int Line 301  static int get_address(char *my_str, int
301  }  }
302    
303  /* Grab a filename.  Whitespace at start is skipped, then goes to EOL. */  /* Grab a filename.  Whitespace at start is skipped, then goes to EOL. */
304  static int parse_file_cmd(sed_cmd_t *sed_cmd, char *filecmdstr, char **retval)  static int parse_file_cmd(/*sed_cmd_t *sed_cmd,*/ const char *filecmdstr, char **retval)
305  {  {
306   int start = 0, idx, hack = 0;   int start = 0, idx, hack = 0;
307    
308   /* Skip whitespace, then grab filename to end of line */   /* Skip whitespace, then grab filename to end of line */
309   while (isspace(filecmdstr[start])) start++;   while (isspace(filecmdstr[start]))
310     start++;
311   idx = start;   idx = start;
312   while (filecmdstr[idx] && filecmdstr[idx] != '\n') idx++;   while (filecmdstr[idx] && filecmdstr[idx] != '\n')
313     idx++;
314    
315   /* If lines glued together, put backslash back. */   /* If lines glued together, put backslash back. */
316   if (filecmdstr[idx] == '\n') hack = 1;   if (filecmdstr[idx] == '\n')
317     hack = 1;
318   if (idx == start)   if (idx == start)
319   bb_error_msg_and_die("empty filename");   bb_error_msg_and_die("empty filename");
320   *retval = xstrndup(filecmdstr+start, idx-start+hack+1);   *retval = xstrndup(filecmdstr+start, idx-start+hack+1);
321   if (hack) (*retval)[idx] = '\\';   if (hack)
322     (*retval)[idx] = '\\';
323    
324   return idx;   return idx;
325  }  }
326    
327  static int parse_subst_cmd(sed_cmd_t *sed_cmd, char *substr)  static int parse_subst_cmd(sed_cmd_t *sed_cmd, const char *substr)
328  {  {
329   int cflags = bbg.regex_type;   int cflags = G.regex_type;
330   char *match;   char *match;
331   int idx = 0;   int idx;
332    
333   /*   /*
334   * A substitution command should look something like this:   * A substitution command should look something like this:
# Line 337  static int parse_subst_cmd(sed_cmd_t *se Line 351  static int parse_subst_cmd(sed_cmd_t *se
351   if (isdigit(substr[idx])) {   if (isdigit(substr[idx])) {
352   if (match[0] != '^') {   if (match[0] != '^') {
353   /* Match 0 treated as all, multiple matches we take the last one. */   /* Match 0 treated as all, multiple matches we take the last one. */
354   char *pos = substr + idx;   const char *pos = substr + idx;
355   /* FIXME: error check? */  /* FIXME: error check? */
356   sed_cmd->which_match = (unsigned short)strtol(substr+idx, &pos, 10);   sed_cmd->which_match = (unsigned)strtol(substr+idx, (char**) &pos, 10);
357   idx = pos - substr;   idx = pos - substr;
358   }   }
359   continue;   continue;
# Line 350  static int parse_subst_cmd(sed_cmd_t *se Line 364  static int parse_subst_cmd(sed_cmd_t *se
364   switch (substr[idx]) {   switch (substr[idx]) {
365   /* Replace all occurrences */   /* Replace all occurrences */
366   case 'g':   case 'g':
367   if (match[0] != '^') sed_cmd->which_match = 0;   if (match[0] != '^')
368     sed_cmd->which_match = 0;
369   break;   break;
370   /* Print pattern space */   /* Print pattern space */
371   case 'p':   case 'p':
# Line 360  static int parse_subst_cmd(sed_cmd_t *se Line 375  static int parse_subst_cmd(sed_cmd_t *se
375   case 'w':   case 'w':
376   {   {
377   char *temp;   char *temp;
378   idx += parse_file_cmd(sed_cmd, substr+idx, &temp);   idx += parse_file_cmd(/*sed_cmd,*/ substr+idx, &temp);
   
379   break;   break;
380   }   }
381   /* Ignore case (gnu exension) */   /* Ignore case (gnu exension) */
# Line 395  out: Line 409  out:
409  /*  /*
410   *  Process the commands arguments   *  Process the commands arguments
411   */   */
412  static char *parse_cmd_args(sed_cmd_t *sed_cmd, char *cmdstr)  static const char *parse_cmd_args(sed_cmd_t *sed_cmd, const char *cmdstr)
413  {  {
414   /* handle (s)ubstitution command */   /* handle (s)ubstitution command */
415   if (sed_cmd->cmd == 's')   if (sed_cmd->cmd == 's')
# Line 415  static char *parse_cmd_args(sed_cmd_t *s Line 429  static char *parse_cmd_args(sed_cmd_t *s
429   break;   break;
430   }   }
431   sed_cmd->string = xstrdup(cmdstr);   sed_cmd->string = xstrdup(cmdstr);
432   parse_escapes(sed_cmd->string, sed_cmd->string, strlen(cmdstr), 0, 0);   /* "\anychar" -> "anychar" */
433     parse_escapes(sed_cmd->string, sed_cmd->string, strlen(cmdstr), '\0', '\0');
434   cmdstr += strlen(cmdstr);   cmdstr += strlen(cmdstr);
435   /* handle file cmds: (r)ead */   /* handle file cmds: (r)ead */
436   } else if (strchr("rw", sed_cmd->cmd)) {   } else if (strchr("rw", sed_cmd->cmd)) {
437   if (sed_cmd->end_line || sed_cmd->end_match)   if (sed_cmd->end_line || sed_cmd->end_match)
438   bb_error_msg_and_die("command only uses one address");   bb_error_msg_and_die("command only uses one address");
439   cmdstr += parse_file_cmd(sed_cmd, cmdstr, &sed_cmd->string);   cmdstr += parse_file_cmd(/*sed_cmd,*/ cmdstr, &sed_cmd->string);
440   if (sed_cmd->cmd == 'w')   if (sed_cmd->cmd == 'w') {
441   sed_cmd->file = xfopen(sed_cmd->string, "w");   sed_cmd->sw_file = xfopen_for_write(sed_cmd->string);
442     sed_cmd->sw_last_char = '\n';
443     }
444   /* handle branch commands */   /* handle branch commands */
445   } else if (strchr(":btT", sed_cmd->cmd)) {   } else if (strchr(":btT", sed_cmd->cmd)) {
446   int length;   int length;
# Line 467  static char *parse_cmd_args(sed_cmd_t *s Line 484  static char *parse_cmd_args(sed_cmd_t *s
484    
485  /* Parse address+command sets, skipping comment lines. */  /* Parse address+command sets, skipping comment lines. */
486    
487  static void add_cmd(char *cmdstr)  static void add_cmd(const char *cmdstr)
488  {  {
489   sed_cmd_t *sed_cmd;   sed_cmd_t *sed_cmd;
490   int temp;   int temp;
491    
492   /* Append this line to any unfinished line from last time. */   /* Append this line to any unfinished line from last time. */
493   if (bbg.add_cmd_line) {   if (G.add_cmd_line) {
494   cmdstr = xasprintf("%s\n%s", bbg.add_cmd_line, cmdstr);   char *tp = xasprintf("%s\n%s", G.add_cmd_line, cmdstr);
495   free(bbg.add_cmd_line);   free(G.add_cmd_line);
496   bbg.add_cmd_line = cmdstr;   cmdstr = G.add_cmd_line = tp;
497   }   }
498    
499   /* If this line ends with backslash, request next line. */   /* If this line ends with backslash, request next line. */
500   temp = strlen(cmdstr);   temp = strlen(cmdstr);
501   if (temp && cmdstr[temp-1] == '\\') {   if (temp && cmdstr[--temp] == '\\') {
502   if (!bbg.add_cmd_line)   if (!G.add_cmd_line)
503   bbg.add_cmd_line = xstrdup(cmdstr);   G.add_cmd_line = xstrdup(cmdstr);
504   bbg.add_cmd_line[temp-1] = 0;   G.add_cmd_line[temp] = '\0';
505   return;   return;
506   }   }
507    
# Line 500  static void add_cmd(char *cmdstr) Line 517  static void add_cmd(char *cmdstr)
517   if (*cmdstr == '#') {   if (*cmdstr == '#') {
518   /* "#n" is the same as using -n on the command line */   /* "#n" is the same as using -n on the command line */
519   if (cmdstr[1] == 'n')   if (cmdstr[1] == 'n')
520   bbg.be_quiet++;   G.be_quiet++;
521   cmdstr = strpbrk(cmdstr, "\n\r");   cmdstr = strpbrk(cmdstr, "\n\r");
522   if (!cmdstr) break;   if (!cmdstr) break;
523   continue;   continue;
# Line 547  static void add_cmd(char *cmdstr) Line 564  static void add_cmd(char *cmdstr)
564   cmdstr = parse_cmd_args(sed_cmd, cmdstr);   cmdstr = parse_cmd_args(sed_cmd, cmdstr);
565    
566   /* Add the command to the command array */   /* Add the command to the command array */
567   bbg.sed_cmd_tail->next = sed_cmd;   G.sed_cmd_tail->next = sed_cmd;
568   bbg.sed_cmd_tail = bbg.sed_cmd_tail->next;   G.sed_cmd_tail = G.sed_cmd_tail->next;
569   }   }
570    
571   /* If we glued multiple lines together, free the memory. */   /* If we glued multiple lines together, free the memory. */
572   free(bbg.add_cmd_line);   free(G.add_cmd_line);
573   bbg.add_cmd_line = NULL;   G.add_cmd_line = NULL;
574  }  }
575    
576  /* Append to a string, reallocating memory as necessary. */  /* Append to a string, reallocating memory as necessary. */
# Line 562  static void add_cmd(char *cmdstr) Line 579  static void add_cmd(char *cmdstr)
579    
580  static void pipe_putc(char c)  static void pipe_putc(char c)
581  {  {
582   if (bbg.pipeline.idx == bbg.pipeline.len) {   if (G.pipeline.idx == G.pipeline.len) {
583   bbg.pipeline.buf = xrealloc(bbg.pipeline.buf,   G.pipeline.buf = xrealloc(G.pipeline.buf,
584   bbg.pipeline.len + PIPE_GROW);   G.pipeline.len + PIPE_GROW);
585   bbg.pipeline.len += PIPE_GROW;   G.pipeline.len += PIPE_GROW;
586   }   }
587   bbg.pipeline.buf[bbg.pipeline.idx++] = c;   G.pipeline.buf[G.pipeline.idx++] = c;
588  }  }
589    
590  static void do_subst_w_backrefs(char *line, char *replace)  static void do_subst_w_backrefs(char *line, char *replace)
# Line 580  static void do_subst_w_backrefs(char *li Line 597  static void do_subst_w_backrefs(char *li
597   if (replace[i] == '\\') {   if (replace[i] == '\\') {
598   unsigned backref = replace[++i] - '0';   unsigned backref = replace[++i] - '0';
599   if (backref <= 9) {   if (backref <= 9) {
600   /* print out the text held in bbg.regmatch[backref] */   /* print out the text held in G.regmatch[backref] */
601   if (bbg.regmatch[backref].rm_so != -1) {   if (G.regmatch[backref].rm_so != -1) {
602   j = bbg.regmatch[backref].rm_so;   j = G.regmatch[backref].rm_so;
603   while (j < bbg.regmatch[backref].rm_eo)   while (j < G.regmatch[backref].rm_eo)
604   pipe_putc(line[j++]);   pipe_putc(line[j++]);
605   }   }
606   continue;   continue;
# Line 597  static void do_subst_w_backrefs(char *li Line 614  static void do_subst_w_backrefs(char *li
614   }   }
615   /* if we find an unescaped '&' print out the whole matched text. */   /* if we find an unescaped '&' print out the whole matched text. */
616   if (replace[i] == '&') {   if (replace[i] == '&') {
617   j = bbg.regmatch[0].rm_so;   j = G.regmatch[0].rm_so;
618   while (j < bbg.regmatch[0].rm_eo)   while (j < G.regmatch[0].rm_eo)
619   pipe_putc(line[j++]);   pipe_putc(line[j++]);
620   continue;   continue;
621   }   }
# Line 611  static int do_subst_command(sed_cmd_t *s Line 628  static int do_subst_command(sed_cmd_t *s
628  {  {
629   char *oldline = *line;   char *oldline = *line;
630   int altered = 0;   int altered = 0;
631   int match_count = 0;   unsigned match_count = 0;
632   regex_t *current_regex;   regex_t *current_regex;
633    
634   /* Handle empty regex. */   /* Handle empty regex. */
635   if (sed_cmd->sub_match == NULL) {   if (sed_cmd->sub_match == NULL) {
636   current_regex = bbg.previous_regex_ptr;   current_regex = G.previous_regex_ptr;
637   if (!current_regex)   if (!current_regex)
638   bb_error_msg_and_die("no previous regexp");   bb_error_msg_and_die("no previous regexp");
639   } else   } else
640   bbg.previous_regex_ptr = current_regex = sed_cmd->sub_match;   G.previous_regex_ptr = current_regex = sed_cmd->sub_match;
641    
642   /* Find the first match */   /* Find the first match */
643   if (REG_NOMATCH == regexec(current_regex, oldline, 10, bbg.regmatch, 0))   if (REG_NOMATCH == regexec(current_regex, oldline, 10, G.regmatch, 0))
644   return 0;   return 0;
645    
646   /* Initialize temporary output buffer. */   /* Initialize temporary output buffer. */
647   bbg.pipeline.buf = xmalloc(PIPE_GROW);   G.pipeline.buf = xmalloc(PIPE_GROW);
648   bbg.pipeline.len = PIPE_GROW;   G.pipeline.len = PIPE_GROW;
649   bbg.pipeline.idx = 0;   G.pipeline.idx = 0;
650    
651   /* Now loop through, substituting for matches */   /* Now loop through, substituting for matches */
652   do {   do {
# Line 639  static int do_subst_command(sed_cmd_t *s Line 656  static int do_subst_command(sed_cmd_t *s
656     echo " a.b" | busybox sed 's [^ .]* x g'     echo " a.b" | busybox sed 's [^ .]* x g'
657     The match_count check is so not to break     The match_count check is so not to break
658     echo "hi" | busybox sed 's/^/!/g' */     echo "hi" | busybox sed 's/^/!/g' */
659   if (!bbg.regmatch[0].rm_so && !bbg.regmatch[0].rm_eo && match_count) {   if (!G.regmatch[0].rm_so && !G.regmatch[0].rm_eo && match_count) {
660   pipe_putc(*oldline++);   pipe_putc(*oldline++);
661   continue;   continue;
662   }   }
# Line 648  static int do_subst_command(sed_cmd_t *s Line 665  static int do_subst_command(sed_cmd_t *s
665    
666   /* If we aren't interested in this match, output old line to   /* If we aren't interested in this match, output old line to
667     end of match and continue */     end of match and continue */
668   if (sed_cmd->which_match && sed_cmd->which_match != match_count) {   if (sed_cmd->which_match
669   for (i = 0; i < bbg.regmatch[0].rm_eo; i++)   && (sed_cmd->which_match != match_count)
670     ) {
671     for (i = 0; i < G.regmatch[0].rm_eo; i++)
672   pipe_putc(*oldline++);   pipe_putc(*oldline++);
673   continue;   continue;
674   }   }
675    
676   /* print everything before the match */   /* print everything before the match */
677   for (i = 0; i < bbg.regmatch[0].rm_so; i++)   for (i = 0; i < G.regmatch[0].rm_so; i++)
678   pipe_putc(oldline[i]);   pipe_putc(oldline[i]);
679    
680   /* then print the substitution string */   /* then print the substitution string */
681   do_subst_w_backrefs(oldline, sed_cmd->string);   do_subst_w_backrefs(oldline, sed_cmd->string);
682    
683   /* advance past the match */   /* advance past the match */
684   oldline += bbg.regmatch[0].rm_eo;   oldline += G.regmatch[0].rm_eo;
685   /* flag that something has changed */   /* flag that something has changed */
686   altered++;   altered++;
687    
688   /* if we're not doing this globally, get out now */   /* if we're not doing this globally, get out now */
689   if (sed_cmd->which_match) break;   if (sed_cmd->which_match)
690   } while (*oldline && (regexec(current_regex, oldline, 10, bbg.regmatch, 0) != REG_NOMATCH));   break;
691     } while (*oldline && (regexec(current_regex, oldline, 10, G.regmatch, 0) != REG_NOMATCH));
692    
693   /* Copy rest of string into output pipeline */   /* Copy rest of string into output pipeline */
694    
# Line 677  static int do_subst_command(sed_cmd_t *s Line 697  static int do_subst_command(sed_cmd_t *s
697   pipe_putc(0);   pipe_putc(0);
698    
699   free(*line);   free(*line);
700   *line = bbg.pipeline.buf;   *line = G.pipeline.buf;
701   return altered;   return altered;
702  }  }
703    
# Line 686  static sed_cmd_t *branch_to(char *label) Line 706  static sed_cmd_t *branch_to(char *label)
706  {  {
707   sed_cmd_t *sed_cmd;   sed_cmd_t *sed_cmd;
708    
709   for (sed_cmd = bbg.sed_cmd_head.next; sed_cmd; sed_cmd = sed_cmd->next) {   for (sed_cmd = G.sed_cmd_head.next; sed_cmd; sed_cmd = sed_cmd->next) {
710   if (sed_cmd->cmd == ':' && sed_cmd->string && !strcmp(sed_cmd->string, label)) {   if (sed_cmd->cmd == ':' && sed_cmd->string && !strcmp(sed_cmd->string, label)) {
711   return sed_cmd;   return sed_cmd;
712   }   }
# Line 696  static sed_cmd_t *branch_to(char *label) Line 716  static sed_cmd_t *branch_to(char *label)
716    
717  static void append(char *s)  static void append(char *s)
718  {  {
719   llist_add_to_end(&bbg.append_head, xstrdup(s));   llist_add_to_end(&G.append_head, xstrdup(s));
720  }  }
721    
722  static void flush_append(void)  static void flush_append(void)
# Line 704  static void flush_append(void) Line 724  static void flush_append(void)
724   char *data;   char *data;
725    
726   /* Output appended lines. */   /* Output appended lines. */
727   while ((data = (char *)llist_pop(&bbg.append_head))) {   while ((data = (char *)llist_pop(&G.append_head))) {
728   fprintf(bbg.nonstdout, "%s\n", data);   fprintf(G.nonstdout, "%s\n", data);
729   free(data);   free(data);
730   }   }
731  }  }
732    
733  static void add_input_file(FILE *file)  static void add_input_file(FILE *file)
734  {  {
735   bbg.input_file_list = xrealloc(bbg.input_file_list,   G.input_file_list = xrealloc_vector(G.input_file_list, 2, G.input_file_count);
736   (bbg.input_file_count + 1) * sizeof(FILE *));   G.input_file_list[G.input_file_count++] = file;
  bbg.input_file_list[bbg.input_file_count++] = file;  
737  }  }
738    
739  /* Get next line of input from bbg.input_file_list, flushing append buffer and  /* Get next line of input from G.input_file_list, flushing append buffer and
740   * noting if we ran out of files without a newline on the last line we read.   * noting if we ran out of files without a newline on the last line we read.
741   */   */
742  static char *get_next_line(int *last_char)  enum {
743     NO_EOL_CHAR = 1,
744     LAST_IS_NUL = 2,
745    };
746    static char *get_next_line(char *gets_char)
747  {  {
748   char *temp = NULL;   char *temp = NULL;
749   int len, lc;   int len;
750     char gc;
751    
  lc = 0;  
752   flush_append();   flush_append();
753   while (bbg.current_input_file < bbg.input_file_count) {  
754     /* will be returned if last line in the file
755     * doesn't end with either '\n' or '\0' */
756     gc = NO_EOL_CHAR;
757     while (G.current_input_file < G.input_file_count) {
758     FILE *fp = G.input_file_list[G.current_input_file];
759   /* Read line up to a newline or NUL byte, inclusive,   /* Read line up to a newline or NUL byte, inclusive,
760   * return malloc'ed char[]. length of the chunk read   * return malloc'ed char[]. length of the chunk read
761   * is stored in len. NULL if EOF/error */   * is stored in len. NULL if EOF/error */
762   temp = bb_get_chunk_from_file(   temp = bb_get_chunk_from_file(fp, &len);
  bbg.input_file_list[bbg.current_input_file], &len);  
763   if (temp) {   if (temp) {
764   /* len > 0 here, it's ok to do temp[len-1] */   /* len > 0 here, it's ok to do temp[len-1] */
765   char c = temp[len-1];   char c = temp[len-1];
766   if (c == '\n' || c == '\0') {   if (c == '\n' || c == '\0') {
767   temp[len-1] = '\0';   temp[len-1] = '\0';
768   lc |= (unsigned char)c;   gc = c;
769   break;   if (c == '\0') {
770     int ch = fgetc(fp);
771     if (ch != EOF)
772     ungetc(ch, fp);
773     else
774     gc = LAST_IS_NUL;
775     }
776   }   }
777   /* will be returned if last line in the file   /* else we put NO_EOL_CHAR into *gets_char */
  * doesn't end with either '\n' or '\0' */  
  lc |= 0x100;  
778   break;   break;
779    
780     /* NB: I had the idea of peeking next file(s) and returning
781     * NO_EOL_CHAR only if it is the *last* non-empty
782     * input file. But there is a case where this won't work:
783     * file1: "a woo\nb woo"
784     * file2: "c no\nd no"
785     * sed -ne 's/woo/bang/p' input1 input2 => "a bang\nb bang"
786     * (note: *no* newline after "b bang"!) */
787   }   }
788   /* Close this file and advance to next one */   /* Close this file and advance to next one */
789   fclose(bbg.input_file_list[bbg.current_input_file++]);   fclose(fp);
790   /* "this is the first line from new input file" */   G.current_input_file++;
  lc |= 0x200;  
791   }   }
792   *last_char = lc;   *gets_char = gc;
793   return temp;   return temp;
794  }  }
795    
796  /* Output line of text. */  /* Output line of text. */
797  /* Note:  /* Note:
798   * The tricks with 0x200 and last_puts_char are there to emulate gnu sed.   * The tricks with NO_EOL_CHAR and last_puts_char are there to emulate gnu sed.
799   * Without them, we had this:   * Without them, we had this:
800   * echo -n thingy >z1   * echo -n thingy >z1
801   * echo -n again >z2   * echo -n again >z2
# Line 769  static char *get_next_line(int *last_cha Line 807  static char *get_next_line(int *last_cha
807   * bbox:   * bbox:
808   * 00000000  74 68 7a 6e 67 79 61 67  61 7a 6e                 |thzngyagazn|   * 00000000  74 68 7a 6e 67 79 61 67  61 7a 6e                 |thzngyagazn|
809   */   */
810    static void puts_maybe_newline(char *s, FILE *file, char *last_puts_char, char last_gets_char)
 static int puts_maybe_newline(char *s, FILE *file, int prev_last_char, int last_char)  
811  {  {
812   static char last_puts_char;   char lpc = *last_puts_char;
813    
814   /* Is this a first line from new file   /* Need to insert a '\n' between two files because first file's
815   * and old file didn't end with '\n'? */   * last line wasn't terminated? */
816   if ((last_char & 0x200) && last_puts_char != '\n') {   if (lpc != '\n' && lpc != '\0') {
817   fputc('\n', file);   fputc('\n', file);
818   last_puts_char = '\n';   lpc = '\n';
819   }   }
820   fputs(s, file);   fputs(s, file);
821   /* why 'x'? - just something which is not '\n' */  
822     /* 'x' - just something which is not '\n', '\0' or NO_EOL_CHAR */
823   if (s[0])   if (s[0])
824   last_puts_char = 'x';   lpc = 'x';
825   if (!(last_char & 0x100)) { /* had trailing '\n' or '\0'? */  
826   last_char &= 0xff;   /* had trailing '\0' and it was last char of file? */
827   fputc(last_char, file);   if (last_gets_char == LAST_IS_NUL) {
828   last_puts_char = last_char;   fputc('\0', file);
829     lpc = 'x'; /* */
830     } else
831     /* had trailing '\n' or '\0'? */
832     if (last_gets_char != NO_EOL_CHAR) {
833     fputc(last_gets_char, file);
834     lpc = last_gets_char;
835   }   }
836    
837   if (ferror(file)) {   if (ferror(file)) {
838   xfunc_error_retval = 4;  /* It's what gnu sed exits with... */   xfunc_error_retval = 4;  /* It's what gnu sed exits with... */
839   bb_error_msg_and_die(bb_msg_write_error);   bb_error_msg_and_die(bb_msg_write_error);
840   }   }
841     *last_puts_char = lpc;
  return last_char;  
842  }  }
843    
844  #define sed_puts(s, n) \  #define sed_puts(s, n) (puts_maybe_newline(s, G.nonstdout, &last_puts_char, n))
845   (prev_last_char = puts_maybe_newline(s, bbg.nonstdout, prev_last_char, n))  
846    static int beg_match(sed_cmd_t *sed_cmd, const char *pattern_space)
847    {
848     int retval = sed_cmd->beg_match && !regexec(sed_cmd->beg_match, pattern_space, 0, NULL, 0);
849     if (retval)
850     G.previous_regex_ptr = sed_cmd->beg_match;
851     return retval;
852    }
853    
854  /* Process all the lines in all the files */  /* Process all the lines in all the files */
855    
856  static void process_files(void)  static void process_files(void)
857  {  {
858   char *pattern_space, *next_line;   char *pattern_space, *next_line;
859   int linenum = 0, prev_last_char = 0;   int linenum = 0;
860   int last_char, next_last_char = 0;   char last_puts_char = '\n';
861     char last_gets_char, next_gets_char;
862   sed_cmd_t *sed_cmd;   sed_cmd_t *sed_cmd;
863   int substituted;   int substituted;
864    
865   /* Prime the pump */   /* Prime the pump */
866   next_line = get_next_line(&next_last_char);   next_line = get_next_line(&next_gets_char);
867    
868   /* go through every line in each file */   /* go through every line in each file */
869  again:   again:
870   substituted = 0;   substituted = 0;
871    
872   /* Advance to next line.  Stop if out of lines. */   /* Advance to next line.  Stop if out of lines. */
873   pattern_space = next_line;   pattern_space = next_line;
874   if (!pattern_space) return;   if (!pattern_space) return;
875   last_char = next_last_char;   last_gets_char = next_gets_char;
876    
877   /* Read one line in advance so we can act on the last line,   /* Read one line in advance so we can act on the last line,
878   * the '$' address */   * the '$' address */
879   next_line = get_next_line(&next_last_char);   next_line = get_next_line(&next_gets_char);
880   linenum++;   linenum++;
881  restart:   restart:
882   /* for every line, go through all the commands */   /* for every line, go through all the commands */
883   for (sed_cmd = bbg.sed_cmd_head.next; sed_cmd; sed_cmd = sed_cmd->next) {   for (sed_cmd = G.sed_cmd_head.next; sed_cmd; sed_cmd = sed_cmd->next) {
884   int old_matched, matched;   int old_matched, matched;
885    
886   old_matched = sed_cmd->in_match;   old_matched = sed_cmd->in_match;
# Line 844  restart: Line 895  restart:
895   /* Or did we match the start of a numerical range? */   /* Or did we match the start of a numerical range? */
896   || (sed_cmd->beg_line > 0 && (sed_cmd->beg_line == linenum))   || (sed_cmd->beg_line > 0 && (sed_cmd->beg_line == linenum))
897   /* Or does this line match our begin address regex? */   /* Or does this line match our begin address regex? */
898   || (sed_cmd->beg_match &&   || (beg_match(sed_cmd, pattern_space))
     !regexec(sed_cmd->beg_match, pattern_space, 0, NULL, 0))  
899   /* Or did we match last line of input? */   /* Or did we match last line of input? */
900   || (sed_cmd->beg_line == -1 && next_line == NULL);   || (sed_cmd->beg_line == -1 && next_line == NULL);
901    
# Line 867  restart: Line 917  restart:
917   /* or does this line matches our last address regex */   /* or does this line matches our last address regex */
918   || (sed_cmd->end_match && old_matched   || (sed_cmd->end_match && old_matched
919       && (regexec(sed_cmd->end_match,       && (regexec(sed_cmd->end_match,
920               pattern_space, 0, NULL, 0) == 0))                   pattern_space, 0, NULL, 0) == 0))
921   );   );
922   }   }
923    
924   /* Skip blocks of commands we didn't match. */   /* Skip blocks of commands we didn't match. */
925   if (sed_cmd->cmd == '{') {   if (sed_cmd->cmd == '{') {
926   if (sed_cmd->invert ? matched : !matched)   if (sed_cmd->invert ? matched : !matched) {
927   while (sed_cmd && sed_cmd->cmd != '}')   while (sed_cmd->cmd != '}') {
928   sed_cmd = sed_cmd->next;   sed_cmd = sed_cmd->next;
929   if (!sed_cmd) bb_error_msg_and_die("unterminated {");   if (!sed_cmd)
930     bb_error_msg_and_die("unterminated {");
931     }
932     }
933   continue;   continue;
934   }   }
935    
# Line 884  restart: Line 937  restart:
937   if (sed_cmd->invert ? !matched : matched) {   if (sed_cmd->invert ? !matched : matched) {
938   /* Update last used regex in case a blank substitute BRE is found */   /* Update last used regex in case a blank substitute BRE is found */
939   if (sed_cmd->beg_match) {   if (sed_cmd->beg_match) {
940   bbg.previous_regex_ptr = sed_cmd->beg_match;   G.previous_regex_ptr = sed_cmd->beg_match;
941   }   }
942    
943   /* actual sedding */   /* actual sedding */
# Line 892  restart: Line 945  restart:
945    
946   /* Print line number */   /* Print line number */
947   case '=':   case '=':
948   fprintf(bbg.nonstdout, "%d\n", linenum);   fprintf(G.nonstdout, "%d\n", linenum);
949   break;   break;
950    
951   /* Write the current pattern space up to the first newline */   /* Write the current pattern space up to the first newline */
# Line 902  restart: Line 955  restart:
955    
956   if (tmp) {   if (tmp) {
957   *tmp = '\0';   *tmp = '\0';
958   sed_puts(pattern_space, 1);   /* TODO: explain why '\n' below */
959     sed_puts(pattern_space, '\n');
960   *tmp = '\n';   *tmp = '\n';
961   break;   break;
962   }   }
# Line 911  restart: Line 965  restart:
965    
966   /* Write the current pattern space to output */   /* Write the current pattern space to output */
967   case 'p':   case 'p':
968   sed_puts(pattern_space, last_char);   /* NB: we print this _before_ the last line
969     * (of current file) is printed. Even if
970     * that line is nonterminated, we print
971     * '\n' here (gnu sed does the same) */
972     sed_puts(pattern_space, '\n');
973   break;   break;
974   /* Delete up through first newline */   /* Delete up through first newline */
975   case 'D':   case 'D':
# Line 937  restart: Line 995  restart:
995    
996   /* handle p option */   /* handle p option */
997   if (sed_cmd->sub_p)   if (sed_cmd->sub_p)
998   sed_puts(pattern_space, last_char);   sed_puts(pattern_space, last_gets_char);
999   /* handle w option */   /* handle w option */
1000   if (sed_cmd->file)   if (sed_cmd->sw_file)
1001   sed_cmd->last_char = puts_maybe_newline(   puts_maybe_newline(
1002   pattern_space, sed_cmd->file,   pattern_space, sed_cmd->sw_file,
1003   sed_cmd->last_char, last_char);   &sed_cmd->sw_last_char, last_gets_char);
1004   break;   break;
1005    
1006   /* Append line to linked list to be printed later */   /* Append line to linked list to be printed later */
# Line 952  restart: Line 1010  restart:
1010    
1011   /* Insert text before this line */   /* Insert text before this line */
1012   case 'i':   case 'i':
1013   sed_puts(sed_cmd->string, 1);   sed_puts(sed_cmd->string, '\n');
1014   break;   break;
1015    
1016   /* Cut and paste text (replace) */   /* Cut and paste text (replace) */
1017   case 'c':   case 'c':
1018   /* Only triggers on last line of a matching range. */   /* Only triggers on last line of a matching range. */
1019   if (!sed_cmd->in_match)   if (!sed_cmd->in_match)
1020   sed_puts(sed_cmd->string, 0);   sed_puts(sed_cmd->string, NO_EOL_CHAR);
1021   goto discard_line;   goto discard_line;
1022    
1023   /* Read file, append contents to output */   /* Read file, append contents to output */
# Line 967  restart: Line 1025  restart:
1025   {   {
1026   FILE *rfile;   FILE *rfile;
1027    
1028   rfile = fopen(sed_cmd->string, "r");   rfile = fopen_for_read(sed_cmd->string);
1029   if (rfile) {   if (rfile) {
1030   char *line;   char *line;
1031    
1032   while ((line = xmalloc_getline(rfile))   while ((line = xmalloc_fgetline(rfile))
1033   != NULL)   != NULL)
1034   append(line);   append(line);
1035   xprint_and_close_file(rfile);   xprint_and_close_file(rfile);
# Line 982  restart: Line 1040  restart:
1040    
1041   /* Write pattern space to file. */   /* Write pattern space to file. */
1042   case 'w':   case 'w':
1043   sed_cmd->last_char = puts_maybe_newline(   puts_maybe_newline(
1044   pattern_space, sed_cmd->file,   pattern_space, sed_cmd->sw_file,
1045   sed_cmd->last_char, last_char);   &sed_cmd->sw_last_char, last_gets_char);
1046   break;   break;
1047    
1048   /* Read next line from input */   /* Read next line from input */
1049   case 'n':   case 'n':
1050   if (!bbg.be_quiet)   if (!G.be_quiet)
1051   sed_puts(pattern_space, last_char);   sed_puts(pattern_space, last_gets_char);
1052   if (next_line) {   if (next_line) {
1053   free(pattern_space);   free(pattern_space);
1054   pattern_space = next_line;   pattern_space = next_line;
1055   last_char = next_last_char;   last_gets_char = next_gets_char;
1056   next_line = get_next_line(&next_last_char);   next_line = get_next_line(&next_gets_char);
1057     substituted = 0;
1058   linenum++;   linenum++;
1059   break;   break;
1060   }   }
# Line 1024  restart: Line 1083  restart:
1083   pattern_space = realloc(pattern_space, len + strlen(next_line) + 2);   pattern_space = realloc(pattern_space, len + strlen(next_line) + 2);
1084   pattern_space[len] = '\n';   pattern_space[len] = '\n';
1085   strcpy(pattern_space + len+1, next_line);   strcpy(pattern_space + len+1, next_line);
1086   last_char = next_last_char;   last_gets_char = next_gets_char;
1087   next_line = get_next_line(&next_last_char);   next_line = get_next_line(&next_gets_char);
1088   linenum++;   linenum++;
1089   break;   break;
1090   }   }
# Line 1062  restart: Line 1121  restart:
1121   }   }
1122   case 'g': /* Replace pattern space with hold space */   case 'g': /* Replace pattern space with hold space */
1123   free(pattern_space);   free(pattern_space);
1124   pattern_space = xstrdup(bbg.hold_space ? bbg.hold_space : "");   pattern_space = xstrdup(G.hold_space ? G.hold_space : "");
1125   break;   break;
1126   case 'G': /* Append newline and hold space to pattern space */   case 'G': /* Append newline and hold space to pattern space */
1127   {   {
# Line 1071  restart: Line 1130  restart:
1130    
1131   if (pattern_space)   if (pattern_space)
1132   pattern_space_size += strlen(pattern_space);   pattern_space_size += strlen(pattern_space);
1133   if (bbg.hold_space)   if (G.hold_space)
1134   hold_space_size = strlen(bbg.hold_space);   hold_space_size = strlen(G.hold_space);
1135   pattern_space = xrealloc(pattern_space,   pattern_space = xrealloc(pattern_space,
1136   pattern_space_size + hold_space_size);   pattern_space_size + hold_space_size);
1137   if (pattern_space_size == 2)   if (pattern_space_size == 2)
1138   pattern_space[0] = 0;   pattern_space[0] = 0;
1139   strcat(pattern_space, "\n");   strcat(pattern_space, "\n");
1140   if (bbg.hold_space)   if (G.hold_space)
1141   strcat(pattern_space, bbg.hold_space);   strcat(pattern_space, G.hold_space);
1142   last_char = '\n';   last_gets_char = '\n';
1143    
1144   break;   break;
1145   }   }
1146   case 'h': /* Replace hold space with pattern space */   case 'h': /* Replace hold space with pattern space */
1147   free(bbg.hold_space);   free(G.hold_space);
1148   bbg.hold_space = xstrdup(pattern_space);   G.hold_space = xstrdup(pattern_space);
1149   break;   break;
1150   case 'H': /* Append newline and pattern space to hold space */   case 'H': /* Append newline and pattern space to hold space */
1151   {   {
1152   int hold_space_size = 2;   int hold_space_size = 2;
1153   int pattern_space_size = 0;   int pattern_space_size = 0;
1154    
1155   if (bbg.hold_space)   if (G.hold_space)
1156   hold_space_size += strlen(bbg.hold_space);   hold_space_size += strlen(G.hold_space);
1157   if (pattern_space)   if (pattern_space)
1158   pattern_space_size = strlen(pattern_space);   pattern_space_size = strlen(pattern_space);
1159   bbg.hold_space = xrealloc(bbg.hold_space,   G.hold_space = xrealloc(G.hold_space,
1160   hold_space_size + pattern_space_size);   hold_space_size + pattern_space_size);
1161    
1162   if (hold_space_size == 2)   if (hold_space_size == 2)
1163   *bbg.hold_space = 0;   *G.hold_space = 0;
1164   strcat(bbg.hold_space, "\n");   strcat(G.hold_space, "\n");
1165   if (pattern_space)   if (pattern_space)
1166   strcat(bbg.hold_space, pattern_space);   strcat(G.hold_space, pattern_space);
1167    
1168   break;   break;
1169   }   }
1170   case 'x': /* Exchange hold and pattern space */   case 'x': /* Exchange hold and pattern space */
1171   {   {
1172   char *tmp = pattern_space;   char *tmp = pattern_space;
1173   pattern_space = bbg.hold_space ? : xzalloc(1);   pattern_space = G.hold_space ? : xzalloc(1);
1174   last_char = '\n';   last_gets_char = '\n';
1175   bbg.hold_space = tmp;   G.hold_space = tmp;
1176   break;   break;
1177   }   }
1178   }   }
# Line 1123  restart: Line 1182  restart:
1182   /*   /*
1183   * exit point from sedding...   * exit point from sedding...
1184   */   */
1185  discard_commands:   discard_commands:
1186   /* we will print the line unless we were told to be quiet ('-n')   /* we will print the line unless we were told to be quiet ('-n')
1187     or if the line was suppressed (ala 'd'elete) */     or if the line was suppressed (ala 'd'elete) */
1188   if (!bbg.be_quiet) sed_puts(pattern_space, last_char);   if (!G.be_quiet)
1189     sed_puts(pattern_space, last_gets_char);
1190    
1191   /* Delete and such jump here. */   /* Delete and such jump here. */
1192  discard_line:   discard_line:
1193   flush_append();   flush_append();
1194   free(pattern_space);   free(pattern_space);
1195    
# Line 1137  discard_line: Line 1197  discard_line:
1197  }  }
1198    
1199  /* It is possible to have a command line argument with embedded  /* It is possible to have a command line argument with embedded
1200     newlines.  This counts as multiple command lines. */   * newlines.  This counts as multiple command lines.
1201     * However, newline can be escaped: 's/e/z\<newline>z/'
1202     * We check for this.
1203     */
1204    
1205  static void add_cmd_block(char *cmdstr)  static void add_cmd_block(char *cmdstr)
1206  {  {
1207   int go = 1;   char *sv, *eol;
  char *temp = xstrdup(cmdstr), *temp2 = temp;  
   
  while (go) {  
  int len = strcspn(temp2, "\n");  
  if (!temp2[len]) go = 0;  
  else temp2[len] = 0;  
  add_cmd(temp2);  
  temp2 += len+1;  
  }  
  free(temp);  
 }  
   
 static void add_cmds_link(llist_t *opt_e)  
 {  
  if (!opt_e) return;  
  add_cmds_link(opt_e->link);  
  add_cmd_block(opt_e->data);  
  free(opt_e);  
 }  
1208    
1209  static void add_files_link(llist_t *opt_f)   cmdstr = sv = xstrdup(cmdstr);
1210  {   do {
1211   char *line;   eol = strchr(cmdstr, '\n');
1212   FILE *cmdfile;   next:
1213   if (!opt_f) return;   if (eol) {
1214   add_files_link(opt_f->link);   /* Count preceding slashes */
1215   cmdfile = xfopen(opt_f->data, "r");   int slashes = 0;
1216   while ((line = xmalloc_getline(cmdfile)) != NULL) {   char *sl = eol;
1217   add_cmd(line);  
1218   free(line);   while (sl != cmdstr && *--sl == '\\')
1219   }   slashes++;
1220   xprint_and_close_file(cmdfile);   /* Odd number of preceding slashes - newline is escaped */
1221   free(opt_f);   if (slashes & 1) {
1222     overlapping_strcpy(eol - 1, eol);
1223     eol = strchr(eol, '\n');
1224     goto next;
1225     }
1226     *eol = '\0';
1227     }
1228     add_cmd(cmdstr);
1229     cmdstr = eol + 1;
1230     } while (eol);
1231     free(sv);
1232  }  }
1233    
1234  int sed_main(int argc, char **argv)  int sed_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1235    int sed_main(int argc UNUSED_PARAM, char **argv)
1236  {  {
1237   enum {   enum {
1238   OPT_in_place = 1 << 0,   OPT_in_place = 1 << 0,
# Line 1186  int sed_main(int argc, char **argv) Line 1241  int sed_main(int argc, char **argv)
1241   llist_t *opt_e, *opt_f;   llist_t *opt_e, *opt_f;
1242   int status = EXIT_SUCCESS;   int status = EXIT_SUCCESS;
1243    
1244   bbg.sed_cmd_tail = &bbg.sed_cmd_head;   INIT_G();
1245    
1246   /* destroy command strings on exit */   /* destroy command strings on exit */
1247   if (ENABLE_FEATURE_CLEAN_UP) atexit(sed_free_and_close_stuff);   if (ENABLE_FEATURE_CLEAN_UP) atexit(sed_free_and_close_stuff);
1248    
1249   /* Lie to autoconf when it starts asking stupid questions. */   /* Lie to autoconf when it starts asking stupid questions. */
1250   if (argc == 2 && !strcmp(argv[1], "--version")) {   if (argv[1] && !strcmp(argv[1], "--version")) {
1251   puts("This is not GNU sed version 4.0");   puts("This is not GNU sed version 4.0");
1252   return 0;   return 0;
1253   }   }
# Line 1201  int sed_main(int argc, char **argv) Line 1256  int sed_main(int argc, char **argv)
1256   opt_e = opt_f = NULL;   opt_e = opt_f = NULL;
1257   opt_complementary = "e::f::" /* can occur multiple times */   opt_complementary = "e::f::" /* can occur multiple times */
1258                      "nn"; /* count -n */                      "nn"; /* count -n */
1259   opt = getopt32(argc, argv, "irne:f:", &opt_e, &opt_f,   opt = getopt32(argv, "irne:f:", &opt_e, &opt_f,
1260      &bbg.be_quiet); /* counter for -n */      &G.be_quiet); /* counter for -n */
1261   argc -= optind;   //argc -= optind;
1262   argv += optind;   argv += optind;
1263   if (opt & OPT_in_place) { // -i   if (opt & OPT_in_place) { // -i
1264   atexit(cleanup_outname);   atexit(cleanup_outname);
1265   }   }
1266   if (opt & 0x2) bbg.regex_type |= REG_EXTENDED; // -r   if (opt & 0x2) G.regex_type |= REG_EXTENDED; // -r
1267   //if (opt & 0x4) bbg.be_quiet++; // -n   //if (opt & 0x4) G.be_quiet++; // -n
1268   if (opt & 0x8) { // -e   while (opt_e) { // -e
1269   /* getopt32 reverses order of arguments, handle it */   add_cmd_block(llist_pop(&opt_e));
1270   add_cmds_link(opt_e);   }
1271   }   while (opt_f) { // -f
1272   if (opt & 0x10) { // -f   char *line;
1273   /* getopt32 reverses order of arguments, handle it */   FILE *cmdfile;
1274   add_files_link(opt_f);   cmdfile = xfopen_for_read(llist_pop(&opt_f));
1275     while ((line = xmalloc_fgetline(cmdfile)) != NULL) {
1276     add_cmd(line);
1277     free(line);
1278     }
1279     fclose(cmdfile);
1280   }   }
1281   /* if we didn't get a pattern from -e or -f, use argv[0] */   /* if we didn't get a pattern from -e or -f, use argv[0] */
1282   if (!(opt & 0x18)) {   if (!(opt & 0x18)) {
1283   if (!argc)   if (!*argv)
1284   bb_show_usage();   bb_show_usage();
1285   add_cmd_block(*argv++);   add_cmd_block(*argv++);
  argc--;  
1286   }   }
1287   /* Flush any unfinished commands. */   /* Flush any unfinished commands. */
1288   add_cmd("");   add_cmd("");
1289    
1290   /* By default, we write to stdout */   /* By default, we write to stdout */
1291   bbg.nonstdout = stdout;   G.nonstdout = stdout;
1292    
1293   /* argv[0..(argc-1)] should be names of file to process. If no   /* argv[0..(argc-1)] should be names of file to process. If no
1294   * files were specified or '-' was specified, take input from stdin.   * files were specified or '-' was specified, take input from stdin.
# Line 1243  int sed_main(int argc, char **argv) Line 1302  int sed_main(int argc, char **argv)
1302   int i;   int i;
1303   FILE *file;   FILE *file;
1304    
1305   for (i = 0; i < argc; i++) {   for (i = 0; argv[i]; i++) {
1306   struct stat statbuf;   struct stat statbuf;
1307   int nonstdoutfd;   int nonstdoutfd;
1308    
# Line 1262  int sed_main(int argc, char **argv) Line 1321  int sed_main(int argc, char **argv)
1321   continue;   continue;
1322   }   }
1323    
1324   bbg.outname = xasprintf("%sXXXXXX", argv[i]);   G.outname = xasprintf("%sXXXXXX", argv[i]);
1325   nonstdoutfd = mkstemp(bbg.outname);   nonstdoutfd = mkstemp(G.outname);
1326   if (-1 == nonstdoutfd)   if (-1 == nonstdoutfd)
1327   bb_error_msg_and_die("no temp file");   bb_perror_msg_and_die("cannot create temp file %s", G.outname);
1328   bbg.nonstdout = fdopen(nonstdoutfd, "w");   G.nonstdout = fdopen(nonstdoutfd, "w");
1329    
1330   /* Set permissions of output file */   /* Set permissions of output file */
1331    
# Line 1274  int sed_main(int argc, char **argv) Line 1333  int sed_main(int argc, char **argv)
1333   fchmod(nonstdoutfd, statbuf.st_mode);   fchmod(nonstdoutfd, statbuf.st_mode);
1334   add_input_file(file);   add_input_file(file);
1335   process_files();   process_files();
1336   fclose(bbg.nonstdout);   fclose(G.nonstdout);
1337    
1338   bbg.nonstdout = stdout;   G.nonstdout = stdout;
1339   /* unlink(argv[i]); */   /* unlink(argv[i]); */
1340   // FIXME: error check / message?   xrename(G.outname, argv[i]);
1341   rename(bbg.outname, argv[i]);   free(G.outname);
1342   free(bbg.outname);   G.outname = NULL;
  bbg.outname = 0;  
1343   }   }
1344   if (bbg.input_file_count > bbg.current_input_file)   if (G.input_file_count > G.current_input_file)
1345   process_files();   process_files();
1346   }   }
1347    

Legend:
Removed from v.532  
changed lines
  Added in v.816