b610615be9
Tested under both libc5 and libc6 and all seems well with these fixes. -Erik
360 lines
7.3 KiB
C
360 lines
7.3 KiB
C
/* vi: set sw=4 ts=4: */
|
|
/*
|
|
* Mini sed implementation for busybox
|
|
*
|
|
*
|
|
* Copyright (C) 1999,2000 by Lineo, inc.
|
|
* Written by Erik Andersen <andersen@lineo.com>, <andersee@debian.org>
|
|
*
|
|
* Modifications for addresses and append command have been
|
|
* written by Marco Pantaleoni <panta@prosa.it>, <panta@elasticworld.org>
|
|
* and are:
|
|
* Copyright (C) 1999 Marco Pantaleoni.
|
|
*
|
|
* 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 Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*
|
|
*/
|
|
|
|
#include "internal.h"
|
|
#include "regexp.h"
|
|
#include <stdio.h>
|
|
#include <dirent.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <signal.h>
|
|
#include <time.h>
|
|
#include <ctype.h>
|
|
|
|
static const char sed_usage[] =
|
|
"sed [-n] -e script [file...]\n"
|
|
#ifndef BB_FEATURE_TRIVIAL_HELP
|
|
"\nAllowed sed scripts come in the following form:\n"
|
|
"\t'ADDR [!] COMMAND'\n\n"
|
|
"\twhere address ADDR can be:\n"
|
|
"\t NUMBER Match specified line number\n"
|
|
"\t $ Match last line\n"
|
|
"\t /REGEXP/ Match specified regexp\n"
|
|
"\t (! inverts the meaning of the match)\n\n"
|
|
"\tand COMMAND can be:\n"
|
|
"\t s/regexp/replacement/[igp]\n"
|
|
"\t which attempt to match regexp against the pattern space\n"
|
|
"\t and if successful replaces the matched portion with replacement.\n\n"
|
|
"\t aTEXT\n"
|
|
"\t which appends TEXT after the pattern space\n"
|
|
"Options:\n"
|
|
"-e\tadd the script to the commands to be executed\n"
|
|
"-n\tsuppress automatic printing of pattern space\n\n"
|
|
#if defined BB_REGEXP
|
|
"This version of sed matches full regular expressions.\n";
|
|
#else
|
|
"This version of sed matches strings (not full regular expressions).\n"
|
|
#endif
|
|
#endif
|
|
;
|
|
|
|
/* Flags & variables */
|
|
|
|
typedef enum { f_none, f_replace, f_append } sed_function;
|
|
|
|
#define NO_LINE -2
|
|
#define LAST_LINE -1
|
|
static int addr_line = NO_LINE;
|
|
static char *addr_pattern = NULL;
|
|
static int negated = 0;
|
|
|
|
#define SKIPSPACES(p) do { while (isspace(*(p))) (p)++; } while (0)
|
|
|
|
#define BUFSIZE 1024
|
|
|
|
static inline int at_last(FILE * fp)
|
|
{
|
|
int res = 0;
|
|
|
|
if (feof(fp))
|
|
return 1;
|
|
else {
|
|
int ch;
|
|
|
|
if ((ch = fgetc(fp)) == EOF)
|
|
res++;
|
|
ungetc(ch, fp);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
static void do_sed_repl(FILE * fp, char *needle, char *newNeedle,
|
|
int ignoreCase, int printFlag, int quietFlag)
|
|
{
|
|
int foundOne = FALSE;
|
|
char haystack[BUFSIZE];
|
|
int line = 1, doit;
|
|
|
|
while (fgets(haystack, BUFSIZE - 1, fp)) {
|
|
doit = 0;
|
|
if (addr_pattern) {
|
|
doit = !find_match(haystack, addr_pattern, FALSE);
|
|
} else if (addr_line == NO_LINE)
|
|
doit = 1;
|
|
else if (addr_line == LAST_LINE) {
|
|
if (at_last(fp))
|
|
doit = 1;
|
|
} else {
|
|
if (line == addr_line)
|
|
doit = 1;
|
|
}
|
|
if (negated)
|
|
doit = 1 - doit;
|
|
if (doit) {
|
|
foundOne =
|
|
replace_match(haystack, needle, newNeedle, ignoreCase);
|
|
|
|
if (foundOne == TRUE && printFlag == TRUE) {
|
|
fprintf(stdout, haystack);
|
|
}
|
|
}
|
|
|
|
if (quietFlag == FALSE) {
|
|
fprintf(stdout, haystack);
|
|
}
|
|
|
|
line++;
|
|
}
|
|
}
|
|
|
|
static void do_sed_append(FILE * fp, char *appendline, int quietFlag)
|
|
{
|
|
char buffer[BUFSIZE];
|
|
int line = 1, doit;
|
|
|
|
while (fgets(buffer, BUFSIZE - 1, fp)) {
|
|
doit = 0;
|
|
if (addr_pattern) {
|
|
doit = !find_match(buffer, addr_pattern, FALSE);
|
|
} else if (addr_line == NO_LINE)
|
|
doit = 1;
|
|
else if (addr_line == LAST_LINE) {
|
|
if (at_last(fp))
|
|
doit = 1;
|
|
} else {
|
|
if (line == addr_line)
|
|
doit = 1;
|
|
}
|
|
if (negated)
|
|
doit = 1 - doit;
|
|
if (quietFlag == FALSE) {
|
|
fprintf(stdout, buffer);
|
|
}
|
|
if (doit) {
|
|
fputs(appendline, stdout);
|
|
fputc('\n', stdout);
|
|
}
|
|
|
|
line++;
|
|
}
|
|
}
|
|
|
|
extern int sed_main(int argc, char **argv)
|
|
{
|
|
FILE *fp;
|
|
char *needle = NULL, *newNeedle = NULL;
|
|
char *name;
|
|
char *cp;
|
|
int ignoreCase = FALSE;
|
|
int printFlag = FALSE;
|
|
int quietFlag = FALSE;
|
|
int stopNow;
|
|
char *line_s = NULL, saved;
|
|
char *appendline = NULL;
|
|
char *pos;
|
|
sed_function sed_f = f_none;
|
|
|
|
argc--;
|
|
argv++;
|
|
if (argc < 1) {
|
|
usage(sed_usage);
|
|
}
|
|
|
|
while (argc > 1) {
|
|
if (**argv != '-')
|
|
usage(sed_usage);
|
|
argc--;
|
|
cp = *argv++;
|
|
stopNow = FALSE;
|
|
|
|
while (*++cp && stopNow == FALSE) {
|
|
switch (*cp) {
|
|
case 'n':
|
|
quietFlag = TRUE;
|
|
break;
|
|
case 'e':
|
|
if (*(cp + 1) == 0 && --argc < 0) {
|
|
usage(sed_usage);
|
|
}
|
|
if (*++cp != 's')
|
|
cp = *argv++;
|
|
|
|
/* Read address if present */
|
|
SKIPSPACES(cp);
|
|
if (*cp == '$') {
|
|
addr_line = LAST_LINE;
|
|
cp++;
|
|
} else {
|
|
if (isdigit(*cp)) { /* LINE ADDRESS */
|
|
line_s = cp;
|
|
while (isdigit(*cp))
|
|
cp++;
|
|
if (cp > line_s) {
|
|
/* numeric line */
|
|
saved = *cp;
|
|
*cp = '\0';
|
|
addr_line = atoi(line_s);
|
|
*cp = saved;
|
|
}
|
|
} else if (*cp == '/') { /* PATTERN ADDRESS */
|
|
pos = addr_pattern = cp + 1;
|
|
pos = strchr(pos, '/');
|
|
if (!pos)
|
|
usage(sed_usage);
|
|
*pos = '\0';
|
|
cp = pos + 1;
|
|
}
|
|
}
|
|
|
|
SKIPSPACES(cp);
|
|
if (*cp == '!') {
|
|
negated++;
|
|
cp++;
|
|
}
|
|
|
|
/* Read command */
|
|
|
|
SKIPSPACES(cp);
|
|
switch (*cp) {
|
|
case 's': /* REPLACE */
|
|
if (strlen(cp) <= 3 || *(cp + 1) != '/')
|
|
break;
|
|
sed_f = f_replace;
|
|
|
|
pos = needle = cp + 2;
|
|
|
|
for (;;) {
|
|
pos = strchr(pos, '/');
|
|
if (pos == NULL) {
|
|
usage(sed_usage);
|
|
}
|
|
if (*(pos - 1) == '\\') {
|
|
pos++;
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
*pos = 0;
|
|
newNeedle = ++pos;
|
|
for (;;) {
|
|
pos = strchr(pos, '/');
|
|
if (pos == NULL) {
|
|
usage(sed_usage);
|
|
}
|
|
if (*(pos - 1) == '\\') {
|
|
pos++;
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
*pos = 0;
|
|
if (pos + 2 != 0) {
|
|
while (*++pos) {
|
|
switch (*pos) {
|
|
case 'i':
|
|
ignoreCase = TRUE;
|
|
break;
|
|
case 'p':
|
|
printFlag = TRUE;
|
|
break;
|
|
case 'g':
|
|
break;
|
|
default:
|
|
usage(sed_usage);
|
|
}
|
|
}
|
|
}
|
|
cp = pos;
|
|
/* fprintf(stderr, "replace '%s' with '%s'\n", needle, newNeedle); */
|
|
break;
|
|
|
|
case 'a': /* APPEND */
|
|
if (strlen(cp) < 2)
|
|
break;
|
|
sed_f = f_append;
|
|
appendline = ++cp;
|
|
/* fprintf(stderr, "append '%s'\n", appendline); */
|
|
break;
|
|
}
|
|
|
|
stopNow = TRUE;
|
|
break;
|
|
|
|
default:
|
|
usage(sed_usage);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (argc == 0) {
|
|
switch (sed_f) {
|
|
case f_none:
|
|
break;
|
|
case f_replace:
|
|
do_sed_repl(stdin, needle, newNeedle, ignoreCase, printFlag,
|
|
quietFlag);
|
|
break;
|
|
case f_append:
|
|
do_sed_append(stdin, appendline, quietFlag);
|
|
break;
|
|
}
|
|
} else {
|
|
while (argc-- > 0) {
|
|
name = *argv++;
|
|
|
|
fp = fopen(name, "r");
|
|
if (fp == NULL) {
|
|
perror(name);
|
|
continue;
|
|
}
|
|
|
|
switch (sed_f) {
|
|
case f_none:
|
|
break;
|
|
case f_replace:
|
|
do_sed_repl(fp, needle, newNeedle, ignoreCase, printFlag,
|
|
quietFlag);
|
|
break;
|
|
case f_append:
|
|
do_sed_append(fp, appendline, quietFlag);
|
|
break;
|
|
}
|
|
|
|
if (ferror(fp))
|
|
perror(name);
|
|
|
|
fclose(fp);
|
|
}
|
|
}
|
|
return(TRUE);
|
|
}
|
|
|
|
|
|
/* END CODE */
|