+ for ( ; str[idx]; idx++) {
+ if (str[idx] == sed_cmd->delimiter && str[idx-1] != '\\')
+ return idx;
+ }
+
+ /* if we make it to here, we've hit the end of the string */
+ return -1;
+}
+
+/*
+ * returns the index in the string just past where the address ends.
+ */
+static int get_address(struct sed_cmd *sed_cmd, const char *str, int *line, regex_t **regex)
+{
+ char *my_str = strdup(str);
+ int idx = 0;
+ char olddelimiter;
+ olddelimiter = sed_cmd->delimiter;
+ sed_cmd->delimiter = '/';
+
+ if (isdigit(my_str[idx])) {
+ do {
+ idx++;
+ } while (isdigit(my_str[idx]));
+ my_str[idx] = 0;
+ *line = atoi(my_str);
+ }
+ else if (my_str[idx] == '$') {
+ *line = -1;
+ idx++;
+ }
+ else if (my_str[idx] == '/') {
+ idx = index_of_next_unescaped_regexp_delim(sed_cmd, my_str, ++idx);
+ if (idx == -1)
+ error_msg_and_die("unterminated match expression");
+ my_str[idx] = '\0';
+ *regex = (regex_t *)xmalloc(sizeof(regex_t));
+ xregcomp(*regex, my_str+1, 0);
+ idx++; /* so it points to the next character after the last '/' */
+ }
+ else {
+ error_msg("get_address: no address found in string\n"
+ "\t(you probably didn't check the string you passed me)");
+ idx = -1;
+ }
+
+ free(my_str);
+ sed_cmd->delimiter = olddelimiter;
+ return idx;
+}
+
+static char *strdup_substr(const char *str, int start, int end)
+{
+ int size = end - start + 1;
+ char *newstr = xmalloc(size);
+ memcpy(newstr, str+start, size-1);
+ newstr[size-1] = '\0';
+ return newstr;
+}
+
+static int parse_subst_cmd(struct sed_cmd *sed_cmd, const char *substr)
+{
+ int oldidx, cflags = REG_NEWLINE;
+ char *match;
+ int idx = 0;
+ int j;
+
+ /*
+ * the string that gets passed to this function should look like this:
+ * s/match/replace/gIp
+ * || | |||
+ * mandatory optional
+ *
+ * (all three of the '/' slashes are mandatory)
+ */
+
+ /* verify that the 's' is followed by something. That something
+ * (typically a 'slash') is now our regexp delimiter... */
+ if (!substr[++idx])
+ error_msg_and_die("bad format in substitution expression");
+ else
+ sed_cmd->delimiter=substr[idx];
+
+ /* save the match string */
+ oldidx = idx+1;
+ idx = index_of_next_unescaped_regexp_delim(sed_cmd, substr, ++idx);
+ if (idx == -1)
+ error_msg_and_die("bad format in substitution expression");
+ match = strdup_substr(substr, oldidx, idx);
+
+ /* determine the number of back references in the match string */
+ /* Note: we compute this here rather than in the do_subst_command()
+ * function to save processor time, at the expense of a little more memory
+ * (4 bits) per sed_cmd */
+
+ /* sed_cmd->num_backrefs = 0; */ /* XXX: not needed? --apparently not */
+ for (j = 0; match[j]; j++) {
+ /* GNU/POSIX sed does not save more than nine backrefs */
+ if (match[j] == '\\' && match[j+1] == '(' && sed_cmd->num_backrefs <= 9)
+ sed_cmd->num_backrefs++;
+ }
+
+ /* save the replacement string */
+ oldidx = idx+1;
+ idx = index_of_next_unescaped_regexp_delim(sed_cmd, substr, ++idx);
+ if (idx == -1)
+ error_msg_and_die("bad format in substitution expression");
+ sed_cmd->replace = strdup_substr(substr, oldidx, idx);
+
+ /* process the flags */
+ while (substr[++idx]) {
+ switch (substr[idx]) {
+ case 'g':
+ sed_cmd->sub_g = 1;
+ break;
+ case 'I':
+ cflags |= REG_ICASE;
+ break;
+ case 'p':
+ sed_cmd->sub_p = 1;
+ break;
+ default:
+ /* any whitespace or semicolon trailing after a s/// is ok */
+ if (strchr("; \t\v\n\r", substr[idx]))
+ goto out;
+ /* else */
+ error_msg_and_die("bad option in substitution expression");