- Added support for semicolon delimited command lines. (woo-hoo!)
- Obsoleted the trim_str function (#if 0'ed out -- maybedelete later) in
favor of strrspn.
- Obsoleted the strrspn function (#if 0'ed out as well) as soon as I
discovered that it wasn't needed either.
- Fixed a subtle bug in parse_subst_cmd where it would choke with an error if
there was any trailing space after the s/match/replace/ expression.
diff --git a/sed.c b/sed.c
index 3a7360f..40400fe 100644
--- a/sed.c
+++ b/sed.c
@@ -132,11 +132,13 @@
}
#endif
+#if 0
/*
* trim_str - trims leading and trailing space from a string
*
* Note: This returns a malloc'ed string so you must store and free it
* XXX: This should be in the utility.c file.
+ * XXX: This is now obsolete. Maybe it belongs nowhere.
*/
static char *trim_str(const char *str)
{
@@ -156,11 +158,28 @@
*
* you know, a strrspn() would really be nice cuz then we could say:
*
- * retstr[strlen(retstr) - strrspn(retstr, " \n\t\v") + 1] = 0;
+ * retstr[strrspn(retstr, " \n\t\v") + 1] = 0;
*/
return retstr;
}
+#endif
+
+#if 0
+/*
+ * strrspn - works just like strspn() but goes from right to left instead of
+ * left to right
+ */
+static size_t strrspn(const char *s, const char *accept)
+{
+ size_t i = strlen(s);
+
+ while (strchr(accept, s[--i]))
+ ;
+
+ return i;
+}
+#endif
/*
* index_of_unescaped_slash - walks left to right through a string beginning
@@ -225,7 +244,7 @@
return newstr;
}
-static void parse_subst_cmd(struct sed_cmd *sed_cmd, const char *substr)
+static int parse_subst_cmd(struct sed_cmd *sed_cmd, const char *substr)
{
int oldidx, cflags = REG_NEWLINE;
char *match;
@@ -261,24 +280,31 @@
/* process the flags */
while (substr[++idx]) {
switch (substr[idx]) {
- case 'g':
- sed_cmd->sub_g = 1;
- break;
- case 'I':
- cflags |= REG_ICASE;
- break;
- default:
- fatalError("bad option in substitution expression\n");
+ case 'g':
+ sed_cmd->sub_g = 1;
+ break;
+ case 'I':
+ cflags |= REG_ICASE;
+ break;
+ default:
+ /* any whitespace or semicolon trailing after a s/// is ok */
+ if (strchr("; \t\v\n\r", substr[idx]))
+ goto out;
+ /* else */
+ fatalError("bad option in substitution expression\n");
}
}
-
+
+out:
/* compile the regex */
sed_cmd->sub_match = (regex_t *)xmalloc(sizeof(regex_t));
xregcomp(sed_cmd->sub_match, match, cflags);
free(match);
+
+ return idx;
}
-static void parse_edit_cmd(struct sed_cmd *sed_cmd, const char *editstr)
+static int parse_edit_cmd(struct sed_cmd *sed_cmd, const char *editstr)
{
int idx = 0;
char *ptr; /* shorthand */
@@ -322,7 +348,7 @@
if (!ptr[idx]) {
ptr[idx] = '\n';
ptr[idx+1] = 0;
- return;
+ return idx;
}
}
/* move the newline over the '\' before it (effectively eats the '\') */
@@ -332,9 +358,11 @@
if (ptr[idx] == '\r')
ptr[idx] = '\n';
}
+
+ return idx;
}
-static void 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;
@@ -344,6 +372,7 @@
* 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(cmdstr, &sed_cmd->beg_line, &sed_cmd->beg_match);
@@ -360,33 +389,45 @@
sed_cmd->cmd = cmdstr[idx];
/* special-case handling for (s)ubstitution */
- if (sed_cmd->cmd == 's')
- parse_subst_cmd(sed_cmd, &cmdstr[idx]);
-
+ if (sed_cmd->cmd == 's') {
+ idx += parse_subst_cmd(sed_cmd, &cmdstr[idx]);
+ }
/* special-case handling for (a)ppend, (i)nsert, and (c)hange */
- if (strchr("aic", cmdstr[idx])) {
+ else if (strchr("aic", cmdstr[idx])) {
if (sed_cmd->end_line || sed_cmd->end_match)
fatalError("only a beginning address can be specified for edit commands\n");
- parse_edit_cmd(sed_cmd, &cmdstr[idx]);
+ idx += parse_edit_cmd(sed_cmd, &cmdstr[idx]);
}
+
+ /* give back whatever's left over */
+ return (char *)&cmdstr[++idx];
}
static void add_cmd_str(const char *cmdstr)
{
- char *my_cmdstr = trim_str(cmdstr);
+ char *mystr = (char *)cmdstr;
- /* if this is a comment, don't even bother */
- if (my_cmdstr[0] == '#') {
- free(my_cmdstr);
- return;
- }
+ do {
- /* grow the array */
- sed_cmds = realloc(sed_cmds, sizeof(struct sed_cmd) * (++ncmds));
- /* zero new element */
- memset(&sed_cmds[ncmds-1], 0, sizeof(struct sed_cmd));
- /* load command string into new array element */
- parse_cmd_str(&sed_cmds[ncmds-1], my_cmdstr);
+ /* trim leading whitespace and semicolons */
+ memmove(mystr, &mystr[strspn(mystr, "; \n\r\t\v")], strlen(mystr));
+ /* if we ate the whole thing, that means there was just trailing
+ * whitespace or a final semicolon. either way, get out */
+ if (strlen(mystr) == 0)
+ return;
+ /* if this is a comment, jump past it and keep going */
+ if (mystr[0] == '#') {
+ mystr = strpbrk(mystr, ";\n\r");
+ continue;
+ }
+ /* grow the array */
+ sed_cmds = realloc(sed_cmds, sizeof(struct sed_cmd) * (++ncmds));
+ /* zero new element */
+ memset(&sed_cmds[ncmds-1], 0, sizeof(struct sed_cmd));
+ /* load command string into new array element, get remainder */
+ mystr = parse_cmd_str(&sed_cmds[ncmds-1], mystr);
+
+ } while (mystr);
}