/* sed.c - Stream editor. * * Copyright 2012 Rob Landley * * See http://opengroup.org/onlinepubs/9699919799/utilities/sed.c USE_SED(NEWTOY(sed, "irne*f*", TOYFLAG_BIN)) config SED bool "sed" default n help usage: sed [-irn] {command | [-e command]...} [FILE...] Stream EDitor, transforms text by appling script of command to each line of input. -e Add expression to the command script (if no -e, use first argument) -i Modify file in place -n No default output (p commands only) -r Use extended regular expression syntex */ #define FOR_sed #include "toys.h" #include "lib/xregcomp.h" GLOBALS( struct arg_list *files; struct arg_list *scripts; void *commands; ) // Digested version of what sed commands can actually tell use to do. struct sed_command { // double_list compatibility (easier to create in-order) struct sed_command *next, *prev; // data string for (saicytb) char c, *data; // Regexes for s/match/data/ and /begin/,/end/command regex_t *rmatch, *rbegin, *rend; // For numeric ranges ala 10,20command long lstart, lstop; // Which match to replace, 0 for all. s and w commands can write to a file int which, outfd; }; // Space. Space. Gotta get past space. Spaaaaaaaace! (But not newline.) static void spaceorb(char **s) { while (**s == ' ' || **s == '\t') ++*s; } // Parse sed commands static void parse_scripts(void) { struct arg_list *script; int which = 0, i; // Loop through list of scripts collated from command line and/or files for (script = TT.scripts; script; script = script->next) { char *str = script->arg; struct sed_command *cmd; // we can get multiple commands from a string (semicolons and such) which++; for (i=1;;) { if (!*str) break; cmd = xzalloc(sizeof(struct sed_command)); // Identify prefix for (;;) { spaceorb(&str); if (*str == '^') { if (cmd->lstart) goto parse_fail; cmd->lstart = -1; str++; continue; } else if (*str == '$') { cmd->lstop = LONG_MAX; str++; break; } else if (isdigit(*str)) { long ll = strtol(str, &str, 10); if (ll<0) goto parse_fail; if (cmd->lstart) { cmd->lstop = ll; break; } else cmd->lstart = ll; } else if (*str == '/' || *str == '\\') { // set begin/end printf("regex\n"); exit(1); } else if (!cmd->lstart && !cmd->rbegin) break; else goto parse_fail; // , with no range after it spaceorb(&str); if (*str != ',') break; str++; } i = stridx("{bcdDgGhHlnNpPstwxyrqia= \t#:}", *str); if (i == -1) goto parse_fail; dlist_add_nomalloc((struct double_list **)&TT.commands, (struct double_list *)cmd); exit(1); } } return; parse_fail: error_exit("bad expression %d@%d: %s", which, i, script->arg+i); } void sed_main(void) { char **files=toys.optargs; // If no -e, use first argument if (!TT.scripts) { if (!*files) error_exit("Need script"); (TT.scripts = xzalloc(sizeof(struct arg_list)))->arg = *(files++); } parse_scripts(); while (*files) dprintf(2,"file=%s\n", *(files++)); }