c71e9c26e28f86d220d318fae1a92dc52883dc9c
[busybox-power] / debian / patches / 1.19.0-hotfixes / busybox-1.19.0-sed.patch
1 --- busybox-1.19.0/editors/sed.c
2 +++ busybox-1.19.0-sed/editors/sed.c
3 @@ -75,6 +75,13 @@
4  #include "libbb.h"
5  #include "xregex.h"
6  
7 +#if 0
8 +# define dbg(...) bb_error_msg(__VA_ARGS__)
9 +#else
10 +# define dbg(...) ((void)0)
11 +#endif
12 +
13 +
14  enum {
15         OPT_in_place = 1 << 0,
16  };
17 @@ -89,6 +96,7 @@ typedef struct sed_cmd_s {
18         regex_t *end_match;     /* sed -e '/match/,/end_match/cmd' */
19         regex_t *sub_match;     /* For 's/sub_match/string/' */
20         int beg_line;           /* 'sed 1p'   0 == apply commands to all lines */
21 +       int beg_line_orig;      /* copy of the above, needed for -i */
22         int end_line;           /* 'sed 1,3p' 0 == one line only. -1 = last line ($) */
23  
24         FILE *sw_file;          /* File (sw) command writes to, -1 for none. */
25 @@ -123,7 +131,7 @@ struct globals {
26         regex_t *previous_regex_ptr;
27  
28         /* linked list of sed commands */
29 -       sed_cmd_t sed_cmd_head, *sed_cmd_tail;
30 +       sed_cmd_t *sed_cmd_head, **sed_cmd_tail;
31  
32         /* Linked list of append lines */
33         llist_t *append_head;
34 @@ -148,7 +156,7 @@ struct BUG_G_too_big {
35  #if ENABLE_FEATURE_CLEAN_UP
36  static void sed_free_and_close_stuff(void)
37  {
38 -       sed_cmd_t *sed_cmd = G.sed_cmd_head.next;
39 +       sed_cmd_t *sed_cmd = G.sed_cmd_head;
40  
41         llist_free(G.append_head, free);
42  
43 @@ -599,6 +607,7 @@ static void add_cmd(const char *cmdstr)
44  
45                 /* first part (if present) is an address: either a '$', a number or a /regex/ */
46                 cmdstr += get_address(cmdstr, &sed_cmd->beg_line, &sed_cmd->beg_match);
47 +               sed_cmd->beg_line_orig = sed_cmd->beg_line;
48  
49                 /* second part (if present) will begin with a comma */
50                 if (*cmdstr == ',') {
51 @@ -630,8 +639,8 @@ static void add_cmd(const char *cmdstr)
52                 cmdstr = parse_cmd_args(sed_cmd, cmdstr);
53  
54                 /* Add the command to the command array */
55 -               G.sed_cmd_tail->next = sed_cmd;
56 -               G.sed_cmd_tail = G.sed_cmd_tail->next;
57 +               *G.sed_cmd_tail = sed_cmd;
58 +               G.sed_cmd_tail = &sed_cmd->next;
59         }
60  
61         /* If we glued multiple lines together, free the memory. */
62 @@ -777,7 +786,7 @@ static sed_cmd_t *branch_to(char *label)
63  {
64         sed_cmd_t *sed_cmd;
65  
66 -       for (sed_cmd = G.sed_cmd_head.next; sed_cmd; sed_cmd = sed_cmd->next) {
67 +       for (sed_cmd = G.sed_cmd_head; sed_cmd; sed_cmd = sed_cmd->next) {
68                 if (sed_cmd->cmd == ':' && sed_cmd->string && !strcmp(sed_cmd->string, label)) {
69                         return sed_cmd;
70                 }
71 @@ -953,24 +962,24 @@ static void process_files(void)
72  
73         /* For every line, go through all the commands */
74   restart:
75 -       for (sed_cmd = G.sed_cmd_head.next; sed_cmd; sed_cmd = sed_cmd->next) {
76 +       for (sed_cmd = G.sed_cmd_head; sed_cmd; sed_cmd = sed_cmd->next) {
77                 int old_matched, matched;
78  
79                 old_matched = sed_cmd->in_match;
80  
81                 /* Determine if this command matches this line: */
82  
83 -               //bb_error_msg("match1:%d", sed_cmd->in_match);
84 -               //bb_error_msg("match2:%d", (!sed_cmd->beg_line && !sed_cmd->end_line
85 -               //              && !sed_cmd->beg_match && !sed_cmd->end_match));
86 -               //bb_error_msg("match3:%d", (sed_cmd->beg_line > 0
87 -               //      && (sed_cmd->end_line || sed_cmd->end_match
88 -               //          ? (sed_cmd->beg_line <= linenum)
89 -               //          : (sed_cmd->beg_line == linenum)
90 -               //          )
91 -               //      )
92 -               //bb_error_msg("match4:%d", (beg_match(sed_cmd, pattern_space)));
93 -               //bb_error_msg("match5:%d", (sed_cmd->beg_line == -1 && next_line == NULL));
94 +               dbg("match1:%d", sed_cmd->in_match);
95 +               dbg("match2:%d", (!sed_cmd->beg_line && !sed_cmd->end_line
96 +                               && !sed_cmd->beg_match && !sed_cmd->end_match));
97 +               dbg("match3:%d", (sed_cmd->beg_line > 0
98 +                       && (sed_cmd->end_line || sed_cmd->end_match
99 +                           ? (sed_cmd->beg_line <= linenum)
100 +                           : (sed_cmd->beg_line == linenum)
101 +                           )
102 +                       ));
103 +               dbg("match4:%d", (beg_match(sed_cmd, pattern_space)));
104 +               dbg("match5:%d", (sed_cmd->beg_line == -1 && next_line == NULL));
105  
106                 /* Are we continuing a previous multi-line match? */
107                 sed_cmd->in_match = sed_cmd->in_match
108 @@ -981,7 +990,14 @@ static void process_files(void)
109                         || (sed_cmd->beg_line > 0
110                             && (sed_cmd->end_line || sed_cmd->end_match
111                                   /* note: even if end is numeric and is < linenum too,
112 -                                  * GNU sed matches! We match too */
113 +                                  * GNU sed matches! We match too, therefore we don't
114 +                                  * check here that linenum <= end.
115 +                                  * Example:
116 +                                  * printf '1\n2\n3\n4\n' | sed -n '1{N;N;d};1p;2,3p;3p;4p'
117 +                                  * first three input lines are deleted;
118 +                                  * 4th line is matched and printed
119 +                                  * by "2,3" (!) and by "4" ranges
120 +                                  */
121                                 ? (sed_cmd->beg_line <= linenum)    /* N,end */
122                                 : (sed_cmd->beg_line == linenum)    /* N */
123                                 )
124 @@ -994,16 +1010,14 @@ static void process_files(void)
125                 /* Snapshot the value */
126                 matched = sed_cmd->in_match;
127  
128 -               //bb_error_msg("cmd:'%c' matched:%d beg_line:%d end_line:%d linenum:%d",
129 -               //sed_cmd->cmd, matched, sed_cmd->beg_line, sed_cmd->end_line, linenum);
130 +               dbg("cmd:'%c' matched:%d beg_line:%d end_line:%d linenum:%d",
131 +                       sed_cmd->cmd, matched, sed_cmd->beg_line, sed_cmd->end_line, linenum);
132  
133                 /* Is this line the end of the current match? */
134  
135                 if (matched) {
136                         /* once matched, "n,xxx" range is dead, disabling it */
137 -                       if (sed_cmd->beg_line > 0
138 -                        && !(option_mask32 & OPT_in_place) /* but not for -i */
139 -                       ) {
140 +                       if (sed_cmd->beg_line > 0) {
141                                 sed_cmd->beg_line = -2;
142                         }
143                         sed_cmd->in_match = !(
144 @@ -1017,7 +1031,8 @@ static void process_files(void)
145                                 /* or does this line matches our last address regex */
146                                 || (sed_cmd->end_match && old_matched
147                                      && (regexec(sed_cmd->end_match,
148 -                                                pattern_space, 0, NULL, 0) == 0))
149 +                                                pattern_space, 0, NULL, 0) == 0)
150 +                               )
151                         );
152                 }
153  
154 @@ -1407,11 +1422,12 @@ int sed_main(int argc UNUSED_PARAM, char
155                 add_input_file(stdin);
156         } else {
157                 int i;
158 -               FILE *file;
159  
160                 for (i = 0; argv[i]; i++) {
161                         struct stat statbuf;
162                         int nonstdoutfd;
163 +                       FILE *file;
164 +                       sed_cmd_t *sed_cmd;
165  
166                         if (LONE_DASH(argv[i]) && !(opt & OPT_in_place)) {
167                                 add_input_file(stdin);
168 @@ -1423,11 +1439,13 @@ int sed_main(int argc UNUSED_PARAM, char
169                                 status = EXIT_FAILURE;
170                                 continue;
171                         }
172 +                       add_input_file(file);
173                         if (!(opt & OPT_in_place)) {
174 -                               add_input_file(file);
175                                 continue;
176                         }
177  
178 +                       /* -i: process each FILE separately: */
179 +
180                         G.outname = xasprintf("%sXXXXXX", argv[i]);
181                         nonstdoutfd = xmkstemp(G.outname);
182                         G.nonstdout = xfdopen_for_write(nonstdoutfd);
183 @@ -1438,15 +1456,20 @@ int sed_main(int argc UNUSED_PARAM, char
184                          * but GNU sed 4.2.1 does not preserve them either */
185                         fchmod(nonstdoutfd, statbuf.st_mode);
186                         fchown(nonstdoutfd, statbuf.st_uid, statbuf.st_gid);
187 -                       add_input_file(file);
188 +
189                         process_files();
190                         fclose(G.nonstdout);
191 -
192                         G.nonstdout = stdout;
193 +
194                         /* unlink(argv[i]); */
195                         xrename(G.outname, argv[i]);
196                         free(G.outname);
197                         G.outname = NULL;
198 +
199 +                       /* Re-enable disabled range matches */
200 +                       for (sed_cmd = G.sed_cmd_head; sed_cmd; sed_cmd = sed_cmd->next) {
201 +                               sed_cmd->beg_line = sed_cmd->beg_line_orig;
202 +                       }
203                 }
204                 /* Here, to handle "sed 'cmds' nonexistent_file" case we did:
205                  * if (G.current_input_file >= G.input_file_count)
206 --- busybox-1.19.0/testsuite/sed.tests
207 +++ busybox-1.19.0-sed/testsuite/sed.tests
208 @@ -6,7 +6,7 @@
209  
210  . ./testing.sh
211  
212 -# testing "description" "arguments" "result" "infile" "stdin"
213 +# testing "description" "commands" "result" "infile" "stdin"
214  
215  # Corner cases
216  testing "sed no files (stdin)" 'sed ""' "hello\n" "" "hello\n"
217 @@ -225,7 +225,7 @@ testing "sed s/xxx/[/" "sed -e 's/xxx/[/
218  #testing "sed -g (exhaustive)" "sed -e 's/[[:space:]]*/,/g'" ",1,2,3,4,5," \
219  #      "" "12345"
220  
221 -# testing "description" "arguments" "result" "infile" "stdin"
222 +# testing "description" "commands" "result" "infile" "stdin"
223  
224  testing "sed n command must reset 'substituted' bit" \
225         "sed 's/1/x/;T;n;: next;s/3/y/;t quit;n;b next;: quit;q'" \
226 @@ -291,6 +291,10 @@ testing "sed understands \r" \
227         "sed 's/r/\r/'" \
228         "\rrr\n" "" "rrr\n"
229  
230 -# testing "description" "arguments" "result" "infile" "stdin"
231 +testing "sed -i finishes ranges correctly" \
232 +       "sed '1,2d' -i input; echo \$?; cat input" \
233 +       "0\n3\n4\n" "1\n2\n3\n4\n" ""
234 +
235 +# testing "description" "commands" "result" "infile" "stdin"
236  
237  exit $FAILCOUNT