Added support for 'r' command in sed.

This commit is contained in:
Mark Whitley 2001-05-11 22:27:13 +00:00
parent cfa88ecb72
commit 1f3b9f297e
2 changed files with 146 additions and 20 deletions

View File

@ -27,6 +27,7 @@
- address matching: num|/matchstr/[,num|/matchstr/|$]command - address matching: num|/matchstr/[,num|/matchstr/|$]command
- commands: (p)rint, (d)elete, (s)ubstitue (with g & I flags) - commands: (p)rint, (d)elete, (s)ubstitue (with g & I flags)
- edit commands: (a)ppend, (i)nsert, (c)hange - edit commands: (a)ppend, (i)nsert, (c)hange
- file commands: (r)ead
- backreferences in substitution expressions (\1, \2...\9) - backreferences in substitution expressions (\1, \2...\9)
(Note: Specifying an address (range) to match is *optional*; commands (Note: Specifying an address (range) to match is *optional*; commands
@ -90,6 +91,11 @@ struct sed_cmd {
/* EDIT COMMAND (a,i,c) SPEICIFIC FIELDS */ /* EDIT COMMAND (a,i,c) SPEICIFIC FIELDS */
char *editline; char *editline;
/* FILE COMMAND (r) SPEICIFIC FIELDS */
char *filename;
}; };
/* globals */ /* globals */
@ -351,6 +357,45 @@ out:
return idx; 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) static char *parse_cmd_str(struct sed_cmd *sed_cmd, const char *cmdstr)
{ {
int idx = 0; int idx = 0;
@ -361,7 +406,6 @@ static char *parse_cmd_str(struct sed_cmd *sed_cmd, const char *cmdstr)
* part1 part2 part3 * part1 part2 part3
*/ */
/* first part (if present) is an address: either a number or a /regex/ */ /* first part (if present) is an address: either a number or a /regex/ */
if (isdigit(cmdstr[idx]) || cmdstr[idx] == '/') if (isdigit(cmdstr[idx]) || cmdstr[idx] == '/')
idx = get_address(sed_cmd, cmdstr, &sed_cmd->beg_line, &sed_cmd->beg_match); 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 */ /* last part (mandatory) will be a command */
if (cmdstr[idx] == '\0') if (cmdstr[idx] == '\0')
error_msg_and_die("missing command"); 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]; sed_cmd->cmd = cmdstr[idx];
/* special-case handling for (s)ubstitution */ /* if it was a single-letter command that takes no arguments (such as 'p'
if (sed_cmd->cmd == 's') { * 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]); 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])) { else if (strchr("aic", cmdstr[idx])) {
if (sed_cmd->end_line || sed_cmd->end_match) if (sed_cmd->end_line || sed_cmd->end_match)
error_msg_and_die("only a beginning address can be specified for edit commands"); error_msg_and_die("only a beginning address can be specified for edit commands");
idx += parse_edit_cmd(sed_cmd, &cmdstr[idx]); idx += parse_edit_cmd(sed_cmd, &cmdstr[idx]);
} }
/* if it was a single-letter command (such as 'p' or 'd') we need to /* handle file cmds: (r)ead */
* increment the index past that command */ else if (sed_cmd->cmd == 'r') {
else if (sed_cmd->end_line || sed_cmd->end_match)
idx++; 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 */ /* give back whatever's left over */
return (char *)&cmdstr[idx]; 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); fputs(sed_cmd->editline, stdout);
altered++; altered++;
break; 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; return altered;

83
sed.c
View File

@ -27,6 +27,7 @@
- address matching: num|/matchstr/[,num|/matchstr/|$]command - address matching: num|/matchstr/[,num|/matchstr/|$]command
- commands: (p)rint, (d)elete, (s)ubstitue (with g & I flags) - commands: (p)rint, (d)elete, (s)ubstitue (with g & I flags)
- edit commands: (a)ppend, (i)nsert, (c)hange - edit commands: (a)ppend, (i)nsert, (c)hange
- file commands: (r)ead
- backreferences in substitution expressions (\1, \2...\9) - backreferences in substitution expressions (\1, \2...\9)
(Note: Specifying an address (range) to match is *optional*; commands (Note: Specifying an address (range) to match is *optional*; commands
@ -90,6 +91,11 @@ struct sed_cmd {
/* EDIT COMMAND (a,i,c) SPEICIFIC FIELDS */ /* EDIT COMMAND (a,i,c) SPEICIFIC FIELDS */
char *editline; char *editline;
/* FILE COMMAND (r) SPEICIFIC FIELDS */
char *filename;
}; };
/* globals */ /* globals */
@ -351,6 +357,45 @@ out:
return idx; 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) static char *parse_cmd_str(struct sed_cmd *sed_cmd, const char *cmdstr)
{ {
int idx = 0; int idx = 0;
@ -361,7 +406,6 @@ static char *parse_cmd_str(struct sed_cmd *sed_cmd, const char *cmdstr)
* part1 part2 part3 * part1 part2 part3
*/ */
/* first part (if present) is an address: either a number or a /regex/ */ /* first part (if present) is an address: either a number or a /regex/ */
if (isdigit(cmdstr[idx]) || cmdstr[idx] == '/') if (isdigit(cmdstr[idx]) || cmdstr[idx] == '/')
idx = get_address(sed_cmd, cmdstr, &sed_cmd->beg_line, &sed_cmd->beg_match); 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 */ /* last part (mandatory) will be a command */
if (cmdstr[idx] == '\0') if (cmdstr[idx] == '\0')
error_msg_and_die("missing command"); 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]; sed_cmd->cmd = cmdstr[idx];
/* special-case handling for (s)ubstitution */ /* if it was a single-letter command that takes no arguments (such as 'p'
if (sed_cmd->cmd == 's') { * 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]); 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])) { else if (strchr("aic", cmdstr[idx])) {
if (sed_cmd->end_line || sed_cmd->end_match) if (sed_cmd->end_line || sed_cmd->end_match)
error_msg_and_die("only a beginning address can be specified for edit commands"); error_msg_and_die("only a beginning address can be specified for edit commands");
idx += parse_edit_cmd(sed_cmd, &cmdstr[idx]); idx += parse_edit_cmd(sed_cmd, &cmdstr[idx]);
} }
/* if it was a single-letter command (such as 'p' or 'd') we need to /* handle file cmds: (r)ead */
* increment the index past that command */ else if (sed_cmd->cmd == 'r') {
else if (sed_cmd->end_line || sed_cmd->end_match)
idx++; 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 */ /* give back whatever's left over */
return (char *)&cmdstr[idx]; 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); fputs(sed_cmd->editline, stdout);
altered++; altered++;
break; 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; return altered;