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
|
- 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
83
sed.c
@ -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;
|
||||||
|
Loading…
Reference in New Issue
Block a user