Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 992 - (hide annotations) (download)
Sun May 30 11:43:19 2010 UTC (13 years, 11 months ago) by niro
File MIME type: text/plain
File size: 35948 byte(s)
-added upstream sed patch
1 niro 532 /* vi: set sw=4 ts=4: */
2     /*
3     * sed.c - very minimalist version of sed
4     *
5     * Copyright (C) 1999,2000,2001 by Lineo, inc. and Mark Whitley
6     * Copyright (C) 1999,2000,2001 by Mark Whitley <markw@codepoet.org>
7     * Copyright (C) 2002 Matt Kraai
8 niro 816 * Copyright (C) 2003 by Glenn McGrath
9 niro 532 * Copyright (C) 2003,2004 by Rob Landley <rob@landley.net>
10     *
11     * MAINTAINER: Rob Landley <rob@landley.net>
12     *
13     * Licensed under GPL version 2, see file LICENSE in this tarball for details.
14     */
15    
16     /* Code overview.
17    
18     Files are laid out to avoid unnecessary function declarations. So for
19     example, every function add_cmd calls occurs before add_cmd in this file.
20    
21     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
23     resulting sed_cmd_t structures are appended to a linked list
24 niro 816 (G.sed_cmd_head/G.sed_cmd_tail).
25 niro 532
26 niro 816 add_input_file() adds a FILE* to the list of input files. We need to
27 niro 532 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 *
30     (which could be stdin) and applying the sed command list (sed_cmd_head) to
31     each of the resulting lines.
32    
33     sed_main() is where external code calls into this, with a command line.
34     */
35    
36    
37     /*
38     Supported features and commands in this version of sed:
39    
40     - comments ('#')
41     - address matching: num|/matchstr/[,num|/matchstr/|$]command
42     - commands: (p)rint, (d)elete, (s)ubstitue (with g & I flags)
43     - edit commands: (a)ppend, (i)nsert, (c)hange
44     - file commands: (r)ead
45     - backreferences in substitution expressions (\0, \1, \2...\9)
46     - grouped commands: {cmd1;cmd2}
47     - transliteration (y/source-chars/dest-chars/)
48     - pattern space hold space storing / swapping (g, h, x)
49     - labels / branching (: label, b, t, T)
50    
51     (Note: Specifying an address (range) to match is *optional*; commands
52     default to the whole pattern space if no specific address match was
53     requested.)
54    
55     Todo:
56     - Create a wrapper around regex to make libc's regex conform with sed
57    
58     Reference http://www.opengroup.org/onlinepubs/007904975/utilities/sed.html
59     */
60    
61 niro 816 #include "libbb.h"
62 niro 532 #include "xregex.h"
63    
64     /* Each sed command turns into one of these structures. */
65     typedef struct sed_cmd_s {
66     /* Ordered by alignment requirements: currently 36 bytes on x86 */
67 niro 816 struct sed_cmd_s *next; /* Next command (linked list, NULL terminated) */
68 niro 532
69     /* address storage */
70     regex_t *beg_match; /* sed -e '/match/cmd' */
71     regex_t *end_match; /* sed -e '/match/,/end_match/cmd' */
72     regex_t *sub_match; /* For 's/sub_match/string/' */
73     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 ($) */
75    
76 niro 816 FILE *sw_file; /* File (sw) command writes to, -1 for none. */
77 niro 532 char *string; /* Data string for (saicytb) commands. */
78    
79 niro 816 unsigned which_match; /* (s) Which match to replace (0 for all) */
80 niro 532
81     /* Bitfields (gcc won't group them if we don't) */
82 niro 816 unsigned invert:1; /* the '!' after the address */
83     unsigned in_match:1; /* Next line also included in match? */
84     unsigned sub_p:1; /* (s) print option */
85 niro 532
86 niro 816 char sw_last_char; /* Last line written by (sw) had no '\n' */
87 niro 532
88     /* GENERAL FIELDS */
89     char cmd; /* The command char: abcdDgGhHilnNpPqrstwxy:={} */
90     } sed_cmd_t;
91    
92 niro 816 static const char semicolon_whitespace[] ALIGN1 = "; \n\r\t\v";
93 niro 532
94 niro 816 struct globals {
95 niro 532 /* options */
96     int be_quiet, regex_type;
97     FILE *nonstdout;
98     char *outname, *hold_space;
99    
100     /* List of input files */
101     int input_file_count, current_input_file;
102     FILE **input_file_list;
103    
104     regmatch_t regmatch[10];
105     regex_t *previous_regex_ptr;
106    
107     /* linked list of sed commands */
108     sed_cmd_t sed_cmd_head, *sed_cmd_tail;
109    
110     /* Linked list of append lines */
111     llist_t *append_head;
112    
113     char *add_cmd_line;
114    
115     struct pipeline {
116     char *buf; /* Space to hold string */
117     int idx; /* Space used */
118     int len; /* Space allocated */
119     } pipeline;
120 niro 816 };
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 niro 532
129    
130     #if ENABLE_FEATURE_CLEAN_UP
131     static void sed_free_and_close_stuff(void)
132     {
133 niro 816 sed_cmd_t *sed_cmd = G.sed_cmd_head.next;
134 niro 532
135 niro 816 llist_free(G.append_head, free);
136 niro 532
137     while (sed_cmd) {
138     sed_cmd_t *sed_cmd_next = sed_cmd->next;
139    
140 niro 816 if (sed_cmd->sw_file)
141     xprint_and_close_file(sed_cmd->sw_file);
142 niro 532
143     if (sed_cmd->beg_match) {
144     regfree(sed_cmd->beg_match);
145     free(sed_cmd->beg_match);
146     }
147     if (sed_cmd->end_match) {
148     regfree(sed_cmd->end_match);
149     free(sed_cmd->end_match);
150     }
151     if (sed_cmd->sub_match) {
152     regfree(sed_cmd->sub_match);
153     free(sed_cmd->sub_match);
154     }
155     free(sed_cmd->string);
156     free(sed_cmd);
157     sed_cmd = sed_cmd_next;
158     }
159    
160 niro 816 free(G.hold_space);
161 niro 532
162 niro 816 while (G.current_input_file < G.input_file_count)
163     fclose(G.input_file_list[G.current_input_file++]);
164 niro 532 }
165     #else
166     void sed_free_and_close_stuff(void);
167     #endif
168    
169     /* If something bad happens during -i operation, delete temp file */
170    
171     static void cleanup_outname(void)
172     {
173 niro 816 if (G.outname) unlink(G.outname);
174 niro 532 }
175    
176 niro 816 /* strcpy, replacing "\from" with 'to'. If to is NUL, replacing "\any" with 'any' */
177 niro 532
178 niro 816 static void parse_escapes(char *dest, const char *string, int len, char from, char to)
179 niro 532 {
180     int i = 0;
181    
182     while (i < len) {
183     if (string[i] == '\\') {
184     if (!to || string[i+1] == from) {
185     *dest++ = to ? to : string[i+1];
186     i += 2;
187     continue;
188     }
189     *dest++ = string[i++];
190     }
191 niro 816 /* TODO: is it safe wrt a string with trailing '\\' ? */
192 niro 532 *dest++ = string[i++];
193     }
194 niro 816 *dest = '\0';
195 niro 532 }
196    
197 niro 816 static char *copy_parsing_escapes(const char *string, int len)
198 niro 532 {
199     char *dest = xmalloc(len + 1);
200    
201     parse_escapes(dest, string, len, 'n', '\n');
202 niro 816 /* GNU sed also recognizes \t */
203     parse_escapes(dest, dest, strlen(dest), 't', '\t');
204 niro 532 return dest;
205     }
206    
207    
208     /*
209     * 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
211 niro 816 * expression delimiter (typically a forward slash ('/')) not preceded by
212 niro 532 * a backslash ('\'). A negative delimiter disables square bracket checking.
213     */
214 niro 816 static int index_of_next_unescaped_regexp_delim(int delimiter, const char *str)
215 niro 532 {
216     int bracket = -1;
217     int escaped = 0;
218     int idx = 0;
219     char ch;
220    
221     if (delimiter < 0) {
222     bracket--;
223     delimiter = -delimiter;
224     }
225    
226     for (; (ch = str[idx]); idx++) {
227     if (bracket >= 0) {
228     if (ch == ']' && !(bracket == idx - 1 || (bracket == idx - 2
229     && str[idx - 1] == '^')))
230     bracket = -1;
231     } else if (escaped)
232     escaped = 0;
233     else if (ch == '\\')
234     escaped = 1;
235     else if (bracket == -1 && ch == '[')
236     bracket = idx;
237     else if (ch == delimiter)
238     return idx;
239     }
240    
241     /* if we make it to here, we've hit the end of the string */
242     bb_error_msg_and_die("unmatched '%c'", delimiter);
243     }
244    
245     /*
246     * Returns the index of the third delimiter
247     */
248 niro 816 static int parse_regex_delim(const char *cmdstr, char **match, char **replace)
249 niro 532 {
250 niro 816 const char *cmdstr_ptr = cmdstr;
251 niro 532 char delimiter;
252     int idx = 0;
253    
254     /* verify that the 's' or 'y' is followed by something. That something
255     * (typically a 'slash') is now our regexp delimiter... */
256     if (*cmdstr == '\0')
257     bb_error_msg_and_die("bad format in substitution expression");
258     delimiter = *cmdstr_ptr++;
259    
260     /* save the match string */
261     idx = index_of_next_unescaped_regexp_delim(delimiter, cmdstr_ptr);
262     *match = copy_parsing_escapes(cmdstr_ptr, idx);
263    
264     /* save the replacement string */
265     cmdstr_ptr += idx + 1;
266     idx = index_of_next_unescaped_regexp_delim(-delimiter, cmdstr_ptr);
267     *replace = copy_parsing_escapes(cmdstr_ptr, idx);
268    
269     return ((cmdstr_ptr - cmdstr) + idx);
270     }
271    
272     /*
273     * returns the index in the string just past where the address ends.
274     */
275 niro 816 static int get_address(const char *my_str, int *linenum, regex_t ** regex)
276 niro 532 {
277 niro 816 const char *pos = my_str;
278 niro 532
279     if (isdigit(*my_str)) {
280 niro 816 *linenum = strtol(my_str, (char**)&pos, 10);
281 niro 532 /* endstr shouldnt ever equal NULL */
282     } else if (*my_str == '$') {
283     *linenum = -1;
284     pos++;
285     } else if (*my_str == '/' || *my_str == '\\') {
286     int next;
287     char delimiter;
288     char *temp;
289    
290     delimiter = '/';
291     if (*my_str == '\\') delimiter = *++pos;
292     next = index_of_next_unescaped_regexp_delim(delimiter, ++pos);
293     temp = copy_parsing_escapes(pos, next);
294     *regex = xmalloc(sizeof(regex_t));
295 niro 816 xregcomp(*regex, temp, G.regex_type|REG_NEWLINE);
296 niro 532 free(temp);
297     /* Move position to next character after last delimiter */
298     pos += (next+1);
299     }
300     return pos - my_str;
301     }
302    
303     /* Grab a filename. Whitespace at start is skipped, then goes to EOL. */
304 niro 816 static int parse_file_cmd(/*sed_cmd_t *sed_cmd,*/ const char *filecmdstr, char **retval)
305 niro 532 {
306     int start = 0, idx, hack = 0;
307    
308     /* Skip whitespace, then grab filename to end of line */
309 niro 816 while (isspace(filecmdstr[start]))
310     start++;
311 niro 532 idx = start;
312 niro 816 while (filecmdstr[idx] && filecmdstr[idx] != '\n')
313     idx++;
314 niro 532
315     /* If lines glued together, put backslash back. */
316 niro 816 if (filecmdstr[idx] == '\n')
317     hack = 1;
318 niro 532 if (idx == start)
319     bb_error_msg_and_die("empty filename");
320     *retval = xstrndup(filecmdstr+start, idx-start+hack+1);
321 niro 816 if (hack)
322     (*retval)[idx] = '\\';
323 niro 532
324     return idx;
325     }
326    
327 niro 816 static int parse_subst_cmd(sed_cmd_t *sed_cmd, const char *substr)
328 niro 532 {
329 niro 816 int cflags = G.regex_type;
330 niro 532 char *match;
331 niro 816 int idx;
332 niro 532
333     /*
334     * A substitution command should look something like this:
335     * s/match/replace/ #gIpw
336     * || | |||
337     * mandatory optional
338     */
339     idx = parse_regex_delim(substr, &match, &sed_cmd->string);
340    
341     /* determine the number of back references in the match string */
342     /* Note: we compute this here rather than in the do_subst_command()
343     * function to save processor time, at the expense of a little more memory
344     * (4 bits) per sed_cmd */
345    
346     /* process the flags */
347    
348     sed_cmd->which_match = 1;
349     while (substr[++idx]) {
350     /* Parse match number */
351     if (isdigit(substr[idx])) {
352     if (match[0] != '^') {
353     /* Match 0 treated as all, multiple matches we take the last one. */
354 niro 816 const char *pos = substr + idx;
355     /* FIXME: error check? */
356     sed_cmd->which_match = (unsigned)strtol(substr+idx, (char**) &pos, 10);
357 niro 532 idx = pos - substr;
358     }
359     continue;
360     }
361     /* Skip spaces */
362 niro 984 if (isspace(substr[idx]))
363     continue;
364 niro 532
365     switch (substr[idx]) {
366     /* Replace all occurrences */
367     case 'g':
368 niro 816 if (match[0] != '^')
369     sed_cmd->which_match = 0;
370 niro 532 break;
371     /* Print pattern space */
372     case 'p':
373     sed_cmd->sub_p = 1;
374     break;
375     /* Write to file */
376     case 'w':
377     {
378     char *temp;
379 niro 816 idx += parse_file_cmd(/*sed_cmd,*/ substr+idx, &temp);
380 niro 532 break;
381     }
382     /* Ignore case (gnu exension) */
383     case 'I':
384     cflags |= REG_ICASE;
385     break;
386     /* Comment */
387     case '#':
388     while (substr[++idx]) /*skip all*/;
389     /* Fall through */
390     /* End of command */
391     case ';':
392     case '}':
393     goto out;
394     default:
395     bb_error_msg_and_die("bad option in substitution expression");
396     }
397     }
398     out:
399     /* compile the match string into a regex */
400     if (*match != '\0') {
401     /* If match is empty, we use last regex used at runtime */
402     sed_cmd->sub_match = xmalloc(sizeof(regex_t));
403     xregcomp(sed_cmd->sub_match, match, cflags);
404     }
405     free(match);
406    
407     return idx;
408     }
409    
410     /*
411     * Process the commands arguments
412     */
413 niro 816 static const char *parse_cmd_args(sed_cmd_t *sed_cmd, const char *cmdstr)
414 niro 532 {
415     /* handle (s)ubstitution command */
416     if (sed_cmd->cmd == 's')
417     cmdstr += parse_subst_cmd(sed_cmd, cmdstr);
418     /* handle edit cmds: (a)ppend, (i)nsert, and (c)hange */
419     else if (strchr("aic", sed_cmd->cmd)) {
420     if ((sed_cmd->end_line || sed_cmd->end_match) && sed_cmd->cmd != 'c')
421 niro 984 bb_error_msg_and_die("only a beginning address can be specified for edit commands");
422 niro 532 for (;;) {
423     if (*cmdstr == '\n' || *cmdstr == '\\') {
424     cmdstr++;
425     break;
426 niro 984 }
427     if (!isspace(*cmdstr))
428 niro 532 break;
429 niro 984 cmdstr++;
430 niro 532 }
431     sed_cmd->string = xstrdup(cmdstr);
432 niro 816 /* "\anychar" -> "anychar" */
433     parse_escapes(sed_cmd->string, sed_cmd->string, strlen(cmdstr), '\0', '\0');
434 niro 532 cmdstr += strlen(cmdstr);
435     /* handle file cmds: (r)ead */
436     } else if (strchr("rw", sed_cmd->cmd)) {
437     if (sed_cmd->end_line || sed_cmd->end_match)
438     bb_error_msg_and_die("command only uses one address");
439 niro 816 cmdstr += parse_file_cmd(/*sed_cmd,*/ cmdstr, &sed_cmd->string);
440     if (sed_cmd->cmd == 'w') {
441     sed_cmd->sw_file = xfopen_for_write(sed_cmd->string);
442     sed_cmd->sw_last_char = '\n';
443     }
444 niro 532 /* handle branch commands */
445     } else if (strchr(":btT", sed_cmd->cmd)) {
446     int length;
447    
448     cmdstr = skip_whitespace(cmdstr);
449     length = strcspn(cmdstr, semicolon_whitespace);
450     if (length) {
451     sed_cmd->string = xstrndup(cmdstr, length);
452     cmdstr += length;
453     }
454     }
455     /* translation command */
456     else if (sed_cmd->cmd == 'y') {
457     char *match, *replace;
458     int i = cmdstr[0];
459    
460     cmdstr += parse_regex_delim(cmdstr, &match, &replace)+1;
461     /* \n already parsed, but \delimiter needs unescaping. */
462     parse_escapes(match, match, strlen(match), i, i);
463     parse_escapes(replace, replace, strlen(replace), i, i);
464    
465     sed_cmd->string = xzalloc((strlen(match) + 1) * 2);
466     for (i = 0; match[i] && replace[i]; i++) {
467     sed_cmd->string[i*2] = match[i];
468     sed_cmd->string[i*2+1] = replace[i];
469     }
470     free(match);
471     free(replace);
472     }
473     /* if it wasnt a single-letter command that takes no arguments
474     * then it must be an invalid command.
475     */
476     else if (strchr("dDgGhHlnNpPqx={}", sed_cmd->cmd) == 0) {
477     bb_error_msg_and_die("unsupported command %c", sed_cmd->cmd);
478     }
479    
480     /* give back whatever's left over */
481     return cmdstr;
482     }
483    
484    
485     /* Parse address+command sets, skipping comment lines. */
486    
487 niro 816 static void add_cmd(const char *cmdstr)
488 niro 532 {
489     sed_cmd_t *sed_cmd;
490 niro 992 unsigned len, n;
491 niro 532
492     /* Append this line to any unfinished line from last time. */
493 niro 816 if (G.add_cmd_line) {
494     char *tp = xasprintf("%s\n%s", G.add_cmd_line, cmdstr);
495     free(G.add_cmd_line);
496     cmdstr = G.add_cmd_line = tp;
497 niro 532 }
498    
499 niro 992 /* If this line ends with unescaped backslash, request next line. */
500     n = len = strlen(cmdstr);
501     while (n && cmdstr[n-1] == '\\')
502     n--;
503     if ((len - n) & 1) { /* if odd number of trailing backslashes */
504 niro 816 if (!G.add_cmd_line)
505     G.add_cmd_line = xstrdup(cmdstr);
506 niro 992 G.add_cmd_line[len-1] = '\0';
507 niro 532 return;
508     }
509    
510     /* Loop parsing all commands in this line. */
511     while (*cmdstr) {
512     /* Skip leading whitespace and semicolons */
513     cmdstr += strspn(cmdstr, semicolon_whitespace);
514    
515     /* If no more commands, exit. */
516     if (!*cmdstr) break;
517    
518     /* if this is a comment, jump past it and keep going */
519     if (*cmdstr == '#') {
520     /* "#n" is the same as using -n on the command line */
521     if (cmdstr[1] == 'n')
522 niro 816 G.be_quiet++;
523 niro 532 cmdstr = strpbrk(cmdstr, "\n\r");
524     if (!cmdstr) break;
525     continue;
526     }
527    
528     /* parse the command
529     * format is: [addr][,addr][!]cmd
530     * |----||-----||-|
531     * part1 part2 part3
532     */
533    
534     sed_cmd = xzalloc(sizeof(sed_cmd_t));
535    
536     /* first part (if present) is an address: either a '$', a number or a /regex/ */
537     cmdstr += get_address(cmdstr, &sed_cmd->beg_line, &sed_cmd->beg_match);
538    
539     /* second part (if present) will begin with a comma */
540     if (*cmdstr == ',') {
541     int idx;
542    
543     cmdstr++;
544     idx = get_address(cmdstr, &sed_cmd->end_line, &sed_cmd->end_match);
545     if (!idx)
546     bb_error_msg_and_die("no address after comma");
547     cmdstr += idx;
548     }
549    
550     /* skip whitespace before the command */
551     cmdstr = skip_whitespace(cmdstr);
552    
553     /* Check for inversion flag */
554     if (*cmdstr == '!') {
555     sed_cmd->invert = 1;
556     cmdstr++;
557    
558     /* skip whitespace before the command */
559     cmdstr = skip_whitespace(cmdstr);
560     }
561    
562     /* last part (mandatory) will be a command */
563     if (!*cmdstr)
564     bb_error_msg_and_die("missing command");
565     sed_cmd->cmd = *(cmdstr++);
566     cmdstr = parse_cmd_args(sed_cmd, cmdstr);
567    
568     /* Add the command to the command array */
569 niro 816 G.sed_cmd_tail->next = sed_cmd;
570     G.sed_cmd_tail = G.sed_cmd_tail->next;
571 niro 532 }
572    
573     /* If we glued multiple lines together, free the memory. */
574 niro 816 free(G.add_cmd_line);
575     G.add_cmd_line = NULL;
576 niro 532 }
577    
578     /* Append to a string, reallocating memory as necessary. */
579    
580     #define PIPE_GROW 64
581    
582     static void pipe_putc(char c)
583     {
584 niro 816 if (G.pipeline.idx == G.pipeline.len) {
585     G.pipeline.buf = xrealloc(G.pipeline.buf,
586     G.pipeline.len + PIPE_GROW);
587     G.pipeline.len += PIPE_GROW;
588 niro 532 }
589 niro 816 G.pipeline.buf[G.pipeline.idx++] = c;
590 niro 532 }
591    
592     static void do_subst_w_backrefs(char *line, char *replace)
593     {
594 niro 984 int i, j;
595 niro 532
596     /* go through the replacement string */
597     for (i = 0; replace[i]; i++) {
598     /* if we find a backreference (\1, \2, etc.) print the backref'ed * text */
599     if (replace[i] == '\\') {
600     unsigned backref = replace[++i] - '0';
601     if (backref <= 9) {
602 niro 816 /* print out the text held in G.regmatch[backref] */
603     if (G.regmatch[backref].rm_so != -1) {
604     j = G.regmatch[backref].rm_so;
605     while (j < G.regmatch[backref].rm_eo)
606 niro 532 pipe_putc(line[j++]);
607     }
608     continue;
609     }
610     /* I _think_ it is impossible to get '\' to be
611     * the last char in replace string. Thus we dont check
612     * for replace[i] == NUL. (counterexample anyone?) */
613     /* if we find a backslash escaped character, print the character */
614     pipe_putc(replace[i]);
615     continue;
616     }
617     /* if we find an unescaped '&' print out the whole matched text. */
618     if (replace[i] == '&') {
619 niro 816 j = G.regmatch[0].rm_so;
620     while (j < G.regmatch[0].rm_eo)
621 niro 532 pipe_putc(line[j++]);
622     continue;
623     }
624     /* Otherwise just output the character. */
625     pipe_putc(replace[i]);
626     }
627     }
628    
629 niro 984 static int do_subst_command(sed_cmd_t *sed_cmd, char **line_p)
630 niro 532 {
631 niro 984 char *line = *line_p;
632 niro 532 int altered = 0;
633 niro 816 unsigned match_count = 0;
634 niro 532 regex_t *current_regex;
635    
636 niro 984 current_regex = sed_cmd->sub_match;
637 niro 532 /* Handle empty regex. */
638 niro 984 if (!current_regex) {
639 niro 816 current_regex = G.previous_regex_ptr;
640 niro 532 if (!current_regex)
641     bb_error_msg_and_die("no previous regexp");
642 niro 984 }
643     G.previous_regex_ptr = current_regex;
644 niro 532
645     /* Find the first match */
646 niro 984 if (REG_NOMATCH == regexec(current_regex, line, 10, G.regmatch, 0))
647 niro 532 return 0;
648    
649     /* Initialize temporary output buffer. */
650 niro 816 G.pipeline.buf = xmalloc(PIPE_GROW);
651     G.pipeline.len = PIPE_GROW;
652     G.pipeline.idx = 0;
653 niro 532
654     /* Now loop through, substituting for matches */
655     do {
656     int i;
657    
658     /* Work around bug in glibc regexec, demonstrated by:
659     echo " a.b" | busybox sed 's [^ .]* x g'
660     The match_count check is so not to break
661     echo "hi" | busybox sed 's/^/!/g' */
662 niro 816 if (!G.regmatch[0].rm_so && !G.regmatch[0].rm_eo && match_count) {
663 niro 984 pipe_putc(*line++);
664 niro 532 continue;
665     }
666    
667     match_count++;
668    
669     /* If we aren't interested in this match, output old line to
670     end of match and continue */
671 niro 816 if (sed_cmd->which_match
672     && (sed_cmd->which_match != match_count)
673     ) {
674     for (i = 0; i < G.regmatch[0].rm_eo; i++)
675 niro 984 pipe_putc(*line++);
676 niro 532 continue;
677     }
678    
679     /* print everything before the match */
680 niro 816 for (i = 0; i < G.regmatch[0].rm_so; i++)
681 niro 984 pipe_putc(line[i]);
682 niro 532
683     /* then print the substitution string */
684 niro 984 do_subst_w_backrefs(line, sed_cmd->string);
685 niro 532
686     /* advance past the match */
687 niro 984 line += G.regmatch[0].rm_eo;
688 niro 532 /* flag that something has changed */
689     altered++;
690    
691     /* if we're not doing this globally, get out now */
692 niro 816 if (sed_cmd->which_match)
693     break;
694 niro 532
695 niro 984 //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 niro 532 /* Copy rest of string into output pipeline */
699 niro 984 while (1) {
700     char c = *line++;
701     pipe_putc(c);
702     if (c == '\0')
703     break;
704     }
705 niro 532
706 niro 984 free(*line_p);
707     *line_p = G.pipeline.buf;
708 niro 532 return altered;
709     }
710    
711     /* Set command pointer to point to this label. (Does not handle null label.) */
712     static sed_cmd_t *branch_to(char *label)
713     {
714     sed_cmd_t *sed_cmd;
715    
716 niro 816 for (sed_cmd = G.sed_cmd_head.next; sed_cmd; sed_cmd = sed_cmd->next) {
717 niro 532 if (sed_cmd->cmd == ':' && sed_cmd->string && !strcmp(sed_cmd->string, label)) {
718     return sed_cmd;
719     }
720     }
721     bb_error_msg_and_die("can't find label for jump to '%s'", label);
722     }
723    
724     static void append(char *s)
725     {
726 niro 816 llist_add_to_end(&G.append_head, xstrdup(s));
727 niro 532 }
728    
729     static void flush_append(void)
730     {
731     char *data;
732    
733     /* Output appended lines. */
734 niro 816 while ((data = (char *)llist_pop(&G.append_head))) {
735     fprintf(G.nonstdout, "%s\n", data);
736 niro 532 free(data);
737     }
738     }
739    
740     static void add_input_file(FILE *file)
741     {
742 niro 816 G.input_file_list = xrealloc_vector(G.input_file_list, 2, G.input_file_count);
743     G.input_file_list[G.input_file_count++] = file;
744 niro 532 }
745    
746 niro 816 /* Get next line of input from G.input_file_list, flushing append buffer and
747 niro 532 * noting if we ran out of files without a newline on the last line we read.
748     */
749 niro 816 enum {
750     NO_EOL_CHAR = 1,
751     LAST_IS_NUL = 2,
752     };
753     static char *get_next_line(char *gets_char)
754 niro 532 {
755     char *temp = NULL;
756 niro 816 int len;
757     char gc;
758 niro 532
759     flush_append();
760 niro 816
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 niro 532 /* Read line up to a newline or NUL byte, inclusive,
767     * return malloc'ed char[]. length of the chunk read
768     * is stored in len. NULL if EOF/error */
769 niro 816 temp = bb_get_chunk_from_file(fp, &len);
770 niro 532 if (temp) {
771     /* len > 0 here, it's ok to do temp[len-1] */
772     char c = temp[len-1];
773     if (c == '\n' || c == '\0') {
774     temp[len-1] = '\0';
775 niro 816 gc = c;
776     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 niro 532 }
784 niro 816 /* else we put NO_EOL_CHAR into *gets_char */
785 niro 532 break;
786 niro 816
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 niro 532 }
795     /* Close this file and advance to next one */
796 niro 816 fclose(fp);
797     G.current_input_file++;
798 niro 532 }
799 niro 816 *gets_char = gc;
800 niro 532 return temp;
801     }
802    
803     /* Output line of text. */
804     /* Note:
805 niro 816 * The tricks with NO_EOL_CHAR and last_puts_char are there to emulate gnu sed.
806 niro 532 * Without them, we had this:
807     * echo -n thingy >z1
808     * echo -n again >z2
809     * >znull
810     * sed "s/i/z/" z1 z2 znull | hexdump -vC
811     * output:
812     * gnu sed 4.1.5:
813     * 00000000 74 68 7a 6e 67 79 0a 61 67 61 7a 6e |thzngy.agazn|
814     * bbox:
815     * 00000000 74 68 7a 6e 67 79 61 67 61 7a 6e |thzngyagazn|
816     */
817 niro 816 static void puts_maybe_newline(char *s, FILE *file, char *last_puts_char, char last_gets_char)
818 niro 532 {
819 niro 816 char lpc = *last_puts_char;
820 niro 532
821 niro 816 /* Need to insert a '\n' between two files because first file's
822     * last line wasn't terminated? */
823     if (lpc != '\n' && lpc != '\0') {
824 niro 532 fputc('\n', file);
825 niro 816 lpc = '\n';
826 niro 532 }
827     fputs(s, file);
828 niro 816
829     /* 'x' - just something which is not '\n', '\0' or NO_EOL_CHAR */
830 niro 532 if (s[0])
831 niro 816 lpc = 'x';
832    
833     /* had trailing '\0' and it was last char of file? */
834     if (last_gets_char == LAST_IS_NUL) {
835     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 niro 532 }
843    
844     if (ferror(file)) {
845     xfunc_error_retval = 4; /* It's what gnu sed exits with... */
846     bb_error_msg_and_die(bb_msg_write_error);
847     }
848 niro 816 *last_puts_char = lpc;
849     }
850 niro 532
851 niro 816 #define sed_puts(s, n) (puts_maybe_newline(s, G.nonstdout, &last_puts_char, n))
852    
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 niro 532 }
860    
861     /* Process all the lines in all the files */
862    
863     static void process_files(void)
864     {
865     char *pattern_space, *next_line;
866 niro 816 int linenum = 0;
867     char last_puts_char = '\n';
868     char last_gets_char, next_gets_char;
869 niro 532 sed_cmd_t *sed_cmd;
870     int substituted;
871    
872     /* Prime the pump */
873 niro 816 next_line = get_next_line(&next_gets_char);
874 niro 532
875 niro 984 /* Go through every line in each file */
876 niro 816 again:
877 niro 532 substituted = 0;
878    
879     /* Advance to next line. Stop if out of lines. */
880     pattern_space = next_line;
881 niro 984 if (!pattern_space)
882     return;
883 niro 816 last_gets_char = next_gets_char;
884 niro 532
885     /* Read one line in advance so we can act on the last line,
886     * the '$' address */
887 niro 816 next_line = get_next_line(&next_gets_char);
888 niro 532 linenum++;
889 niro 984
890     /* For every line, go through all the commands */
891 niro 816 restart:
892     for (sed_cmd = G.sed_cmd_head.next; sed_cmd; sed_cmd = sed_cmd->next) {
893 niro 532 int old_matched, matched;
894    
895     old_matched = sed_cmd->in_match;
896    
897     /* Determine if this command matches this line: */
898    
899     /* Are we continuing a previous multi-line match? */
900     sed_cmd->in_match = sed_cmd->in_match
901     /* Or is no range necessary? */
902     || (!sed_cmd->beg_line && !sed_cmd->end_line
903     && !sed_cmd->beg_match && !sed_cmd->end_match)
904     /* Or did we match the start of a numerical range? */
905 niro 984 || (sed_cmd->beg_line > 0 && (sed_cmd->beg_line <= linenum))
906 niro 532 /* Or does this line match our begin address regex? */
907 niro 816 || (beg_match(sed_cmd, pattern_space))
908 niro 532 /* Or did we match last line of input? */
909     || (sed_cmd->beg_line == -1 && next_line == NULL);
910    
911     /* Snapshot the value */
912     matched = sed_cmd->in_match;
913    
914 niro 984 //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 niro 532 /* Is this line the end of the current match? */
918    
919     if (matched) {
920 niro 984 /* once matched, "n,xxx" range is dead, disabling it */
921     if (sed_cmd->beg_line > 0)
922     sed_cmd->beg_line = -2;
923 niro 532 sed_cmd->in_match = !(
924     /* has the ending line come, or is this a single address command? */
925     (sed_cmd->end_line ?
926     sed_cmd->end_line == -1 ?
927     !next_line
928     : (sed_cmd->end_line <= linenum)
929     : !sed_cmd->end_match
930     )
931     /* or does this line matches our last address regex */
932     || (sed_cmd->end_match && old_matched
933     && (regexec(sed_cmd->end_match,
934 niro 816 pattern_space, 0, NULL, 0) == 0))
935 niro 532 );
936     }
937    
938 niro 984 /* Skip blocks of commands we didn't match */
939 niro 532 if (sed_cmd->cmd == '{') {
940 niro 816 if (sed_cmd->invert ? matched : !matched) {
941 niro 992 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 niro 532 sed_cmd = sed_cmd->next;
951 niro 816 if (!sed_cmd)
952     bb_error_msg_and_die("unterminated {");
953     }
954     }
955 niro 532 continue;
956     }
957    
958     /* Okay, so did this line match? */
959 niro 984 if (sed_cmd->invert ? matched : !matched)
960     continue; /* no */
961 niro 532
962 niro 984 /* Update last used regex in case a blank substitute BRE is found */
963     if (sed_cmd->beg_match) {
964     G.previous_regex_ptr = sed_cmd->beg_match;
965     }
966 niro 532
967 niro 984 /* actual sedding */
968     switch (sed_cmd->cmd) {
969 niro 532
970 niro 984 /* Print line number */
971     case '=':
972     fprintf(G.nonstdout, "%d\n", linenum);
973     break;
974 niro 532
975 niro 984 /* Write the current pattern space up to the first newline */
976     case 'P':
977     {
978     char *tmp = strchr(pattern_space, '\n');
979 niro 532
980 niro 984 if (tmp) {
981     *tmp = '\0';
982     /* TODO: explain why '\n' below */
983 niro 816 sed_puts(pattern_space, '\n');
984 niro 984 *tmp = '\n';
985 niro 532 break;
986     }
987 niro 984 /* Fall Through */
988     }
989 niro 532
990 niro 984 /* Write the current pattern space to output */
991     case 'p':
992     /* NB: we print this _before_ the last line
993     * (of current file) is printed. Even if
994     * that line is nonterminated, we print
995     * '\n' here (gnu sed does the same) */
996     sed_puts(pattern_space, '\n');
997     break;
998     /* Delete up through first newline */
999     case 'D':
1000     {
1001     char *tmp = strchr(pattern_space, '\n');
1002 niro 532
1003 niro 984 if (tmp) {
1004     tmp = xstrdup(tmp+1);
1005     free(pattern_space);
1006     pattern_space = tmp;
1007     goto restart;
1008     }
1009     }
1010     /* discard this line. */
1011     case 'd':
1012     goto discard_line;
1013 niro 532
1014 niro 984 /* Substitute with regex */
1015     case 's':
1016     if (!do_subst_command(sed_cmd, &pattern_space))
1017 niro 532 break;
1018 niro 984 substituted |= 1;
1019 niro 532
1020 niro 984 /* handle p option */
1021     if (sed_cmd->sub_p)
1022     sed_puts(pattern_space, last_gets_char);
1023     /* 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 niro 532
1030 niro 984 /* Append line to linked list to be printed later */
1031     case 'a':
1032     append(sed_cmd->string);
1033     break;
1034 niro 532
1035 niro 984 /* Insert text before this line */
1036     case 'i':
1037     sed_puts(sed_cmd->string, '\n');
1038     break;
1039 niro 532
1040 niro 984 /* 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 niro 992 sed_puts(sed_cmd->string, '\n');
1045 niro 984 goto discard_line;
1046 niro 532
1047 niro 984 /* Read file, append contents to output */
1048     case 'r':
1049     {
1050     FILE *rfile;
1051 niro 532
1052 niro 984 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 niro 532 }
1061    
1062 niro 984 break;
1063     }
1064 niro 532
1065 niro 984 /* Write pattern space to file. */
1066     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 niro 532
1072 niro 984 /* 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 niro 816 last_gets_char = next_gets_char;
1080     next_line = get_next_line(&next_gets_char);
1081 niro 984 substituted = 0;
1082 niro 532 linenum++;
1083     break;
1084     }
1085 niro 984 /* fall through */
1086 niro 532
1087 niro 984 /* Quit. End of script, end of input. */
1088     case 'q':
1089     /* Exit the outer while loop */
1090     free(next_line);
1091     next_line = NULL;
1092     goto discard_commands;
1093 niro 532
1094 niro 984 /* Append the next line to the current line */
1095     case 'N':
1096     {
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);
1102     next_line = NULL;
1103     goto discard_line;
1104     /* append next_line, read new next_line. */
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 */
1117     case 't':
1118     if (!substituted) break;
1119     substituted = 0;
1120     /* Fall through */
1121     /* Test/branch if substitution didn't occur */
1122     case 'T':
1123     if (substituted) break;
1124     /* Fall through */
1125     /* Branch to label */
1126     case 'b':
1127     if (!sed_cmd->string) goto discard_commands;
1128     else sed_cmd = branch_to(sed_cmd->string);
1129     break;
1130     /* Transliterate characters */
1131     case 'y':
1132     {
1133     int i, j;
1134    
1135     for (i = 0; pattern_space[i]; i++) {
1136     for (j = 0; sed_cmd->string[j]; j += 2) {
1137     if (pattern_space[i] == sed_cmd->string[j]) {
1138     pattern_space[i] = sed_cmd->string[j + 1];
1139     break;
1140 niro 532 }
1141     }
1142     }
1143    
1144 niro 984 break;
1145     }
1146     case 'g': /* Replace pattern space with hold space */
1147     free(pattern_space);
1148     pattern_space = xstrdup(G.hold_space ? G.hold_space : "");
1149     break;
1150     case 'G': /* Append newline and hold space to pattern space */
1151     {
1152     int pattern_space_size = 2;
1153     int hold_space_size = 0;
1154 niro 532
1155 niro 984 if (pattern_space)
1156     pattern_space_size += strlen(pattern_space);
1157     if (G.hold_space)
1158     hold_space_size = strlen(G.hold_space);
1159     pattern_space = xrealloc(pattern_space,
1160     pattern_space_size + hold_space_size);
1161     if (pattern_space_size == 2)
1162     pattern_space[0] = 0;
1163     strcat(pattern_space, "\n");
1164     if (G.hold_space)
1165     strcat(pattern_space, G.hold_space);
1166     last_gets_char = '\n';
1167 niro 532
1168 niro 984 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 niro 532
1179 niro 984 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 niro 532
1186 niro 984 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 niro 532 }
1194 niro 984 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 niro 532
1205     /*
1206 niro 984 * Exit point from sedding...
1207 niro 532 */
1208 niro 816 discard_commands:
1209 niro 532 /* we will print the line unless we were told to be quiet ('-n')
1210     or if the line was suppressed (ala 'd'elete) */
1211 niro 816 if (!G.be_quiet)
1212     sed_puts(pattern_space, last_gets_char);
1213 niro 532
1214     /* Delete and such jump here. */
1215 niro 816 discard_line:
1216 niro 532 flush_append();
1217     free(pattern_space);
1218    
1219     goto again;
1220     }
1221    
1222     /* It is possible to have a command line argument with embedded
1223 niro 816 * 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 niro 532
1228     static void add_cmd_block(char *cmdstr)
1229     {
1230 niro 816 char *sv, *eol;
1231 niro 532
1232 niro 816 cmdstr = sv = xstrdup(cmdstr);
1233     do {
1234     eol = strchr(cmdstr, '\n');
1235     next:
1236     if (eol) {
1237     /* Count preceding slashes */
1238     int slashes = 0;
1239     char *sl = eol;
1240 niro 532
1241 niro 816 while (sl != cmdstr && *--sl == '\\')
1242     slashes++;
1243     /* Odd number of preceding slashes - newline is escaped */
1244     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 niro 532 }
1256    
1257 niro 816 int sed_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1258     int sed_main(int argc UNUSED_PARAM, char **argv)
1259 niro 532 {
1260     enum {
1261     OPT_in_place = 1 << 0,
1262     };
1263     unsigned opt;
1264     llist_t *opt_e, *opt_f;
1265     int status = EXIT_SUCCESS;
1266    
1267 niro 816 INIT_G();
1268 niro 532
1269     /* destroy command strings on exit */
1270     if (ENABLE_FEATURE_CLEAN_UP) atexit(sed_free_and_close_stuff);
1271    
1272     /* Lie to autoconf when it starts asking stupid questions. */
1273 niro 816 if (argv[1] && !strcmp(argv[1], "--version")) {
1274 niro 532 puts("This is not GNU sed version 4.0");
1275     return 0;
1276     }
1277    
1278     /* do normal option parsing */
1279     opt_e = opt_f = NULL;
1280     opt_complementary = "e::f::" /* can occur multiple times */
1281     "nn"; /* count -n */
1282 niro 816 opt = getopt32(argv, "irne:f:", &opt_e, &opt_f,
1283     &G.be_quiet); /* counter for -n */
1284     //argc -= optind;
1285 niro 532 argv += optind;
1286     if (opt & OPT_in_place) { // -i
1287     atexit(cleanup_outname);
1288     }
1289 niro 816 if (opt & 0x2) G.regex_type |= REG_EXTENDED; // -r
1290     //if (opt & 0x4) G.be_quiet++; // -n
1291     while (opt_e) { // -e
1292     add_cmd_block(llist_pop(&opt_e));
1293 niro 532 }
1294 niro 816 while (opt_f) { // -f
1295     char *line;
1296     FILE *cmdfile;
1297     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 niro 532 }
1304     /* if we didn't get a pattern from -e or -f, use argv[0] */
1305     if (!(opt & 0x18)) {
1306 niro 816 if (!*argv)
1307 niro 532 bb_show_usage();
1308     add_cmd_block(*argv++);
1309     }
1310     /* Flush any unfinished commands. */
1311     add_cmd("");
1312    
1313     /* By default, we write to stdout */
1314 niro 816 G.nonstdout = stdout;
1315 niro 532
1316     /* argv[0..(argc-1)] should be names of file to process. If no
1317     * files were specified or '-' was specified, take input from stdin.
1318     * Otherwise, we process all the files specified. */
1319     if (argv[0] == NULL) {
1320     if (opt & OPT_in_place)
1321     bb_error_msg_and_die(bb_msg_requires_arg, "-i");
1322     add_input_file(stdin);
1323     process_files();
1324     } else {
1325     int i;
1326     FILE *file;
1327    
1328 niro 816 for (i = 0; argv[i]; i++) {
1329 niro 532 struct stat statbuf;
1330     int nonstdoutfd;
1331    
1332     if (LONE_DASH(argv[i]) && !(opt & OPT_in_place)) {
1333     add_input_file(stdin);
1334     process_files();
1335     continue;
1336     }
1337     file = fopen_or_warn(argv[i], "r");
1338     if (!file) {
1339     status = EXIT_FAILURE;
1340     continue;
1341     }
1342     if (!(opt & OPT_in_place)) {
1343     add_input_file(file);
1344     continue;
1345     }
1346    
1347 niro 816 G.outname = xasprintf("%sXXXXXX", argv[i]);
1348     nonstdoutfd = mkstemp(G.outname);
1349 niro 532 if (-1 == nonstdoutfd)
1350 niro 984 bb_perror_msg_and_die("can't create temp file %s", G.outname);
1351     G.nonstdout = xfdopen_for_write(nonstdoutfd);
1352 niro 532
1353 niro 984 /* Set permissions/owner of output file */
1354 niro 532 fstat(fileno(file), &statbuf);
1355     fchmod(nonstdoutfd, statbuf.st_mode);
1356 niro 984 fchown(nonstdoutfd, statbuf.st_uid, statbuf.st_gid);
1357 niro 532 add_input_file(file);
1358     process_files();
1359 niro 816 fclose(G.nonstdout);
1360 niro 532
1361 niro 816 G.nonstdout = stdout;
1362 niro 532 /* unlink(argv[i]); */
1363 niro 816 xrename(G.outname, argv[i]);
1364     free(G.outname);
1365     G.outname = NULL;
1366 niro 532 }
1367 niro 816 if (G.input_file_count > G.current_input_file)
1368 niro 532 process_files();
1369     }
1370    
1371     return status;
1372     }