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