+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(filenamelen + 1);
+ safe_strncpy(sed_cmd->filename, &filecmdstr[idx], filenamelen + 1);
+
+ return idx + filenamelen;
+}
+
+
+static char *parse_cmd_str(struct sed_cmd *sed_cmd, const char *cmdstr)
+{
+ int idx = 0;
+
+ /* parse the command
+ * format is: [addr][,addr]cmd
+ * |----||-----||-|
+ * 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);
+
+ /* second part (if present) will begin with a comma */
+ if (cmdstr[idx] == ',')
+ idx += get_address(sed_cmd, &cmdstr[++idx], &sed_cmd->end_line, &sed_cmd->end_match);
+
+ /* last part (mandatory) will be a command */
+ if (cmdstr[idx] == '\0')
+ error_msg_and_die("missing command");
+ sed_cmd->cmd = cmdstr[idx];
+
+ /* 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 command */
+ else if (sed_cmd->cmd == 's') {
+ idx += parse_subst_cmd(sed_cmd, &cmdstr[idx]);
+ }
+ /* handle edit cmds: (a)ppend, (i)nsert, and (c)hange */
+ else if (strchr("aic", sed_cmd->cmd)) {
+ if ((sed_cmd->end_line || sed_cmd->end_match) && sed_cmd->cmd != 'c')
+ error_msg_and_die("only a beginning address can be specified for edit commands");
+ idx += parse_edit_cmd(sed_cmd, &cmdstr[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");