Support grouped commands, i.e. {cmd1;cmd2}

This commit is contained in:
Glenn L McGrath 2003-03-09 15:12:24 +00:00
parent 4c6523a90b
commit f50ce3135f

View File

@ -30,7 +30,8 @@
- edit commands: (a)ppend, (i)nsert, (c)hange - edit commands: (a)ppend, (i)nsert, (c)hange
- file commands: (r)ead - file commands: (r)ead
- backreferences in substitution expressions (\1, \2...\9) - backreferences in substitution expressions (\1, \2...\9)
- grouped commands: {cmd1;cmd2}
(Note: Specifying an address (range) to match is *optional*; commands (Note: Specifying an address (range) to match is *optional*; commands
default to the whole pattern space if no specific address match was default to the whole pattern space if no specific address match was
requested.) requested.)
@ -226,7 +227,7 @@ static int parse_subst_cmd(sed_cmd_t * const sed_cmd, const char *substr)
/* verify that the 's' is followed by something. That something /* verify that the 's' is followed by something. That something
* (typically a 'slash') is now our regexp delimiter... */ * (typically a 'slash') is now our regexp delimiter... */
if (!substr[++idx]) if (substr[idx] == '\0')
error_msg_and_die("bad format in substitution expression"); error_msg_and_die("bad format in substitution expression");
else else
sed_cmd->delimiter=substr[idx]; sed_cmd->delimiter=substr[idx];
@ -287,11 +288,6 @@ out:
return idx; return idx;
} }
static void move_back(char *str, int offset)
{
memmove(str, str + offset, strlen(str + offset) + 1);
}
static int parse_edit_cmd(sed_cmd_t *sed_cmd, const char *editstr) static int parse_edit_cmd(sed_cmd_t *sed_cmd, const char *editstr)
{ {
int i, j; int i, j;
@ -317,15 +313,15 @@ static int parse_edit_cmd(sed_cmd_t *sed_cmd, const char *editstr)
* is a-ok. * is a-ok.
* *
*/ */
if ((*editstr != '\\') || ((editstr[1] != '\n') && (editstr[1] != '\r'))) {
if (editstr[1] != '\\' || (editstr[2] != '\n' && editstr[2] != '\r'))
error_msg_and_die("bad format in edit expression"); error_msg_and_die("bad format in edit expression");
}
/* store the edit line text */ /* store the edit line text */
sed_cmd->editline = xmalloc(strlen(&editstr[3]) + 2); sed_cmd->editline = xmalloc(strlen(&editstr[2]) + 2);
for (i = 3, j = 0; editstr[i] != '\0' && strchr("\r\n", editstr[i]) == NULL; for (i = 2, j = 0; editstr[i] != '\0' && strchr("\r\n", editstr[i]) == NULL;
i++, j++) { i++, j++) {
if (editstr[i] == '\\' && strchr("\n\r", editstr[i+1]) != NULL) { if ((editstr[i] == '\\') && strchr("\n\r", editstr[i+1]) != NULL) {
sed_cmd->editline[j] = '\n'; sed_cmd->editline[j] = '\n';
i++; i++;
} else } else
@ -382,59 +378,6 @@ static int parse_file_cmd(sed_cmd_t *sed_cmd, const char *filecmdstr)
static char *parse_cmd_str(sed_cmd_t * const sed_cmd, char *cmdstr) static char *parse_cmd_str(sed_cmd_t * const sed_cmd, char *cmdstr)
{ {
/* parse the command
* format is: [addr][,addr]cmd
* |----||-----||-|
* part1 part2 part3
*/
/* first part (if present) is an address: either a '$', a number or a /regex/ */
cmdstr += get_address(&sed_cmd->delimiter, cmdstr, &sed_cmd->beg_line, &sed_cmd->beg_match);
/* second part (if present) will begin with a comma */
if (*cmdstr == ',') {
int tmp_idx;
cmdstr++;
tmp_idx = get_address(&sed_cmd->delimiter, cmdstr, &sed_cmd->end_line, &sed_cmd->end_match);
if (tmp_idx == 0) {
error_msg_and_die("get_address: no address found in string\n"
"\t(you probably didn't check the string you passed me)");
}
cmdstr += tmp_idx;
}
/* skip whitespace before the command */
while (isspace(*cmdstr))
cmdstr++;
/* there my be the inversion flag between part2 and part3 */
sed_cmd->invert = 0;
if (*cmdstr == '!') {
sed_cmd->invert = 1;
cmdstr++;
#ifdef SED_FEATURE_STRICT_CHECKING
/* According to the spec
* It is unspecified whether <blank>s can follow a '!' character,
* and conforming applications shall not follow a '!' character
* with <blank>s.
*/
if (isblank(*cmdstr) {
error_msg_and_die("blank follows '!'");
}
#else
/* skip whitespace before the command */
while (isspace(*cmdstr))
cmdstr++;
#endif
}
/* last part (mandatory) will be a command */
if (*cmdstr == '\0')
error_msg_and_die("missing command");
sed_cmd->cmd = *cmdstr;
/* if it was a single-letter command that takes no arguments (such as 'p' /* 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 */ * or 'd') all we need to do is increment the index past that command */
if (strchr("pd=", sed_cmd->cmd)) { if (strchr("pd=", sed_cmd->cmd)) {
@ -456,6 +399,7 @@ static char *parse_cmd_str(sed_cmd_t * const sed_cmd, char *cmdstr)
error_msg_and_die("Command only uses one address"); error_msg_and_die("Command only uses one address");
cmdstr += parse_file_cmd(sed_cmd, cmdstr); cmdstr += parse_file_cmd(sed_cmd, cmdstr);
} }
/* handle grouped commands */
else { else {
error_msg_and_die("Unsupported command %c", sed_cmd->cmd); error_msg_and_die("Unsupported command %c", sed_cmd->cmd);
} }
@ -464,31 +408,105 @@ static char *parse_cmd_str(sed_cmd_t * const sed_cmd, char *cmdstr)
return(cmdstr); return(cmdstr);
} }
static void add_cmd_str(const char * const cmdstr) static char *add_cmd(sed_cmd_t *sed_cmd, char *cmdstr)
{ {
char *mystr = (char *)cmdstr;
/* Skip over leading whitespace and semicolons */
cmdstr += strspn(cmdstr, semicolon_whitespace);
do { /* if we ate the whole thing, that means there was just trailing
* whitespace or a final / no-op semicolon. either way, get out */
if (*cmdstr == '\0') {
return(NULL);
}
/* trim leading whitespace and semicolons */ /* if this is a comment, jump past it and keep going */
move_back(mystr, strspn(mystr, semicolon_whitespace)); if (*cmdstr == '#') {
/* if we ate the whole thing, that means there was just trailing return(strpbrk(cmdstr, "\n\r"));
* whitespace or a final / no-op semicolon. either way, get out */ }
if (strlen(mystr) == 0)
return; /* parse the command
/* if this is a comment, jump past it and keep going */ * format is: [addr][,addr]cmd
if (mystr[0] == '#') { * |----||-----||-|
mystr = strpbrk(mystr, "\n\r"); * part1 part2 part3
continue; */
/* first part (if present) is an address: either a '$', a number or a /regex/ */
cmdstr += get_address(&(sed_cmd->delimiter), cmdstr, &sed_cmd->beg_line, &sed_cmd->beg_match);
/* second part (if present) will begin with a comma */
if (*cmdstr == ',') {
int idx;
cmdstr++;
idx = get_address(&(sed_cmd->delimiter), cmdstr, &sed_cmd->end_line, &sed_cmd->end_match);
if (idx == 0) {
error_msg_and_die("get_address: no address found in string\n"
"\t(you probably didn't check the string you passed me)");
} }
/* grow the array */ cmdstr += idx;
sed_cmds = xrealloc(sed_cmds, sizeof(sed_cmd_t *) * (++ncmds)); }
/* zero new element */
sed_cmds[ncmds-1] = xcalloc(1, sizeof(sed_cmd_t));
/* load command string into new array element, get remainder */
mystr = parse_cmd_str(sed_cmds[ncmds-1], mystr);
} while (mystr && strlen(mystr)); /* skip whitespace before the command */
while (isspace(*cmdstr)) {
cmdstr++;
}
/* there my be the inversion flag between part2 and part3 */
if (*cmdstr == '!') {
sed_cmd->invert = 1;
cmdstr++;
#ifdef SED_FEATURE_STRICT_CHECKING
/* According to the spec
* It is unspecified whether <blank>s can follow a '!' character,
* and conforming applications shall not follow a '!' character
* with <blank>s.
*/
if (isblank(cmdstr[idx]) {
error_msg_and_die("blank follows '!'");
}
#else
/* skip whitespace before the command */
while (isspace(*cmdstr)) {
cmdstr++;
}
#endif
}
/* last part (mandatory) will be a command */
if (*cmdstr == '\0')
error_msg_and_die("missing command");
sed_cmd->cmd = *cmdstr;
cmdstr++;
if (sed_cmd->cmd == '{') {
do {
char *end_ptr = strpbrk(cmdstr, ";}");
*end_ptr = '\0';
add_cmd(sed_cmd, cmdstr);
cmdstr = end_ptr + 1;
} while (*cmdstr != '\0');
} else {
cmdstr = parse_cmd_str(sed_cmd, cmdstr);
/* Add the command to the command array */
sed_cmds = xrealloc(sed_cmds, sizeof(sed_cmd_t) * (++ncmds));
sed_cmds[ncmds-1] = xmalloc(sizeof(sed_cmd_t));
memcpy(sed_cmds[ncmds-1], sed_cmd, sizeof(sed_cmd_t));
}
return(cmdstr);
}
static void add_cmd_str(char *cmdstr)
{
do {
sed_cmd_t *sed_cmd;
sed_cmd = xcalloc(1, sizeof(sed_cmd_t));
cmdstr = add_cmd(sed_cmd, cmdstr);
} while (cmdstr && strlen(cmdstr));
} }
@ -868,7 +886,6 @@ extern int sed_main(int argc, char **argv)
} }
} }
/* argv[(optind)..(argc-1)] should be names of file to process. If no /* argv[(optind)..(argc-1)] should be names of file to process. If no
* files were specified or '-' was specified, take input from stdin. * files were specified or '-' was specified, take input from stdin.
* Otherwise, we process all the files specified. */ * Otherwise, we process all the files specified. */