*
* Copyright (C) 1999,2000,2001 by Lineo, inc. and Mark Whitley
* Copyright (C) 1999,2000,2001 by Mark Whitley <markw@codepoet.org>
+ * Copyright (C) 2002 Matt Kraai
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
/* the command */
char cmd; /* p,d,s (add more at your leisure :-) */
+
+ /* inversion flag */
+ int invert; /* the '!' after the address */
};
/* globals */
const char * const semicolon_whitespace = "; \n\r\t\v\0";
#ifdef CONFIG_FEATURE_CLEAN_UP
-static void destroy_cmd_strs()
+static void destroy_cmd_strs(void)
{
if (sed_cmds == NULL)
return;
static int get_address(struct sed_cmd *sed_cmd, const char *str, int *linenum, regex_t **regex)
{
char *my_str = xstrdup(str);
- int idx = 0;
+ int idx = 0, idx_start = 1;
char olddelimiter;
olddelimiter = sed_cmd->delimiter;
sed_cmd->delimiter = '/';
*linenum = -1;
idx++;
}
- else if (my_str[idx] == '/') {
+ else if (my_str[idx] == '/' || my_str[idx] == '\\') {
+ if (my_str[idx] == '\\') {
+ idx_start++;
+ sed_cmd-> delimiter = 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, REG_NEWLINE);
+ xregcomp(*regex, my_str+idx_start, REG_NEWLINE);
idx++; /* so it points to the next character after the last '/' */
}
else {
static int parse_edit_cmd(struct sed_cmd *sed_cmd, const char *editstr)
{
- int idx = 0;
- int slashes_eaten = 0;
- char *ptr; /* shorthand */
+ int i, j;
/*
* the string that gets passed to this function should look like this:
*
*/
- if (editstr[1] != '\\' && (editstr[2] != '\n' || editstr[2] != '\r'))
+ if (editstr[1] != '\\' || (editstr[2] != '\n' && editstr[2] != '\r'))
error_msg_and_die("bad format in edit expression");
/* store the edit line text */
- /* make editline big enough to accomodate the extra '\n' we will tack on
- * to the end */
sed_cmd->editline = xmalloc(strlen(&editstr[3]) + 2);
- strcpy(sed_cmd->editline, &editstr[3]);
- ptr = sed_cmd->editline;
-
- /* now we need to go through * and: s/\\[\r\n]$/\n/g on the edit line */
- while (ptr[idx]) {
- while (ptr[idx] != '\\' || (ptr[idx+1] != '\n' && ptr[idx+1] != '\r')) {
- idx++;
- if (!ptr[idx]) {
- goto out;
- }
- }
- /* move the newline over the '\' before it (effectively eats the '\') */
- move_back(&ptr[idx], 1);
- slashes_eaten++;
- /* substitue \r for \n if needed */
- if (ptr[idx] == '\r')
- ptr[idx] = '\n';
+ for (i = 3, j = 0; editstr[i] != '\0' && strchr("\r\n", editstr[i]) == NULL;
+ i++, j++) {
+ if (editstr[i] == '\\' && strchr("\n\r", editstr[i+1]) != NULL) {
+ sed_cmd->editline[j] = '\n';
+ i++;
+ } else
+ sed_cmd->editline[j] = editstr[i];
}
-out:
/* figure out if we need to add a newline */
- if (ptr[idx-1] != '\n') {
- ptr[idx] = '\n';
- idx++;
- }
+ if (sed_cmd->editline[j-1] != '\n')
+ sed_cmd->editline[j++] = '\n';
/* terminate string */
- ptr[idx]= 0;
-
- /* this accounts for discrepancies between the modified string and the
- * original string passed in to this function */
-
- /* adjust for opening 2 chars [aic]\ */
+ sed_cmd->editline[j] = '\0';
- return idx + slashes_eaten + 2;
+ return i;
}
*/
/* first part (if present) is an address: either a number or a /regex/ */
- if (isdigit(cmdstr[idx]) || cmdstr[idx] == '/')
+ if (isdigit(cmdstr[idx]) || cmdstr[idx] == '/' || ( cmdstr[idx] == '\\' && cmdstr[idx+1] != '\\'))
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);
+ if (cmdstr[idx] == ',') {
+ idx++;
+ idx += get_address(sed_cmd, &cmdstr[idx], &sed_cmd->end_line, &sed_cmd->end_match);
+ }
+
+ /* skip whitespace before the command */
+ while (isspace(cmdstr[idx]))
+ idx++;
+
+ /* there my be the inversion flag between part2 and part3 */
+ sed_cmd->invert = 0;
+ if (cmdstr[idx] == '!') {
+ sed_cmd->invert = 1;
+ idx++;
+
+ /* skip whitespace before the command */
+ while (isspace(cmdstr[idx]))
+ idx++;
+ }
/* last part (mandatory) will be a command */
if (cmdstr[idx] == '\0')
/* for every line, go through all the commands */
for (i = 0; i < ncmds; i++) {
struct sed_cmd *sed_cmd = &sed_cmds[i];
-
+ int deleted = 0;
/*
* entry point into sedding...
*/
- if (
+ int matched = (
/* no range necessary */
(sed_cmd->beg_line == 0 && sed_cmd->end_line == 0 &&
sed_cmd->beg_match == NULL &&
(sed_cmd->beg_match && (regexec(sed_cmd->beg_match, line, 0, NULL, 0) == 0)) ||
/* we are currently within the beginning & ending address range */
still_in_range
- ) {
+ );
+
+ if (sed_cmd->invert ^ matched) {
/*
* actual sedding
case 'd':
altered++;
+ deleted = 1;
break;
case 's':
/* else if we couldn't open the output file,
* no biggie, just don't print anything */
altered++;
- }
+ }
break;
}
+ }
- /*
- * exit point from sedding...
- */
+ /*
+ * exit point from sedding...
+ */
+ if (matched) {
if (
/* this is a single-address command or... */
(sed_cmd->end_line == 0 && sed_cmd->end_match == NULL) || (
still_in_range = 1;
}
}
+
+ if (deleted)
+ break;
}
/* we will print the line unless we were told to be quiet or if the