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 992 by niro, Sun May 30 11:43:19 2010 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;
360   }   }
361   /* Skip spaces */   /* Skip spaces */
362   if (isspace(substr[idx])) continue;   if (isspace(substr[idx]))
363     continue;
364    
365   switch (substr[idx]) {   switch (substr[idx]) {
366   /* Replace all occurrences */   /* Replace all occurrences */
367   case 'g':   case 'g':
368   if (match[0] != '^') sed_cmd->which_match = 0;   if (match[0] != '^')
369     sed_cmd->which_match = 0;
370   break;   break;
371   /* Print pattern space */   /* Print pattern space */
372   case 'p':   case 'p':
# Line 360  static int parse_subst_cmd(sed_cmd_t *se Line 376  static int parse_subst_cmd(sed_cmd_t *se
376   case 'w':   case 'w':
377   {   {
378   char *temp;   char *temp;
379   idx += parse_file_cmd(sed_cmd, substr+idx, &temp);   idx += parse_file_cmd(/*sed_cmd,*/ substr+idx, &temp);
   
380   break;   break;
381   }   }
382   /* Ignore case (gnu exension) */   /* Ignore case (gnu exension) */
# Line 395  out: Line 410  out:
410  /*  /*
411   *  Process the commands arguments   *  Process the commands arguments
412   */   */
413  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)
414  {  {
415   /* handle (s)ubstitution command */   /* handle (s)ubstitution command */
416   if (sed_cmd->cmd == 's')   if (sed_cmd->cmd == 's')
# Line 403  static char *parse_cmd_args(sed_cmd_t *s Line 418  static char *parse_cmd_args(sed_cmd_t *s
418   /* handle edit cmds: (a)ppend, (i)nsert, and (c)hange */   /* handle edit cmds: (a)ppend, (i)nsert, and (c)hange */
419   else if (strchr("aic", sed_cmd->cmd)) {   else if (strchr("aic", sed_cmd->cmd)) {
420   if ((sed_cmd->end_line || sed_cmd->end_match) && sed_cmd->cmd != 'c')   if ((sed_cmd->end_line || sed_cmd->end_match) && sed_cmd->cmd != 'c')
421   bb_error_msg_and_die   bb_error_msg_and_die("only a beginning address can be specified for edit commands");
  ("only a beginning address can be specified for edit commands");  
422   for (;;) {   for (;;) {
423   if (*cmdstr == '\n' || *cmdstr == '\\') {   if (*cmdstr == '\n' || *cmdstr == '\\') {
424   cmdstr++;   cmdstr++;
425   break;   break;
426   } else if (isspace(*cmdstr))   }
427   cmdstr++;   if (!isspace(*cmdstr))
  else  
428   break;   break;
429     cmdstr++;
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;   unsigned len, n;
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 unescaped backslash, request next line. */
500   temp = strlen(cmdstr);   n = len = strlen(cmdstr);
501   if (temp && cmdstr[temp-1] == '\\') {   while (n && cmdstr[n-1] == '\\')
502   if (!bbg.add_cmd_line)   n--;
503   bbg.add_cmd_line = xstrdup(cmdstr);   if ((len - n) & 1) { /* if odd number of trailing backslashes */
504   bbg.add_cmd_line[temp-1] = 0;   if (!G.add_cmd_line)
505     G.add_cmd_line = xstrdup(cmdstr);
506     G.add_cmd_line[len-1] = '\0';
507   return;   return;
508   }   }
509    
# Line 500  static void add_cmd(char *cmdstr) Line 519  static void add_cmd(char *cmdstr)
519   if (*cmdstr == '#') {   if (*cmdstr == '#') {
520   /* "#n" is the same as using -n on the command line */   /* "#n" is the same as using -n on the command line */
521   if (cmdstr[1] == 'n')   if (cmdstr[1] == 'n')
522   bbg.be_quiet++;   G.be_quiet++;
523   cmdstr = strpbrk(cmdstr, "\n\r");   cmdstr = strpbrk(cmdstr, "\n\r");
524   if (!cmdstr) break;   if (!cmdstr) break;
525   continue;   continue;
# Line 547  static void add_cmd(char *cmdstr) Line 566  static void add_cmd(char *cmdstr)
566   cmdstr = parse_cmd_args(sed_cmd, cmdstr);   cmdstr = parse_cmd_args(sed_cmd, cmdstr);
567    
568   /* Add the command to the command array */   /* Add the command to the command array */
569   bbg.sed_cmd_tail->next = sed_cmd;   G.sed_cmd_tail->next = sed_cmd;
570   bbg.sed_cmd_tail = bbg.sed_cmd_tail->next;   G.sed_cmd_tail = G.sed_cmd_tail->next;
571   }   }
572    
573   /* If we glued multiple lines together, free the memory. */   /* If we glued multiple lines together, free the memory. */
574   free(bbg.add_cmd_line);   free(G.add_cmd_line);
575   bbg.add_cmd_line = NULL;   G.add_cmd_line = NULL;
576  }  }
577    
578  /* 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 581  static void add_cmd(char *cmdstr)
581    
582  static void pipe_putc(char c)  static void pipe_putc(char c)
583  {  {
584   if (bbg.pipeline.idx == bbg.pipeline.len) {   if (G.pipeline.idx == G.pipeline.len) {
585   bbg.pipeline.buf = xrealloc(bbg.pipeline.buf,   G.pipeline.buf = xrealloc(G.pipeline.buf,
586   bbg.pipeline.len + PIPE_GROW);   G.pipeline.len + PIPE_GROW);
587   bbg.pipeline.len += PIPE_GROW;   G.pipeline.len += PIPE_GROW;
588   }   }
589   bbg.pipeline.buf[bbg.pipeline.idx++] = c;   G.pipeline.buf[G.pipeline.idx++] = c;
590  }  }
591    
592  static void do_subst_w_backrefs(char *line, char *replace)  static void do_subst_w_backrefs(char *line, char *replace)
593  {  {
594   int i,j;   int i, j;
595    
596   /* go through the replacement string */   /* go through the replacement string */
597   for (i = 0; replace[i]; i++) {   for (i = 0; replace[i]; i++) {
# Line 580  static void do_subst_w_backrefs(char *li Line 599  static void do_subst_w_backrefs(char *li
599   if (replace[i] == '\\') {   if (replace[i] == '\\') {
600   unsigned backref = replace[++i] - '0';   unsigned backref = replace[++i] - '0';
601   if (backref <= 9) {   if (backref <= 9) {
602   /* print out the text held in bbg.regmatch[backref] */   /* print out the text held in G.regmatch[backref] */
603   if (bbg.regmatch[backref].rm_so != -1) {   if (G.regmatch[backref].rm_so != -1) {
604   j = bbg.regmatch[backref].rm_so;   j = G.regmatch[backref].rm_so;
605   while (j < bbg.regmatch[backref].rm_eo)   while (j < G.regmatch[backref].rm_eo)
606   pipe_putc(line[j++]);   pipe_putc(line[j++]);
607   }   }
608   continue;   continue;
# Line 597  static void do_subst_w_backrefs(char *li Line 616  static void do_subst_w_backrefs(char *li
616   }   }
617   /* if we find an unescaped '&' print out the whole matched text. */   /* if we find an unescaped '&' print out the whole matched text. */
618   if (replace[i] == '&') {   if (replace[i] == '&') {
619   j = bbg.regmatch[0].rm_so;   j = G.regmatch[0].rm_so;
620   while (j < bbg.regmatch[0].rm_eo)   while (j < G.regmatch[0].rm_eo)
621   pipe_putc(line[j++]);   pipe_putc(line[j++]);
622   continue;   continue;
623   }   }
# Line 607  static void do_subst_w_backrefs(char *li Line 626  static void do_subst_w_backrefs(char *li
626   }   }
627  }  }
628    
629  static int do_subst_command(sed_cmd_t *sed_cmd, char **line)  static int do_subst_command(sed_cmd_t *sed_cmd, char **line_p)
630  {  {
631   char *oldline = *line;   char *line = *line_p;
632   int altered = 0;   int altered = 0;
633   int match_count = 0;   unsigned match_count = 0;
634   regex_t *current_regex;   regex_t *current_regex;
635    
636     current_regex = sed_cmd->sub_match;
637   /* Handle empty regex. */   /* Handle empty regex. */
638   if (sed_cmd->sub_match == NULL) {   if (!current_regex) {
639   current_regex = bbg.previous_regex_ptr;   current_regex = G.previous_regex_ptr;
640   if (!current_regex)   if (!current_regex)
641   bb_error_msg_and_die("no previous regexp");   bb_error_msg_and_die("no previous regexp");
642   } else   }
643   bbg.previous_regex_ptr = current_regex = sed_cmd->sub_match;   G.previous_regex_ptr = current_regex;
644    
645   /* Find the first match */   /* Find the first match */
646   if (REG_NOMATCH == regexec(current_regex, oldline, 10, bbg.regmatch, 0))   if (REG_NOMATCH == regexec(current_regex, line, 10, G.regmatch, 0))
647   return 0;   return 0;
648    
649   /* Initialize temporary output buffer. */   /* Initialize temporary output buffer. */
650   bbg.pipeline.buf = xmalloc(PIPE_GROW);   G.pipeline.buf = xmalloc(PIPE_GROW);
651   bbg.pipeline.len = PIPE_GROW;   G.pipeline.len = PIPE_GROW;
652   bbg.pipeline.idx = 0;   G.pipeline.idx = 0;
653    
654   /* Now loop through, substituting for matches */   /* Now loop through, substituting for matches */
655   do {   do {
# Line 639  static int do_subst_command(sed_cmd_t *s Line 659  static int do_subst_command(sed_cmd_t *s
659     echo " a.b" | busybox sed 's [^ .]* x g'     echo " a.b" | busybox sed 's [^ .]* x g'
660     The match_count check is so not to break     The match_count check is so not to break
661     echo "hi" | busybox sed 's/^/!/g' */     echo "hi" | busybox sed 's/^/!/g' */
662   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) {
663   pipe_putc(*oldline++);   pipe_putc(*line++);
664   continue;   continue;
665   }   }
666    
# Line 648  static int do_subst_command(sed_cmd_t *s Line 668  static int do_subst_command(sed_cmd_t *s
668    
669   /* If we aren't interested in this match, output old line to   /* If we aren't interested in this match, output old line to
670     end of match and continue */     end of match and continue */
671   if (sed_cmd->which_match && sed_cmd->which_match != match_count) {   if (sed_cmd->which_match
672   for (i = 0; i < bbg.regmatch[0].rm_eo; i++)   && (sed_cmd->which_match != match_count)
673   pipe_putc(*oldline++);   ) {
674     for (i = 0; i < G.regmatch[0].rm_eo; i++)
675     pipe_putc(*line++);
676   continue;   continue;
677   }   }
678    
679   /* print everything before the match */   /* print everything before the match */
680   for (i = 0; i < bbg.regmatch[0].rm_so; i++)   for (i = 0; i < G.regmatch[0].rm_so; i++)
681   pipe_putc(oldline[i]);   pipe_putc(line[i]);
682    
683   /* then print the substitution string */   /* then print the substitution string */
684   do_subst_w_backrefs(oldline, sed_cmd->string);   do_subst_w_backrefs(line, sed_cmd->string);
685    
686   /* advance past the match */   /* advance past the match */
687   oldline += bbg.regmatch[0].rm_eo;   line += G.regmatch[0].rm_eo;
688   /* flag that something has changed */   /* flag that something has changed */
689   altered++;   altered++;
690    
691   /* if we're not doing this globally, get out now */   /* if we're not doing this globally, get out now */
692   if (sed_cmd->which_match) break;   if (sed_cmd->which_match)
693   } while (*oldline && (regexec(current_regex, oldline, 10, bbg.regmatch, 0) != REG_NOMATCH));   break;
694    
695   /* Copy rest of string into output pipeline */  //maybe (G.regmatch[0].rm_eo ? REG_NOTBOL : 0) instead of unconditional REG_NOTBOL?
696     } while (*line && regexec(current_regex, line, 10, G.regmatch, REG_NOTBOL) != REG_NOMATCH);
697    
698   while (*oldline)   /* Copy rest of string into output pipeline */
699   pipe_putc(*oldline++);   while (1) {
700   pipe_putc(0);   char c = *line++;
701     pipe_putc(c);
702     if (c == '\0')
703     break;
704     }
705    
706   free(*line);   free(*line_p);
707   *line = bbg.pipeline.buf;   *line_p = G.pipeline.buf;
708   return altered;   return altered;
709  }  }
710    
# Line 686  static sed_cmd_t *branch_to(char *label) Line 713  static sed_cmd_t *branch_to(char *label)
713  {  {
714   sed_cmd_t *sed_cmd;   sed_cmd_t *sed_cmd;
715    
716   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) {
717   if (sed_cmd->cmd == ':' && sed_cmd->string && !strcmp(sed_cmd->string, label)) {   if (sed_cmd->cmd == ':' && sed_cmd->string && !strcmp(sed_cmd->string, label)) {
718   return sed_cmd;   return sed_cmd;
719   }   }
# Line 696  static sed_cmd_t *branch_to(char *label) Line 723  static sed_cmd_t *branch_to(char *label)
723    
724  static void append(char *s)  static void append(char *s)
725  {  {
726   llist_add_to_end(&bbg.append_head, xstrdup(s));   llist_add_to_end(&G.append_head, xstrdup(s));
727  }  }
728    
729  static void flush_append(void)  static void flush_append(void)
# Line 704  static void flush_append(void) Line 731  static void flush_append(void)
731   char *data;   char *data;
732    
733   /* Output appended lines. */   /* Output appended lines. */
734   while ((data = (char *)llist_pop(&bbg.append_head))) {   while ((data = (char *)llist_pop(&G.append_head))) {
735   fprintf(bbg.nonstdout, "%s\n", data);   fprintf(G.nonstdout, "%s\n", data);
736   free(data);   free(data);
737   }   }
738  }  }
739    
740  static void add_input_file(FILE *file)  static void add_input_file(FILE *file)
741  {  {
742   bbg.input_file_list = xrealloc(bbg.input_file_list,   G.input_file_list = xrealloc_vector(G.input_file_list, 2, G.input_file_count);
743   (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;  
744  }  }
745    
746  /* 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
747   * 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.
748   */   */
749  static char *get_next_line(int *last_char)  enum {
750     NO_EOL_CHAR = 1,
751     LAST_IS_NUL = 2,
752    };
753    static char *get_next_line(char *gets_char)
754  {  {
755   char *temp = NULL;   char *temp = NULL;
756   int len, lc;   int len;
757     char gc;
758    
  lc = 0;  
759   flush_append();   flush_append();
760   while (bbg.current_input_file < bbg.input_file_count) {  
761     /* will be returned if last line in the file
762     * doesn't end with either '\n' or '\0' */
763     gc = NO_EOL_CHAR;
764     while (G.current_input_file < G.input_file_count) {
765     FILE *fp = G.input_file_list[G.current_input_file];
766   /* Read line up to a newline or NUL byte, inclusive,   /* Read line up to a newline or NUL byte, inclusive,
767   * return malloc'ed char[]. length of the chunk read   * return malloc'ed char[]. length of the chunk read
768   * is stored in len. NULL if EOF/error */   * is stored in len. NULL if EOF/error */
769   temp = bb_get_chunk_from_file(   temp = bb_get_chunk_from_file(fp, &len);
  bbg.input_file_list[bbg.current_input_file], &len);  
770   if (temp) {   if (temp) {
771   /* len > 0 here, it's ok to do temp[len-1] */   /* len > 0 here, it's ok to do temp[len-1] */
772   char c = temp[len-1];   char c = temp[len-1];
773   if (c == '\n' || c == '\0') {   if (c == '\n' || c == '\0') {
774   temp[len-1] = '\0';   temp[len-1] = '\0';
775   lc |= (unsigned char)c;   gc = c;
776   break;   if (c == '\0') {
777     int ch = fgetc(fp);
778     if (ch != EOF)
779     ungetc(ch, fp);
780     else
781     gc = LAST_IS_NUL;
782     }
783   }   }
784   /* 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;  
785   break;   break;
786    
787     /* NB: I had the idea of peeking next file(s) and returning
788     * NO_EOL_CHAR only if it is the *last* non-empty
789     * input file. But there is a case where this won't work:
790     * file1: "a woo\nb woo"
791     * file2: "c no\nd no"
792     * sed -ne 's/woo/bang/p' input1 input2 => "a bang\nb bang"
793     * (note: *no* newline after "b bang"!) */
794   }   }
795   /* Close this file and advance to next one */   /* Close this file and advance to next one */
796   fclose(bbg.input_file_list[bbg.current_input_file++]);   fclose(fp);
797   /* "this is the first line from new input file" */   G.current_input_file++;
  lc |= 0x200;  
798   }   }
799   *last_char = lc;   *gets_char = gc;
800   return temp;   return temp;
801  }  }
802    
803  /* Output line of text. */  /* Output line of text. */
804  /* Note:  /* Note:
805   * 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.
806   * Without them, we had this:   * Without them, we had this:
807   * echo -n thingy >z1   * echo -n thingy >z1
808   * echo -n again >z2   * echo -n again >z2
# Line 769  static char *get_next_line(int *last_cha Line 814  static char *get_next_line(int *last_cha
814   * bbox:   * bbox:
815   * 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|
816   */   */
817    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)  
818  {  {
819   static char last_puts_char;   char lpc = *last_puts_char;
820    
821   /* Is this a first line from new file   /* Need to insert a '\n' between two files because first file's
822   * and old file didn't end with '\n'? */   * last line wasn't terminated? */
823   if ((last_char & 0x200) && last_puts_char != '\n') {   if (lpc != '\n' && lpc != '\0') {
824   fputc('\n', file);   fputc('\n', file);
825   last_puts_char = '\n';   lpc = '\n';
826   }   }
827   fputs(s, file);   fputs(s, file);
828   /* why 'x'? - just something which is not '\n' */  
829     /* 'x' - just something which is not '\n', '\0' or NO_EOL_CHAR */
830   if (s[0])   if (s[0])
831   last_puts_char = 'x';   lpc = 'x';
832   if (!(last_char & 0x100)) { /* had trailing '\n' or '\0'? */  
833   last_char &= 0xff;   /* had trailing '\0' and it was last char of file? */
834   fputc(last_char, file);   if (last_gets_char == LAST_IS_NUL) {
835   last_puts_char = last_char;   fputc('\0', file);
836     lpc = 'x'; /* */
837     } else
838     /* had trailing '\n' or '\0'? */
839     if (last_gets_char != NO_EOL_CHAR) {
840     fputc(last_gets_char, file);
841     lpc = last_gets_char;
842   }   }
843    
844   if (ferror(file)) {   if (ferror(file)) {
845   xfunc_error_retval = 4;  /* It's what gnu sed exits with... */   xfunc_error_retval = 4;  /* It's what gnu sed exits with... */
846   bb_error_msg_and_die(bb_msg_write_error);   bb_error_msg_and_die(bb_msg_write_error);
847   }   }
848     *last_puts_char = lpc;
  return last_char;  
849  }  }
850    
851  #define sed_puts(s, n) \  #define sed_puts(s, n) (puts_maybe_newline(s, G.nonstdout, &last_puts_char, n))
852   (prev_last_char = puts_maybe_newline(s, bbg.nonstdout, prev_last_char, n))  
853    static int beg_match(sed_cmd_t *sed_cmd, const char *pattern_space)
854    {
855     int retval = sed_cmd->beg_match && !regexec(sed_cmd->beg_match, pattern_space, 0, NULL, 0);
856     if (retval)
857     G.previous_regex_ptr = sed_cmd->beg_match;
858     return retval;
859    }
860    
861  /* Process all the lines in all the files */  /* Process all the lines in all the files */
862    
863  static void process_files(void)  static void process_files(void)
864  {  {
865   char *pattern_space, *next_line;   char *pattern_space, *next_line;
866   int linenum = 0, prev_last_char = 0;   int linenum = 0;
867   int last_char, next_last_char = 0;   char last_puts_char = '\n';
868     char last_gets_char, next_gets_char;
869   sed_cmd_t *sed_cmd;   sed_cmd_t *sed_cmd;
870   int substituted;   int substituted;
871    
872   /* Prime the pump */   /* Prime the pump */
873   next_line = get_next_line(&next_last_char);   next_line = get_next_line(&next_gets_char);
874    
875   /* go through every line in each file */   /* Go through every line in each file */
876  again:   again:
877   substituted = 0;   substituted = 0;
878    
879   /* Advance to next line.  Stop if out of lines. */   /* Advance to next line.  Stop if out of lines. */
880   pattern_space = next_line;   pattern_space = next_line;
881   if (!pattern_space) return;   if (!pattern_space)
882   last_char = next_last_char;   return;
883     last_gets_char = next_gets_char;
884    
885   /* 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,
886   * the '$' address */   * the '$' address */
887   next_line = get_next_line(&next_last_char);   next_line = get_next_line(&next_gets_char);
888   linenum++;   linenum++;
889  restart:  
890   /* for every line, go through all the commands */   /* For every line, go through all the commands */
891   for (sed_cmd = bbg.sed_cmd_head.next; sed_cmd; sed_cmd = sed_cmd->next) {   restart:
892     for (sed_cmd = G.sed_cmd_head.next; sed_cmd; sed_cmd = sed_cmd->next) {
893   int old_matched, matched;   int old_matched, matched;
894    
895   old_matched = sed_cmd->in_match;   old_matched = sed_cmd->in_match;
# Line 842  restart: Line 902  restart:
902   || (!sed_cmd->beg_line && !sed_cmd->end_line   || (!sed_cmd->beg_line && !sed_cmd->end_line
903   && !sed_cmd->beg_match && !sed_cmd->end_match)   && !sed_cmd->beg_match && !sed_cmd->end_match)
904   /* Or did we match the start of a numerical range? */   /* Or did we match the start of a numerical range? */
905   || (sed_cmd->beg_line > 0 && (sed_cmd->beg_line == linenum))   || (sed_cmd->beg_line > 0 && (sed_cmd->beg_line <= linenum))
906   /* Or does this line match our begin address regex? */   /* Or does this line match our begin address regex? */
907   || (sed_cmd->beg_match &&   || (beg_match(sed_cmd, pattern_space))
     !regexec(sed_cmd->beg_match, pattern_space, 0, NULL, 0))  
908   /* Or did we match last line of input? */   /* Or did we match last line of input? */
909   || (sed_cmd->beg_line == -1 && next_line == NULL);   || (sed_cmd->beg_line == -1 && next_line == NULL);
910    
911   /* Snapshot the value */   /* Snapshot the value */
   
912   matched = sed_cmd->in_match;   matched = sed_cmd->in_match;
913    
914     //bb_error_msg("cmd:'%c' matched:%d beg_line:%d end_line:%d linenum:%d",
915     //sed_cmd->cmd, matched, sed_cmd->beg_line, sed_cmd->end_line, linenum);
916    
917   /* Is this line the end of the current match? */   /* Is this line the end of the current match? */
918    
919   if (matched) {   if (matched) {
920     /* once matched, "n,xxx" range is dead, disabling it */
921     if (sed_cmd->beg_line > 0)
922     sed_cmd->beg_line = -2;
923   sed_cmd->in_match = !(   sed_cmd->in_match = !(
924   /* has the ending line come, or is this a single address command? */   /* has the ending line come, or is this a single address command? */
925   (sed_cmd->end_line ?   (sed_cmd->end_line ?
# Line 867  restart: Line 931  restart:
931   /* or does this line matches our last address regex */   /* or does this line matches our last address regex */
932   || (sed_cmd->end_match && old_matched   || (sed_cmd->end_match && old_matched
933       && (regexec(sed_cmd->end_match,       && (regexec(sed_cmd->end_match,
934               pattern_space, 0, NULL, 0) == 0))                   pattern_space, 0, NULL, 0) == 0))
935   );   );
936   }   }
937    
938   /* Skip blocks of commands we didn't match. */   /* Skip blocks of commands we didn't match */
939   if (sed_cmd->cmd == '{') {   if (sed_cmd->cmd == '{') {
940   if (sed_cmd->invert ? matched : !matched)   if (sed_cmd->invert ? matched : !matched) {
941   while (sed_cmd && sed_cmd->cmd != '}')   unsigned nest_cnt = 0;
942     while (1) {
943     if (sed_cmd->cmd == '{')
944     nest_cnt++;
945     if (sed_cmd->cmd == '}') {
946     nest_cnt--;
947     if (nest_cnt == 0)
948     break;
949     }
950   sed_cmd = sed_cmd->next;   sed_cmd = sed_cmd->next;
951   if (!sed_cmd) bb_error_msg_and_die("unterminated {");   if (!sed_cmd)
952     bb_error_msg_and_die("unterminated {");
953     }
954     }
955   continue;   continue;
956   }   }
957    
958   /* Okay, so did this line match? */   /* Okay, so did this line match? */
959   if (sed_cmd->invert ? !matched : matched) {   if (sed_cmd->invert ? matched : !matched)
960   /* Update last used regex in case a blank substitute BRE is found */   continue; /* no */
  if (sed_cmd->beg_match) {  
  bbg.previous_regex_ptr = sed_cmd->beg_match;  
  }  
961    
962   /* actual sedding */   /* Update last used regex in case a blank substitute BRE is found */
963   switch (sed_cmd->cmd) {   if (sed_cmd->beg_match) {
964     G.previous_regex_ptr = sed_cmd->beg_match;
965     }
966    
967   /* Print line number */   /* actual sedding */
968   case '=':   switch (sed_cmd->cmd) {
  fprintf(bbg.nonstdout, "%d\n", linenum);  
  break;  
969    
970   /* Write the current pattern space up to the first newline */   /* Print line number */
971   case 'P':   case '=':
972   {   fprintf(G.nonstdout, "%d\n", linenum);
973   char *tmp = strchr(pattern_space, '\n');   break;
   
  if (tmp) {  
  *tmp = '\0';  
  sed_puts(pattern_space, 1);  
  *tmp = '\n';  
  break;  
  }  
  /* Fall Through */  
  }  
974    
975   /* Write the current pattern space to output */   /* Write the current pattern space up to the first newline */
976   case 'p':   case 'P':
977   sed_puts(pattern_space, last_char);   {
978     char *tmp = strchr(pattern_space, '\n');
979    
980     if (tmp) {
981     *tmp = '\0';
982     /* TODO: explain why '\n' below */
983     sed_puts(pattern_space, '\n');
984     *tmp = '\n';
985   break;   break;
  /* Delete up through first newline */  
  case 'D':  
  {  
  char *tmp = strchr(pattern_space, '\n');  
   
  if (tmp) {  
  tmp = xstrdup(tmp+1);  
  free(pattern_space);  
  pattern_space = tmp;  
  goto restart;  
  }  
986   }   }
987   /* discard this line. */   /* Fall Through */
988   case 'd':   }
  goto discard_line;  
989    
990   /* Substitute with regex */   /* Write the current pattern space to output */
991   case 's':   case 'p':
992   if (!do_subst_command(sed_cmd, &pattern_space))   /* NB: we print this _before_ the last line
993   break;   * (of current file) is printed. Even if
994   substituted |= 1;   * that line is nonterminated, we print
995     * '\n' here (gnu sed does the same) */
996   /* handle p option */   sed_puts(pattern_space, '\n');
997   if (sed_cmd->sub_p)   break;
998   sed_puts(pattern_space, last_char);   /* Delete up through first newline */
999   /* handle w option */   case 'D':
1000   if (sed_cmd->file)   {
1001   sed_cmd->last_char = puts_maybe_newline(   char *tmp = strchr(pattern_space, '\n');
  pattern_space, sed_cmd->file,  
  sed_cmd->last_char, last_char);  
  break;  
1002    
1003   /* Append line to linked list to be printed later */   if (tmp) {
1004   case 'a':   tmp = xstrdup(tmp+1);
1005   append(sed_cmd->string);   free(pattern_space);
1006     pattern_space = tmp;
1007     goto restart;
1008     }
1009     }
1010     /* discard this line. */
1011     case 'd':
1012     goto discard_line;
1013    
1014     /* Substitute with regex */
1015     case 's':
1016     if (!do_subst_command(sed_cmd, &pattern_space))
1017   break;   break;
1018     substituted |= 1;
1019    
1020   /* Insert text before this line */   /* handle p option */
1021   case 'i':   if (sed_cmd->sub_p)
1022   sed_puts(sed_cmd->string, 1);   sed_puts(pattern_space, last_gets_char);
1023   break;   /* handle w option */
1024     if (sed_cmd->sw_file)
1025     puts_maybe_newline(
1026     pattern_space, sed_cmd->sw_file,
1027     &sed_cmd->sw_last_char, last_gets_char);
1028     break;
1029    
1030   /* Cut and paste text (replace) */   /* Append line to linked list to be printed later */
1031   case 'c':   case 'a':
1032   /* Only triggers on last line of a matching range. */   append(sed_cmd->string);
1033   if (!sed_cmd->in_match)   break;
  sed_puts(sed_cmd->string, 0);  
  goto discard_line;  
1034    
1035   /* Read file, append contents to output */   /* Insert text before this line */
1036   case 'r':   case 'i':
1037   {   sed_puts(sed_cmd->string, '\n');
1038   FILE *rfile;   break;
   
  rfile = fopen(sed_cmd->string, "r");  
  if (rfile) {  
  char *line;  
   
  while ((line = xmalloc_getline(rfile))  
  != NULL)  
  append(line);  
  xprint_and_close_file(rfile);  
  }  
1039    
1040   break;   /* Cut and paste text (replace) */
1041     case 'c':
1042     /* Only triggers on last line of a matching range. */
1043     if (!sed_cmd->in_match)
1044     sed_puts(sed_cmd->string, '\n');
1045     goto discard_line;
1046    
1047     /* Read file, append contents to output */
1048     case 'r':
1049     {
1050     FILE *rfile;
1051    
1052     rfile = fopen_for_read(sed_cmd->string);
1053     if (rfile) {
1054     char *line;
1055    
1056     while ((line = xmalloc_fgetline(rfile))
1057     != NULL)
1058     append(line);
1059     xprint_and_close_file(rfile);
1060   }   }
1061    
1062   /* Write pattern space to file. */   break;
1063   case 'w':   }
1064   sed_cmd->last_char = puts_maybe_newline(  
1065   pattern_space, sed_cmd->file,   /* Write pattern space to file. */
1066   sed_cmd->last_char, last_char);   case 'w':
1067     puts_maybe_newline(
1068     pattern_space, sed_cmd->sw_file,
1069     &sed_cmd->sw_last_char, last_gets_char);
1070     break;
1071    
1072     /* Read next line from input */
1073     case 'n':
1074     if (!G.be_quiet)
1075     sed_puts(pattern_space, last_gets_char);
1076     if (next_line) {
1077     free(pattern_space);
1078     pattern_space = next_line;
1079     last_gets_char = next_gets_char;
1080     next_line = get_next_line(&next_gets_char);
1081     substituted = 0;
1082     linenum++;
1083   break;   break;
1084     }
1085     /* fall through */
1086    
1087   /* Read next line from input */   /* Quit.  End of script, end of input. */
1088   case 'n':   case 'q':
1089   if (!bbg.be_quiet)   /* Exit the outer while loop */
1090   sed_puts(pattern_space, last_char);   free(next_line);
1091   if (next_line) {   next_line = NULL;
1092   free(pattern_space);   goto discard_commands;
  pattern_space = next_line;  
  last_char = next_last_char;  
  next_line = get_next_line(&next_last_char);  
  linenum++;  
  break;  
  }  
  /* fall through */  
1093    
1094   /* Quit.  End of script, end of input. */   /* Append the next line to the current line */
1095   case 'q':   case 'N':
1096   /* Exit the outer while loop */   {
1097     int len;
1098     /* If no next line, jump to end of script and exit. */
1099     if (next_line == NULL) {
1100     /* Jump to end of script and exit */
1101   free(next_line);   free(next_line);
1102   next_line = NULL;   next_line = NULL;
1103   goto discard_commands;   goto discard_line;
1104     /* append next_line, read new next_line. */
  /* Append the next line to the current line */  
  case 'N':  
  {  
  int len;  
  /* If no next line, jump to end of script and exit. */  
  if (next_line == NULL) {  
  /* Jump to end of script and exit */  
  free(next_line);  
  next_line = NULL;  
  goto discard_line;  
  /* append next_line, read new next_line. */  
  }  
  len = strlen(pattern_space);  
  pattern_space = realloc(pattern_space, len + strlen(next_line) + 2);  
  pattern_space[len] = '\n';  
  strcpy(pattern_space + len+1, next_line);  
  last_char = next_last_char;  
  next_line = get_next_line(&next_last_char);  
  linenum++;  
  break;  
1105   }   }
1106     len = strlen(pattern_space);
1107     pattern_space = xrealloc(pattern_space, len + strlen(next_line) + 2);
1108     pattern_space[len] = '\n';
1109     strcpy(pattern_space + len+1, next_line);
1110     last_gets_char = next_gets_char;
1111     next_line = get_next_line(&next_gets_char);
1112     linenum++;
1113     break;
1114     }
1115    
1116   /* Test/branch if substitution occurred */   /* Test/branch if substitution occurred */
1117   case 't':   case 't':
1118   if (!substituted) break;   if (!substituted) break;
1119   substituted = 0;   substituted = 0;
1120   /* Fall through */   /* Fall through */
1121   /* Test/branch if substitution didn't occur */   /* Test/branch if substitution didn't occur */
1122   case 'T':   case 'T':
1123   if (substituted) break;   if (substituted) break;
1124   /* Fall through */   /* Fall through */
1125   /* Branch to label */   /* Branch to label */
1126   case 'b':   case 'b':
1127   if (!sed_cmd->string) goto discard_commands;   if (!sed_cmd->string) goto discard_commands;
1128   else sed_cmd = branch_to(sed_cmd->string);   else sed_cmd = branch_to(sed_cmd->string);
1129   break;   break;
1130   /* Transliterate characters */   /* Transliterate characters */
1131   case 'y':   case 'y':
1132   {   {
1133   int i, j;   int i, j;
1134    
1135   for (i = 0; pattern_space[i]; i++) {   for (i = 0; pattern_space[i]; i++) {
1136   for (j = 0; sed_cmd->string[j]; j += 2) {   for (j = 0; sed_cmd->string[j]; j += 2) {
1137   if (pattern_space[i] == sed_cmd->string[j]) {   if (pattern_space[i] == sed_cmd->string[j]) {
1138   pattern_space[i] = sed_cmd->string[j + 1];   pattern_space[i] = sed_cmd->string[j + 1];
1139   break;   break;
  }  
1140   }   }
1141   }   }
   
  break;  
1142   }   }
  case 'g': /* Replace pattern space with hold space */  
  free(pattern_space);  
  pattern_space = xstrdup(bbg.hold_space ? bbg.hold_space : "");  
  break;  
  case 'G': /* Append newline and hold space to pattern space */  
  {  
  int pattern_space_size = 2;  
  int hold_space_size = 0;  
   
  if (pattern_space)  
  pattern_space_size += strlen(pattern_space);  
  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 (bbg.hold_space)  
  strcat(pattern_space, bbg.hold_space);  
  last_char = '\n';  
1143    
1144   break;   break;
1145   }   }
1146   case 'h': /* Replace hold space with pattern space */   case 'g': /* Replace pattern space with hold space */
1147   free(bbg.hold_space);   free(pattern_space);
1148   bbg.hold_space = xstrdup(pattern_space);   pattern_space = xstrdup(G.hold_space ? G.hold_space : "");
1149   break;   break;
1150   case 'H': /* Append newline and pattern space to hold space */   case 'G': /* Append newline and hold space to pattern space */
1151   {   {
1152   int hold_space_size = 2;   int pattern_space_size = 2;
1153   int pattern_space_size = 0;   int hold_space_size = 0;
   
  if (bbg.hold_space)  
  hold_space_size += strlen(bbg.hold_space);  
  if (pattern_space)  
  pattern_space_size = strlen(pattern_space);  
  bbg.hold_space = xrealloc(bbg.hold_space,  
  hold_space_size + pattern_space_size);  
   
  if (hold_space_size == 2)  
  *bbg.hold_space = 0;  
  strcat(bbg.hold_space, "\n");  
  if (pattern_space)  
  strcat(bbg.hold_space, pattern_space);  
1154    
1155   break;   if (pattern_space)
1156   }   pattern_space_size += strlen(pattern_space);
1157   case 'x': /* Exchange hold and pattern space */   if (G.hold_space)
1158   {   hold_space_size = strlen(G.hold_space);
1159   char *tmp = pattern_space;   pattern_space = xrealloc(pattern_space,
1160   pattern_space = bbg.hold_space ? : xzalloc(1);   pattern_space_size + hold_space_size);
1161   last_char = '\n';   if (pattern_space_size == 2)
1162   bbg.hold_space = tmp;   pattern_space[0] = 0;
1163   break;   strcat(pattern_space, "\n");
1164   }   if (G.hold_space)
1165   }   strcat(pattern_space, G.hold_space);
1166     last_gets_char = '\n';
1167    
1168     break;
1169   }   }
1170   }   case 'h': /* Replace hold space with pattern space */
1171     free(G.hold_space);
1172     G.hold_space = xstrdup(pattern_space);
1173     break;
1174     case 'H': /* Append newline and pattern space to hold space */
1175     {
1176     int hold_space_size = 2;
1177     int pattern_space_size = 0;
1178    
1179     if (G.hold_space)
1180     hold_space_size += strlen(G.hold_space);
1181     if (pattern_space)
1182     pattern_space_size = strlen(pattern_space);
1183     G.hold_space = xrealloc(G.hold_space,
1184     hold_space_size + pattern_space_size);
1185    
1186     if (hold_space_size == 2)
1187     *G.hold_space = 0;
1188     strcat(G.hold_space, "\n");
1189     if (pattern_space)
1190     strcat(G.hold_space, pattern_space);
1191    
1192     break;
1193     }
1194     case 'x': /* Exchange hold and pattern space */
1195     {
1196     char *tmp = pattern_space;
1197     pattern_space = G.hold_space ? G.hold_space : xzalloc(1);
1198     last_gets_char = '\n';
1199     G.hold_space = tmp;
1200     break;
1201     }
1202     } /* switch */
1203     } /* for each cmd */
1204    
1205   /*   /*
1206   * exit point from sedding...   * Exit point from sedding...
1207   */   */
1208  discard_commands:   discard_commands:
1209   /* 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')
1210     or if the line was suppressed (ala 'd'elete) */     or if the line was suppressed (ala 'd'elete) */
1211   if (!bbg.be_quiet) sed_puts(pattern_space, last_char);   if (!G.be_quiet)
1212     sed_puts(pattern_space, last_gets_char);
1213    
1214   /* Delete and such jump here. */   /* Delete and such jump here. */
1215  discard_line:   discard_line:
1216   flush_append();   flush_append();
1217   free(pattern_space);   free(pattern_space);
1218    
# Line 1137  discard_line: Line 1220  discard_line:
1220  }  }
1221    
1222  /* It is possible to have a command line argument with embedded  /* It is possible to have a command line argument with embedded
1223     newlines.  This counts as multiple command lines. */   * newlines.  This counts as multiple command lines.
1224     * However, newline can be escaped: 's/e/z\<newline>z/'
1225     * We check for this.
1226     */
1227    
1228  static void add_cmd_block(char *cmdstr)  static void add_cmd_block(char *cmdstr)
1229  {  {
1230   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);  
 }  
1231    
1232  static void add_files_link(llist_t *opt_f)   cmdstr = sv = xstrdup(cmdstr);
1233  {   do {
1234   char *line;   eol = strchr(cmdstr, '\n');
1235   FILE *cmdfile;   next:
1236   if (!opt_f) return;   if (eol) {
1237   add_files_link(opt_f->link);   /* Count preceding slashes */
1238   cmdfile = xfopen(opt_f->data, "r");   int slashes = 0;
1239   while ((line = xmalloc_getline(cmdfile)) != NULL) {   char *sl = eol;
1240   add_cmd(line);  
1241   free(line);   while (sl != cmdstr && *--sl == '\\')
1242   }   slashes++;
1243   xprint_and_close_file(cmdfile);   /* Odd number of preceding slashes - newline is escaped */
1244   free(opt_f);   if (slashes & 1) {
1245     overlapping_strcpy(eol - 1, eol);
1246     eol = strchr(eol, '\n');
1247     goto next;
1248     }
1249     *eol = '\0';
1250     }
1251     add_cmd(cmdstr);
1252     cmdstr = eol + 1;
1253     } while (eol);
1254     free(sv);
1255  }  }
1256    
1257  int sed_main(int argc, char **argv)  int sed_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1258    int sed_main(int argc UNUSED_PARAM, char **argv)
1259  {  {
1260   enum {   enum {
1261   OPT_in_place = 1 << 0,   OPT_in_place = 1 << 0,
# Line 1186  int sed_main(int argc, char **argv) Line 1264  int sed_main(int argc, char **argv)
1264   llist_t *opt_e, *opt_f;   llist_t *opt_e, *opt_f;
1265   int status = EXIT_SUCCESS;   int status = EXIT_SUCCESS;
1266    
1267   bbg.sed_cmd_tail = &bbg.sed_cmd_head;   INIT_G();
1268    
1269   /* destroy command strings on exit */   /* destroy command strings on exit */
1270   if (ENABLE_FEATURE_CLEAN_UP) atexit(sed_free_and_close_stuff);   if (ENABLE_FEATURE_CLEAN_UP) atexit(sed_free_and_close_stuff);
1271    
1272   /* Lie to autoconf when it starts asking stupid questions. */   /* Lie to autoconf when it starts asking stupid questions. */
1273   if (argc == 2 && !strcmp(argv[1], "--version")) {   if (argv[1] && !strcmp(argv[1], "--version")) {
1274   puts("This is not GNU sed version 4.0");   puts("This is not GNU sed version 4.0");
1275   return 0;   return 0;
1276   }   }
# Line 1201  int sed_main(int argc, char **argv) Line 1279  int sed_main(int argc, char **argv)
1279   opt_e = opt_f = NULL;   opt_e = opt_f = NULL;
1280   opt_complementary = "e::f::" /* can occur multiple times */   opt_complementary = "e::f::" /* can occur multiple times */
1281                      "nn"; /* count -n */                      "nn"; /* count -n */
1282   opt = getopt32(argc, argv, "irne:f:", &opt_e, &opt_f,   opt = getopt32(argv, "irne:f:", &opt_e, &opt_f,
1283      &bbg.be_quiet); /* counter for -n */      &G.be_quiet); /* counter for -n */
1284   argc -= optind;   //argc -= optind;
1285   argv += optind;   argv += optind;
1286   if (opt & OPT_in_place) { // -i   if (opt & OPT_in_place) { // -i
1287   atexit(cleanup_outname);   atexit(cleanup_outname);
1288   }   }
1289   if (opt & 0x2) bbg.regex_type |= REG_EXTENDED; // -r   if (opt & 0x2) G.regex_type |= REG_EXTENDED; // -r
1290   //if (opt & 0x4) bbg.be_quiet++; // -n   //if (opt & 0x4) G.be_quiet++; // -n
1291   if (opt & 0x8) { // -e   while (opt_e) { // -e
1292   /* getopt32 reverses order of arguments, handle it */   add_cmd_block(llist_pop(&opt_e));
1293   add_cmds_link(opt_e);   }
1294   }   while (opt_f) { // -f
1295   if (opt & 0x10) { // -f   char *line;
1296   /* getopt32 reverses order of arguments, handle it */   FILE *cmdfile;
1297   add_files_link(opt_f);   cmdfile = xfopen_for_read(llist_pop(&opt_f));
1298     while ((line = xmalloc_fgetline(cmdfile)) != NULL) {
1299     add_cmd(line);
1300     free(line);
1301     }
1302     fclose(cmdfile);
1303   }   }
1304   /* 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] */
1305   if (!(opt & 0x18)) {   if (!(opt & 0x18)) {
1306   if (!argc)   if (!*argv)
1307   bb_show_usage();   bb_show_usage();
1308   add_cmd_block(*argv++);   add_cmd_block(*argv++);
  argc--;  
1309   }   }
1310   /* Flush any unfinished commands. */   /* Flush any unfinished commands. */
1311   add_cmd("");   add_cmd("");
1312    
1313   /* By default, we write to stdout */   /* By default, we write to stdout */
1314   bbg.nonstdout = stdout;   G.nonstdout = stdout;
1315    
1316   /* 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
1317   * 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 1325  int sed_main(int argc, char **argv)
1325   int i;   int i;
1326   FILE *file;   FILE *file;
1327    
1328   for (i = 0; i < argc; i++) {   for (i = 0; argv[i]; i++) {
1329   struct stat statbuf;   struct stat statbuf;
1330   int nonstdoutfd;   int nonstdoutfd;
1331    
# Line 1262  int sed_main(int argc, char **argv) Line 1344  int sed_main(int argc, char **argv)
1344   continue;   continue;
1345   }   }
1346    
1347   bbg.outname = xasprintf("%sXXXXXX", argv[i]);   G.outname = xasprintf("%sXXXXXX", argv[i]);
1348   nonstdoutfd = mkstemp(bbg.outname);   nonstdoutfd = mkstemp(G.outname);
1349   if (-1 == nonstdoutfd)   if (-1 == nonstdoutfd)
1350   bb_error_msg_and_die("no temp file");   bb_perror_msg_and_die("can't create temp file %s", G.outname);
1351   bbg.nonstdout = fdopen(nonstdoutfd, "w");   G.nonstdout = xfdopen_for_write(nonstdoutfd);
   
  /* Set permissions of output file */  
1352    
1353     /* Set permissions/owner of output file */
1354   fstat(fileno(file), &statbuf);   fstat(fileno(file), &statbuf);
1355   fchmod(nonstdoutfd, statbuf.st_mode);   fchmod(nonstdoutfd, statbuf.st_mode);
1356     fchown(nonstdoutfd, statbuf.st_uid, statbuf.st_gid);
1357   add_input_file(file);   add_input_file(file);
1358   process_files();   process_files();
1359   fclose(bbg.nonstdout);   fclose(G.nonstdout);
1360    
1361   bbg.nonstdout = stdout;   G.nonstdout = stdout;
1362   /* unlink(argv[i]); */   /* unlink(argv[i]); */
1363   // FIXME: error check / message?   xrename(G.outname, argv[i]);
1364   rename(bbg.outname, argv[i]);   free(G.outname);
1365   free(bbg.outname);   G.outname = NULL;
  bbg.outname = 0;  
1366   }   }
1367   if (bbg.input_file_count > bbg.current_input_file)   if (G.input_file_count > G.current_input_file)
1368   process_files();   process_files();
1369   }   }
1370    

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