blob: 22e07c07af76ecc6bbaa9f8dd67941376c652d86 [file] [log] [blame]
/* sed.c - Stream editor.
*
* Copyright 2012 Rob Landley <rob@landley.net>
*
* 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"
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++));
}