Added support for 'r' command in sed.
authorMark Whitley <markw@lineo.com>
Fri, 11 May 2001 22:27:13 +0000 (22:27 -0000)
committerMark Whitley <markw@lineo.com>
Fri, 11 May 2001 22:27:13 +0000 (22:27 -0000)
editors/sed.c
sed.c

index 1342a66433554a7bf9dce8fb02cfaee22ac2427f..156ad3adaa30774168811ab780609e52a75ebb3a 100644 (file)
@@ -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;
diff --git a/sed.c b/sed.c
index 1342a66433554a7bf9dce8fb02cfaee22ac2427f..156ad3adaa30774168811ab780609e52a75ebb3a 100644 (file)
--- a/sed.c
+++ b/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;