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

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