diff options
-rw-r--r-- | editors/sed.c | 83 | ||||
-rw-r--r-- | sed.c | 83 |
2 files changed, 146 insertions, 20 deletions
diff --git a/editors/sed.c b/editors/sed.c index 1342a6643..156ad3ada 100644 --- a/editors/sed.c +++ b/editors/sed.c @@ -27,6 +27,7 @@ - address matching: num|/matchstr/[,num|/matchstr/|$]command - commands: (p)rint, (d)elete, (s)ubstitue (with g & I flags) - edit commands: (a)ppend, (i)nsert, (c)hange + - file commands: (r)ead - backreferences in substitution expressions (\1, \2...\9) (Note: Specifying an address (range) to match is *optional*; commands @@ -90,6 +91,11 @@ struct sed_cmd { /* EDIT COMMAND (a,i,c) SPEICIFIC FIELDS */ char *editline; + + + /* FILE COMMAND (r) SPEICIFIC FIELDS */ + + char *filename; }; /* globals */ @@ -351,6 +357,45 @@ out: return idx; } + +static int parse_file_cmd(struct sed_cmd *sed_cmd, const char *filecmdstr) +{ + int idx = 0; + int filenamelen = 0; + + /* + * the string that gets passed to this function should look like this: + * '[ ]filename' + * | | + * | a filename + * | + * optional whitespace + + * re: the file to be read, the GNU manual says the following: "Note that + * if filename cannot be read, it is treated as if it were an empty file, + * without any error indication." Thus, all of the following commands are + * perfectly leagal: + * + * sed -e '1r noexist' + * sed -e '1r ;' + * sed -e '1r' + */ + + /* the file command may be followed by whitespace; move past it. */ + while (isspace(filecmdstr[++idx])) + { ; } + + /* the first non-whitespace we get is a filename. the filename ends when we + * hit a normal sed command terminator or end of string */ + filenamelen = strcspn(&filecmdstr[idx], "; \n\r\t\v\0"); + sed_cmd->filename = xmalloc(sizeof(char) * filenamelen + 1); + strncpy(sed_cmd->filename, &filecmdstr[idx], filenamelen); + sed_cmd->filename[filenamelen] = 0; + + return idx + filenamelen; +} + + static char *parse_cmd_str(struct sed_cmd *sed_cmd, const char *cmdstr) { int idx = 0; @@ -361,7 +406,6 @@ static char *parse_cmd_str(struct sed_cmd *sed_cmd, const char *cmdstr) * part1 part2 part3 */ - /* first part (if present) is an address: either a number or a /regex/ */ if (isdigit(cmdstr[idx]) || cmdstr[idx] == '/') idx = get_address(sed_cmd, cmdstr, &sed_cmd->beg_line, &sed_cmd->beg_match); @@ -373,24 +417,32 @@ static char *parse_cmd_str(struct sed_cmd *sed_cmd, const char *cmdstr) /* last part (mandatory) will be a command */ if (cmdstr[idx] == '\0') error_msg_and_die("missing command"); - if (!strchr("pdsaic", cmdstr[idx])) /* <-- XXX add new commands here */ - error_msg_and_die("invalid command"); sed_cmd->cmd = cmdstr[idx]; - /* special-case handling for (s)ubstitution */ - if (sed_cmd->cmd == 's') { + /* if it was a single-letter command that takes no arguments (such as 'p' + * or 'd') all we need to do is increment the index past that command */ + if (strchr("pd", cmdstr[idx])) { + idx++; + } + /* handle (s)ubstitution */ + else if (sed_cmd->cmd == 's') { idx += parse_subst_cmd(sed_cmd, &cmdstr[idx]); } - /* special-case handling for (a)ppend, (i)nsert, and (c)hange */ + /* handle edit cmds: (a)ppend, (i)nsert, and (c)hange */ else if (strchr("aic", cmdstr[idx])) { if (sed_cmd->end_line || sed_cmd->end_match) error_msg_and_die("only a beginning address can be specified for edit commands"); idx += parse_edit_cmd(sed_cmd, &cmdstr[idx]); } - /* if it was a single-letter command (such as 'p' or 'd') we need to - * increment the index past that command */ - else - idx++; + /* handle file cmds: (r)ead */ + else if (sed_cmd->cmd == 'r') { + if (sed_cmd->end_line || sed_cmd->end_match) + error_msg_and_die("Command only uses one address"); + idx += parse_file_cmd(sed_cmd, &cmdstr[idx]); + } + else { + error_msg_and_die("invalid command"); + } /* give back whatever's left over */ return (char *)&cmdstr[idx]; @@ -598,6 +650,17 @@ static int do_sed_command(const struct sed_cmd *sed_cmd, const char *line) fputs(sed_cmd->editline, stdout); altered++; break; + + case 'r': { + FILE *file; + fputs(line, stdout); + file = fopen(sed_cmd->filename, "r"); + if (file) + print_file(file); + /* else if we couldn't open the file, no biggie, just don't print anything */ + altered++; + } + break; } return altered; @@ -27,6 +27,7 @@ - address matching: num|/matchstr/[,num|/matchstr/|$]command - commands: (p)rint, (d)elete, (s)ubstitue (with g & I flags) - edit commands: (a)ppend, (i)nsert, (c)hange + - file commands: (r)ead - backreferences in substitution expressions (\1, \2...\9) (Note: Specifying an address (range) to match is *optional*; commands @@ -90,6 +91,11 @@ struct sed_cmd { /* EDIT COMMAND (a,i,c) SPEICIFIC FIELDS */ char *editline; + + + /* FILE COMMAND (r) SPEICIFIC FIELDS */ + + char *filename; }; /* globals */ @@ -351,6 +357,45 @@ out: return idx; } + +static int parse_file_cmd(struct sed_cmd *sed_cmd, const char *filecmdstr) +{ + int idx = 0; + int filenamelen = 0; + + /* + * the string that gets passed to this function should look like this: + * '[ ]filename' + * | | + * | a filename + * | + * optional whitespace + + * re: the file to be read, the GNU manual says the following: "Note that + * if filename cannot be read, it is treated as if it were an empty file, + * without any error indication." Thus, all of the following commands are + * perfectly leagal: + * + * sed -e '1r noexist' + * sed -e '1r ;' + * sed -e '1r' + */ + + /* the file command may be followed by whitespace; move past it. */ + while (isspace(filecmdstr[++idx])) + { ; } + + /* the first non-whitespace we get is a filename. the filename ends when we + * hit a normal sed command terminator or end of string */ + filenamelen = strcspn(&filecmdstr[idx], "; \n\r\t\v\0"); + sed_cmd->filename = xmalloc(sizeof(char) * filenamelen + 1); + strncpy(sed_cmd->filename, &filecmdstr[idx], filenamelen); + sed_cmd->filename[filenamelen] = 0; + + return idx + filenamelen; +} + + static char *parse_cmd_str(struct sed_cmd *sed_cmd, const char *cmdstr) { int idx = 0; @@ -361,7 +406,6 @@ static char *parse_cmd_str(struct sed_cmd *sed_cmd, const char *cmdstr) * part1 part2 part3 */ - /* first part (if present) is an address: either a number or a /regex/ */ if (isdigit(cmdstr[idx]) || cmdstr[idx] == '/') idx = get_address(sed_cmd, cmdstr, &sed_cmd->beg_line, &sed_cmd->beg_match); @@ -373,24 +417,32 @@ static char *parse_cmd_str(struct sed_cmd *sed_cmd, const char *cmdstr) /* last part (mandatory) will be a command */ if (cmdstr[idx] == '\0') error_msg_and_die("missing command"); - if (!strchr("pdsaic", cmdstr[idx])) /* <-- XXX add new commands here */ - error_msg_and_die("invalid command"); sed_cmd->cmd = cmdstr[idx]; - /* special-case handling for (s)ubstitution */ - if (sed_cmd->cmd == 's') { + /* if it was a single-letter command that takes no arguments (such as 'p' + * or 'd') all we need to do is increment the index past that command */ + if (strchr("pd", cmdstr[idx])) { + idx++; + } + /* handle (s)ubstitution */ + else if (sed_cmd->cmd == 's') { idx += parse_subst_cmd(sed_cmd, &cmdstr[idx]); } - /* special-case handling for (a)ppend, (i)nsert, and (c)hange */ + /* handle edit cmds: (a)ppend, (i)nsert, and (c)hange */ else if (strchr("aic", cmdstr[idx])) { if (sed_cmd->end_line || sed_cmd->end_match) error_msg_and_die("only a beginning address can be specified for edit commands"); idx += parse_edit_cmd(sed_cmd, &cmdstr[idx]); } - /* if it was a single-letter command (such as 'p' or 'd') we need to - * increment the index past that command */ - else - idx++; + /* handle file cmds: (r)ead */ + else if (sed_cmd->cmd == 'r') { + if (sed_cmd->end_line || sed_cmd->end_match) + error_msg_and_die("Command only uses one address"); + idx += parse_file_cmd(sed_cmd, &cmdstr[idx]); + } + else { + error_msg_and_die("invalid command"); + } /* give back whatever's left over */ return (char *)&cmdstr[idx]; @@ -598,6 +650,17 @@ static int do_sed_command(const struct sed_cmd *sed_cmd, const char *line) fputs(sed_cmd->editline, stdout); altered++; break; + + case 'r': { + FILE *file; + fputs(line, stdout); + file = fopen(sed_cmd->filename, "r"); + if (file) + print_file(file); + /* else if we couldn't open the file, no biggie, just don't print anything */ + altered++; + } + break; } return altered; |