Added support for 'r' command in sed.
This commit is contained in:
parent
cfa88ecb72
commit
1f3b9f297e
@ -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;
|
||||
|
83
sed.c
83
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;
|
||||
|
Loading…
Reference in New Issue
Block a user