1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61#include "libbb.h"
62#include "xregex.h"
63
64
65typedef struct sed_cmd_s {
66
67 struct sed_cmd_s *next;
68
69
70 regex_t *beg_match;
71 regex_t *end_match;
72 regex_t *sub_match;
73 int beg_line;
74 int end_line;
75
76 FILE *sw_file;
77 char *string;
78
79 unsigned which_match;
80
81
82 unsigned invert:1;
83 unsigned in_match:1;
84 unsigned sub_p:1;
85
86 char sw_last_char;
87
88
89 char cmd;
90} sed_cmd_t;
91
92static const char semicolon_whitespace[] ALIGN1 = "; \n\r\t\v";
93
94struct globals {
95
96 int be_quiet, regex_type;
97 FILE *nonstdout;
98 char *outname, *hold_space;
99
100
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
108 sed_cmd_t sed_cmd_head, *sed_cmd_tail;
109
110
111 llist_t *append_head;
112
113 char *add_cmd_line;
114
115 struct pipeline {
116 char *buf;
117 int idx;
118 int len;
119 } pipeline;
120};
121#define G (*(struct globals*)&bb_common_bufsiz1)
122void 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
131static void sed_free_and_close_stuff(void)
132{
133 sed_cmd_t *sed_cmd = G.sed_cmd_head.next;
134
135 llist_free(G.append_head, free);
136
137 while (sed_cmd) {
138 sed_cmd_t *sed_cmd_next = sed_cmd->next;
139
140 if (sed_cmd->sw_file)
141 xprint_and_close_file(sed_cmd->sw_file);
142
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 free(G.hold_space);
161
162 while (G.current_input_file < G.input_file_count)
163 fclose(G.input_file_list[G.current_input_file++]);
164}
165#else
166void sed_free_and_close_stuff(void);
167#endif
168
169
170
171static void cleanup_outname(void)
172{
173 if (G.outname) unlink(G.outname);
174}
175
176
177
178static void parse_escapes(char *dest, const char *string, int len, char from, char to)
179{
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
192 *dest++ = string[i++];
193 }
194 *dest = '\0';
195}
196
197static char *copy_parsing_escapes(const char *string, int len)
198{
199 char *dest = xmalloc(len + 1);
200
201 parse_escapes(dest, string, len, 'n', '\n');
202
203 parse_escapes(dest, dest, strlen(dest), 't', '\t');
204 return dest;
205}
206
207
208
209
210
211
212
213
214static int index_of_next_unescaped_regexp_delim(int delimiter, const char *str)
215{
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
242 bb_error_msg_and_die("unmatched '%c'", delimiter);
243}
244
245
246
247
248static int parse_regex_delim(const char *cmdstr, char **match, char **replace)
249{
250 const char *cmdstr_ptr = cmdstr;
251 char delimiter;
252 int idx = 0;
253
254
255
256 if (*cmdstr == '\0')
257 bb_error_msg_and_die("bad format in substitution expression");
258 delimiter = *cmdstr_ptr++;
259
260
261 idx = index_of_next_unescaped_regexp_delim(delimiter, cmdstr_ptr);
262 *match = copy_parsing_escapes(cmdstr_ptr, idx);
263
264
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
274
275static int get_address(const char *my_str, int *linenum, regex_t ** regex)
276{
277 const char *pos = my_str;
278
279 if (isdigit(*my_str)) {
280 *linenum = strtol(my_str, (char**)&pos, 10);
281
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 xregcomp(*regex, temp, G.regex_type|REG_NEWLINE);
296 free(temp);
297
298 pos += (next+1);
299 }
300 return pos - my_str;
301}
302
303
304static int parse_file_cmd( const char *filecmdstr, char **retval)
305{
306 int start = 0, idx, hack = 0;
307
308
309 while (isspace(filecmdstr[start]))
310 start++;
311 idx = start;
312 while (filecmdstr[idx] && filecmdstr[idx] != '\n')
313 idx++;
314
315
316 if (filecmdstr[idx] == '\n')
317 hack = 1;
318 if (idx == start)
319 bb_error_msg_and_die("empty filename");
320 *retval = xstrndup(filecmdstr+start, idx-start+hack+1);
321 if (hack)
322 (*retval)[idx] = '\\';
323
324 return idx;
325}
326
327static int parse_subst_cmd(sed_cmd_t *sed_cmd, const char *substr)
328{
329 int cflags = G.regex_type;
330 char *match;
331 int idx;
332
333
334
335
336
337
338
339 idx = parse_regex_delim(substr, &match, &sed_cmd->string);
340
341
342
343
344
345
346
347
348 sed_cmd->which_match = 1;
349 while (substr[++idx]) {
350
351 if (isdigit(substr[idx])) {
352 if (match[0] != '^') {
353
354 const char *pos = substr + idx;
355
356 sed_cmd->which_match = (unsigned)strtol(substr+idx, (char**) &pos, 10);
357 idx = pos - substr;
358 }
359 continue;
360 }
361
362 if (isspace(substr[idx])) continue;
363
364 switch (substr[idx]) {
365
366 case 'g':
367 if (match[0] != '^')
368 sed_cmd->which_match = 0;
369 break;
370
371 case 'p':
372 sed_cmd->sub_p = 1;
373 break;
374
375 case 'w':
376 {
377 char *temp;
378 idx += parse_file_cmd( substr+idx, &temp);
379 break;
380 }
381
382 case 'I':
383 cflags |= REG_ICASE;
384 break;
385
386 case '#':
387 while (substr[++idx]) ;
388
389
390 case ';':
391 case '}':
392 goto out;
393 default:
394 bb_error_msg_and_die("bad option in substitution expression");
395 }
396 }
397out:
398
399 if (*match != '\0') {
400
401 sed_cmd->sub_match = xmalloc(sizeof(regex_t));
402 xregcomp(sed_cmd->sub_match, match, cflags);
403 }
404 free(match);
405
406 return idx;
407}
408
409
410
411
412static const char *parse_cmd_args(sed_cmd_t *sed_cmd, const char *cmdstr)
413{
414
415 if (sed_cmd->cmd == 's')
416 cmdstr += parse_subst_cmd(sed_cmd, cmdstr);
417
418 else if (strchr("aic", sed_cmd->cmd)) {
419 if ((sed_cmd->end_line || sed_cmd->end_match) && sed_cmd->cmd != 'c')
420 bb_error_msg_and_die
421 ("only a beginning address can be specified for edit commands");
422 for (;;) {
423 if (*cmdstr == '\n' || *cmdstr == '\\') {
424 cmdstr++;
425 break;
426 } else if (isspace(*cmdstr))
427 cmdstr++;
428 else
429 break;
430 }
431 sed_cmd->string = xstrdup(cmdstr);
432
433 parse_escapes(sed_cmd->string, sed_cmd->string, strlen(cmdstr), '\0', '\0');
434 cmdstr += strlen(cmdstr);
435
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 cmdstr += parse_file_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
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
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
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
474
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
481 return cmdstr;
482}
483
484
485
486
487static void add_cmd(const char *cmdstr)
488{
489 sed_cmd_t *sed_cmd;
490 int temp;
491
492
493 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 }
498
499
500 temp = strlen(cmdstr);
501 if (temp && cmdstr[--temp] == '\\') {
502 if (!G.add_cmd_line)
503 G.add_cmd_line = xstrdup(cmdstr);
504 G.add_cmd_line[temp] = '\0';
505 return;
506 }
507
508
509 while (*cmdstr) {
510
511 cmdstr += strspn(cmdstr, semicolon_whitespace);
512
513
514 if (!*cmdstr) break;
515
516
517 if (*cmdstr == '#') {
518
519 if (cmdstr[1] == 'n')
520 G.be_quiet++;
521 cmdstr = strpbrk(cmdstr, "\n\r");
522 if (!cmdstr) break;
523 continue;
524 }
525
526
527
528
529
530
531
532 sed_cmd = xzalloc(sizeof(sed_cmd_t));
533
534
535 cmdstr += get_address(cmdstr, &sed_cmd->beg_line, &sed_cmd->beg_match);
536
537
538 if (*cmdstr == ',') {
539 int idx;
540
541 cmdstr++;
542 idx = get_address(cmdstr, &sed_cmd->end_line, &sed_cmd->end_match);
543 if (!idx)
544 bb_error_msg_and_die("no address after comma");
545 cmdstr += idx;
546 }
547
548
549 cmdstr = skip_whitespace(cmdstr);
550
551
552 if (*cmdstr == '!') {
553 sed_cmd->invert = 1;
554 cmdstr++;
555
556
557 cmdstr = skip_whitespace(cmdstr);
558 }
559
560
561 if (!*cmdstr)
562 bb_error_msg_and_die("missing command");
563 sed_cmd->cmd = *(cmdstr++);
564 cmdstr = parse_cmd_args(sed_cmd, cmdstr);
565
566
567 G.sed_cmd_tail->next = sed_cmd;
568 G.sed_cmd_tail = G.sed_cmd_tail->next;
569 }
570
571
572 free(G.add_cmd_line);
573 G.add_cmd_line = NULL;
574}
575
576
577
578#define PIPE_GROW 64
579
580static void pipe_putc(char c)
581{
582 if (G.pipeline.idx == G.pipeline.len) {
583 G.pipeline.buf = xrealloc(G.pipeline.buf,
584 G.pipeline.len + PIPE_GROW);
585 G.pipeline.len += PIPE_GROW;
586 }
587 G.pipeline.buf[G.pipeline.idx++] = c;
588}
589
590static void do_subst_w_backrefs(char *line, char *replace)
591{
592 int i,j;
593
594
595 for (i = 0; replace[i]; i++) {
596
597 if (replace[i] == '\\') {
598 unsigned backref = replace[++i] - '0';
599 if (backref <= 9) {
600
601 if (G.regmatch[backref].rm_so != -1) {
602 j = G.regmatch[backref].rm_so;
603 while (j < G.regmatch[backref].rm_eo)
604 pipe_putc(line[j++]);
605 }
606 continue;
607 }
608
609
610
611
612 pipe_putc(replace[i]);
613 continue;
614 }
615
616 if (replace[i] == '&') {
617 j = G.regmatch[0].rm_so;
618 while (j < G.regmatch[0].rm_eo)
619 pipe_putc(line[j++]);
620 continue;
621 }
622
623 pipe_putc(replace[i]);
624 }
625}
626
627static int do_subst_command(sed_cmd_t *sed_cmd, char **line)
628{
629 char *oldline = *line;
630 int altered = 0;
631 unsigned match_count = 0;
632 regex_t *current_regex;
633
634
635 if (sed_cmd->sub_match == NULL) {
636 current_regex = G.previous_regex_ptr;
637 if (!current_regex)
638 bb_error_msg_and_die("no previous regexp");
639 } else
640 G.previous_regex_ptr = current_regex = sed_cmd->sub_match;
641
642
643 if (REG_NOMATCH == regexec(current_regex, oldline, 10, G.regmatch, 0))
644 return 0;
645
646
647 G.pipeline.buf = xmalloc(PIPE_GROW);
648 G.pipeline.len = PIPE_GROW;
649 G.pipeline.idx = 0;
650
651
652 do {
653 int i;
654
655
656
657
658
659 if (!G.regmatch[0].rm_so && !G.regmatch[0].rm_eo && match_count) {
660 pipe_putc(*oldline++);
661 continue;
662 }
663
664 match_count++;
665
666
667
668 if (sed_cmd->which_match
669 && (sed_cmd->which_match != match_count)
670 ) {
671 for (i = 0; i < G.regmatch[0].rm_eo; i++)
672 pipe_putc(*oldline++);
673 continue;
674 }
675
676
677 for (i = 0; i < G.regmatch[0].rm_so; i++)
678 pipe_putc(oldline[i]);
679
680
681 do_subst_w_backrefs(oldline, sed_cmd->string);
682
683
684 oldline += G.regmatch[0].rm_eo;
685
686 altered++;
687
688
689 if (sed_cmd->which_match)
690 break;
691 } while (*oldline && (regexec(current_regex, oldline, 10, G.regmatch, 0) != REG_NOMATCH));
692
693
694
695 while (*oldline)
696 pipe_putc(*oldline++);
697 pipe_putc(0);
698
699 free(*line);
700 *line = G.pipeline.buf;
701 return altered;
702}
703
704
705static sed_cmd_t *branch_to(char *label)
706{
707 sed_cmd_t *sed_cmd;
708
709 for (sed_cmd = G.sed_cmd_head.next; sed_cmd; sed_cmd = sed_cmd->next) {
710 if (sed_cmd->cmd == ':' && sed_cmd->string && !strcmp(sed_cmd->string, label)) {
711 return sed_cmd;
712 }
713 }
714 bb_error_msg_and_die("can't find label for jump to '%s'", label);
715}
716
717static void append(char *s)
718{
719 llist_add_to_end(&G.append_head, xstrdup(s));
720}
721
722static void flush_append(void)
723{
724 char *data;
725
726
727 while ((data = (char *)llist_pop(&G.append_head))) {
728 fprintf(G.nonstdout, "%s\n", data);
729 free(data);
730 }
731}
732
733static void add_input_file(FILE *file)
734{
735 G.input_file_list = xrealloc_vector(G.input_file_list, 2, G.input_file_count);
736 G.input_file_list[G.input_file_count++] = file;
737}
738
739
740
741
742enum {
743 NO_EOL_CHAR = 1,
744 LAST_IS_NUL = 2,
745};
746static char *get_next_line(char *gets_char)
747{
748 char *temp = NULL;
749 int len;
750 char gc;
751
752 flush_append();
753
754
755
756 gc = NO_EOL_CHAR;
757 while (G.current_input_file < G.input_file_count) {
758 FILE *fp = G.input_file_list[G.current_input_file];
759
760
761
762 temp = bb_get_chunk_from_file(fp, &len);
763 if (temp) {
764
765 char c = temp[len-1];
766 if (c == '\n' || c == '\0') {
767 temp[len-1] = '\0';
768 gc = c;
769 if (c == '\0') {
770 int ch = fgetc(fp);
771 if (ch != EOF)
772 ungetc(ch, fp);
773 else
774 gc = LAST_IS_NUL;
775 }
776 }
777
778 break;
779
780
781
782
783
784
785
786
787 }
788
789 fclose(fp);
790 G.current_input_file++;
791 }
792 *gets_char = gc;
793 return temp;
794}
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810static void puts_maybe_newline(char *s, FILE *file, char *last_puts_char, char last_gets_char)
811{
812 char lpc = *last_puts_char;
813
814
815
816 if (lpc != '\n' && lpc != '\0') {
817 fputc('\n', file);
818 lpc = '\n';
819 }
820 fputs(s, file);
821
822
823 if (s[0])
824 lpc = 'x';
825
826
827 if (last_gets_char == LAST_IS_NUL) {
828 fputc('\0', file);
829 lpc = 'x';
830 } else
831
832 if (last_gets_char != NO_EOL_CHAR) {
833 fputc(last_gets_char, file);
834 lpc = last_gets_char;
835 }
836
837 if (ferror(file)) {
838 xfunc_error_retval = 4;
839 bb_error_msg_and_die(bb_msg_write_error);
840 }
841 *last_puts_char = lpc;
842}
843
844#define sed_puts(s, n) (puts_maybe_newline(s, G.nonstdout, &last_puts_char, n))
845
846static int beg_match(sed_cmd_t *sed_cmd, const char *pattern_space)
847{
848 int retval = sed_cmd->beg_match && !regexec(sed_cmd->beg_match, pattern_space, 0, NULL, 0);
849 if (retval)
850 G.previous_regex_ptr = sed_cmd->beg_match;
851 return retval;
852}
853
854
855
856static void process_files(void)
857{
858 char *pattern_space, *next_line;
859 int linenum = 0;
860 char last_puts_char = '\n';
861 char last_gets_char, next_gets_char;
862 sed_cmd_t *sed_cmd;
863 int substituted;
864
865
866 next_line = get_next_line(&next_gets_char);
867
868
869 again:
870 substituted = 0;
871
872
873 pattern_space = next_line;
874 if (!pattern_space) return;
875 last_gets_char = next_gets_char;
876
877
878
879 next_line = get_next_line(&next_gets_char);
880 linenum++;
881 restart:
882
883 for (sed_cmd = G.sed_cmd_head.next; sed_cmd; sed_cmd = sed_cmd->next) {
884 int old_matched, matched;
885
886 old_matched = sed_cmd->in_match;
887
888
889
890
891 sed_cmd->in_match = sed_cmd->in_match
892
893 || (!sed_cmd->beg_line && !sed_cmd->end_line
894 && !sed_cmd->beg_match && !sed_cmd->end_match)
895
896 || (sed_cmd->beg_line > 0 && (sed_cmd->beg_line == linenum))
897
898 || (beg_match(sed_cmd, pattern_space))
899
900 || (sed_cmd->beg_line == -1 && next_line == NULL);
901
902
903
904 matched = sed_cmd->in_match;
905
906
907
908 if (matched) {
909 sed_cmd->in_match = !(
910
911 (sed_cmd->end_line ?
912 sed_cmd->end_line == -1 ?
913 !next_line
914 : (sed_cmd->end_line <= linenum)
915 : !sed_cmd->end_match
916 )
917
918 || (sed_cmd->end_match && old_matched
919 && (regexec(sed_cmd->end_match,
920 pattern_space, 0, NULL, 0) == 0))
921 );
922 }
923
924
925 if (sed_cmd->cmd == '{') {
926 if (sed_cmd->invert ? matched : !matched) {
927 while (sed_cmd->cmd != '}') {
928 sed_cmd = sed_cmd->next;
929 if (!sed_cmd)
930 bb_error_msg_and_die("unterminated {");
931 }
932 }
933 continue;
934 }
935
936
937 if (sed_cmd->invert ? !matched : matched) {
938
939 if (sed_cmd->beg_match) {
940 G.previous_regex_ptr = sed_cmd->beg_match;
941 }
942
943
944 switch (sed_cmd->cmd) {
945
946
947 case '=':
948 fprintf(G.nonstdout, "%d\n", linenum);
949 break;
950
951
952 case 'P':
953 {
954 char *tmp = strchr(pattern_space, '\n');
955
956 if (tmp) {
957 *tmp = '\0';
958
959 sed_puts(pattern_space, '\n');
960 *tmp = '\n';
961 break;
962 }
963
964 }
965
966
967 case 'p':
968
969
970
971
972 sed_puts(pattern_space, '\n');
973 break;
974
975 case 'D':
976 {
977 char *tmp = strchr(pattern_space, '\n');
978
979 if (tmp) {
980 tmp = xstrdup(tmp+1);
981 free(pattern_space);
982 pattern_space = tmp;
983 goto restart;
984 }
985 }
986
987 case 'd':
988 goto discard_line;
989
990
991 case 's':
992 if (!do_subst_command(sed_cmd, &pattern_space))
993 break;
994 substituted |= 1;
995
996
997 if (sed_cmd->sub_p)
998 sed_puts(pattern_space, last_gets_char);
999
1000 if (sed_cmd->sw_file)
1001 puts_maybe_newline(
1002 pattern_space, sed_cmd->sw_file,
1003 &sed_cmd->sw_last_char, last_gets_char);
1004 break;
1005
1006
1007 case 'a':
1008 append(sed_cmd->string);
1009 break;
1010
1011
1012 case 'i':
1013 sed_puts(sed_cmd->string, '\n');
1014 break;
1015
1016
1017 case 'c':
1018
1019 if (!sed_cmd->in_match)
1020 sed_puts(sed_cmd->string, NO_EOL_CHAR);
1021 goto discard_line;
1022
1023
1024 case 'r':
1025 {
1026 FILE *rfile;
1027
1028 rfile = fopen_for_read(sed_cmd->string);
1029 if (rfile) {
1030 char *line;
1031
1032 while ((line = xmalloc_fgetline(rfile))
1033 != NULL)
1034 append(line);
1035 xprint_and_close_file(rfile);
1036 }
1037
1038 break;
1039 }
1040
1041
1042 case 'w':
1043 puts_maybe_newline(
1044 pattern_space, sed_cmd->sw_file,
1045 &sed_cmd->sw_last_char, last_gets_char);
1046 break;
1047
1048
1049 case 'n':
1050 if (!G.be_quiet)
1051 sed_puts(pattern_space, last_gets_char);
1052 if (next_line) {
1053 free(pattern_space);
1054 pattern_space = next_line;
1055 last_gets_char = next_gets_char;
1056 next_line = get_next_line(&next_gets_char);
1057 substituted = 0;
1058 linenum++;
1059 break;
1060 }
1061
1062
1063
1064 case 'q':
1065
1066 free(next_line);
1067 next_line = NULL;
1068 goto discard_commands;
1069
1070
1071 case 'N':
1072 {
1073 int len;
1074
1075 if (next_line == NULL) {
1076
1077 free(next_line);
1078 next_line = NULL;
1079 goto discard_line;
1080
1081 }
1082 len = strlen(pattern_space);
1083 pattern_space = realloc(pattern_space, len + strlen(next_line) + 2);
1084 pattern_space[len] = '\n';
1085 strcpy(pattern_space + len+1, next_line);
1086 last_gets_char = next_gets_char;
1087 next_line = get_next_line(&next_gets_char);
1088 linenum++;
1089 break;
1090 }
1091
1092
1093 case 't':
1094 if (!substituted) break;
1095 substituted = 0;
1096
1097
1098 case 'T':
1099 if (substituted) break;
1100
1101
1102 case 'b':
1103 if (!sed_cmd->string) goto discard_commands;
1104 else sed_cmd = branch_to(sed_cmd->string);
1105 break;
1106
1107 case 'y':
1108 {
1109 int i, j;
1110
1111 for (i = 0; pattern_space[i]; i++) {
1112 for (j = 0; sed_cmd->string[j]; j += 2) {
1113 if (pattern_space[i] == sed_cmd->string[j]) {
1114 pattern_space[i] = sed_cmd->string[j + 1];
1115 break;
1116 }
1117 }
1118 }
1119
1120 break;
1121 }
1122 case 'g':
1123 free(pattern_space);
1124 pattern_space = xstrdup(G.hold_space ? G.hold_space : "");
1125 break;
1126 case 'G':
1127 {
1128 int pattern_space_size = 2;
1129 int hold_space_size = 0;
1130
1131 if (pattern_space)
1132 pattern_space_size += strlen(pattern_space);
1133 if (G.hold_space)
1134 hold_space_size = strlen(G.hold_space);
1135 pattern_space = xrealloc(pattern_space,
1136 pattern_space_size + hold_space_size);
1137 if (pattern_space_size == 2)
1138 pattern_space[0] = 0;
1139 strcat(pattern_space, "\n");
1140 if (G.hold_space)
1141 strcat(pattern_space, G.hold_space);
1142 last_gets_char = '\n';
1143
1144 break;
1145 }
1146 case 'h':
1147 free(G.hold_space);
1148 G.hold_space = xstrdup(pattern_space);
1149 break;
1150 case 'H':
1151 {
1152 int hold_space_size = 2;
1153 int pattern_space_size = 0;
1154
1155 if (G.hold_space)
1156 hold_space_size += strlen(G.hold_space);
1157 if (pattern_space)
1158 pattern_space_size = strlen(pattern_space);
1159 G.hold_space = xrealloc(G.hold_space,
1160 hold_space_size + pattern_space_size);
1161
1162 if (hold_space_size == 2)
1163 *G.hold_space = 0;
1164 strcat(G.hold_space, "\n");
1165 if (pattern_space)
1166 strcat(G.hold_space, pattern_space);
1167
1168 break;
1169 }
1170 case 'x':
1171 {
1172 char *tmp = pattern_space;
1173 pattern_space = G.hold_space ? : xzalloc(1);
1174 last_gets_char = '\n';
1175 G.hold_space = tmp;
1176 break;
1177 }
1178 }
1179 }
1180 }
1181
1182
1183
1184
1185 discard_commands:
1186
1187
1188 if (!G.be_quiet)
1189 sed_puts(pattern_space, last_gets_char);
1190
1191
1192 discard_line:
1193 flush_append();
1194 free(pattern_space);
1195
1196 goto again;
1197}
1198
1199
1200
1201
1202
1203
1204
1205static void add_cmd_block(char *cmdstr)
1206{
1207 char *sv, *eol;
1208
1209 cmdstr = sv = xstrdup(cmdstr);
1210 do {
1211 eol = strchr(cmdstr, '\n');
1212 next:
1213 if (eol) {
1214
1215 int slashes = 0;
1216 char *sl = eol;
1217
1218 while (sl != cmdstr && *--sl == '\\')
1219 slashes++;
1220
1221 if (slashes & 1) {
1222 overlapping_strcpy(eol - 1, eol);
1223 eol = strchr(eol, '\n');
1224 goto next;
1225 }
1226 *eol = '\0';
1227 }
1228 add_cmd(cmdstr);
1229 cmdstr = eol + 1;
1230 } while (eol);
1231 free(sv);
1232}
1233
1234int sed_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1235int sed_main(int argc UNUSED_PARAM, char **argv)
1236{
1237 enum {
1238 OPT_in_place = 1 << 0,
1239 };
1240 unsigned opt;
1241 llist_t *opt_e, *opt_f;
1242 int status = EXIT_SUCCESS;
1243
1244 INIT_G();
1245
1246
1247 if (ENABLE_FEATURE_CLEAN_UP) atexit(sed_free_and_close_stuff);
1248
1249
1250 if (argv[1] && !strcmp(argv[1], "--version")) {
1251 puts("This is not GNU sed version 4.0");
1252 return 0;
1253 }
1254
1255
1256 opt_e = opt_f = NULL;
1257 opt_complementary = "e::f::"
1258 "nn";
1259 opt = getopt32(argv, "irne:f:", &opt_e, &opt_f,
1260 &G.be_quiet);
1261
1262 argv += optind;
1263 if (opt & OPT_in_place) {
1264 atexit(cleanup_outname);
1265 }
1266 if (opt & 0x2) G.regex_type |= REG_EXTENDED;
1267
1268 while (opt_e) {
1269 add_cmd_block(llist_pop(&opt_e));
1270 }
1271 while (opt_f) {
1272 char *line;
1273 FILE *cmdfile;
1274 cmdfile = xfopen_for_read(llist_pop(&opt_f));
1275 while ((line = xmalloc_fgetline(cmdfile)) != NULL) {
1276 add_cmd(line);
1277 free(line);
1278 }
1279 fclose(cmdfile);
1280 }
1281
1282 if (!(opt & 0x18)) {
1283 if (!*argv)
1284 bb_show_usage();
1285 add_cmd_block(*argv++);
1286 }
1287
1288 add_cmd("");
1289
1290
1291 G.nonstdout = stdout;
1292
1293
1294
1295
1296 if (argv[0] == NULL) {
1297 if (opt & OPT_in_place)
1298 bb_error_msg_and_die(bb_msg_requires_arg, "-i");
1299 add_input_file(stdin);
1300 process_files();
1301 } else {
1302 int i;
1303 FILE *file;
1304
1305 for (i = 0; argv[i]; i++) {
1306 struct stat statbuf;
1307 int nonstdoutfd;
1308
1309 if (LONE_DASH(argv[i]) && !(opt & OPT_in_place)) {
1310 add_input_file(stdin);
1311 process_files();
1312 continue;
1313 }
1314 file = fopen_or_warn(argv[i], "r");
1315 if (!file) {
1316 status = EXIT_FAILURE;
1317 continue;
1318 }
1319 if (!(opt & OPT_in_place)) {
1320 add_input_file(file);
1321 continue;
1322 }
1323
1324 G.outname = xasprintf("%sXXXXXX", argv[i]);
1325 nonstdoutfd = mkstemp(G.outname);
1326 if (-1 == nonstdoutfd)
1327 bb_perror_msg_and_die("cannot create temp file %s", G.outname);
1328 G.nonstdout = fdopen(nonstdoutfd, "w");
1329
1330
1331
1332 fstat(fileno(file), &statbuf);
1333 fchmod(nonstdoutfd, statbuf.st_mode);
1334 add_input_file(file);
1335 process_files();
1336 fclose(G.nonstdout);
1337
1338 G.nonstdout = stdout;
1339
1340 xrename(G.outname, argv[i]);
1341 free(G.outname);
1342 G.outname = NULL;
1343 }
1344 if (G.input_file_count > G.current_input_file)
1345 process_files();
1346 }
1347
1348 return status;
1349}
1350