OSDN Git Service

merge in oc-release history after reset to master
[android-x86/external-toybox.git] / toys / posix / sed.c
1 /* sed.c - stream editor. Thing that does s/// and other stuff.
2  *
3  * Copyright 2014 Rob Landley <rob@landley.net>
4  *
5  * See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/sed.html
6  *
7  * TODO: lines > 2G could wrap signed int length counters. Not just getline()
8  * but N and s///
9  * TODO: make y// handle unicode
10  * TODO: handle error return from emit(), error_msg/exit consistently
11  *       What's the right thing to do for -i when write fails? Skip to next?
12
13 USE_SED(NEWTOY(sed, "(help)(version)e*f*inEr[+Er]", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_LOCALE|TOYFLAG_NOHELP))
14
15 config SED
16   bool "sed"
17   default y
18   help
19     usage: sed [-inrE] [-e SCRIPT]...|SCRIPT [-f SCRIPT_FILE]... [FILE...]
20
21     Stream editor. Apply one or more editing SCRIPTs to each line of input
22     (from FILE or stdin) producing output (by default to stdout).
23
24     -e  add SCRIPT to list
25     -f  add contents of SCRIPT_FILE to list
26     -i  Edit each file in place.
27     -n  No default output. (Use the p command to output matched lines.)
28     -r  Use extended regular expression syntax.
29     -E  Alias for -r.
30     -s  Treat input files separately (implied by -i)
31
32     A SCRIPT is a series of one or more COMMANDs separated by newlines or
33     semicolons. All -e SCRIPTs are concatenated together as if separated
34     by newlines, followed by all lines from -f SCRIPT_FILEs, in order.
35     If no -e or -f SCRIPTs are specified, the first argument is the SCRIPT.
36
37     Each COMMAND may be preceded by an address which limits the command to
38     apply only to the specified line(s). Commands without an address apply to
39     every line. Addresses are of the form:
40
41       [ADDRESS[,ADDRESS]]COMMAND
42
43     The ADDRESS may be a decimal line number (starting at 1), a /regular
44     expression/ within a pair of forward slashes, or the character "$" which
45     matches the last line of input. (In -s or -i mode this matches the last
46     line of each file, otherwise just the last line of the last file.) A single
47     address matches one line, a pair of comma separated addresses match
48     everything from the first address to the second address (inclusive). If
49     both addresses are regular expressions, more than one range of lines in
50     each file can match.
51
52     REGULAR EXPRESSIONS in sed are started and ended by the same character
53     (traditionally / but anything except a backslash or a newline works).
54     Backslashes may be used to escape the delimiter if it occurs in the
55     regex, and for the usual printf escapes (\abcefnrtv and octal, hex,
56     and unicode). An empty regex repeats the previous one. ADDRESS regexes
57     (above) require the first delimeter to be escaped with a backslash when
58     it isn't a forward slash (to distinguish it from the COMMANDs below).
59
60     Sed mostly operates on individual lines one at a time. It reads each line,
61     processes it, and either writes it to the output or discards it before
62     reading the next line. Sed can remember one additional line in a separate
63     buffer (using the h, H, g, G, and x commands), and can read the next line
64     of input early (using the n and N command), but other than that command
65     scripts operate on individual lines of text.
66
67     Each COMMAND starts with a single character. The following commands take
68     no arguments:
69
70       {  Start a new command block, continuing until a corresponding "}".
71          Command blocks may nest. If the block has an address, commands within
72          the block are only run for lines within the block's address range.
73
74       }  End command block (this command cannot have an address)
75
76       d  Delete this line and move on to the next one
77          (ignores remaining COMMANDs)
78
79       D  Delete one line of input and restart command SCRIPT (same as "d"
80          unless you've glued lines together with "N" or similar)
81
82       g  Get remembered line (overwriting current line)
83
84       G  Get remembered line (appending to current line)
85
86       h  Remember this line (overwriting remembered line)
87
88       H  Remember this line (appending to remembered line, if any)
89
90       l  Print line, escaping \abfrtv (but not newline), octal escaping other
91          nonprintable characters, wrapping lines to terminal width with a
92          backslash, and appending $ to actual end of line.
93
94       n  Print default output and read next line, replacing current line
95          (If no next line available, quit processing script)
96
97       N  Append next line of input to this line, separated by a newline
98          (This advances the line counter for address matching and "=", if no
99          next line available quit processing script without default output)
100
101       p  Print this line
102
103       P  Print this line up to first newline (from "N")
104
105       q  Quit (print default output, no more commands processed or lines read)
106
107       x  Exchange this line with remembered line (overwrite in both directions)
108
109       =  Print the current line number (followed by a newline)
110
111     The following commands (may) take an argument. The "text" arguments (to
112     the "a", "b", and "c" commands) may end with an unescaped "\" to append
113     the next line (for which leading whitespace is not skipped), and also
114     treat ";" as a literal character (use "\;" instead).
115
116       a [text]   Append text to output before attempting to read next line
117
118       b [label]  Branch, jumps to :label (or with no label, to end of SCRIPT)
119
120       c [text]   Delete line, output text at end of matching address range
121                  (ignores remaining COMMANDs)
122
123       i [text]   Print text
124
125       r [file]   Append contents of file to output before attempting to read
126                  next line.
127
128       s/S/R/F    Search for regex S, replace matched text with R using flags F.
129                  The first character after the "s" (anything but newline or
130                  backslash) is the delimiter, escape with \ to use normally.
131
132                  The replacement text may contain "&" to substitute the matched
133                  text (escape it with backslash for a literal &), or \1 through
134                  \9 to substitute a parenthetical subexpression in the regex.
135                  You can also use the normal backslash escapes such as \n and
136                  a backslash at the end of the line appends the next line.
137
138                  The flags are:
139
140                  [0-9]    A number, substitute only that occurrence of pattern
141                  g        Global, substitute all occurrences of pattern
142                  i        Ignore case when matching
143                  p        Print the line if match was found and replaced
144                  w [file] Write (append) line to file if match replaced
145
146       t [label]  Test, jump to :label only if an "s" command found a match in
147                  this line since last test (replacing with same text counts)
148
149       T [label]  Test false, jump only if "s" hasn't found a match.
150
151       w [file]   Write (append) line to file
152
153       y/old/new/ Change each character in 'old' to corresponding character
154                  in 'new' (with standard backslash escapes, delimiter can be
155                  any repeated character except \ or \n)
156
157       : [label]  Labeled target for jump commands
158
159       #  Comment, ignore rest of this line of SCRIPT
160
161     Deviations from posix: allow extended regular expressions with -r,
162     editing in place with -i, separate with -s, printf escapes in text, line
163     continuations, semicolons after all commands, 2-address anywhere an
164     address is allowed, "T" command, multiline continuations for [abc],
165     \; to end [abc] argument before end of line.
166 */
167
168 #define FOR_sed
169 #include "toys.h"
170
171 GLOBALS(
172   struct arg_list *f;
173   struct arg_list *e;
174
175   // processed pattern list
176   struct double_list *pattern;
177
178   char *nextline, *remember;
179   void *restart, *lastregex;
180   long nextlen, rememberlen, count;
181   int fdout, noeol;
182   unsigned xx;
183 )
184
185 // Linked list of parsed sed commands. Offset fields indicate location where
186 // regex or string starts, ala offset+(char *)struct, because we remalloc()
187 // these to expand them for multiline inputs, and pointers would have to be
188 // individually adjusted.
189
190 struct sedcmd {
191   struct sedcmd *next, *prev;
192
193   // Begin and end of each match
194   long lmatch[2]; // line number of match
195   int rmatch[2];  // offset of regex struct for prefix matches (/abc/,/def/p)
196   int arg1, arg2, w; // offset of two arguments per command, plus s//w filename
197   unsigned not, hit;
198   unsigned sflags; // s///flag bits: i=1, g=2, p=4
199   char c; // action
200 };
201
202 // Write out line with potential embedded NUL, handling eol/noeol
203 static int emit(char *line, long len, int eol)
204 {
205   int l, old = line[len];
206
207   if (TT.noeol && !writeall(TT.fdout, "\n", 1)) return 1;
208   TT.noeol = !eol;
209   if (eol) line[len++] = '\n';
210   if (!len) return 0;
211   l = writeall(TT.fdout, line, len);
212   if (eol) line[len-1] = old;
213   if (l != len) {
214     perror_msg("short write");
215
216     return 1;
217   }
218
219   return 0;
220 }
221
222 // Extend allocation to include new string, with newline between if newlen<0
223
224 static char *extend_string(char **old, char *new, int oldlen, int newlen)
225 {
226   int newline = newlen < 0;
227   char *s;
228
229   if (newline) newlen = -newlen;
230   s = *old = xrealloc(*old, oldlen+newlen+newline+1);
231   if (newline) s[oldlen++] = '\n';
232   memcpy(s+oldlen, new, newlen);
233   s[oldlen+newlen] = 0;
234
235   return s+oldlen+newlen+1;
236 }
237
238 // An empty regex repeats the previous one
239 static void *get_regex(void *trump, int offset)
240 {
241   if (!offset) {
242     if (!TT.lastregex) error_exit("no previous regex");
243     return TT.lastregex;
244   }
245
246   return TT.lastregex = offset+(char *)trump;
247 }
248
249 // Apply pattern to line from input file
250 static void process_line(char **pline, long plen)
251 {
252   struct append {
253     struct append *next, *prev;
254     int file;
255     char *str;
256   } *append = 0;
257   char *line = TT.nextline;
258   long len = TT.nextlen;
259   struct sedcmd *command;
260   int eol = 0, tea = 0;
261
262   // Grab next line for deferred processing (EOF detection: we get a NULL
263   // pline at EOF to flush last line). Note that only end of _last_ input
264   // file matches $ (unless we're doing -i).
265   TT.nextline = 0;
266   TT.nextlen = 0;
267   if (pline) {
268     TT.nextline = *pline;
269     TT.nextlen = plen;
270     *pline = 0;
271   }
272
273   if (!line || !len) return;
274   if (line[len-1] == '\n') line[--len] = eol++;
275   TT.count++;
276
277   // The restart-1 is because we added one to make sure it wasn't NULL,
278   // otherwise N as last command would restart script
279   command = TT.restart ? ((struct sedcmd *)TT.restart)-1 : (void *)TT.pattern;
280   TT.restart = 0;
281
282   while (command) {
283     char *str, c = command->c;
284
285     // Have we got a line or regex matching range for this rule?
286     if (*command->lmatch || *command->rmatch) {
287       int miss = 0;
288       long lm;
289
290       // In a match that might end?
291       if (command->hit) {
292         if (!(lm = command->lmatch[1])) {
293           if (!command->rmatch[1]) command->hit = 0;
294           else {
295             void *rm = get_regex(command, command->rmatch[1]);
296
297             // regex match end includes matching line, so defer deactivation
298             if (line && !regexec0(rm, line, len, 0, 0, 0)) miss = 1;
299           }
300         } else if (lm > 0 && lm < TT.count) command->hit = 0;
301
302       // Start a new match?
303       } else {
304         if (!(lm = *command->lmatch)) {
305           void *rm = get_regex(command, *command->rmatch);
306
307           if (line && !regexec0(rm, line, len, 0, 0, 0)) command->hit++;
308         } else if (lm == TT.count || (lm == -1 && !pline)) command->hit++;
309
310         if (!command->lmatch[1] && !command->rmatch[1]) miss = 1;
311       } 
312
313       // Didn't match?
314       lm = !(command->hit ^ command->not);
315
316       // Deferred disable from regex end match
317       if (miss || command->lmatch[1] == TT.count) command->hit = 0;
318
319       if (lm) {
320         // Handle skipping curly bracket command group
321         if (c == '{') {
322           int curly = 1;
323
324           while (curly) {
325             command = command->next;
326             if (command->c == '{') curly++;
327             if (command->c == '}') curly--;
328           }
329         }
330         command = command->next;
331         continue;
332       }
333     }
334
335     // A deleted line can still update line match state for later commands
336     if (!line) {
337       command = command->next;
338       continue;
339     }
340
341     // Process command
342
343     if (c=='a' || c=='r') {
344       struct append *a = xzalloc(sizeof(struct append));
345       if (command->arg1) a->str = command->arg1+(char *)command;
346       a->file = c=='r';
347       dlist_add_nomalloc((void *)&append, (void *)a);
348     } else if (c=='b' || c=='t' || c=='T') {
349       int t = tea;
350
351       if (c != 'b') tea = 0;
352       if (c=='b' || t^(c=='T')) {
353         if (!command->arg1) break;
354         str = command->arg1+(char *)command;
355         for (command = (void *)TT.pattern; command; command = command->next)
356           if (command->c == ':' && !strcmp(command->arg1+(char *)command, str))
357             break;
358         if (!command) error_exit("no :%s", str);
359       }
360     } else if (c=='c') {
361       str = command->arg1+(char *)command;
362       if (!command->hit) emit(str, strlen(str), 1);
363       free(line);
364       line = 0;
365       continue;
366     } else if (c=='d') {
367       free(line);
368       line = 0;
369       continue;
370     } else if (c=='D') {
371       // Delete up to \n or end of buffer
372       str = line;
373       while ((str-line)<len) if (*(str++) == '\n') break;
374       len -= str - line;
375       memmove(line, str, len);
376
377       // if "delete" blanks line, disable further processing
378       // otherwise trim and restart script
379       if (!len) {
380         free(line);
381         line = 0;
382       } else {
383         line[len] = 0;
384         command = (void *)TT.pattern;
385       }
386       continue;
387     } else if (c=='g') {
388       free(line);
389       line = xstrdup(TT.remember);
390       len = TT.rememberlen;
391     } else if (c=='G') {
392       line = xrealloc(line, len+TT.rememberlen+2);
393       line[len++] = '\n';
394       memcpy(line+len, TT.remember, TT.rememberlen);
395       line[len += TT.rememberlen] = 0;
396     } else if (c=='h') {
397       free(TT.remember);
398       TT.remember = xstrdup(line);
399       TT.rememberlen = len;
400     } else if (c=='H') {
401       TT.remember = xrealloc(TT.remember, TT.rememberlen+len+2);
402       TT.remember[TT.rememberlen++] = '\n';
403       memcpy(TT.remember+TT.rememberlen, line, len);
404       TT.remember[TT.rememberlen += len] = 0;
405     } else if (c=='i') {
406       str = command->arg1+(char *)command;
407       emit(str, strlen(str), 1);
408     } else if (c=='l') {
409       int i, x, off;
410
411       if (!TT.xx) {
412         terminal_size(&TT.xx, 0);
413         if (!TT.xx) TT.xx = 80;
414         if (TT.xx > sizeof(toybuf)-10) TT.xx = sizeof(toybuf)-10;
415         if (TT.xx > 4) TT.xx -= 4;
416       }
417
418       for (i = off = 0; i<len; i++) {
419         if (off >= TT.xx) {
420           toybuf[off++] = '\\';
421           emit(toybuf, off, 1);
422           off = 0;
423         }
424         x = stridx("\\\a\b\f\r\t\v", line[i]);
425         if (x != -1) {
426           toybuf[off++] = '\\';
427           toybuf[off++] = "\\abfrtv"[x];
428         } else if (line[i] >= ' ') toybuf[off++] = line[i];
429         else off += sprintf(toybuf+off, "\\%03o", line[i]);
430       }
431       toybuf[off++] = '$';
432       emit(toybuf, off, 1);
433     } else if (c=='n') {
434       TT.restart = command->next+1;
435
436       break;
437     } else if (c=='N') {
438       // Can't just grab next line because we could have multiple N and
439       // we need to actually read ahead to get N;$p EOF detection right.
440       if (pline) {
441         TT.restart = command->next+1;
442         extend_string(&line, TT.nextline, len, -TT.nextlen);
443         free(TT.nextline);
444         TT.nextline = line;
445         TT.nextlen += len + 1;
446         line = 0;
447       }
448
449       // Pending append goes out right after N
450       goto done; 
451     } else if (c=='p' || c=='P') {
452       char *l = (c=='P') ? strchr(line, '\n') : 0;
453
454       if (emit(line, l ? l-line : len, eol)) break;
455     } else if (c=='q') {
456       if (pline) *pline = (void *)1;
457       free(TT.nextline);
458       TT.nextline = 0;
459       TT.nextlen = 0;
460
461       break;
462     } else if (c=='s') {
463       char *rline = line, *new = command->arg2 + (char *)command, *swap, *rswap;
464       regmatch_t *match = (void *)toybuf;
465       regex_t *reg = get_regex(command, command->arg1);
466       int mflags = 0, count = 0, zmatch = 1, rlen = len, mlen, off, newlen;
467
468       // Find match in remaining line (up to remaining len)
469       while (!regexec0(reg, rline, rlen, 10, match, mflags)) {
470         mflags = REG_NOTBOL;
471
472         // Zero length matches don't count immediately after a previous match
473         mlen = match[0].rm_eo-match[0].rm_so;
474         if (!mlen && !zmatch) {
475           if (!rlen--) break;
476           rline++;
477           zmatch++;
478           continue;
479         } else zmatch = 0;
480
481         // If we're replacing only a specific match, skip if this isn't it
482         off = command->sflags>>3;
483         if (off && off != ++count) {
484           rline += match[0].rm_eo;
485           rlen -= match[0].rm_eo;
486
487           continue;
488         }
489         // The fact getline() can allocate unbounded amounts of memory is
490         // a bigger issue, but while we're here check for integer overflow
491         if (match[0].rm_eo > INT_MAX) perror_exit(0);
492
493         // newlen = strlen(new) but with \1 and & and printf escapes
494         for (off = newlen = 0; new[off]; off++) {
495           int cc = -1;
496
497           if (new[off] == '&') cc = 0;
498           else if (new[off] == '\\') cc = new[++off] - '0';
499           if (cc < 0 || cc > 9) {
500             newlen++;
501             continue;
502           }
503           newlen += match[cc].rm_eo-match[cc].rm_so;
504         }
505
506         // Allocate new size, copy start/end around match. (Can't extend in
507         // place because backrefs may refer to text after it's overwritten.)
508         len += newlen-mlen;
509         swap = xmalloc(len+1);
510         rswap = swap+(rline-line)+match[0].rm_so;
511         memcpy(swap, line, (rline-line)+match[0].rm_so);
512         memcpy(rswap+newlen, rline+match[0].rm_eo, (rlen -= match[0].rm_eo)+1);
513
514         // copy in new replacement text
515         for (off = mlen = 0; new[off]; off++) {
516           int cc = 0, ll;
517
518           if (new[off] == '\\') {
519             cc = new[++off] - '0';
520             if (cc<0 || cc>9) {
521               if (!(rswap[mlen++] = unescape(new[off])))
522                 rswap[mlen-1] = new[off];
523
524               continue;
525             } else if (match[cc].rm_so == -1) error_exit("no s//\\%d/", cc);
526           } else if (new[off] != '&') {
527             rswap[mlen++] = new[off];
528
529             continue;
530           }
531
532           ll = match[cc].rm_eo-match[cc].rm_so;
533           memcpy(rswap+mlen, rline+match[cc].rm_so, ll);
534           mlen += ll;
535         }
536
537         rline = rswap+newlen;
538         free(line);
539         line = swap;
540
541         // Stop after first substitution unless we have flag g
542         if (!(command->sflags & 2)) break;
543       }
544
545       if (mflags) {
546         // flag p
547         if (command->sflags & 4) emit(line, len, eol);
548
549         tea = 1;
550         if (command->w) goto writenow;
551       }
552     } else if (c=='w') {
553       int fd, noeol;
554       char *name;
555
556 writenow:
557       // Swap out emit() context
558       fd = TT.fdout;
559       noeol = TT.noeol;
560
561       // We save filehandle and newline status before filename
562       name = command->w + (char *)command;
563       memcpy(&TT.fdout, name, 4);
564       name += 4;
565       TT.noeol = *(name++);
566
567       // write, then save/restore context
568       if (emit(line, len, eol))
569         perror_exit("w '%s'", command->arg1+(char *)command);
570       *(--name) = TT.noeol;
571       TT.noeol = noeol;
572       TT.fdout = fd;
573     } else if (c=='x') {
574       long swap = TT.rememberlen;
575
576       str = TT.remember;
577       TT.remember = line;
578       line = str;
579       TT.rememberlen = len;
580       len = swap;
581     } else if (c=='y') {
582       char *from, *to = (char *)command;
583       int i, j;
584
585       from = to+command->arg1;
586       to += command->arg2;
587
588       for (i = 0; i < len; i++) {
589         j = stridx(from, line[i]);
590         if (j != -1) line[i] = to[j];
591       }
592     } else if (c=='=') {
593       sprintf(toybuf, "%ld", TT.count);
594       emit(toybuf, strlen(toybuf), 1);
595     }
596
597     command = command->next;
598   }
599
600   if (line && !(toys.optflags & FLAG_n)) emit(line, len, eol);
601
602 done:
603   if (dlist_terminate(append)) while (append) {
604     struct append *a = append->next;
605
606     if (append->file) {
607       int fd = open(append->str, O_RDONLY);
608
609       // Force newline if noeol pending
610       if (fd != -1) {
611         if (TT.noeol) xwrite(TT.fdout, "\n", 1);
612         TT.noeol = 0;
613         xsendfile(fd, TT.fdout);
614         close(fd);
615       }
616     } else if (append->str) emit(append->str, strlen(append->str), 1);
617     else emit(line, 0, 0);
618     free(append);
619     append = a;
620   }
621   free(line);
622 }
623
624 // Callback called on each input file
625 static void do_sed(int fd, char *name)
626 {
627   int i = toys.optflags & FLAG_i;
628   char *tmp;
629
630   if (i) {
631     struct sedcmd *command;
632
633     if (!fd && !strcmp(name, "-")) {
634       error_msg("-i on stdin");
635       return;
636     }
637     TT.fdout = copy_tempfile(fd, name, &tmp);
638     TT.count = 0;
639     for (command = (void *)TT.pattern; command; command = command->next)
640       command->hit = 0;
641   }
642   do_lines(fd, process_line);
643   if (i) {
644     process_line(0, 0);
645     replace_tempfile(-1, TT.fdout, &tmp);
646     TT.fdout = 1;
647     TT.nextline = 0;
648     TT.nextlen = TT.noeol = 0;
649   }
650 }
651
652 // Copy chunk of string between two delimiters, converting printf escapes.
653 // returns processed copy of string (0 if error), *pstr advances to next
654 // unused char. if delim (or *delim) is 0 uses/saves starting char as delimiter
655 // if regxex, ignore delimiter in [ranges]
656 static char *unescape_delimited_string(char **pstr, char *delim)
657 {
658   char *to, *from, mode = 0, d;
659
660   // Grab leading delimiter (if necessary), allocate space for new string
661   from = *pstr;
662   if (!delim || !*delim) {
663     if (!(d = *(from++))) return 0;
664     if (d == '\\') d = *(from++);
665     if (!d || d == '\\') return 0;
666     if (delim) *delim = d;
667   } else d = *delim;
668   to = delim = xmalloc(strlen(*pstr)+1);
669
670   while (mode || *from != d) {
671     if (!*from) return 0;
672
673     // delimiter in regex character range doesn't count
674     if (*from == '[') {
675       if (!mode) {
676         mode = ']';
677         if (from[1]=='-' || from[1]==']') *(to++) = *(from++);
678       } else if (mode == ']' && strchr(".=:", from[1])) {
679         *(to++) = *(from++);
680         mode = *from;
681       }
682     } else if (*from == mode) {
683       if (mode == ']') mode = 0;
684       else {
685         *(to++) = *(from++);
686         mode = ']';
687       }
688     // Length 1 range (X-X with same X) is "undefined" and makes regcomp err,
689     // but the perl build does it, so we need to filter it out.
690     } else if (mode && *from == '-' && from[-1] == from[1]) {
691       from+=2;
692       continue;
693     } else if (*from == '\\') {
694       if (!from[1]) return 0;
695
696       // Check escaped end delimiter before printf style escapes.
697       if (from[1] == d) from++;
698       else if (from[1]=='\\') *(to++) = *(from++);
699       else {
700         char c = unescape(from[1]);
701
702         if (c) {
703           *(to++) = c;
704           from+=2;
705           continue;
706         } else if (!mode) *(to++) = *(from++);
707       }
708     }
709     *(to++) = *(from++);
710   }
711   *to = 0;
712   *pstr = from+1;
713
714   return delim;
715 }
716
717 // Translate pattern strings into command structures. Each command structure
718 // is a single allocation (which requires some math and remalloc at times).
719 static void parse_pattern(char **pline, long len)
720 {
721   struct sedcmd *command = (void *)TT.pattern;
722   char *line, *reg, c, *errstart;
723   int i;
724
725   line = errstart = pline ? *pline : "";
726   if (len && line[len-1]=='\n') line[--len] = 0;
727
728   // Append this line to previous multiline command? (hit indicates type.)
729   // During parsing "hit" stores data about line continuations, but in
730   // process_line() it means the match range attached to this command
731   // is active, so processing the continuation must zero it again.
732   if (command && command->prev->hit) {
733     // Remove half-finished entry from list so remalloc() doesn't confuse it
734     TT.pattern = TT.pattern->prev;
735     command = dlist_pop(&TT.pattern);
736     c = command->c;
737     reg = (char *)command;
738     reg += command->arg1 + strlen(reg + command->arg1);
739
740     // Resume parsing for 'a' or 's' command. (Only two that can do this.)
741     // TODO: using 256 to indicate 'a' means our s/// delimiter can't be
742     // a unicode character.
743     if (command->hit < 256) goto resume_s;
744     else goto resume_a;
745   }
746
747   // Loop through commands in this line.
748
749   command = 0;
750   for (;;) {
751     if (command) dlist_add_nomalloc(&TT.pattern, (void *)command);
752
753     // If there's no more data on this line, return.
754     for (;;) {
755       while (isspace(*line) || *line == ';') line++;
756       if (*line == '#') while (*line && *line != '\n') line++;
757       else break;
758     }
759     if (!*line) return;
760
761     // We start by writing data into toybuf. Later we'll allocate the
762     // ex
763
764     errstart = line;
765     memset(toybuf, 0, sizeof(struct sedcmd));
766     command = (void *)toybuf;
767     reg = toybuf + sizeof(struct sedcmd);
768
769     // Parse address range (if any)
770     for (i = 0; i < 2; i++) {
771       if (*line == ',') line++;
772       else if (i) break;
773
774       if (isdigit(*line)) command->lmatch[i] = strtol(line, &line, 0);
775       else if (*line == '$') {
776         command->lmatch[i] = -1;
777         line++;
778       } else if (*line == '/' || *line == '\\') {
779         char *s = line;
780
781         if (!(s = unescape_delimited_string(&line, 0))) goto error;
782         if (!*s) command->rmatch[i] = 0;
783         else {
784           xregcomp((void *)reg, s, (toys.optflags & FLAG_r)*REG_EXTENDED);
785           command->rmatch[i] = reg-toybuf;
786           reg += sizeof(regex_t);
787         }
788         free(s);
789       } else break;
790     }
791
792     while (isspace(*line)) line++;
793     if (!*line) break;
794
795     while (*line == '!') {
796       command->not = 1;
797       line++;
798     }
799     while (isspace(*line)) line++;
800
801     c = command->c = *(line++);
802     if (strchr("}:", c) && i) break;
803     if (strchr("aiqr=", c) && i>1) break;
804
805     // Add step to pattern
806     command = xmemdup(toybuf, reg-toybuf);
807     reg = (reg-toybuf) + (char *)command;
808
809     // Parse arguments by command type
810     if (c == '{') TT.nextlen++;
811     else if (c == '}') {
812       if (!TT.nextlen--) break;
813     } else if (c == 's') {
814       char *end, delim = 0;
815
816       // s/pattern/replacement/flags
817
818       // line continuations use arg1 (back at the start of the function),
819       // so let's fill out arg2 first (since the regex part can't be multiple
820       // lines) and swap them back later.
821
822       // get pattern (just record, we parse it later)
823       command->arg2 = reg - (char *)command;
824       if (!(TT.remember = unescape_delimited_string(&line, &delim)))
825         goto error;
826
827       reg += sizeof(regex_t);
828       command->arg1 = reg-(char *)command;
829       command->hit = delim;
830 resume_s:
831       // get replacement - don't replace escapes yet because \1 and \& need
832       // processing later, after we replace \\ with \ we can't tell \\1 from \1
833       end = line;
834       while (*end != command->hit) {
835         if (!*end) goto error;
836         if (*end++ == '\\') {
837           if (!*end || *end == '\n') {
838             end[-1] = '\n';
839             break;
840           }
841           end++;
842         }
843       }
844
845       reg = extend_string((void *)&command, line, reg-(char *)command,end-line);
846       line = end;
847       // line continuation? (note: '\n' can't be a valid delim).
848       if (*line == command->hit) command->hit = 0;
849       else {
850         if (!*line) continue;
851         reg--;
852         line++;
853         goto resume_s;
854       }
855
856       // swap arg1/arg2 so they're back in order arguments occur.
857       i = command->arg1;
858       command->arg1 = command->arg2;
859       command->arg2 = i;
860
861       // get flags
862       for (line++; *line; line++) {
863         long l;
864
865         if (isspace(*line) && *line != '\n') continue;
866
867         if (0 <= (l = stridx("igp", *line))) command->sflags |= 1<<l;
868         else if (!(command->sflags>>3) && 0<(l = strtol(line, &line, 10))) {
869           command->sflags |= l << 3;
870           line--;
871         } else break;
872       }
873
874       // We deferred actually parsing the regex until we had the s///i flag
875       // allocating the space was done by extend_string() above
876       if (!*TT.remember) command->arg1 = 0;
877       else xregcomp((void *)(command->arg1 + (char *)command), TT.remember,
878         ((toys.optflags & FLAG_r)*REG_EXTENDED)|((command->sflags&1)*REG_ICASE));
879       free(TT.remember);
880       TT.remember = 0;
881       if (*line == 'w') {
882         line++;
883         goto writenow;
884       }
885     } else if (c == 'w') {
886       int fd, delim;
887       char *cc;
888
889       // Since s/// uses arg1 and arg2, and w needs a persistent filehandle and
890       // eol status, and to retain the filename for error messages, we'd need
891       // to go up to arg5 just for this. Compromise: dynamically allocate the
892       // filehandle and eol status.
893
894 writenow:
895       while (isspace(*line)) line++;
896       if (!*line) goto error;
897       for (cc = line; *cc; cc++) if (*cc == '\\' && cc[1] == ';') break;
898       delim = *cc;
899       *cc = 0;
900       fd = xcreate(line, O_WRONLY|O_CREAT|O_TRUNC, 0644);
901       *cc = delim;
902
903       command->w = reg - (char *)command;
904       command = xrealloc(command, command->w+(cc-line)+6);
905       reg = command->w + (char *)command;
906
907       memcpy(reg, &fd, 4);
908       reg += 4;
909       *(reg++) = 0;
910       memcpy(reg, line, delim);
911       reg += delim;
912       *(reg++) = 0;
913
914       line = cc;
915       if (delim) line += 2;
916     } else if (c == 'y') {
917       char *s, delim = 0;
918       int len;
919
920       if (!(s = unescape_delimited_string(&line, &delim))) goto error;
921       command->arg1 = reg-(char *)command;
922       len = strlen(s);
923       reg = extend_string((void *)&command, s, reg-(char *)command, len);
924       free(s);
925       command->arg2 = reg-(char *)command;
926       if (!(s = unescape_delimited_string(&line, &delim))) goto error;
927       if (len != strlen(s)) goto error;
928       reg = extend_string((void *)&command, s, reg-(char*)command, len);
929       free(s);
930     } else if (strchr("abcirtTw:", c)) {
931       int end;
932
933       // trim leading spaces
934       while (isspace(*line) && *line != '\n') line++;
935
936       // Resume logic differs from 's' case because we don't add a newline
937       // unless it's after something, so we add it on return instead.
938 resume_a:
939       command->hit = 0;
940
941       // btT: end with space or semicolon, aicrw continue to newline.
942       if (!(end = strcspn(line, strchr(":btT", c) ? "; \t\r\n\v\f" : "\n"))) {
943         // Argument's optional for btT
944         if (strchr("btT", c)) continue;
945         else if (!command->arg1) break;
946       }
947
948       // Extend allocation to include new string. We use offsets instead of
949       // pointers so realloc() moving stuff doesn't break things. Ok to write
950       // \n over NUL terminator because call to extend_string() adds it back.
951       if (!command->arg1) command->arg1 = reg - (char*)command;
952       else if (*(command->arg1+(char *)command)) *(reg++) = '\n';
953       else if (!pline) {
954         command->arg1 = 0;
955         continue;
956       }
957       reg = extend_string((void *)&command, line, reg - (char *)command, end);
958
959       // Recopy data to remove escape sequences and handle line continuation.
960       if (strchr("aci", c)) {
961         reg -= end+1;
962         for (i = end; i; i--) {
963           if ((*reg++ = *line++)=='\\') {
964
965             // escape at end of line: resume if -e escaped literal newline,
966             // else request callback and resume with next line
967             if (!--i) {
968               *--reg = 0;
969               if (*line) {
970                 line++;
971                 goto resume_a;
972               }
973               command->hit = 256;
974               break;
975             }
976             if (!(reg[-1] = unescape(*line))) reg[-1] = *line;
977             line++;
978           }
979         }
980         *reg = 0;
981       } else line += end;
982
983     // Commands that take no arguments
984     } else if (!strchr("{dDgGhHlnNpPqx=", c)) break;
985   }
986
987 error:
988   error_exit("bad pattern '%s'@%ld (%c)", errstart, line-errstart+1L, *line);
989 }
990
991 void sed_main(void)
992 {
993   struct arg_list *al;
994   char **args = toys.optargs;
995
996   // Lie to autoconf when it asks stupid questions, so configure regexes
997   // that look for "GNU sed version %f" greater than some old buggy number
998   // don't fail us for not matching their narrow expectations.
999   if (toys.optflags & FLAG_version) {
1000     xprintf("This is not GNU sed version 9.0\n");
1001     return;
1002   }
1003
1004   // Handling our own --version means we handle our own --help too.
1005   if (toys.optflags&FLAG_help) help_exit(0);
1006
1007   // Parse pattern into commands.
1008
1009   // If no -e or -f, first argument is the pattern.
1010   if (!TT.e && !TT.f) {
1011     if (!*toys.optargs) error_exit("no pattern");
1012     (TT.e = xzalloc(sizeof(struct arg_list)))->arg = *(args++);
1013   }
1014
1015   // Option parsing infrastructure can't interlace "-e blah -f blah -e blah"
1016   // so handle all -e, then all -f. (At least the behavior's consistent.)
1017
1018   for (al = TT.e; al; al = al->next) parse_pattern(&al->arg, strlen(al->arg));
1019   for (al = TT.f; al; al = al->next) do_lines(xopenro(al->arg), parse_pattern);
1020   parse_pattern(0, 0);
1021   dlist_terminate(TT.pattern);
1022   if (TT.nextlen) error_exit("no }");  
1023
1024   TT.fdout = 1;
1025   TT.remember = xstrdup("");
1026
1027   // Inflict pattern upon input files. Long version because !O_CLOEXEC
1028   loopfiles_rw(args, O_RDONLY|WARN_ONLY, 0, do_sed);
1029
1030   if (!(toys.optflags & FLAG_i)) process_line(0, 0);
1031
1032   // todo: need to close fd when done for TOYBOX_FREE?
1033 }