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
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85#include "libbb.h"
86#include "common_bufsiz.h"
87#include "xregex.h"
88
89#if 0
90# define dbg(...) bb_error_msg(__VA_ARGS__)
91#else
92# define dbg(...) ((void)0)
93#endif
94
95
96enum {
97 OPT_in_place = 1 << 0,
98};
99
100
101typedef struct sed_cmd_s {
102
103 struct sed_cmd_s *next;
104
105
106 regex_t *beg_match;
107 regex_t *end_match;
108 regex_t *sub_match;
109 int beg_line;
110 int beg_line_orig;
111 int end_line;
112 int end_line_orig;
113
114 FILE *sw_file;
115 char *string;
116
117 unsigned which_match;
118
119
120 unsigned invert:1;
121 unsigned in_match:1;
122 unsigned sub_p:1;
123
124 char sw_last_char;
125
126
127 char cmd;
128} sed_cmd_t;
129
130static const char semicolon_whitespace[] ALIGN1 = "; \n\r\t\v";
131
132struct globals {
133
134 int be_quiet, regex_type;
135
136 FILE *nonstdout;
137 char *outname, *hold_space;
138 smallint exitcode;
139
140
141 int current_input_file, last_input_file;
142 char **input_file_list;
143 FILE *current_fp;
144
145 regmatch_t regmatch[10];
146 regex_t *previous_regex_ptr;
147
148
149 sed_cmd_t *sed_cmd_head, **sed_cmd_tail;
150
151
152 llist_t *append_head;
153
154 char *add_cmd_line;
155
156 struct pipeline {
157 char *buf;
158 int idx;
159 int len;
160 } pipeline;
161} FIX_ALIASING;
162#define G (*(struct globals*)bb_common_bufsiz1)
163#define INIT_G() do { \
164 setup_common_bufsiz(); \
165 BUILD_BUG_ON(sizeof(G) > COMMON_BUFSIZE); \
166 G.sed_cmd_tail = &G.sed_cmd_head; \
167} while (0)
168
169
170#if ENABLE_FEATURE_CLEAN_UP
171static void sed_free_and_close_stuff(void)
172{
173 sed_cmd_t *sed_cmd = G.sed_cmd_head;
174
175 llist_free(G.append_head, free);
176
177 while (sed_cmd) {
178 sed_cmd_t *sed_cmd_next = sed_cmd->next;
179
180 if (sed_cmd->sw_file)
181 fclose(sed_cmd->sw_file);
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200 free(sed_cmd->string);
201 free(sed_cmd);
202 sed_cmd = sed_cmd_next;
203 }
204
205 free(G.hold_space);
206
207 if (G.current_fp)
208 fclose(G.current_fp);
209}
210#else
211void sed_free_and_close_stuff(void);
212#endif
213
214
215
216static void cleanup_outname(void)
217{
218 if (G.outname) unlink(G.outname);
219}
220
221
222
223static unsigned parse_escapes(char *dest, const char *string, int len, char from, char to)
224{
225 char *d = dest;
226 int i = 0;
227
228 if (len == -1)
229 len = strlen(string);
230
231 while (i < len) {
232 if (string[i] == '\\') {
233 if (!to || string[i+1] == from) {
234 if ((*d = to ? to : string[i+1]) == '\0')
235 return d - dest;
236 i += 2;
237 d++;
238 continue;
239 }
240 i++;
241 *d++ = '\\';
242
243 }
244 if ((*d = string[i++]) == '\0')
245 return d - dest;
246 d++;
247 }
248 *d = '\0';
249 return d - dest;
250}
251
252static char *copy_parsing_escapes(const char *string, int len)
253{
254 const char *s;
255 char *dest = xmalloc(len + 1);
256
257
258
259 for (s = "\nn\tt\rr"; *s; s += 2) {
260 len = parse_escapes(dest, string, len, s[1], s[0]);
261 string = dest;
262 }
263 return dest;
264}
265
266
267
268
269
270
271
272
273static int index_of_next_unescaped_regexp_delim(int delimiter, const char *str)
274{
275 int bracket = -1;
276 int escaped = 0;
277 int idx = 0;
278 char ch;
279
280 if (delimiter < 0) {
281 bracket--;
282 delimiter = -delimiter;
283 }
284
285 for (; (ch = str[idx]) != '\0'; idx++) {
286 if (bracket >= 0) {
287 if (ch == ']'
288 && !(bracket == idx - 1 || (bracket == idx - 2 && str[idx - 1] == '^'))
289 ) {
290 bracket = -1;
291 }
292 } else if (escaped)
293 escaped = 0;
294 else if (ch == '\\')
295 escaped = 1;
296 else if (bracket == -1 && ch == '[')
297 bracket = idx;
298 else if (ch == delimiter)
299 return idx;
300 }
301
302
303 bb_error_msg_and_die("unmatched '%c'", delimiter);
304}
305
306
307
308
309static int parse_regex_delim(const char *cmdstr, char **match, char **replace)
310{
311 const char *cmdstr_ptr = cmdstr;
312 unsigned char delimiter;
313 int idx = 0;
314
315
316
317 if (*cmdstr == '\0')
318 bb_simple_error_msg_and_die("bad format in substitution expression");
319 delimiter = *cmdstr_ptr++;
320
321
322 idx = index_of_next_unescaped_regexp_delim(delimiter, cmdstr_ptr);
323 *match = copy_parsing_escapes(cmdstr_ptr, idx);
324
325
326 cmdstr_ptr += idx + 1;
327 idx = index_of_next_unescaped_regexp_delim(- (int)delimiter, cmdstr_ptr);
328 *replace = copy_parsing_escapes(cmdstr_ptr, idx);
329
330 return ((cmdstr_ptr - cmdstr) + idx);
331}
332
333
334
335
336static int get_address(const char *my_str, int *linenum, regex_t ** regex)
337{
338 const char *pos = my_str;
339
340 if (isdigit(*my_str)) {
341 *linenum = strtol(my_str, (char**)&pos, 10);
342
343 } else if (*my_str == '$') {
344 *linenum = -1;
345 pos++;
346 } else if (*my_str == '/' || *my_str == '\\') {
347 int next;
348 char delimiter;
349 char *temp;
350
351 delimiter = '/';
352 if (*my_str == '\\')
353 delimiter = *++pos;
354 next = index_of_next_unescaped_regexp_delim(delimiter, ++pos);
355 if (next != 0) {
356 temp = copy_parsing_escapes(pos, next);
357 G.previous_regex_ptr = *regex = xzalloc(sizeof(regex_t));
358 xregcomp(*regex, temp, G.regex_type);
359 free(temp);
360 } else {
361 *regex = G.previous_regex_ptr;
362 if (!G.previous_regex_ptr)
363 bb_simple_error_msg_and_die("no previous regexp");
364 }
365
366 pos += (next+1);
367 }
368 return pos - my_str;
369}
370
371
372static int parse_file_cmd( const char *filecmdstr, char **retval)
373{
374 const char *start;
375 const char *eol;
376
377
378 start = skip_whitespace(filecmdstr);
379 eol = strchrnul(start, '\n');
380 if (eol == start)
381 bb_simple_error_msg_and_die("empty filename");
382
383 if (*eol) {
384
385 *retval = xstrndup(start, eol-start + 1);
386 (*retval)[eol-start] = '\\';
387 } else {
388
389 *retval = xstrdup(start);
390 }
391
392 return eol - filecmdstr;
393}
394
395static int parse_subst_cmd(sed_cmd_t *sed_cmd, const char *substr)
396{
397 int cflags = G.regex_type;
398 char *match;
399 int idx;
400
401
402
403
404
405
406
407 idx = parse_regex_delim(substr, &match, &sed_cmd->string);
408
409
410
411
412
413
414
415
416 sed_cmd->which_match = 1;
417 dbg("s flags:'%s'", substr + idx + 1);
418 while (substr[++idx]) {
419 dbg("s flag:'%c'", substr[idx]);
420
421 if (isdigit(substr[idx])) {
422 if (match[0] != '^') {
423
424 const char *pos = substr + idx;
425
426 sed_cmd->which_match = (unsigned)strtol(substr+idx, (char**) &pos, 10);
427 idx = pos - substr - 1;
428 }
429 continue;
430 }
431
432 if (isspace(substr[idx]))
433 continue;
434
435 switch (substr[idx]) {
436
437 case 'g':
438 if (match[0] != '^')
439 sed_cmd->which_match = 0;
440 break;
441
442 case 'p':
443 sed_cmd->sub_p = 1;
444 break;
445
446 case 'w':
447 {
448 char *fname;
449 idx += parse_file_cmd( substr+idx+1, &fname);
450 sed_cmd->sw_file = xfopen_for_write(fname);
451 sed_cmd->sw_last_char = '\n';
452 free(fname);
453 break;
454 }
455
456 case 'i':
457 case 'I':
458 cflags |= REG_ICASE;
459 break;
460
461 case '#':
462
463 idx += strlen(substr + idx);
464
465
466 case ';':
467 case '}':
468 goto out;
469 default:
470 dbg("s bad flags:'%s'", substr + idx);
471 bb_simple_error_msg_and_die("bad option in substitution expression");
472 }
473 }
474 out:
475
476 if (*match != '\0') {
477
478 sed_cmd->sub_match = xzalloc(sizeof(regex_t));
479 dbg("xregcomp('%s',%x)", match, cflags);
480 xregcomp(sed_cmd->sub_match, match, cflags);
481 dbg("regcomp ok");
482 }
483 free(match);
484
485 return idx;
486}
487
488
489
490
491static const char *parse_cmd_args(sed_cmd_t *sed_cmd, const char *cmdstr)
492{
493 static const char cmd_letters[] ALIGN1 = "saicrw:btTydDgGhHlnNpPqx={}";
494 enum {
495 IDX_s = 0,
496 IDX_a,
497 IDX_i,
498 IDX_c,
499 IDX_r,
500 IDX_w,
501 IDX_colon,
502 IDX_b,
503 IDX_t,
504 IDX_T,
505 IDX_y,
506 IDX_d,
507 IDX_D,
508 IDX_g,
509 IDX_G,
510 IDX_h,
511 IDX_H,
512 IDX_l,
513 IDX_n,
514 IDX_N,
515 IDX_p,
516 IDX_P,
517 IDX_q,
518 IDX_x,
519 IDX_equal,
520 IDX_lbrace,
521 IDX_rbrace,
522 IDX_nul
523 };
524 unsigned idx;
525
526 BUILD_BUG_ON(sizeof(cmd_letters)-1 != IDX_nul);
527
528 idx = strchrnul(cmd_letters, sed_cmd->cmd) - cmd_letters;
529
530
531 if (idx == IDX_s) {
532 cmdstr += parse_subst_cmd(sed_cmd, cmdstr);
533 }
534
535 else if (idx <= IDX_c) {
536 unsigned len;
537
538 if (idx < IDX_c) {
539 if (sed_cmd->end_line || sed_cmd->end_match)
540 bb_error_msg_and_die("command '%c' uses only one address", sed_cmd->cmd);
541 }
542 for (;;) {
543 if (*cmdstr == '\n' || *cmdstr == '\\') {
544 cmdstr++;
545 break;
546 }
547 if (!isspace(*cmdstr))
548 break;
549 cmdstr++;
550 }
551 len = strlen(cmdstr);
552 sed_cmd->string = copy_parsing_escapes(cmdstr, len);
553 cmdstr += len;
554
555 parse_escapes(sed_cmd->string, sed_cmd->string, -1, '\0', '\0');
556 }
557
558 else if (idx <= IDX_w) {
559 if (idx < IDX_w) {
560 if (sed_cmd->end_line || sed_cmd->end_match)
561 bb_error_msg_and_die("command '%c' uses only one address", sed_cmd->cmd);
562 }
563 cmdstr += parse_file_cmd( cmdstr, &sed_cmd->string);
564 if (sed_cmd->cmd == 'w') {
565 sed_cmd->sw_file = xfopen_for_write(sed_cmd->string);
566 sed_cmd->sw_last_char = '\n';
567 }
568 }
569
570 else if (idx <= IDX_T) {
571 int length;
572
573 cmdstr = skip_whitespace(cmdstr);
574 length = strcspn(cmdstr, semicolon_whitespace);
575 if (length) {
576 sed_cmd->string = xstrndup(cmdstr, length);
577 cmdstr += length;
578 }
579 }
580
581 else if (idx == IDX_y) {
582 char *match, *replace;
583 int i = cmdstr[0];
584
585 cmdstr += parse_regex_delim(cmdstr, &match, &replace)+1;
586
587 parse_escapes(match, match, -1, i, i);
588 parse_escapes(replace, replace, -1, i, i);
589
590 sed_cmd->string = xzalloc((strlen(match) + 1) * 2);
591 for (i = 0; match[i] && replace[i]; i++) {
592 sed_cmd->string[i*2] = match[i];
593 sed_cmd->string[i*2+1] = replace[i];
594 }
595 free(match);
596 free(replace);
597 }
598
599
600
601 else if (idx >= IDX_nul) {
602 bb_error_msg_and_die("unsupported command %c", sed_cmd->cmd);
603 }
604
605
606 return cmdstr;
607}
608
609
610
611
612static void add_cmd(const char *cmdstr)
613{
614 sed_cmd_t *sed_cmd;
615 unsigned len, n;
616
617
618 if (G.add_cmd_line) {
619 char *tp = xasprintf("%s\n%s", G.add_cmd_line, cmdstr);
620 free(G.add_cmd_line);
621 cmdstr = G.add_cmd_line = tp;
622 }
623
624
625 n = len = strlen(cmdstr);
626 while (n && cmdstr[n-1] == '\\')
627 n--;
628 if ((len - n) & 1) {
629 if (!G.add_cmd_line)
630 G.add_cmd_line = xstrdup(cmdstr);
631 G.add_cmd_line[len-1] = '\0';
632 return;
633 }
634
635
636 while (*cmdstr) {
637
638 cmdstr += strspn(cmdstr, semicolon_whitespace);
639
640
641 if (!*cmdstr) break;
642
643
644 if (*cmdstr == '#') {
645
646 if (cmdstr[1] == 'n')
647 G.be_quiet++;
648 cmdstr = strpbrk(cmdstr, "\n\r");
649 if (!cmdstr) break;
650 continue;
651 }
652
653
654
655
656
657
658
659 sed_cmd = xzalloc(sizeof(sed_cmd_t));
660
661
662 cmdstr += get_address(cmdstr, &sed_cmd->beg_line, &sed_cmd->beg_match);
663 sed_cmd->beg_line_orig = sed_cmd->beg_line;
664
665
666 if (*cmdstr == ',') {
667 int idx;
668
669 cmdstr++;
670 if (*cmdstr == '+' && isdigit(cmdstr[1])) {
671
672
673
674
675
676
677
678
679
680 char *end;
681
682 idx = strtol(cmdstr+1, &end, 10);
683 sed_cmd->end_line = -2 - idx;
684 cmdstr = end;
685 } else {
686 idx = get_address(cmdstr, &sed_cmd->end_line, &sed_cmd->end_match);
687 cmdstr += idx;
688 idx--;
689 }
690 if (idx < 0)
691 bb_simple_error_msg_and_die("no address after comma");
692 sed_cmd->end_line_orig = sed_cmd->end_line;
693 }
694
695
696 cmdstr = skip_whitespace(cmdstr);
697
698
699 if (*cmdstr == '!') {
700 sed_cmd->invert = 1;
701 cmdstr++;
702
703
704 cmdstr = skip_whitespace(cmdstr);
705 }
706
707
708 if (!*cmdstr)
709 bb_simple_error_msg_and_die("missing command");
710 sed_cmd->cmd = *cmdstr++;
711 cmdstr = parse_cmd_args(sed_cmd, cmdstr);
712
713
714
715
716
717
718
719
720 *G.sed_cmd_tail = sed_cmd;
721 G.sed_cmd_tail = &sed_cmd->next;
722 }
723
724
725 free(G.add_cmd_line);
726 G.add_cmd_line = NULL;
727}
728
729
730
731#define PIPE_GROW 64
732
733static void pipe_putc(char c)
734{
735 if (G.pipeline.idx == G.pipeline.len) {
736 G.pipeline.buf = xrealloc(G.pipeline.buf,
737 G.pipeline.len + PIPE_GROW);
738 G.pipeline.len += PIPE_GROW;
739 }
740 G.pipeline.buf[G.pipeline.idx++] = c;
741}
742
743static void do_subst_w_backrefs(char *line, char *replace)
744{
745 int i, j;
746
747
748 for (i = 0; replace[i]; i++) {
749
750 if (replace[i] == '\\') {
751 unsigned backref = replace[++i] - '0';
752 if (backref <= 9) {
753
754 if (G.regmatch[backref].rm_so != -1) {
755 j = G.regmatch[backref].rm_so;
756 while (j < G.regmatch[backref].rm_eo)
757 pipe_putc(line[j++]);
758 }
759 continue;
760 }
761
762
763
764
765 pipe_putc(replace[i]);
766 continue;
767 }
768
769 if (replace[i] == '&') {
770 j = G.regmatch[0].rm_so;
771 while (j < G.regmatch[0].rm_eo)
772 pipe_putc(line[j++]);
773 continue;
774 }
775
776 pipe_putc(replace[i]);
777 }
778}
779
780static int do_subst_command(sed_cmd_t *sed_cmd, char **line_p)
781{
782 char *line = *line_p;
783 unsigned match_count = 0;
784 bool altered = 0;
785 bool prev_match_empty = 1;
786 bool tried_at_eol = 0;
787 regex_t *current_regex;
788
789 current_regex = sed_cmd->sub_match;
790
791 if (!current_regex) {
792 current_regex = G.previous_regex_ptr;
793 if (!current_regex)
794 bb_simple_error_msg_and_die("no previous regexp");
795 }
796 G.previous_regex_ptr = current_regex;
797
798
799 dbg("matching '%s'", line);
800 if (REG_NOMATCH == regexec(current_regex, line, 10, G.regmatch, 0)) {
801 dbg("no match");
802 return 0;
803 }
804 dbg("match");
805
806
807 G.pipeline.buf = xmalloc(PIPE_GROW);
808 G.pipeline.len = PIPE_GROW;
809 G.pipeline.idx = 0;
810
811
812 do {
813 int start = G.regmatch[0].rm_so;
814 int end = G.regmatch[0].rm_eo;
815 int i;
816
817 match_count++;
818
819
820
821 if (sed_cmd->which_match
822 && (sed_cmd->which_match != match_count)
823 ) {
824 for (i = 0; i < end; i++)
825 pipe_putc(*line++);
826
827 if (start == end && *line)
828 pipe_putc(*line++);
829 goto next;
830 }
831
832
833 for (i = 0; i < start; i++)
834 pipe_putc(line[i]);
835
836
837
838
839
840
841
842
843 if (prev_match_empty || start != 0 || start != end) {
844
845 dbg("inserting replacement at %d in '%s'", start, line);
846 do_subst_w_backrefs(line, sed_cmd->string);
847
848 altered = 1;
849 } else {
850 dbg("NOT inserting replacement at %d in '%s'", start, line);
851 }
852
853
854
855
856 prev_match_empty = (start == end);
857 if (prev_match_empty) {
858 if (!line[end]) {
859 tried_at_eol = 1;
860 } else {
861 pipe_putc(line[end]);
862 end++;
863 }
864 }
865
866
867 dbg("line += %d", end);
868 line += end;
869
870
871 if (sed_cmd->which_match != 0)
872 break;
873 next:
874
875 if (*line == '\0') {
876 if (tried_at_eol)
877 break;
878 tried_at_eol = 1;
879 }
880
881
882 } while (regexec(current_regex, line, 10, G.regmatch, REG_NOTBOL) != REG_NOMATCH);
883
884
885 while (1) {
886 char c = *line++;
887 pipe_putc(c);
888 if (c == '\0')
889 break;
890 }
891
892 free(*line_p);
893 *line_p = G.pipeline.buf;
894 return altered;
895}
896
897
898static sed_cmd_t *branch_to(char *label)
899{
900 sed_cmd_t *sed_cmd;
901
902 for (sed_cmd = G.sed_cmd_head; sed_cmd; sed_cmd = sed_cmd->next) {
903 if (sed_cmd->cmd == ':'
904 && sed_cmd->string
905 && strcmp(sed_cmd->string, label) == 0
906 ) {
907 return sed_cmd;
908 }
909 }
910 bb_error_msg_and_die("can't find label for jump to '%s'", label);
911}
912
913static void append(char *s)
914{
915 llist_add_to_end(&G.append_head, s);
916}
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932enum {
933 NO_EOL_CHAR = 1,
934 LAST_IS_NUL = 2,
935};
936static void puts_maybe_newline(char *s, FILE *file, char *last_puts_char, char last_gets_char)
937{
938 char lpc = *last_puts_char;
939
940
941
942 if (lpc != '\n' && lpc != '\0') {
943 fputc('\n', file);
944 lpc = '\n';
945 }
946 fputs(s, file);
947
948
949 if (s[0])
950 lpc = 'x';
951
952
953 if (last_gets_char == LAST_IS_NUL) {
954 fputc('\0', file);
955 lpc = 'x';
956 } else
957
958 if (last_gets_char != NO_EOL_CHAR) {
959 fputc(last_gets_char, file);
960 lpc = last_gets_char;
961 }
962
963 if (ferror(file)) {
964 xfunc_error_retval = 4;
965 bb_simple_error_msg_and_die(bb_msg_write_error);
966 }
967 *last_puts_char = lpc;
968}
969
970static void flush_append(char *last_puts_char)
971{
972 char *data;
973
974
975 while ((data = (char *)llist_pop(&G.append_head)) != NULL) {
976
977
978
979
980
981
982
983
984
985 puts_maybe_newline(data, G.nonstdout, last_puts_char, '\n');
986 free(data);
987 }
988}
989
990
991
992
993static char *get_next_line(char *gets_char, char *last_puts_char)
994{
995 char *temp = NULL;
996 size_t len;
997 char gc;
998
999 flush_append(last_puts_char);
1000
1001
1002
1003 gc = NO_EOL_CHAR;
1004 for (; G.current_input_file <= G.last_input_file; G.current_input_file++) {
1005 FILE *fp = G.current_fp;
1006 if (!fp) {
1007 const char *path = G.input_file_list[G.current_input_file];
1008 fp = stdin;
1009 if (path != bb_msg_standard_input) {
1010 fp = fopen_or_warn(path, "r");
1011 if (!fp) {
1012 G.exitcode = EXIT_FAILURE;
1013 continue;
1014 }
1015 }
1016 G.current_fp = fp;
1017 }
1018
1019
1020
1021 temp = bb_get_chunk_from_file(fp, &len);
1022 if (temp) {
1023
1024 char c = temp[len-1];
1025 if (c == '\n' || c == '\0') {
1026 temp[len-1] = '\0';
1027 gc = c;
1028 if (c == '\0') {
1029 int ch = fgetc(fp);
1030 if (ch != EOF)
1031 ungetc(ch, fp);
1032 else
1033 gc = LAST_IS_NUL;
1034 }
1035 }
1036
1037 break;
1038
1039
1040
1041
1042
1043
1044
1045
1046 }
1047
1048 fclose_if_not_stdin(fp);
1049 G.current_fp = NULL;
1050 }
1051 *gets_char = gc;
1052 return temp;
1053}
1054
1055#define sed_puts(s, n) (puts_maybe_newline(s, G.nonstdout, &last_puts_char, n))
1056
1057static int beg_match(sed_cmd_t *sed_cmd, const char *pattern_space)
1058{
1059 int retval = sed_cmd->beg_match && !regexec(sed_cmd->beg_match, pattern_space, 0, NULL, 0);
1060 if (retval)
1061 G.previous_regex_ptr = sed_cmd->beg_match;
1062 return retval;
1063}
1064
1065
1066
1067static void process_files(void)
1068{
1069 char *pattern_space, *next_line;
1070 int linenum = 0;
1071 char last_puts_char = '\n';
1072 char last_gets_char, next_gets_char;
1073 sed_cmd_t *sed_cmd;
1074 int substituted;
1075
1076
1077 next_line = get_next_line(&next_gets_char, &last_puts_char);
1078
1079
1080 again:
1081 substituted = 0;
1082
1083
1084 pattern_space = next_line;
1085 if (!pattern_space)
1086 return;
1087 last_gets_char = next_gets_char;
1088
1089
1090
1091 next_line = get_next_line(&next_gets_char, &last_puts_char);
1092 linenum++;
1093
1094
1095 restart:
1096 for (sed_cmd = G.sed_cmd_head; sed_cmd; sed_cmd = sed_cmd->next) {
1097 int old_matched, matched;
1098
1099 old_matched = sed_cmd->in_match;
1100 if (!old_matched)
1101 sed_cmd->end_line = sed_cmd->end_line_orig;
1102
1103
1104
1105 dbg("match1:%d", sed_cmd->in_match);
1106 dbg("match2:%d", (!sed_cmd->beg_line && !sed_cmd->end_line
1107 && !sed_cmd->beg_match && !sed_cmd->end_match));
1108 dbg("match3:%d", (sed_cmd->beg_line > 0
1109 && (sed_cmd->end_line || sed_cmd->end_match
1110 ? (sed_cmd->beg_line <= linenum)
1111 : (sed_cmd->beg_line == linenum)
1112 )
1113 ));
1114 dbg("match4:%d", (beg_match(sed_cmd, pattern_space)));
1115 dbg("match5:%d", (sed_cmd->beg_line == -1 && next_line == NULL));
1116
1117
1118 sed_cmd->in_match = sed_cmd->in_match
1119
1120 || (!sed_cmd->beg_line && !sed_cmd->end_line
1121 && !sed_cmd->beg_match && !sed_cmd->end_match)
1122
1123 || (sed_cmd->beg_line > 0
1124 && (sed_cmd->end_line || sed_cmd->end_match
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134 ? (sed_cmd->beg_line <= linenum)
1135 : (sed_cmd->beg_line == linenum)
1136 )
1137 )
1138
1139 || (beg_match(sed_cmd, pattern_space))
1140
1141 || (sed_cmd->beg_line == -1 && next_line == NULL);
1142
1143
1144 matched = sed_cmd->in_match;
1145
1146 dbg("cmd:'%c' matched:%d beg_line:%d end_line:%d linenum:%d",
1147 sed_cmd->cmd, matched, sed_cmd->beg_line, sed_cmd->end_line, linenum);
1148
1149
1150
1151 if (matched) {
1152 if (sed_cmd->end_line <= -2) {
1153
1154 sed_cmd->end_line = linenum + (-sed_cmd->end_line - 2);
1155 }
1156
1157 if (sed_cmd->beg_line > 0) {
1158 sed_cmd->beg_line = -2;
1159 }
1160 dbg("end1:%d", sed_cmd->end_line ? sed_cmd->end_line == -1
1161 ? !next_line : (sed_cmd->end_line <= linenum)
1162 : !sed_cmd->end_match);
1163 dbg("end2:%d", sed_cmd->end_match && old_matched
1164 && !regexec(sed_cmd->end_match,pattern_space, 0, NULL, 0));
1165 sed_cmd->in_match = !(
1166
1167 (sed_cmd->end_line
1168 ? sed_cmd->end_line == -1
1169 ? !next_line
1170 : (sed_cmd->end_line <= linenum)
1171 : !sed_cmd->end_match
1172 )
1173
1174 || (sed_cmd->end_match && old_matched
1175 && (regexec(sed_cmd->end_match,
1176 pattern_space, 0, NULL, 0) == 0)
1177 )
1178 );
1179 }
1180
1181
1182 if (sed_cmd->cmd == '{') {
1183 if (sed_cmd->invert ? matched : !matched) {
1184 unsigned nest_cnt = 0;
1185 while (1) {
1186 if (sed_cmd->cmd == '{')
1187 nest_cnt++;
1188 if (sed_cmd->cmd == '}') {
1189 nest_cnt--;
1190 if (nest_cnt == 0)
1191 break;
1192 }
1193 sed_cmd = sed_cmd->next;
1194 if (!sed_cmd)
1195 bb_simple_error_msg_and_die("unterminated {");
1196 }
1197 }
1198 continue;
1199 }
1200
1201
1202 if (sed_cmd->invert ? matched : !matched)
1203 continue;
1204
1205
1206 if (sed_cmd->beg_match) {
1207 G.previous_regex_ptr = sed_cmd->beg_match;
1208 }
1209
1210
1211 dbg("pattern_space:'%s' next_line:'%s' cmd:%c",
1212 pattern_space, next_line, sed_cmd->cmd);
1213 switch (sed_cmd->cmd) {
1214
1215
1216 case '=':
1217 fprintf(G.nonstdout, "%d\n", linenum);
1218 break;
1219
1220
1221 case 'P':
1222 {
1223 char *tmp = strchr(pattern_space, '\n');
1224 if (tmp) {
1225 *tmp = '\0';
1226
1227 sed_puts(pattern_space, '\n');
1228 *tmp = '\n';
1229 break;
1230 }
1231
1232 }
1233
1234
1235 case 'p':
1236
1237
1238
1239
1240 sed_puts(pattern_space, '\n');
1241 break;
1242
1243 case 'D':
1244 {
1245 char *tmp = strchr(pattern_space, '\n');
1246 if (tmp) {
1247 overlapping_strcpy(pattern_space, tmp + 1);
1248 goto restart;
1249 }
1250 }
1251
1252 case 'd':
1253 goto discard_line;
1254
1255
1256 case 's':
1257 if (!do_subst_command(sed_cmd, &pattern_space))
1258 break;
1259 dbg("do_subst_command succeeded:'%s'", pattern_space);
1260 substituted |= 1;
1261
1262
1263 if (sed_cmd->sub_p)
1264 sed_puts(pattern_space, last_gets_char);
1265
1266 if (sed_cmd->sw_file)
1267 puts_maybe_newline(
1268 pattern_space, sed_cmd->sw_file,
1269 &sed_cmd->sw_last_char, last_gets_char);
1270 break;
1271
1272
1273 case 'a':
1274 append(xstrdup(sed_cmd->string));
1275 break;
1276
1277
1278 case 'i':
1279 sed_puts(sed_cmd->string, '\n');
1280 break;
1281
1282
1283 case 'c':
1284
1285 if (!sed_cmd->in_match)
1286 sed_puts(sed_cmd->string, '\n');
1287 goto discard_line;
1288
1289
1290 case 'r':
1291 {
1292 FILE *rfile;
1293 rfile = fopen_for_read(sed_cmd->string);
1294 if (rfile) {
1295 char *line;
1296 while ((line = xmalloc_fgetline(rfile))
1297 != NULL)
1298 append(line);
1299 fclose(rfile);
1300 }
1301
1302 break;
1303 }
1304
1305
1306 case 'w':
1307 puts_maybe_newline(
1308 pattern_space, sed_cmd->sw_file,
1309 &sed_cmd->sw_last_char, last_gets_char);
1310 break;
1311
1312
1313 case 'n':
1314 if (!G.be_quiet)
1315 sed_puts(pattern_space, last_gets_char);
1316 if (next_line == NULL) {
1317
1318 goto discard_line;
1319 }
1320 free(pattern_space);
1321 pattern_space = next_line;
1322 last_gets_char = next_gets_char;
1323 next_line = get_next_line(&next_gets_char, &last_puts_char);
1324 substituted = 0;
1325 linenum++;
1326 break;
1327
1328
1329 case 'q':
1330
1331 free(next_line);
1332 next_line = NULL;
1333 goto discard_commands;
1334
1335
1336 case 'N':
1337 {
1338 int len;
1339
1340
1341
1342
1343
1344
1345
1346
1347 if (next_line == NULL) {
1348
1349 goto discard_commands;
1350 }
1351
1352 len = strlen(pattern_space);
1353 pattern_space = xrealloc(pattern_space, len + strlen(next_line) + 2);
1354 pattern_space[len] = '\n';
1355 strcpy(pattern_space + len+1, next_line);
1356 last_gets_char = next_gets_char;
1357 next_line = get_next_line(&next_gets_char, &last_puts_char);
1358 linenum++;
1359 break;
1360 }
1361
1362
1363 case 't':
1364 if (!substituted) break;
1365 substituted = 0;
1366
1367
1368 case 'T':
1369 if (substituted) break;
1370
1371
1372 case 'b':
1373 if (!sed_cmd->string) goto discard_commands;
1374 else sed_cmd = branch_to(sed_cmd->string);
1375 break;
1376
1377 case 'y':
1378 {
1379 int i, j;
1380 for (i = 0; pattern_space[i]; i++) {
1381 for (j = 0; sed_cmd->string[j]; j += 2) {
1382 if (pattern_space[i] == sed_cmd->string[j]) {
1383 pattern_space[i] = sed_cmd->string[j + 1];
1384 break;
1385 }
1386 }
1387 }
1388
1389 break;
1390 }
1391 case 'g':
1392 free(pattern_space);
1393 pattern_space = xstrdup(G.hold_space ? G.hold_space : "");
1394 break;
1395 case 'G':
1396 {
1397 int pattern_space_size = 2;
1398 int hold_space_size = 0;
1399
1400 if (pattern_space)
1401 pattern_space_size += strlen(pattern_space);
1402 if (G.hold_space)
1403 hold_space_size = strlen(G.hold_space);
1404 pattern_space = xrealloc(pattern_space,
1405 pattern_space_size + hold_space_size);
1406 if (pattern_space_size == 2)
1407 pattern_space[0] = 0;
1408 strcat(pattern_space, "\n");
1409 if (G.hold_space)
1410 strcat(pattern_space, G.hold_space);
1411 last_gets_char = '\n';
1412
1413 break;
1414 }
1415 case 'h':
1416 free(G.hold_space);
1417 G.hold_space = xstrdup(pattern_space);
1418 break;
1419 case 'H':
1420 {
1421 int hold_space_size = 2;
1422 int pattern_space_size = 0;
1423
1424 if (G.hold_space)
1425 hold_space_size += strlen(G.hold_space);
1426 if (pattern_space)
1427 pattern_space_size = strlen(pattern_space);
1428 G.hold_space = xrealloc(G.hold_space,
1429 hold_space_size + pattern_space_size);
1430
1431 if (hold_space_size == 2)
1432 *G.hold_space = 0;
1433 strcat(G.hold_space, "\n");
1434 if (pattern_space)
1435 strcat(G.hold_space, pattern_space);
1436
1437 break;
1438 }
1439 case 'x':
1440 {
1441 char *tmp = pattern_space;
1442 pattern_space = G.hold_space ? G.hold_space : xzalloc(1);
1443 last_gets_char = '\n';
1444 G.hold_space = tmp;
1445 break;
1446 }
1447 }
1448 }
1449
1450
1451
1452
1453 discard_commands:
1454
1455
1456 if (!G.be_quiet)
1457 sed_puts(pattern_space, last_gets_char);
1458
1459
1460 discard_line:
1461 flush_append(&last_puts_char );
1462 free(pattern_space);
1463
1464 goto again;
1465}
1466
1467
1468
1469
1470
1471
1472
1473static void add_cmd_block(char *cmdstr)
1474{
1475 char *sv, *eol;
1476
1477 cmdstr = sv = xstrdup(cmdstr);
1478 do {
1479 eol = strchr(cmdstr, '\n');
1480 if (eol)
1481 *eol = '\0';
1482 add_cmd(cmdstr);
1483 cmdstr = eol + 1;
1484 } while (eol);
1485 free(sv);
1486}
1487
1488int sed_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1489int sed_main(int argc UNUSED_PARAM, char **argv)
1490{
1491 unsigned opt;
1492 llist_t *opt_e, *opt_f;
1493 char *opt_i;
1494
1495#if ENABLE_LONG_OPTS
1496 static const char sed_longopts[] ALIGN1 =
1497
1498 "in-place\0" Optional_argument "i"
1499 "regexp-extended\0" No_argument "r"
1500 "quiet\0" No_argument "n"
1501 "silent\0" No_argument "n"
1502 "expression\0" Required_argument "e"
1503 "file\0" Required_argument "f";
1504#endif
1505
1506 INIT_G();
1507
1508
1509 if (ENABLE_FEATURE_CLEAN_UP) atexit(sed_free_and_close_stuff);
1510
1511
1512 if (argv[1] && strcmp(argv[1], "--version") == 0) {
1513 puts("This is not GNU sed version 4.0");
1514 return 0;
1515 }
1516
1517
1518 opt_e = opt_f = NULL;
1519 opt_i = NULL;
1520
1521
1522
1523
1524
1525 opt = getopt32long(argv, "^"
1526 "i::rEne:*f:*"
1527 "\0" "nn",
1528 sed_longopts,
1529 &opt_i, &opt_e, &opt_f,
1530 &G.be_quiet);
1531
1532 argv += optind;
1533 if (opt & OPT_in_place) {
1534 die_func = cleanup_outname;
1535 }
1536 if (opt & (2|4))
1537 G.regex_type |= REG_EXTENDED;
1538
1539
1540 while (opt_e) {
1541 add_cmd_block(llist_pop(&opt_e));
1542 }
1543 while (opt_f) {
1544 char *line;
1545 FILE *cmdfile;
1546 cmdfile = xfopen_stdin(llist_pop(&opt_f));
1547 while ((line = xmalloc_fgetline(cmdfile)) != NULL) {
1548 add_cmd(line);
1549 free(line);
1550 }
1551 fclose_if_not_stdin(cmdfile);
1552 }
1553
1554 if (!(opt & 0x30)) {
1555 if (!*argv)
1556 bb_show_usage();
1557 add_cmd_block(*argv++);
1558 }
1559
1560 add_cmd("");
1561
1562
1563 G.nonstdout = stdout;
1564
1565
1566
1567
1568 G.input_file_list = argv;
1569 if (!argv[0]) {
1570 if (opt & OPT_in_place)
1571 bb_error_msg_and_die(bb_msg_requires_arg, "-i");
1572 argv[0] = (char*)bb_msg_standard_input;
1573
1574 } else {
1575 goto start;
1576
1577 for (; *argv; argv++) {
1578 struct stat statbuf;
1579 int nonstdoutfd;
1580 sed_cmd_t *sed_cmd;
1581
1582 G.last_input_file++;
1583 start:
1584 if (!(opt & OPT_in_place)) {
1585 if (LONE_DASH(*argv)) {
1586 *argv = (char*)bb_msg_standard_input;
1587 process_files();
1588 }
1589 continue;
1590 }
1591
1592
1593
1594 if (stat(*argv, &statbuf) != 0) {
1595 bb_simple_perror_msg(*argv);
1596 G.exitcode = EXIT_FAILURE;
1597 G.current_input_file++;
1598 continue;
1599 }
1600 G.outname = xasprintf("%sXXXXXX", *argv);
1601 nonstdoutfd = xmkstemp(G.outname);
1602 G.nonstdout = xfdopen_for_write(nonstdoutfd);
1603
1604
1605
1606 fchmod(nonstdoutfd, statbuf.st_mode);
1607 fchown(nonstdoutfd, statbuf.st_uid, statbuf.st_gid);
1608
1609 process_files();
1610 fclose(G.nonstdout);
1611 G.nonstdout = stdout;
1612
1613 if (opt_i) {
1614 char *backupname = xasprintf("%s%s", *argv, opt_i);
1615 xrename(*argv, backupname);
1616 free(backupname);
1617 }
1618
1619 xrename(G.outname, *argv);
1620 free(G.outname);
1621 G.outname = NULL;
1622
1623
1624 for (sed_cmd = G.sed_cmd_head; sed_cmd; sed_cmd = sed_cmd->next) {
1625 sed_cmd->beg_line = sed_cmd->beg_line_orig;
1626 sed_cmd->end_line = sed_cmd->end_line_orig;
1627 }
1628 }
1629
1630
1631
1632
1633
1634 }
1635
1636 process_files();
1637
1638 return G.exitcode;
1639}
1640