Lines Matching +full:command +full:- +full:line
1 /* sed.c - stream editor. Thing that does s/// and other stuff.
13 * What's the right thing to do for -i when write fails? Skip to next?
16 * Deviations from POSIX: allow extended regular expressions with -r,
17 * editing in place with -i, separate with -s, NUL-delimited strings with -z,
18 * printf escapes in text, line continuations, semicolons after all commands,
19 * 2-address anywhere an address is allowed, "T" command, multiline
20 * continuations for [abc], \; to end [abc] argument before end of line.
23 * Added --tarxform mode to support tar --xform
25 USE_SED(NEWTOY(sed, "(help)(version)(tarxform)e*f*i:;nErz(null-data)s[+Er]", TOYFLAG_BIN|TOYFLAG_AU…
31 usage: sed [-inrszE] [-e SCRIPT]...|SCRIPT [-f SCRIPT_FILE]... [FILE...]
35 -e Add SCRIPT to list
36 -f Add contents of SCRIPT_FILE to list
37 -i Edit each file in place (-iEXT keeps backup file with extension EXT)
38 -n No default output (use the p command to output matched lines)
39 -r Use extended regular expression syntax
40 -E POSIX alias for -r
41 -s Treat input files separately (implied by -i)
42 -z Use \0 rather than \n as input line separator
45 All -e SCRIPTs and -f SCRIPT_FILE contents are combined in order as if
46 separated by newlines. If no -e or -f then first argument is the SCRIPT.
48 COMMANDs apply to every line unless prefixed with an ADDRESS of the form:
50 [ADDRESS[,ADDRESS]][!]COMMAND
52 ADDRESS is a line number (starting at 1), a /REGULAR EXPRESSION/, or $ for
53 last line (-s or -i makes it last line of each file). One address matches one
54 line, ADDRESS,ADDRESS matches from first to second inclusive. Two regexes can
63 Sed reads each line of input, processes it, and writes it out or discards it
64 before reading the next. Sed can remember one additional line in a separate
65 buffer (the h, H, g, G, and x commands), and can read the next line of input
68 Each COMMAND starts with a single character. Commands with no arguments are:
70 ! Run this command when the ADDRESS _didn't_ match.
71 { Start new command block, continuing until a corresponding "}".
72 Command blocks nest and can have ADDRESSes applying to the whole block.
73 } End command block (this COMMAND cannot have an address)
74 d Delete this line and move on to the next one
76 D Delete one line of input and restart command SCRIPT (same as "d"
78 g Get remembered line (overwriting current line)
79 G Get remembered line (appending to current line)
80 h Remember this line (overwriting remembered line)
81 H Remember this line (appending to remembered line, if any)
82 l Print line escaping \abfrtvn, octal escape other nonprintng chars,
83 wrap lines to terminal width with \, append $ to end of line.
84 n Print default output and read next line over current line (quit at EOF)
85 N Append \n and next line of input to this line. Quit at EOF without
86 default output. Advances line counter for ADDRESS and "=".
87 p Print this line
88 P Print this line up to first newline (from "N")
90 x Exchange this line with remembered line (overwrite in both directions)
91 = Print the current line number (plus newline)
92 # Comment, ignores rest of this line of SCRIPT (until newline)
97 a TEXT Append text to output before reading next line
101 r FILE Append contents of FILE to output before reading next line.
106 line appends next line of SCRIPT. The flags in F are:
107 [0-9] A number N, substitute only Nth match
110 p Print resulting line when match found and replaced
111 w [file] Write (append) line to file when match replaced
112 t LABEL Test, jump if s/// command matched this line since last test
114 w FILE Write (append) line to file
120 the next line (leading whitespace is not skipped), and treat ";" as a
151 long lmatch[2]; // line number of match
153 int arg1, arg2, w; // offset of two arguments per command, plus s//w filename
168 // Write out line with potential embedded NUL, handling eol/noeol
169 static int emit(char *line, long len, int eol) in emit() argument
171 int l = len, old = line[len]; in emit()
176 memcpy(TT.tarxform+TT.tarxlen, line, len); in emit()
181 if (eol) line[len++] = TT.delim; in emit()
183 l = writeall(TT.fdout, line, len); in emit()
184 if (eol) line[len-1] = old; in emit()
203 if (newline) newlen = -newlen; in extend_string()
213 static void *get_regex(void *command, int offset) in get_regex() argument
220 return TT.lastregex = offset+(char *)command; in get_regex()
223 // Apply pattern to line from input file
231 char *line; in sed_line() local
233 struct sedcmd *command; in sed_line() local
239 line = *pline; in sed_line()
244 line = TT.nextline; in sed_line()
247 // Ignore EOF for all files before last unless -i or -s in sed_line()
250 // Grab next line for deferred processing (EOF detection: we get a NULL in sed_line()
251 // pline at EOF to flush last line). Note that only end of _last_ input in sed_line()
252 // file matches $ (unless we're doing -i). in sed_line()
262 if (!line || !len) return; in sed_line()
263 if (line[len-1] == TT.delim) line[--len] = eol++; in sed_line()
265 TT.xftype = line[--len]; in sed_line()
266 line[len] = 0; in sed_line()
270 // To prevent N as last command from restarting script, we added 1 to restart in sed_line()
272 // references instead of pointers assume ptr-1 can never be NULL (demonstrably in sed_line()
274 // we get a -fpointers-are-not-references compiler option. in sed_line()
275 command = (void *)(TT.restart ? ((unsigned long)TT.restart)-1 in sed_line()
279 while (command) { in sed_line()
280 char *str, c = command->c; in sed_line()
282 // Have we got a line or regex matching range for this rule? in sed_line()
283 if (*command->lmatch || *command->rmatch) { in sed_line()
288 if (command->hit) { in sed_line()
289 if (!(lm = command->lmatch[1])) { in sed_line()
290 if (!command->rmatch[1]) command->hit = 0; in sed_line()
292 void *rm = get_regex(command, command->rmatch[1]); in sed_line()
294 // regex match end includes matching line, so defer deactivation in sed_line()
295 if (line && !regexec0(rm, line, len, 0, 0, 0)) miss = 1; in sed_line()
297 } else if (lm > 0 && lm < TT.count) command->hit = 0; in sed_line()
298 else if (lm < -1 && TT.count == command->hit+(-lm-1)) command->hit = 0; in sed_line()
302 if (!(lm = *command->lmatch)) { in sed_line()
303 void *rm = get_regex(command, *command->rmatch); in sed_line()
305 if (line && !regexec0(rm, line, len, 0, 0, 0)) in sed_line()
306 command->hit = TT.count; in sed_line()
307 } else if (lm == TT.count || (lm == -1 && !pline)) in sed_line()
308 command->hit = TT.count; in sed_line()
310 if (!command->lmatch[1] && !command->rmatch[1]) miss = 1; in sed_line()
314 lm = !(command->not^!!command->hit); in sed_line()
317 if (miss || command->lmatch[1] == TT.count) command->hit = 0; in sed_line()
320 // Handle skipping curly bracket command group in sed_line()
325 command = command->next; in sed_line()
326 if (command->c == '{') curly++; in sed_line()
327 if (command->c == '}') curly--; in sed_line()
330 command = command->next; in sed_line()
335 // A deleted line can still update line match state for later commands in sed_line()
336 if (!line) { in sed_line()
337 command = command->next; in sed_line()
341 // Process command in sed_line()
345 if (command->arg1) a->str = command->arg1+(char *)command; in sed_line()
346 a->file = c=='r'; in sed_line()
353 if (!command->arg1) break; in sed_line()
354 str = command->arg1+(char *)command; in sed_line()
355 for (command = (void *)TT.pattern; command; command = command->next) in sed_line()
356 if (command->c == ':' && !strcmp(command->arg1+(char *)command, str)) in sed_line()
358 if (!command) error_exit("no :%s", str); in sed_line()
361 str = command->arg1+(char *)command; in sed_line()
362 if (!command->hit) emit(str, strlen(str), 1); in sed_line()
363 free(line); in sed_line()
364 line = 0; in sed_line()
367 free(line); in sed_line()
368 line = 0; in sed_line()
372 str = line; in sed_line()
373 while ((str-line)<len) if (*(str++) == TT.delim) break; in sed_line()
374 len -= str - line; in sed_line()
375 memmove(line, str, len); in sed_line()
377 // if "delete" blanks line, disable further processing in sed_line()
380 free(line); in sed_line()
381 line = 0; in sed_line()
383 line[len] = 0; in sed_line()
384 command = (void *)TT.pattern; in sed_line()
388 free(line); in sed_line()
389 line = xmemdup(TT.remember, TT.rememberlen+1); in sed_line()
392 line = xrealloc(line, len+TT.rememberlen+2); in sed_line()
393 line[len++] = TT.delim; in sed_line()
394 memcpy(line+len, TT.remember, TT.rememberlen); in sed_line()
395 line[len += TT.rememberlen] = 0; in sed_line()
398 TT.remember = xstrdup(line); in sed_line()
403 memcpy(TT.remember+TT.rememberlen, line, len); in sed_line()
406 str = command->arg1+(char *)command; in sed_line()
414 if (TT.xx > sizeof(toybuf)-10) TT.xx = sizeof(toybuf)-10; in sed_line()
415 if (TT.xx > 4) TT.xx -= 4; in sed_line()
424 x = stridx("\\\a\b\f\r\t\v\n", line[i]); in sed_line()
425 if (x != -1) { in sed_line()
428 } else if (line[i] >= ' ') toybuf[off++] = line[i]; in sed_line()
429 else off += sprintf(toybuf+off, "\\%03o", line[i]); in sed_line()
435 TT.restart = (void *)(((unsigned long)command->next)+1); in sed_line()
439 // Can't just grab next line because we could have multiple N and in sed_line()
443 TT.restart = (void *)(((unsigned long)command->next)+1); in sed_line()
444 extend_string(&line, TT.nextline, len, -TT.nextlen); in sed_line()
446 TT.nextline = line; in sed_line()
448 line = 0; in sed_line()
454 char *l = (c=='P') ? strchr(line, TT.delim) : 0; in sed_line()
456 if (emit(line, l ? l-line : len, eol)) break; in sed_line()
460 if (!toys.exitval && command->arg1) in sed_line()
461 toys.exitval = atoi(command->arg1+(char *)command); in sed_line()
464 if (c=='Q') line = 0; in sed_line()
468 char *rline = line, *new = command->arg2 + (char *)command, *l2 = 0; in sed_line()
470 regex_t *reg = get_regex(command, command->arg1); in sed_line()
474 // Skip suppressed --tarxform types in sed_line()
475 if (TT.xftype && (command->sflags & (SFLAG_R<<stridx("rsh", TT.xftype)))); in sed_line()
477 // Loop finding match in remaining line (up to remaining len) in sed_line()
478 else while (!regexec0(reg, rline, len-(rline-line), 10, match, mflags)) { in sed_line()
479 mlen = match[0].rm_eo-match[0].rm_so; in sed_line()
481 // xform matches ending in / aren't allowed to match entire line in sed_line()
482 if ((command->sflags & SFLAG_slash) && mlen==len) { in sed_line()
483 while (len && ++bonk && line[--len]=='/'); in sed_line()
491 if (rline-line == len) break; in sed_line()
498 off = command->sflags>>8; in sed_line()
512 int cc = -1; in sed_line()
515 else if (new[off] == '\\') cc = new[++off] - '0'; in sed_line()
520 newlen += match[cc].rm_eo-match[cc].rm_so; in sed_line()
526 l2l += newlen-mlen; in sed_line()
529 if (l2used && !l2old) memcpy(l2, rline-l2used, l2used); in sed_line()
542 cc = new[++off] - '0'; in sed_line()
545 l2[l2used+mlen-1] = new[off]; in sed_line()
548 } else if (cc > reg->re_nsub) error_exit("no s//\\%d/", cc); in sed_line()
555 if (match[cc].rm_so != -1) { in sed_line()
556 ll = match[cc].rm_eo-match[cc].rm_so; in sed_line()
564 if (!(command->sflags & SFLAG_g)) break; in sed_line()
568 // If we made any changes, finish off l2 and swap it for line in sed_line()
571 mlen = len-(rline-line); in sed_line()
574 free(line); in sed_line()
575 line = l2; in sed_line()
579 if (command->sflags & SFLAG_p) emit(line, len, eol); in sed_line()
582 if (command->w) goto writenow; in sed_line()
596 name = command->w + (char *)command; in sed_line()
602 if (emit(line, len, eol)) in sed_line()
603 perror_exit("w '%s'", command->arg1+(char *)command); in sed_line()
604 *(--name) = TT.noeol; in sed_line()
611 TT.remember = line; in sed_line()
612 line = str; in sed_line()
616 char *from, *to = (char *)command; in sed_line()
619 from = to+command->arg1; in sed_line()
620 to += command->arg2; in sed_line()
623 j = stridx(from, line[i]); in sed_line()
624 if (j != -1) line[i] = to[j]; in sed_line()
631 command = command->next; in sed_line()
635 if (line && !FLAG(n)) emit(line, len, eol); in sed_line()
637 // TODO: should "sed -z ax" use \n instead of NUL? in sed_line()
639 struct append *a = append->next; in sed_line()
641 if (append->file) { in sed_line()
642 int fd = open(append->str, O_RDONLY); in sed_line()
645 if (fd != -1) { in sed_line()
651 } else if (append->str) emit(append->str, strlen(append->str), 1); in sed_line()
652 else emit(line, 0, 0); in sed_line()
656 free(line); in sed_line()
659 dprintf(TT.fdout, "%08x", --TT.tarxlen); in sed_line()
671 if (!fd) return error_msg("-i on stdin"); in do_sed_file()
675 struct sedcmd *command; in do_sed_file() local
678 for (command = (void *)TT.pattern; command; command = command->next) in do_sed_file()
679 command->hit = 0; in do_sed_file()
687 replace_tempfile(-1, TT.fdout, &tmp); in do_sed_file()
721 if (from[1]=='-' || from[1]==']') *(to++) = *(from++); in unescape_delimited_string()
732 // Length 1 range (X-X with same X) is "undefined" and makes regcomp err, in unescape_delimited_string()
734 } else if (mode && *from == '-' && from[-1] == from[1]) { in unescape_delimited_string()
761 // Translate pattern strings into command structures. Each command structure
765 struct sedcmd *command = (void *)TT.pattern; in parse_pattern() local
766 char *line, *reg, c, *errstart; in parse_pattern() local
769 line = errstart = pline ? *pline : ""; in parse_pattern()
770 if (len && line[len-1]=='\n') line[--len] = 0; in parse_pattern()
772 // Append this line to previous multiline command? (hit indicates type.) in parse_pattern()
773 // During parsing "hit" stores data about line continuations, but in in parse_pattern()
774 // sed_line() it means the match range attached to this command in parse_pattern()
776 if (command && command->prev->hit) { in parse_pattern()
777 // Remove half-finished entry from list so remalloc() doesn't confuse it in parse_pattern()
778 TT.pattern = TT.pattern->prev; in parse_pattern()
779 command = dlist_pop(&TT.pattern); in parse_pattern()
780 c = command->c; in parse_pattern()
781 reg = (char *)command; in parse_pattern()
782 reg += command->arg1 + strlen(reg + command->arg1); in parse_pattern()
784 // Resume parsing for 'a' or 's' command. (Only two that can do this.) in parse_pattern()
787 if (command->hit < 256) goto resume_s; in parse_pattern()
791 // Loop through commands in this line. in parse_pattern()
793 command = 0; in parse_pattern()
795 if (command) dlist_add_nomalloc(&TT.pattern, (void *)command); in parse_pattern()
797 // If there's no more data on this line, return. in parse_pattern()
799 while (isspace(*line) || *line == ';') line++; in parse_pattern()
800 if (*line == '#') while (*line && *line != '\n') line++; in parse_pattern()
803 if (!*line) return; in parse_pattern()
805 if (FLAG(tarxform) && strstart(&line, "flags=")) { in parse_pattern()
807 while (0<=(i = stridx("rRsShH", *line))) { in parse_pattern()
810 line++; in parse_pattern()
817 errstart = line; in parse_pattern()
819 command = (void *)toybuf; in parse_pattern()
824 if (*line == ',') line++; in parse_pattern()
827 if (i && *line == '+' && isdigit(line[1])) { in parse_pattern()
828 line++; in parse_pattern()
829 command->lmatch[i] = -2-strtol(line, &line, 0); in parse_pattern()
830 } else if (isdigit(*line)) command->lmatch[i] = strtol(line, &line, 0); in parse_pattern()
831 else if (*line == '$') { in parse_pattern()
832 command->lmatch[i] = -1; in parse_pattern()
833 line++; in parse_pattern()
834 } else if (*line == '/' || *line == '\\') { in parse_pattern()
835 char *s = line; in parse_pattern()
837 if (!(s = unescape_delimited_string(&line, 0))) goto error; in parse_pattern()
838 if (!*s) command->rmatch[i] = 0; in parse_pattern()
841 command->rmatch[i] = reg-toybuf; in parse_pattern()
848 while (isspace(*line)) line++; in parse_pattern()
849 if (!*line) break; in parse_pattern()
851 if (*line == '!') { in parse_pattern()
852 command->not = 1; in parse_pattern()
853 line++; in parse_pattern()
855 while (isspace(*line)) line++; in parse_pattern()
856 if (!*line) break; in parse_pattern()
858 c = command->c = *(line++); in parse_pattern()
863 command = xmemdup(toybuf, reg-toybuf); in parse_pattern()
864 reg = (reg-toybuf) + (char *)command; in parse_pattern()
866 // Parse arguments by command type in parse_pattern()
869 if (!TT.nextlen--) break; in parse_pattern()
876 // line continuations use arg1 (back at the start of the function), in parse_pattern()
881 command->arg2 = reg - (char *)command; in parse_pattern()
882 if (!(TT.remember = unescape_delimited_string(&line, &delim))) in parse_pattern()
886 command->arg1 = reg-(char *)command; in parse_pattern()
887 command->hit = delim; in parse_pattern()
889 // get replacement - don't replace escapes yet because \1 and \& need in parse_pattern()
891 end = line; in parse_pattern()
892 while (*end != command->hit) { in parse_pattern()
896 end[-1] = '\n'; in parse_pattern()
903 reg = extend_string((void *)&command, line, reg-(char *)command,end-line); in parse_pattern()
904 line = end; in parse_pattern()
905 // line continuation? (note: '\n' can't be a valid delim). in parse_pattern()
906 if (*line == command->hit) command->hit = 0; in parse_pattern()
908 if (!*line) continue; in parse_pattern()
909 reg--; in parse_pattern()
910 line++; in parse_pattern()
915 i = command->arg1; in parse_pattern()
916 command->arg1 = command->arg2; in parse_pattern()
917 command->arg2 = i; in parse_pattern()
918 command->sflags = TT.xflags*SFLAG_R; in parse_pattern()
921 for (line++; *line; line++) { in parse_pattern()
924 if (isspace(*line) && *line != '\n') continue; in parse_pattern()
925 if (0 <= (l = stridx("igpx", *line))) command->sflags |= 1<<l; in parse_pattern()
926 else if (*line == 'I') command->sflags |= 1<<0; in parse_pattern()
927 else if (FLAG(tarxform) && 0 <= (l = stridx("RSH", *line))) in parse_pattern()
928 command->sflags |= SFLAG_R<<l; in parse_pattern()
930 else if (FLAG(tarxform) && 0 <= (l = stridx("rsh", *line))) in parse_pattern()
931 command->sflags &= ~(SFLAG_R<<l); in parse_pattern()
932 else if (!(command->sflags>>8) && 0<(l = strtol(line, &line, 10))) { in parse_pattern()
933 command->sflags |= l << 8; in parse_pattern()
934 line--; in parse_pattern()
937 flags = (FLAG(r) || (command->sflags & SFLAG_x)) ? REG_EXTENDED : 0; in parse_pattern()
938 if (command->sflags & SFLAG_i) flags |= REG_ICASE; in parse_pattern()
942 if (!*TT.remember) command->arg1 = 0; in parse_pattern()
944 xregcomp((void *)(command->arg1+(char *)command), TT.remember, flags); in parse_pattern()
945 if (FLAG(tarxform) && TT.remember[strlen(TT.remember)-1]=='/') in parse_pattern()
946 command->sflags |= SFLAG_slash; in parse_pattern()
950 if (*line == 'w') { in parse_pattern()
951 line++; in parse_pattern()
964 while (isspace(*line)) line++; in parse_pattern()
965 if (!*line) goto error; in parse_pattern()
966 for (cc = line; *cc; cc++) if (*cc == '\\' && cc[1] == ';') break; in parse_pattern()
969 fd = xcreate(line, O_WRONLY|O_CREAT|O_TRUNC|O_APPEND, 0644); in parse_pattern()
972 command->w = reg - (char *)command; in parse_pattern()
973 command = xrealloc(command, command->w+(cc-line)+6); in parse_pattern()
974 reg = command->w + (char *)command; in parse_pattern()
979 memcpy(reg, line, delim); in parse_pattern()
983 line = cc; in parse_pattern()
984 if (delim) line += 2; in parse_pattern()
989 if (!(s = unescape_delimited_string(&line, &delim))) goto error; in parse_pattern()
990 command->arg1 = reg-(char *)command; in parse_pattern()
992 reg = extend_string((void *)&command, s, reg-(char *)command, len); in parse_pattern()
994 command->arg2 = reg-(char *)command; in parse_pattern()
995 if (!(s = unescape_delimited_string(&line, &delim))) goto error; in parse_pattern()
997 reg = extend_string((void *)&command, s, reg-(char*)command, len); in parse_pattern()
1003 while (isspace(*line) && *line != '\n') line++; in parse_pattern()
1008 command->hit = 0; in parse_pattern()
1011 if (!(end = strcspn(line, strchr(":btTqQ", c) ? "}; \t\r\n\v\f" : "\n"))){ in parse_pattern()
1014 else if (!command->arg1) break; in parse_pattern()
1018 for (i = 0; i<end && isdigit(line[i]); i++); in parse_pattern()
1020 line += i; in parse_pattern()
1028 if (!command->arg1) command->arg1 = reg - (char*)command; in parse_pattern()
1029 else if (*(command->arg1+(char *)command)) *(reg++) = '\n'; in parse_pattern()
1031 command->arg1 = 0; in parse_pattern()
1034 reg = extend_string((void *)&command, line, reg - (char *)command, end); in parse_pattern()
1036 // Recopy data to remove escape sequences and handle line continuation. in parse_pattern()
1038 reg -= end+1; in parse_pattern()
1039 for (i = end; i; i--) { in parse_pattern()
1040 if ((*reg++ = *line++)=='\\') { in parse_pattern()
1042 // escape at end of line: resume if -e escaped literal newline, in parse_pattern()
1043 // else request callback and resume with next line in parse_pattern()
1044 if (!--i) { in parse_pattern()
1045 *--reg = 0; in parse_pattern()
1046 if (*line) { in parse_pattern()
1047 line++; in parse_pattern()
1050 command->hit = 256; in parse_pattern()
1053 if (!(reg[-1] = unescape(*line))) reg[-1] = *line; in parse_pattern()
1054 line++; in parse_pattern()
1058 } else line += end; in parse_pattern()
1065 error_exit("bad pattern '%s'@%ld (%c)", errstart, line-errstart+1L, *line); in parse_pattern()
1083 // If no -e or -f, first argument is the pattern. in sed_main()
1086 (TT.e = xzalloc(sizeof(struct arg_list)))->arg = *(args++); in sed_main()
1089 // -e and -f care about order, so use argv[] to recreate original order in sed_main()
1091 if (TT.e && instr(TT.e->arg, *aa)) { in sed_main()
1092 parse_pattern(&TT.e->arg, strlen(TT.e->arg)); in sed_main()
1095 if (TT.f && instr(TT.f->arg, *aa)) { in sed_main()
1096 do_lines(xopenro(TT.f->arg), TT.delim, parse_pattern); in sed_main()
1110 // Provide EOF flush at end of cumulative input for non-i mode. in sed_main()