1
2
3/* FIXME: summary
4 * decide whether we enforce valid UTF-8, right now it's enforced in certain
5 * parts of the script, but not the input...
6 * nul bytes cause explosions due to use of libc string functions. thoughts?
7 * lack of newline at end of file, currently we add one. what should we do?
8 * allow "\\t" for "\t" etc. in regex? in replacement text?
9 * POSIX says don't flush on N when out of input, but GNU and busybox do.
10 */
11
12#include "config.h"
13#include "utf.h"
14#include "util.h"
15
16#include <ctype.h>
17#include <errno.h>
18#include <libgen.h>
19#include <regex.h>
20#include <stdlib.h>
21#include <string.h>
22#include <sys/stat.h>
23#include <unistd.h>
24
25/* Types */
26
27/* used as queue for writes and stack for {,:,b,t */
28typedef struct {
29 void **data;
30 size_t size;
31 size_t cap;
32} Vec;
33
34/* used for arbitrary growth, str is a C string
35 * FIXME: does it make sense to keep track of length? or just rely on libc
36 * string functions? If we want to support nul bytes everything changes
37 */
38typedef struct {
39 char *str;
40 size_t cap;
41} String;
42
43typedef struct Cmd Cmd;
44typedef struct {
45 void (*fn)(Cmd *);
46 char *(*getarg)(Cmd *, char *);
47 void (*freearg)(Cmd *);
48 unsigned char naddr;
49} Fninfo;
50
51typedef struct {
52 union {
53 size_t lineno;
54 regex_t *re;
55 } u;
56 enum {
57 IGNORE, /* empty address, ignore */
58 EVERY, /* every line */
59 LINE, /* line number */
60 LAST, /* last line ($) */
61 REGEX, /* use included regex */
62 LASTRE, /* use most recently used regex */
63 } type;
64} Addr;
65
66/* DISCUSS: naddr is not strictly necessary, but very helpful
67 * naddr == 0 iff beg.type == EVERY && end.type == IGNORE
68 * naddr == 1 iff beg.type != IGNORE && end.type == IGNORE
69 * naddr == 2 iff beg.type != IGNORE && end.type != IGNORE
70 */
71typedef struct {
72 Addr beg;
73 Addr end;
74 unsigned char naddr;
75} Range;
76
77typedef struct {
78 regex_t *re; /* if NULL use last regex */
79 String repl;
80 FILE *file;
81 size_t occurrence; /* 0 for all (g flag) */
82 Rune delim;
83 unsigned int p : 1;
84} Sarg;
85
86typedef struct {
87 Rune *set1;
88 Rune *set2;
89} Yarg;
90
91typedef struct {
92 String str; /* a,c,i text. r file path */
93 void (*print)(char *, FILE *); /* check_puts for a, write_file for r,
94 unused for c,i */
95} ACIRarg;
96
97struct Cmd {
98 Range range;
99 Fninfo *fninfo;
100 union {
101 Cmd *jump; /* used for b,t when running */
102 char *label; /* used for :,b,t when building */
103 ptrdiff_t offset; /* used for { (pointers break during realloc) */
104 FILE *file; /* used for w */
105
106 /* FIXME: Should the following be in the union? or pointers and
107 * malloc? */
108 Sarg s;
109 Yarg y;
110 ACIRarg acir;
111 } u; /* I find your lack of anonymous unions disturbing */
112 unsigned int in_match : 1;
113 unsigned int negate : 1;
114};
115
116/* Files for w command (and s' w flag) */
117typedef struct {
118 char *path;
119 FILE *file;
120} Wfile;
121
122/*
123 * Function Declarations
124 */
125
126/* Dynamically allocated arrays and strings */
127static void resize(void **ptr, size_t *nmemb, size_t size, size_t new_nmemb, void **next);
128static void *pop(Vec *v);
129static void push(Vec *v, void *p);
130static void stracat(String *dst, char *src);
131static void strnacat(String *dst, char *src, size_t n);
132static void stracpy(String *dst, char *src);
133
134/* Cleanup and errors */
135static void usage(void);
136
137/* Parsing functions and related utilities */
138static void compile(char *s, int isfile);
139static int read_line(FILE *f, String *s);
140static char *make_range(Range *range, char *s);
141static char *make_addr(Addr *addr, char *s);
142static char *find_delim(char *s, Rune delim, int do_brackets);
143static char *chompr(char *s, Rune rune);
144static char *chomp(char *s);
145static Rune *strtorunes(char *s, size_t nrunes);
146static long stol(char *s, char **endp);
147static size_t escapes(char *beg, char *end, Rune delim, int n_newline);
148static size_t echarntorune(Rune *r, char *s, size_t n);
149static void insert_labels(void);
150
151/* Get and Free arg and related utilities */
152static char *get_aci_arg(Cmd *c, char *s);
153static void aci_append(Cmd *c, char *s);
154static void free_acir_arg(Cmd *c);
155static char *get_bt_arg(Cmd *c, char *s);
156static char *get_r_arg(Cmd *c, char *s);
157static char *get_s_arg(Cmd *c, char *s);
158static void free_s_arg(Cmd *c);
159static char *get_w_arg(Cmd *c, char *s);
160static char *get_y_arg(Cmd *c, char *s);
161static void free_y_arg(Cmd *c);
162static char *get_colon_arg(Cmd *c, char *s);
163static char *get_lbrace_arg(Cmd *c, char *s);
164static char *get_rbrace_arg(Cmd *c, char *s);
165static char *semicolon_arg(char *s);
166
167/* Running */
168static void run(void);
169static int in_range(Cmd *c);
170static int match_addr(Addr *a);
171static int next_file(void);
172static int is_eof(FILE *f);
173static void do_writes(void);
174static void write_file(char *path, FILE *out);
175static void check_puts(char *s, FILE *f);
176static void write_patt(char *s, FILE *f);
177static void update_ranges(Cmd *beg, Cmd *end);
178
179/* Sed functions */
180static void cmd_y(Cmd *c);
181static void cmd_x(Cmd *c);
182static void cmd_w(Cmd *c);
183static void cmd_t(Cmd *c);
184static void cmd_s(Cmd *c);
185static void cmd_r(Cmd *c);
186static void cmd_q(Cmd *c);
187static void cmd_P(Cmd *c);
188static void cmd_p(Cmd *c);
189static void cmd_N(Cmd *c);
190static void cmd_n(Cmd *c);
191static void cmd_l(Cmd *c);
192static void cmd_i(Cmd *c);
193static void cmd_H(Cmd *c);
194static void cmd_h(Cmd *c);
195static void cmd_G(Cmd *c);
196static void cmd_g(Cmd *c);
197static void cmd_D(Cmd *c);
198static void cmd_d(Cmd *c);
199static void cmd_c(Cmd *c);
200static void cmd_b(Cmd *c);
201static void cmd_a(Cmd *c);
202static void cmd_colon(Cmd *c);
203static void cmd_equal(Cmd *c);
204static void cmd_lbrace(Cmd *c);
205static void cmd_rbrace(Cmd *c);
206static void cmd_last(Cmd *c);
207
208/* Actions */
209static void new_line(void);
210static void app_line(void);
211static void new_next(void);
212static void old_next(void);
213
214/*
215 * Globals
216 */
217static Vec braces, labels, branches; /* holds ptrdiff_t. addrs of {, :, bt */
218static Vec writes; /* holds cmd*. writes scheduled by a and r commands */
219static Vec wfiles; /* holds Wfile*. files for w and s///w commands */
220
221static Cmd *prog, *pc; /* Program, program counter */
222static size_t pcap;
223static size_t lineno;
224#if FEATURE_SED_PRESERVE_NEWLINE
225static int hadnl = 1;
226#endif
227
228static regex_t *lastre; /* last used regex for empty regex search */
229static char **files; /* list of file names from argv */
230static FILE *file; /* current file we are reading */
231static int ret; /* exit status */
232
233static String patt, hold, genbuf;
234
235static struct {
236 unsigned int n : 1; /* -n (no print) */
237 unsigned int E : 1; /* -E (extended re) */
238 unsigned int s : 1; /* s/// replacement happened */
239 unsigned int aci_cont : 1; /* a,c,i text continuation */
240 unsigned int s_cont : 1; /* s/// replacement text continuation */
241 unsigned int halt : 1; /* halt execution */
242} gflags;
243
244/* FIXME: move character inside Fninfo and only use 26*sizeof(Fninfo) instead of
245 * 127*sizeof(Fninfo) bytes */
246static Fninfo fns[] = {
247 ['a'] = {cmd_a, get_aci_arg, free_acir_arg, 1}, /* schedule write of text for later */
248 ['b'] = {cmd_b, get_bt_arg, NULL, 2}, /* branch to label char *label when building, Cmd *jump
249 when running */
250 ['c'] = {cmd_c, get_aci_arg, free_acir_arg, 2}, /* delete pattern space. at 0 or 1 addr or end
251 of 2 addr, write text */
252 ['d'] = {cmd_d, NULL, NULL, 2}, /* delete pattern space */
253 ['D'] = {cmd_D, NULL, NULL, 2}, /* delete to first newline and start new cycle without
254 reading (if no newline, d) */
255 ['g'] = {cmd_g, NULL, NULL, 2}, /* replace pattern space with hold space */
256 ['G'] = {cmd_G, NULL, NULL, 2}, /* append newline and hold space to pattern space */
257 ['h'] = {cmd_h, NULL, NULL, 2}, /* replace hold space with pattern space */
258 ['H'] = {cmd_H, NULL, NULL, 2}, /* append newline and pattern space to hold space */
259 ['i'] = {cmd_i, get_aci_arg, free_acir_arg, 1}, /* write text */
260 ['l'] = {cmd_l, NULL, NULL, 2}, /* write pattern space in 'visually unambiguous form' */
261 ['n'] = {cmd_n, NULL, NULL, 2}, /* write pattern space (unless -n) read to replace pattern
262 space (if no input, quit) */
263 ['N'] = {cmd_N, NULL, NULL, 2}, /* append to pattern space separated by newline, line
264 number changes (if no input, quit) */
265 ['p'] = {cmd_p, NULL, NULL, 2}, /* write pattern space */
266 ['P'] = {cmd_P, NULL, NULL, 2}, /* write pattern space up to first newline */
267 ['q'] = {cmd_q, NULL, NULL, 1}, /* quit */
268 ['r'] = {cmd_r, get_r_arg, free_acir_arg, 1}, /* write contents of file (unable to open/read
269 treated as empty file) */
270 ['s'] = {cmd_s, get_s_arg, free_s_arg, 2}, /* find/replace/all that crazy s stuff */
271 ['t'] = {cmd_t, get_bt_arg, NULL, 2}, /* if s/// succeeded (since input or last t) branch to
272 label (branch to end if no label) */
273 ['w'] = {cmd_w, get_w_arg, NULL, 2}, /* append pattern space to file */
274 ['x'] = {cmd_x, NULL, NULL, 2}, /* exchange pattern and hold spaces */
275 ['y'] = {cmd_y, get_y_arg, free_y_arg, 2}, /* replace runes in set1 with runes in set2 */
276 [':'] = {cmd_colon, get_colon_arg, NULL, 0}, /* defines label for later b and t commands */
277 ['='] = {cmd_equal, NULL, NULL, 1}, /* printf("%d\n", line_number); */
278 ['{'] = {cmd_lbrace, get_lbrace_arg, NULL, 2}, /* if we match, run commands, otherwise jump to
279 close */
280 ['}'] = {cmd_rbrace, get_rbrace_arg, NULL, 0}, /* noop, hold onto open for ease of building
281 scripts */
282
283 [0x7f] = {NULL, NULL, NULL, 0}, /* index is checked with isascii(3p).
284 fill out rest of array */
285};
286
287/*
288 * Function Definitions
289 */
290
291/* given memory pointed to by *ptr that currently holds *nmemb members of size
292 * size, realloc to hold new_nmemb members, return new_nmemb in *memb and one
293 * past old end in *next. if realloc fails...explode
294 */
295static void
296resize(void **ptr, size_t *nmemb, size_t size, size_t new_nmemb, void **next)
297{
298 void *n, *tmp;
299
300 if (new_nmemb) {
301 tmp = ereallocarray(*ptr, new_nmemb, size);
302 } else { /* turns out realloc(*ptr, 0) != free(*ptr) */
303 free(*ptr);
304 tmp = NULL;
305 }
306 n = (char *)tmp + *nmemb * size;
307 *nmemb = new_nmemb;
308 *ptr = tmp;
309 if (next)
310 *next = n;
311}
312
313static void *
314pop(Vec *v)
315{
316 if (!v->size)
317 return NULL;
318 return v->data[--v->size];
319}
320
321static void
322push(Vec *v, void *p)
323{
324 if (v->size == v->cap)
325 resize((void **)&v->data, &v->cap, sizeof(*v->data), v->cap * 2 + 1, NULL);
326 v->data[v->size++] = p;
327}
328
329static void
330stracat(String *dst, char *src)
331{
332 int new = !dst->cap;
333 size_t len;
334
335 len = (new ? 0 : strlen(dst->str)) + strlen(src) + 1;
336 if (dst->cap < len)
337 resize((void **)&dst->str, &dst->cap, 1, len * 2, NULL);
338 if (new)
339 *dst->str = '\0';
340 strcat(dst->str, src);
341}
342
343static void
344strnacat(String *dst, char *src, size_t n)
345{
346 int new = !dst->cap;
347 size_t len;
348
349 len = strlen(src);
350 len = (new ? 0 : strlen(dst->str)) + MIN(n, len) + 1;
351 if (dst->cap < len)
352 resize((void **)&dst->str, &dst->cap, 1, len * 2, NULL);
353 if (new)
354 *dst->str = '\0';
355 strlcat(dst->str, src, len);
356}
357
358static void
359stracpy(String *dst, char *src)
360{
361 size_t len;
362
363 len = strlen(src) + 1;
364 if (dst->cap < len)
365 resize((void **)&dst->str, &dst->cap, 1, len * 2, NULL);
366 strcpy(dst->str, src);
367}
368
369static void
370leprintf(char *s)
371{
372 if (errno)
373 eprintf("%zu: %s: %s\n", lineno, s, strerror(errno));
374 else
375 eprintf("%zu: %s\n", lineno, s);
376}
377
378/* FIXME: write usage message */
379#if FEATURE_SED_INPLACE
380static int iflag = 0;
381static char *backup_suffix = NULL;
382
383static int
384create_temp_file(const char *orig_path, char **temp_path)
385{
386 char *dir, *dircopy, *tmpl;
387 int fd;
388
389 dircopy = estrdup(orig_path);
390 dir = dirname(dircopy);
391 tmpl = emalloc(strlen(dir) + 16);
392 sprintf(tmpl, "%s/sedtmpXXXXXX", dir);
393 free(dircopy);
394
395 fd = mkstemp(tmpl);
396 if (fd < 0) {
397 free(tmpl);
398 return -1;
399 }
400 *temp_path = tmpl;
401 return fd;
402}
403#endif
404
405static void
406usage(void)
407{
408 eprintf(
409 "usage: sed [-nrE] script [file ...]\n"
410 " sed [-nrE] -e script [-e script] ... [-f scriptfile] "
411 "... [file ...]\n"
412 " sed [-nrE] [-e script] ... -f scriptfile [-f "
413 "scriptfile] ... [file ...]\n"
414 );
415}
416
417/* Differences from POSIX
418 * we allows semicolons and trailing blanks inside {}
419 * we allow spaces after ! (and in between !s)
420 * we allow extended regular expressions (-E)
421 */
422static void
423compile(char *s, int isfile)
424{
425 FILE *f;
426
427 if (isfile) {
428 f = fopen(s, "r");
429 if (!f)
430 eprintf("fopen %s:", s);
431 } else {
432 if (!*s) /* empty string script */
433 return;
434 f = fmemopen(s, strlen(s), "r");
435 if (!f)
436 eprintf("fmemopen:");
437 }
438
439 /* NOTE: get arg functions can't use genbuf */
440 while (read_line(f, &genbuf) != EOF) {
441 s = genbuf.str;
442
443 /* if the first two characters of the script are "#n" default
444 * output shall be suppressed */
445 if (++lineno == 1 && *s == '#' && s[1] == 'n') {
446 gflags.n = 1;
447 continue;
448 }
449
450 if (gflags.aci_cont) {
451 aci_append(pc - 1, s);
452 continue;
453 }
454 if (gflags.s_cont)
455 s = (pc - 1)->fninfo->getarg(pc - 1, s);
456
457 while (*s) {
458 s = chompr(s, ';');
459 if (!*s || *s == '#')
460 break;
461
462 if ((size_t)(pc - prog) == pcap)
463 resize((void **)&prog, &pcap, sizeof(*prog), pcap * 2 + 1, (void **)&pc);
464
465 pc->range.beg.type = pc->range.end.type = IGNORE;
466 pc->fninfo = NULL;
467 pc->in_match = 0;
468
469 s = make_range(&pc->range, s);
470 s = chomp(s);
471 pc->negate = *s == '!';
472 s = chompr(s, '!');
473
474 if (!isascii(*s) || !(pc->fninfo = &fns[(unsigned)*s])->fn)
475 leprintf("bad sed function");
476 if (pc->range.naddr > pc->fninfo->naddr)
477 leprintf("wrong number of addresses");
478 s++;
479
480 if (pc->fninfo->getarg)
481 s = pc->fninfo->getarg(pc, s);
482
483 pc++;
484 }
485 }
486
487 fshut(f, s);
488}
489
490/* FIXME: if we decide to honor lack of trailing newline, set/clear a global
491 * flag when reading a line
492 */
493static int
494read_line(FILE *f, String *s)
495{
496 ssize_t len;
497
498 if (!f)
499 return EOF;
500
501 if ((len = getline(&s->str, &s->cap, f)) < 0) {
502 if (ferror(f))
503 eprintf("getline:");
504 return EOF;
505 }
506#if FEATURE_SED_PRESERVE_NEWLINE
507 if (len > 0)
508 hadnl = (s->str[len - 1] == '\n');
509#endif
510 if (s->str[--len] == '\n')
511 s->str[len] = '\0';
512 return 0;
513}
514
515/* read first range from s, return pointer to one past end of range */
516static char *
517make_range(Range *range, char *s)
518{
519 s = make_addr(&range->beg, s);
520
521 if (*s == ',')
522 s = make_addr(&range->end, s + 1);
523 else
524 range->end.type = IGNORE;
525
526 if (range->beg.type == EVERY && range->end.type == IGNORE)
527 range->naddr = 0;
528 else if (range->beg.type != IGNORE && range->end.type == IGNORE)
529 range->naddr = 1;
530 else if (range->beg.type != IGNORE && range->end.type != IGNORE)
531 range->naddr = 2;
532 else
533 leprintf("this is impossible...");
534
535 return s;
536}
537
538/* read first addr from s, return pointer to one past end of addr */
539static char *
540make_addr(Addr *addr, char *s)
541{
542 Rune r;
543 char *p = s + strlen(s);
544 size_t rlen = echarntorune(&r, s, p - s);
545
546 if (r == '$') {
547 addr->type = LAST;
548 s += rlen;
549 } else if (isdigitrune(r)) {
550 addr->type = LINE;
551 addr->u.lineno = stol(s, &s);
552 } else if (r == '/' || r == '\\') {
553 Rune delim;
554 if (r == '\\') {
555 s += rlen;
556 rlen = echarntorune(&r, s, p - s);
557 }
558 if (r == '\\')
559 leprintf("bad delimiter '\\'");
560 delim = r;
561 s += rlen;
562 rlen = echarntorune(&r, s, p - s);
563 if (r == delim) {
564 addr->type = LASTRE;
565 s += rlen;
566 } else {
567 addr->type = REGEX;
568 p = find_delim(s, delim, 1);
569 if (!*p)
570 leprintf("unclosed regex");
571 p -= escapes(s, p, delim, 0);
572 *p++ = '\0';
573 addr->u.re = emalloc(sizeof(*addr->u.re));
574 eregcomp(addr->u.re, s, gflags.E ? REG_EXTENDED : 0);
575 s = p;
576 }
577 } else {
578 addr->type = EVERY;
579 }
580
581 return s;
582}
583
584/* return pointer to first delim in s that is not escaped
585 * and if do_brackets is set, not in [] (note possible [::], [..], [==], inside
586 * []) return pointer to trailing nul byte if no delim found
587 *
588 * any escaped character that is not special is just itself (POSIX undefined)
589 * FIXME: pull out into some util thing, will be useful for ed as well
590 */
591static char *
592find_delim(char *s, Rune delim, int do_brackets)
593{
594 enum {
595 OUTSIDE, /* not in brackets */
596 BRACKETS_OPENING, /* last char was first [ or last two were
597 first [^ */
598 BRACKETS_INSIDE, /* inside [] */
599 INSIDE_OPENING, /* inside [] and last char was [ */
600 CLASS_INSIDE, /* inside class [::], or colating element [..] or
601 [==], inside [] */
602 CLASS_CLOSING, /* inside class [::], or colating element [..] or
603 [==], and last character was the respective :
604 . or = */
605 } state = OUTSIDE;
606
607 Rune r, c = 0; /* no c won't be used uninitialized, shutup -Wall */
608 size_t rlen;
609 int escape = 0;
610 char *end = s + strlen(s);
611
612 for (; *s; s += rlen) {
613 rlen = echarntorune(&r, s, end - s);
614
615 if (state == BRACKETS_OPENING && r == '^') {
616 continue;
617 } else if (state == BRACKETS_OPENING && r == ']') {
618 state = BRACKETS_INSIDE;
619 continue;
620 } else if (state == BRACKETS_OPENING) {
621 state = BRACKETS_INSIDE;
622 }
623
624 if (state == CLASS_CLOSING && r == ']') {
625 state = BRACKETS_INSIDE;
626 } else if (state == CLASS_CLOSING) {
627 state = CLASS_INSIDE;
628 } else if (state == CLASS_INSIDE && r == c) {
629 state = CLASS_CLOSING;
630 } else if (state == INSIDE_OPENING && (r == ':' || r == '.' || r == '=')) {
631 state = CLASS_INSIDE;
632 c = r;
633 } else if (state == INSIDE_OPENING && r == ']') {
634 state = OUTSIDE;
635 } else if (state == INSIDE_OPENING) {
636 state = BRACKETS_INSIDE;
637 } else if (state == BRACKETS_INSIDE && r == '[') {
638 state = INSIDE_OPENING;
639 } else if (state == BRACKETS_INSIDE && r == ']') {
640 state = OUTSIDE;
641 } else if (state == OUTSIDE && escape) {
642 escape = 0;
643 } else if (state == OUTSIDE && r == '\\') {
644 escape = 1;
645 } else if (state == OUTSIDE && r == delim)
646 return s;
647 else if (state == OUTSIDE && do_brackets && r == '[') {
648 state = BRACKETS_OPENING;
649 }
650 }
651 return s;
652}
653
654static char *
655chomp(char *s)
656{
657 return chompr(s, 0);
658}
659
660/* eat all leading whitespace and occurrences of rune */
661static char *
662chompr(char *s, Rune rune)
663{
664 Rune r;
665 size_t rlen;
666 char *end = s + strlen(s);
667
668 while (*s && (rlen = echarntorune(&r, s, end - s)) && (isspacerune(r) || r == rune))
669 s += rlen;
670 return s;
671}
672
673/* convert first nrunes Runes from UTF-8 string s in allocated Rune*
674 * NOTE: sequence must be valid UTF-8, check first */
675static Rune *
676strtorunes(char *s, size_t nrunes)
677{
678 Rune *rs, *rp;
679
680 rp = rs = ereallocarray(NULL, nrunes + 1, sizeof(*rs));
681
682 while (nrunes--)
683 s += chartorune(rp++, s);
684
685 *rp = '\0';
686 return rs;
687}
688
689static long
690stol(char *s, char **endp)
691{
692 long n;
693 errno = 0;
694 n = strtol(s, endp, 10);
695
696 if (errno)
697 leprintf("strtol:");
698 if (*endp == s)
699 leprintf("strtol: invalid number");
700
701 return n;
702}
703
704/* from beg to end replace "\\d" with "d" and "\\n" with "\n" (where d is delim)
705 * if delim is 'n' and n_newline is 0 then "\\n" is replaced with "n" (normal)
706 * if delim is 'n' and n_newline is 1 then "\\n" is replaced with "\n" (y
707 * command) if delim is 0 all escaped characters represent themselves (aci text)
708 * memmove rest of string (beyond end) into place
709 * return the number of converted escapes (backslashes removed)
710 * FIXME: this has had too many corner cases slapped on and is ugly. rewrite
711 * better
712 */
713static size_t
714escapes(char *beg, char *end, Rune delim, int n_newline)
715{
716 size_t num = 0;
717 char *src = beg, *dst = beg;
718
719 while (src < end) {
720 /* handle escaped backslash specially so we don't think the
721 * second backslash is escaping something */
722 if (*src == '\\' && src[1] == '\\') {
723 *dst++ = *src++;
724 if (delim)
725 *dst++ = *src++;
726 else
727 src++;
728 } else if (*src == '\\' && !delim) {
729 src++;
730 } else if (*src == '\\' && src[1]) {
731 Rune r;
732 size_t rlen;
733 num++;
734 src++;
735 rlen = echarntorune(&r, src, end - src);
736
737 if (r == 'n' && delim == 'n') {
738 *src = n_newline ? '\n' : 'n'; /* src so we can still
739 memmove() */
740 } else if (r == 'n') {
741 *src = '\n';
742 } else if (r != delim) {
743 *dst++ = '\\';
744 num--;
745 }
746
747 memmove(dst, src, rlen);
748 dst += rlen;
749 src += rlen;
750 } else {
751 *dst++ = *src++;
752 }
753 }
754 memmove(dst, src, strlen(src) + 1);
755 return num;
756}
757
758static size_t
759echarntorune(Rune *r, char *s, size_t n)
760{
761 size_t rlen = charntorune(r, s, n);
762 if (!rlen || *r == Runeerror)
763 leprintf("invalid UTF-8");
764 return rlen;
765}
766
767static void
768insert_labels(void)
769{
770 size_t i;
771 Cmd *from, *to;
772
773 while (branches.size) {
774 from = prog + (ptrdiff_t)pop(&branches);
775
776 if (!from->u.label) { /* no label branch to end of script */
777 from->u.jump = pc - 1;
778 } else {
779 for (i = 0; i < labels.size; i++) {
780 to = prog + (ptrdiff_t)labels.data[i];
781 if (!strcmp(from->u.label, to->u.label)) {
782 from->u.jump = to;
783 break;
784 }
785 }
786 if (i == labels.size)
787 leprintf("bad label");
788 }
789 }
790}
791
792/*
793 * Getargs / Freeargs
794 * Read argument from s, return pointer to one past last character of argument
795 */
796
797/* POSIX compliant
798 * i\
799 * foobar
800 *
801 * also allow the following non POSIX compliant
802 * i # empty line
803 * ifoobar
804 * ifoobar\
805 * baz
806 *
807 * FIXME: GNU and busybox discard leading spaces
808 * i foobar
809 * i foobar
810 * ifoobar
811 * are equivalent in GNU and busybox. We don't. Should we?
812 */
813static char *
814get_aci_arg(Cmd *c, char *s)
815{
816 c->u.acir.print = check_puts;
817 c->u.acir.str = (String){NULL, 0};
818
819 gflags.aci_cont = !!*s; /* no continue flag if empty string */
820
821 /* neither empty string nor POSIX compliant */
822 if (*s && !(*s == '\\' && !s[1]))
823 aci_append(c, s);
824
825 return s + strlen(s);
826}
827
828static void
829aci_append(Cmd *c, char *s)
830{
831 char *end = s + strlen(s), *p = end;
832
833 gflags.aci_cont = 0;
834 while (--p >= s && *p == '\\')
835 gflags.aci_cont = !gflags.aci_cont;
836
837 if (gflags.aci_cont)
838 *--end = '\n';
839
840 escapes(s, end, 0, 0);
841 stracat(&c->u.acir.str, s);
842}
843
844static void
845free_acir_arg(Cmd *c)
846{
847 free(c->u.acir.str.str);
848}
849
850/* POSIX dictates that label is rest of line, including semicolons, trailing
851 * whitespace, closing braces, etc. and can be limited to 8 bytes
852 *
853 * I allow a semicolon or closing brace to terminate a label name, it's not
854 * POSIX compliant, but it's useful and every sed version I've tried to date
855 * does the same.
856 *
857 * FIXME: POSIX dictates that leading whitespace is ignored but trailing
858 * whitespace is not. This is annoying and we should probably get rid of it.
859 */
860static char *
861get_bt_arg(Cmd *c, char *s)
862{
863 char *p = semicolon_arg(s = chomp(s));
864
865 if (p != s) {
866 c->u.label = estrndup(s, p - s);
867 } else {
868 c->u.label = NULL;
869 }
870
871 push(&branches, (void *)(c - prog));
872
873 return p;
874}
875
876/* POSIX dictates file name is rest of line including semicolons, trailing
877 * whitespace, closing braces, etc. and file name must be preceded by a space
878 *
879 * I allow a semicolon or closing brace to terminate a file name and don't
880 * enforce leading space.
881 *
882 * FIXME: decide whether trailing whitespace should be included and fix
883 * accordingly
884 */
885static char *
886get_r_arg(Cmd *c, char *s)
887{
888 char *p = semicolon_arg(s = chomp(s));
889
890 if (p == s)
891 leprintf("no file name");
892
893 c->u.acir.str.str = estrndup(s, p - s);
894 c->u.acir.print = write_file;
895
896 return p;
897}
898
899/* we allow "\\n" in replacement text to mean "\n" (undefined in POSIX)
900 *
901 * FIXME: allow other escapes in regex and replacement? if so change escapes()
902 */
903static char *
904get_s_arg(Cmd *c, char *s)
905{
906 Rune delim, r;
907 Cmd buf;
908 char *p;
909 int esc, lastre;
910
911 /* s/Find/Replace/Flags */
912
913 /* Find */
914 if (!gflags.s_cont) { /* NOT continuing from literal newline in
915 replacement text */
916 lastre = 0;
917 c->u.s.repl = (String){NULL, 0};
918 c->u.s.occurrence = 1;
919 c->u.s.file = NULL;
920 c->u.s.p = 0;
921
922 if (!*s || *s == '\\')
923 leprintf("bad delimiter");
924
925 p = s + strlen(s);
926 s += echarntorune(&delim, s, p - s);
927 c->u.s.delim = delim;
928
929 echarntorune(&r, s, p - s);
930 if (r == delim) /* empty regex */
931 lastre = 1;
932
933 p = find_delim(s, delim, 1);
934 if (!*p)
935 leprintf("missing second delimiter");
936 p -= escapes(s, p, delim, 0);
937 *p = '\0';
938
939 if (lastre) {
940 c->u.s.re = NULL;
941 } else {
942 c->u.s.re = emalloc(sizeof(*c->u.s.re));
943 /* FIXME: different eregcomp that calls fatal */
944 eregcomp(c->u.s.re, s, gflags.E ? REG_EXTENDED : 0);
945 }
946 s = p + runelen(delim);
947 }
948
949 /* Replace */
950 delim = c->u.s.delim;
951
952 p = find_delim(s, delim, 0);
953 p -= escapes(s, p, delim, 0);
954 if (!*p) { /* no third delimiter */
955 /* FIXME: same backslash counting as aci_append() */
956 if (p[-1] != '\\')
957 leprintf(
958 "missing third delimiter or "
959 "<backslash><newline>"
960 );
961 p[-1] = '\n';
962 gflags.s_cont = 1;
963 } else {
964 gflags.s_cont = 0;
965 }
966
967 /* check for bad references in replacement text */
968 *p = '\0';
969 for (esc = 0, p = s; *p; p++) {
970 if (esc) {
971 esc = 0;
972 if (isdigit(*p) && c->u.s.re && (size_t)(*p - '0') > c->u.s.re->re_nsub)
973 leprintf(
974 "back reference number greater than "
975 "number of groups"
976 );
977 } else if (*p == '\\') {
978 esc = 1;
979 }
980 }
981 stracat(&c->u.s.repl, s);
982
983 if (gflags.s_cont)
984 return p;
985
986 s = p + runelen(delim);
987
988 /* Flags */
989 p = semicolon_arg(s = chomp(s));
990
991 /* FIXME: currently for simplicity take last of g or occurrence flags
992 * and ignore multiple p flags. need to fix that */
993 for (; s < p; s++) {
994 if (isdigit(*s)) {
995 c->u.s.occurrence = stol(s, &s);
996 s--; /* for loop will advance pointer */
997 } else {
998 switch (*s) {
999 case 'g':
1000 c->u.s.occurrence = 0;
1001 break;
1002 case 'p':
1003 c->u.s.p = 1;
1004 break;
1005 case 'w':
1006 /* must be last flag, take everything up to
1007 * newline/semicolon s == p after this */
1008 s = get_w_arg(&buf, chomp(s + 1));
1009 c->u.s.file = buf.u.file;
1010 break;
1011 }
1012 }
1013 }
1014 return p;
1015}
1016
1017static void
1018free_s_arg(Cmd *c)
1019{
1020 if (c->u.s.re)
1021 regfree(c->u.s.re);
1022 free(c->u.s.re);
1023 free(c->u.s.repl.str);
1024}
1025
1026/* see get_r_arg notes */
1027static char *
1028get_w_arg(Cmd *c, char *s)
1029{
1030 char *p = semicolon_arg(s = chomp(s));
1031 Wfile *w, **wp;
1032
1033 if (p == s)
1034 leprintf("no file name");
1035
1036 for (wp = (Wfile **)wfiles.data; (size_t)(wp - (Wfile **)wfiles.data) < wfiles.size; wp++) {
1037 if (strlen((*wp)->path) == (size_t)(p - s) && !strncmp(s, (*wp)->path, p - s)) {
1038 c->u.file = (*wp)->file;
1039 return p;
1040 }
1041 }
1042
1043 w = emalloc(sizeof(*w));
1044 w->path = estrndup(s, p - s);
1045
1046 if (!(w->file = fopen(w->path, "w")))
1047 leprintf("fopen failed");
1048
1049 c->u.file = w->file;
1050
1051 push(&wfiles, w);
1052 return p;
1053}
1054
1055static char *
1056get_y_arg(Cmd *c, char *s)
1057{
1058 Rune delim;
1059 char *p = s + strlen(s);
1060 size_t rlen = echarntorune(&delim, s, p - s);
1061 size_t nrunes1, nrunes2;
1062
1063 c->u.y.set1 = c->u.y.set2 = NULL;
1064
1065 s += rlen;
1066 p = find_delim(s, delim, 0);
1067 p -= escapes(s, p, delim, 1);
1068 nrunes1 = utfnlen(s, p - s);
1069 c->u.y.set1 = strtorunes(s, nrunes1);
1070
1071 s = p + rlen;
1072 p = find_delim(s, delim, 0);
1073 p -= escapes(s, p, delim, 1);
1074 nrunes2 = utfnlen(s, p - s);
1075
1076 if (nrunes1 != nrunes2)
1077 leprintf("different set lengths");
1078
1079 c->u.y.set2 = strtorunes(s, utfnlen(s, p - s));
1080
1081 return p + rlen;
1082}
1083
1084static void
1085free_y_arg(Cmd *c)
1086{
1087 free(c->u.y.set1);
1088 free(c->u.y.set2);
1089}
1090
1091/* see get_bt_arg notes */
1092static char *
1093get_colon_arg(Cmd *c, char *s)
1094{
1095 char *p = semicolon_arg(s = chomp(s));
1096
1097 if (p == s)
1098 leprintf("no label name");
1099
1100 c->u.label = estrndup(s, p - s);
1101 push(&labels, (void *)(c - prog));
1102 return p;
1103}
1104
1105static char *
1106get_lbrace_arg(Cmd *c, char *s)
1107{
1108 push(&braces, (void *)(c - prog));
1109 return s;
1110}
1111
1112static char *
1113get_rbrace_arg(Cmd *c, char *s)
1114{
1115 Cmd *lbrace;
1116
1117 if (!braces.size)
1118 leprintf("extra }");
1119
1120 lbrace = prog + (ptrdiff_t)pop(&braces);
1121 lbrace->u.offset = c - prog;
1122 return s;
1123}
1124
1125/* s points to beginning of an argument that may be semicolon terminated
1126 * return pointer to semicolon or nul byte after string
1127 * or closing brace as to not force ; before }
1128 * FIXME: decide whether or not to eat trailing whitespace for arguments that
1129 * we allow semicolon/brace termination that POSIX doesn't
1130 * b, r, t, w, :
1131 * POSIX says trailing whitespace is part of label name, file name, etc.
1132 * we should probably eat it
1133 */
1134static char *
1135semicolon_arg(char *s)
1136{
1137 char *p = strpbrk(s, ";}");
1138 if (!p)
1139 p = s + strlen(s);
1140 return p;
1141}
1142
1143static void
1144run(void)
1145{
1146 lineno = 0;
1147 if (braces.size)
1148 leprintf("extra {");
1149
1150 /* genbuf has already been initialized, patt will be in new_line
1151 * (or we'll halt) */
1152 stracpy(&hold, "");
1153
1154 insert_labels();
1155 next_file();
1156 new_line();
1157
1158 for (pc = prog; !gflags.halt; pc++)
1159 pc->fninfo->fn(pc);
1160}
1161
1162/* return true if we are in range for c, set c->in_match appropriately */
1163static int
1164in_range(Cmd *c)
1165{
1166 if (match_addr(&c->range.beg)) {
1167 if (c->range.naddr == 2) {
1168 if (c->range.end.type == LINE && c->range.end.u.lineno <= lineno)
1169 c->in_match = 0;
1170 else
1171 c->in_match = 1;
1172 }
1173 return !c->negate;
1174 }
1175 if (c->in_match && match_addr(&c->range.end)) {
1176 c->in_match = 0;
1177 return !c->negate;
1178 }
1179 return c->in_match ^ c->negate;
1180}
1181
1182/* return true if addr matches current line */
1183static int
1184match_addr(Addr *a)
1185{
1186 switch (a->type) {
1187 default:
1188 case IGNORE:
1189 return 0;
1190 case EVERY:
1191 return 1;
1192 case LINE:
1193 return lineno == a->u.lineno;
1194 case LAST:
1195 while (is_eof(file) && !next_file())
1196 ;
1197 return !file;
1198 case REGEX:
1199 lastre = a->u.re;
1200 return !regexec(a->u.re, patt.str, 0, NULL, 0);
1201 case LASTRE:
1202 if (!lastre)
1203 leprintf("no previous regex");
1204 return !regexec(lastre, patt.str, 0, NULL, 0);
1205 }
1206}
1207
1208/* move to next input file
1209 * stdin if first call and no files
1210 * return 0 for success and 1 for no more files
1211 */
1212static int
1213next_file(void)
1214{
1215 static unsigned char first = 1;
1216
1217 if (file == stdin)
1218 clearerr(file);
1219 else if (file)
1220 fshut(file, "<file>");
1221 /* given no files, default to stdin */
1222 file = first && !*files ? stdin : NULL;
1223 first = 0;
1224
1225 while (!file && *files) {
1226 if (!strcmp(*files, "-")) {
1227 file = stdin;
1228 } else if (!(file = fopen(*files, "r"))) {
1229 /* warn this file didn't open, but move on to next */
1230 weprintf("fopen %s:", *files);
1231 ret = 1;
1232 }
1233 files++;
1234 }
1235
1236 return !file;
1237}
1238
1239/* test if stream is at EOF */
1240static int
1241is_eof(FILE *f)
1242{
1243 int c;
1244
1245 if (!f || feof(f))
1246 return 1;
1247
1248 c = fgetc(f);
1249 if (c == EOF && ferror(f))
1250 eprintf("fgetc:");
1251 if (c != EOF && ungetc(c, f) == EOF)
1252 eprintf("ungetc EOF\n");
1253
1254 return c == EOF;
1255}
1256
1257/* perform writes that were scheduled
1258 * for aci this is check_puts(string, stdout)
1259 * for r this is write_file(path, stdout)
1260 */
1261static void
1262do_writes(void)
1263{
1264 Cmd *c;
1265 size_t i;
1266
1267 for (i = 0; i < writes.size; i++) {
1268 c = writes.data[i];
1269 c->u.acir.print(c->u.acir.str.str, stdout);
1270 }
1271 writes.size = 0;
1272}
1273
1274/* used for r's u.acir.print()
1275 * FIXME: something like util's concat() would be better
1276 */
1277static void
1278write_file(char *path, FILE *out)
1279{
1280 FILE *in = fopen(path, "r");
1281 if (!in) /* no file is treated as empty file */
1282 return;
1283
1284 while (read_line(in, &genbuf) != EOF)
1285 check_puts(genbuf.str, out);
1286
1287 fshut(in, path);
1288}
1289
1290static void
1291check_puts(char *s, FILE *f)
1292{
1293 if (s && fputs(s, f) == EOF)
1294 eprintf("fputs:");
1295 if (fputs("\n", f) == EOF)
1296 eprintf("fputs:");
1297}
1298
1299static void
1300write_patt(char *s, FILE *f)
1301{
1302#if FEATURE_SED_PRESERVE_NEWLINE
1303 if (s && fputs(s, f) == EOF)
1304 eprintf("fputs:");
1305 if (hadnl) {
1306 if (fputs("\n", f) == EOF)
1307 eprintf("fputs:");
1308 }
1309#else
1310 check_puts(s, f);
1311#endif
1312}
1313
1314/* iterate from beg to end updating ranges so we don't miss any commands
1315 * e.g. sed -n '1d;1,3p' should still print lines 2 and 3
1316 */
1317static void
1318update_ranges(Cmd *beg, Cmd *end)
1319{
1320 while (beg < end)
1321 in_range(beg++);
1322}
1323
1324/*
1325 * Sed functions
1326 */
1327static void
1328cmd_a(Cmd *c)
1329{
1330 if (in_range(c))
1331 push(&writes, c);
1332}
1333
1334static void
1335cmd_b(Cmd *c)
1336{
1337 if (!in_range(c))
1338 return;
1339
1340 /* if we jump backwards update to end, otherwise update to destination
1341 */
1342 update_ranges(c + 1, c->u.jump > c ? c->u.jump : prog + pcap);
1343 pc = c->u.jump;
1344}
1345
1346static void
1347cmd_c(Cmd *c)
1348{
1349 if (!in_range(c))
1350 return;
1351
1352 /* write the text on the last line of the match */
1353 if (!c->in_match)
1354 check_puts(c->u.acir.str.str, stdout);
1355 /* otherwise start the next cycle without printing pattern space
1356 * effectively deleting the text */
1357 new_next();
1358}
1359
1360static void
1361cmd_d(Cmd *c)
1362{
1363 if (!in_range(c))
1364 return;
1365
1366 new_next();
1367}
1368
1369static void
1370cmd_D(Cmd *c)
1371{
1372 char *p;
1373
1374 if (!in_range(c))
1375 return;
1376
1377 if ((p = strchr(patt.str, '\n'))) {
1378 p++;
1379 memmove(patt.str, p, strlen(p) + 1);
1380 old_next();
1381 } else {
1382 new_next();
1383 }
1384}
1385
1386static void
1387cmd_g(Cmd *c)
1388{
1389 if (in_range(c))
1390 stracpy(&patt, hold.str);
1391}
1392
1393static void
1394cmd_G(Cmd *c)
1395{
1396 if (!in_range(c))
1397 return;
1398
1399 stracat(&patt, "\n");
1400 stracat(&patt, hold.str);
1401}
1402
1403static void
1404cmd_h(Cmd *c)
1405{
1406 if (in_range(c))
1407 stracpy(&hold, patt.str);
1408}
1409
1410static void
1411cmd_H(Cmd *c)
1412{
1413 if (!in_range(c))
1414 return;
1415
1416 stracat(&hold, "\n");
1417 stracat(&hold, patt.str);
1418}
1419
1420static void
1421cmd_i(Cmd *c)
1422{
1423 if (in_range(c))
1424 check_puts(c->u.acir.str.str, stdout);
1425}
1426
1427/* I think it makes sense to print invalid UTF-8 sequences in octal to satisfy
1428 * the "visually unambiguous form" sed(1p)
1429 */
1430static void
1431cmd_l(Cmd *c)
1432{
1433 Rune r;
1434 char *p, *end;
1435 size_t rlen;
1436
1437 char *escapes[] = {
1438 /* FIXME: 7 entries and search instead of 127 */
1439 ['\\'] = "\\\\",
1440 ['\a'] = "\\a",
1441 ['\b'] = "\\b",
1442 ['\f'] = "\\f",
1443 ['\r'] = "\\r",
1444 ['\t'] = "\\t",
1445 ['\v'] = "\\v",
1446 [0x7f] = NULL, /* fill out the table */
1447 };
1448
1449 if (!in_range(c))
1450 return;
1451
1452 /* FIXME: line wrapping. sed(1p) says "length at which folding occurs is
1453 * unspecified, but should be appropraite for the output device"
1454 * just wrap at 80 Runes?
1455 */
1456 for (p = patt.str, end = p + strlen(p); p < end; p += rlen) {
1457 if (isascii(*p) && escapes[(unsigned int)*p]) {
1458 fputs(escapes[(unsigned int)*p], stdout);
1459 rlen = 1;
1460 } else if (!(rlen = charntorune(&r, p, end - p))) {
1461 /* ran out of chars, print the bytes of the short
1462 * sequence */
1463 for (; p < end; p++)
1464 printf("\\%03hho", (unsigned char)*p);
1465 break;
1466 } else if (r == Runeerror) {
1467 for (; rlen; rlen--, p++)
1468 printf("\\%03hho", (unsigned char)*p);
1469 } else {
1470 while (fwrite(p, rlen, 1, stdout) < 1 && errno == EINTR)
1471 ;
1472 if (ferror(stdout))
1473 eprintf("fwrite:");
1474 }
1475 }
1476 check_puts("$", stdout);
1477}
1478
1479static void
1480cmd_n(Cmd *c)
1481{
1482 if (!in_range(c))
1483 return;
1484
1485 if (!gflags.n)
1486 write_patt(patt.str, stdout);
1487 do_writes();
1488 new_line();
1489}
1490
1491static void
1492cmd_N(Cmd *c)
1493{
1494 if (!in_range(c))
1495 return;
1496 do_writes();
1497 app_line();
1498}
1499
1500static void
1501cmd_p(Cmd *c)
1502{
1503 if (in_range(c))
1504 write_patt(patt.str, stdout);
1505}
1506
1507static void
1508cmd_P(Cmd *c)
1509{
1510 char *p;
1511
1512 if (!in_range(c))
1513 return;
1514
1515 if ((p = strchr(patt.str, '\n')))
1516 *p = '\0';
1517
1518 write_patt(patt.str, stdout);
1519
1520 if (p)
1521 *p = '\n';
1522}
1523
1524static void
1525cmd_q(Cmd *c)
1526{
1527 if (!in_range(c))
1528 return;
1529
1530 if (!gflags.n)
1531 check_puts(patt.str, stdout);
1532 do_writes();
1533 gflags.halt = 1;
1534}
1535
1536static void
1537cmd_r(Cmd *c)
1538{
1539 if (in_range(c))
1540 push(&writes, c);
1541}
1542
1543static void
1544cmd_s(Cmd *c)
1545{
1546 String tmp;
1547 Rune r;
1548 size_t plen, rlen, len;
1549 char *p, *s, *end;
1550 unsigned int matches = 0, last_empty = 1, qflag = 0, cflags = 0;
1551 regex_t *re;
1552 regmatch_t *rm, *pmatch = NULL;
1553
1554 if (!in_range(c))
1555 return;
1556
1557 if (!c->u.s.re && !lastre)
1558 leprintf("no previous regex");
1559
1560 re = c->u.s.re ? c->u.s.re : lastre;
1561 lastre = re;
1562
1563 plen = re->re_nsub + 1;
1564 pmatch = ereallocarray(NULL, plen, sizeof(regmatch_t));
1565
1566 *genbuf.str = '\0';
1567 s = patt.str;
1568
1569 while (!qflag && !regexec(re, s, plen, pmatch, cflags)) {
1570 cflags = REG_NOTBOL; /* match against beginning of line first
1571 time, but not again */
1572 if (!*s) /* match against empty string first time, but not again
1573 */
1574 qflag = 1;
1575
1576 /* don't substitute if last match was not empty but this one is.
1577 * s_a*_._g
1578 * foobar -> .f.o.o.b.r.
1579 */
1580 if ((last_empty || pmatch[0].rm_eo) && (++matches == c->u.s.occurrence || !c->u.s.occurrence)) {
1581 /* copy over everything before the match */
1582 strnacat(&genbuf, s, pmatch[0].rm_so);
1583
1584 /* copy over replacement text, taking into account &,
1585 * backreferences, and \ escapes */
1586 for (p = c->u.s.repl.str, len = strcspn(p, "\\&"); *p; len = strcspn(++p, "\\&")) {
1587 strnacat(&genbuf, p, len);
1588 p += len;
1589 switch (*p) {
1590 default:
1591 leprintf("this shouldn't be possible");
1592 break;
1593 case '\0':
1594 /* we're at the end, back up one so the
1595 * ++p will put us on the null byte to
1596 * break out of the loop */
1597 --p;
1598 break;
1599 case '&':
1600 strnacat(&genbuf, s + pmatch[0].rm_so, pmatch[0].rm_eo - pmatch[0].rm_so);
1601 break;
1602 case '\\':
1603 if (isdigit(*++p)) { /* backreference */
1604 /* only need to check here if
1605 * using lastre, otherwise we
1606 * checked when building */
1607 if (!c->u.s.re && (size_t)(*p - '0') > re->re_nsub)
1608 leprintf(
1609 "back "
1610 "reference "
1611 "number "
1612 "greater than "
1613 "number of "
1614 "groups"
1615 );
1616 rm = &pmatch[*p - '0'];
1617 strnacat(&genbuf, s + rm->rm_so, rm->rm_eo - rm->rm_so);
1618 } else { /* character after backslash
1619 taken literally (well one
1620 byte, but it works) */
1621 strnacat(&genbuf, p, 1);
1622 }
1623 break;
1624 }
1625 }
1626 } else {
1627 /* not replacing, copy over everything up to and
1628 * including the match */
1629 strnacat(&genbuf, s, pmatch[0].rm_eo);
1630 }
1631
1632 if (!pmatch[0].rm_eo) { /* empty match, advance one rune and add
1633 it to output */
1634 end = s + strlen(s);
1635 rlen = charntorune(&r, s, end - s);
1636
1637 if (!rlen) { /* ran out of bytes, copy short sequence */
1638 stracat(&genbuf, s);
1639 s = end;
1640 } else { /* copy whether or not it's a good rune */
1641 strnacat(&genbuf, s, rlen);
1642 s += rlen;
1643 }
1644 }
1645 last_empty = !pmatch[0].rm_eo;
1646 s += pmatch[0].rm_eo;
1647 }
1648 free(pmatch);
1649
1650 if (!(matches && matches >= c->u.s.occurrence)) /* no replacement */
1651 return;
1652
1653 gflags.s = 1;
1654
1655 stracat(&genbuf, s);
1656
1657 tmp = patt;
1658 patt = genbuf;
1659 genbuf = tmp;
1660
1661 if (c->u.s.p)
1662 write_patt(patt.str, stdout);
1663 if (c->u.s.file)
1664 write_patt(patt.str, c->u.s.file);
1665}
1666
1667static void
1668cmd_t(Cmd *c)
1669{
1670 if (!in_range(c) || !gflags.s)
1671 return;
1672
1673 /* if we jump backwards update to end, otherwise update to destination
1674 */
1675 update_ranges(c + 1, c->u.jump > c ? c->u.jump : prog + pcap);
1676 pc = c->u.jump;
1677 gflags.s = 0;
1678}
1679
1680static void
1681cmd_w(Cmd *c)
1682{
1683 if (in_range(c))
1684 write_patt(patt.str, c->u.file);
1685}
1686
1687static void
1688cmd_x(Cmd *c)
1689{
1690 String tmp;
1691
1692 if (!in_range(c))
1693 return;
1694
1695 tmp = patt;
1696 patt = hold;
1697 hold = tmp;
1698}
1699
1700static void
1701cmd_y(Cmd *c)
1702{
1703 String tmp;
1704 Rune r, *rp;
1705 size_t n, rlen;
1706 char *s, *end, buf[UTFmax];
1707
1708 if (!in_range(c))
1709 return;
1710
1711 *genbuf.str = '\0';
1712 for (s = patt.str, end = s + strlen(s); *s; s += rlen) {
1713 if (!(rlen = charntorune(&r, s, end - s))) { /* ran out of chars, copy rest */
1714 stracat(&genbuf, s);
1715 break;
1716 } else if (r == Runeerror) { /* bad UTF-8 sequence, copy bytes */
1717 strnacat(&genbuf, s, rlen);
1718 } else {
1719 for (rp = c->u.y.set1; *rp; rp++)
1720 if (*rp == r)
1721 break;
1722 if (*rp) { /* found r in set1, replace with Rune from
1723 set2 */
1724 n = runetochar(buf, c->u.y.set2 + (rp - c->u.y.set1));
1725 strnacat(&genbuf, buf, n);
1726 } else {
1727 strnacat(&genbuf, s, rlen);
1728 }
1729 }
1730 }
1731 tmp = patt;
1732 patt = genbuf;
1733 genbuf = tmp;
1734}
1735
1736static void
1737cmd_colon(Cmd *c)
1738{
1739 (void)c;
1740}
1741
1742static void
1743cmd_equal(Cmd *c)
1744{
1745 if (in_range(c))
1746 printf("%zu\n", lineno);
1747}
1748
1749static void
1750cmd_lbrace(Cmd *c)
1751{
1752 Cmd *jump;
1753
1754 if (in_range(c))
1755 return;
1756
1757 /* update ranges on all commands we skip */
1758 jump = prog + c->u.offset;
1759 update_ranges(c + 1, jump);
1760 pc = jump;
1761}
1762
1763static void
1764cmd_rbrace(Cmd *c)
1765{
1766 (void)c;
1767}
1768
1769/* not actually a sed function, but acts like one, put in last spot of script */
1770static void
1771cmd_last(Cmd *c)
1772{
1773 (void)c;
1774 if (!gflags.n)
1775 write_patt(patt.str, stdout);
1776 do_writes();
1777 new_next();
1778}
1779
1780/*
1781 * Actions
1782 */
1783
1784/* read new line, continue current cycle */
1785static void
1786new_line(void)
1787{
1788 while (read_line(file, &patt) == EOF) {
1789 if (next_file()) {
1790 gflags.halt = 1;
1791 return;
1792 }
1793 }
1794 gflags.s = 0;
1795 lineno++;
1796}
1797
1798/* append new line, continue current cycle
1799 * FIXME: used for N, POSIX specifies do not print pattern space when out of
1800 * input, but GNU does so busybox does as well. Currently we don't.
1801 * Should we?
1802 */
1803static void
1804app_line(void)
1805{
1806 while (read_line(file, &genbuf) == EOF) {
1807 if (next_file()) {
1808 gflags.halt = 1;
1809 return;
1810 }
1811 }
1812
1813 stracat(&patt, "\n");
1814 stracat(&patt, genbuf.str);
1815 gflags.s = 0;
1816 lineno++;
1817}
1818
1819/* read new line, start new cycle */
1820static void
1821new_next(void)
1822{
1823 *patt.str = '\0';
1824 update_ranges(pc + 1, prog + pcap);
1825 new_line();
1826 pc = prog - 1;
1827}
1828
1829/* keep old pattern space, start new cycle */
1830static void
1831old_next(void)
1832{
1833 update_ranges(pc + 1, prog + pcap);
1834 pc = prog - 1;
1835}
1836
1837// ?man sed: stream editor
1838// ?man arguments: script [file ...]
1839// ?man stream editor for filtering and transforming text
1840int
1841main(int argc, char *argv[])
1842{
1843 char *arg;
1844 int script = 0;
1845
1846 ARGBEGIN
1847 {
1848 // ?man -n: print line numbers or counts
1849 case 'n':
1850 gflags.n = 1;
1851 break;
1852 // ?man -r: operate recursively
1853 case 'r':
1854 // ?man -E: specify option flag
1855 case 'E':
1856 gflags.E = 1;
1857 break;
1858 // ?man -e:str: specify expression or pattern
1859 case 'e':
1860 arg = EARGF(usage());
1861 compile(arg, 0);
1862 script = 1;
1863 break;
1864 // ?man -f:str: force the operation
1865 case 'f':
1866 arg = EARGF(usage());
1867 compile(arg, 1);
1868 script = 1;
1869 break;
1870#if FEATURE_SED_INPLACE
1871 // ?man -i: interactive mode or prompt for confirmation
1872 case 'i':
1873 iflag = 1;
1874 if (argv[0][1] != '\0') {
1875 backup_suffix = &argv[0][1];
1876 brk_ = 1;
1877 } else {
1878 backup_suffix = "";
1879 }
1880 break;
1881#endif
1882 default:
1883 usage();
1884 }
1885 ARGEND
1886
1887 /* no script to run */
1888 if (!script && !argc)
1889 usage();
1890
1891 /* no script yet, next argument is script */
1892 if (!script)
1893 compile(*argv++, 0);
1894
1895 /* shrink/grow memory to fit and add our last instruction */
1896 resize((void **)&prog, &pcap, sizeof(*prog), pc - prog + 1, NULL);
1897 pc = prog + pcap - 1;
1898 pc->fninfo = &(Fninfo){cmd_last, NULL, NULL, 0};
1899
1900#if FEATURE_SED_INPLACE
1901 if (iflag) {
1902 char *single_file[2] = {NULL, NULL};
1903 char **orig_files = argv;
1904 int i;
1905
1906 if (!*orig_files)
1907 eprintf("no input files\n");
1908
1909 for (i = 0; orig_files[i]; i++) {
1910 char *temp_path = NULL;
1911 int temp_fd;
1912 int real_stdout;
1913 struct stat st;
1914 Cmd *c;
1915
1916 if (strcmp(orig_files[i], "-") == 0) {
1917 weprintf("cannot edit stdin in-place\n");
1918 ret = 1;
1919 continue;
1920 }
1921
1922 if (stat(orig_files[i], &st) < 0) {
1923 weprintf("stat %s:", orig_files[i]);
1924 ret = 1;
1925 continue;
1926 }
1927
1928 temp_fd = create_temp_file(orig_files[i], &temp_path);
1929 if (temp_fd < 0) {
1930 weprintf("create_temp_file:");
1931 ret = 1;
1932 continue;
1933 }
1934
1935 real_stdout = dup(1);
1936 if (real_stdout < 0) {
1937 weprintf("dup stdout:");
1938 close(temp_fd);
1939 free(temp_path);
1940 ret = 1;
1941 continue;
1942 }
1943 if (dup2(temp_fd, 1) < 0) {
1944 weprintf("dup2 stdout:");
1945 close(temp_fd);
1946 close(real_stdout);
1947 free(temp_path);
1948 ret = 1;
1949 continue;
1950 }
1951 close(temp_fd);
1952
1953 single_file[0] = orig_files[i];
1954 files = single_file;
1955
1956 /* reset state for next file */
1957 lineno = 0;
1958 gflags.halt = 0;
1959 stracpy(&hold, "");
1960 stracpy(&patt, "");
1961 writes.size = 0;
1962 for (c = prog; c->fninfo->fn != cmd_last; c++) {
1963 c->in_match = 0;
1964 }
1965
1966 run();
1967
1968 fflush(stdout);
1969 dup2(real_stdout, 1);
1970 close(real_stdout);
1971
1972 if (backup_suffix && *backup_suffix) {
1973 char *backup_path = emalloc(strlen(orig_files[i]) + strlen(backup_suffix) + 1);
1974 sprintf(backup_path, "%s%s", orig_files[i], backup_suffix);
1975 if (rename(orig_files[i], backup_path) < 0) {
1976 weprintf("rename %s to %s:", orig_files[i], backup_path);
1977 unlink(temp_path);
1978 free(backup_path);
1979 free(temp_path);
1980 ret = 1;
1981 continue;
1982 }
1983 free(backup_path);
1984 } else {
1985 unlink(orig_files[i]);
1986 }
1987
1988 if (rename(temp_path, orig_files[i]) < 0) {
1989 weprintf("rename %s to %s:", temp_path, orig_files[i]);
1990 unlink(temp_path);
1991 free(temp_path);
1992 ret = 1;
1993 continue;
1994 }
1995
1996 chmod(orig_files[i], st.st_mode);
1997 chown(orig_files[i], st.st_uid, st.st_gid);
1998
1999 free(temp_path);
2000 }
2001 } else
2002#endif
2003 {
2004 files = argv;
2005 run();
2006 }
2007
2008 ret |= fshut(stdin, "<stdin>") | fshut(stdout, "<stdout>");
2009
2010 return ret;
2011}