Add sed_inplace 2002.06.17, a modified version of the sed(1) which
can do in-place editing of files.
This commit is contained in:
parent
ffdfc549c2
commit
25742c03d3
Notes:
svn2git
2021-03-31 03:12:20 +00:00
svn path=/head/; revision=61415
@ -298,6 +298,7 @@
|
||||
SUBDIR += sdf
|
||||
SUBDIR += sdocbook-xml
|
||||
SUBDIR += se-ispell
|
||||
SUBDIR += sed_inplace
|
||||
SUBDIR += sgmlformat
|
||||
SUBDIR += sgmls
|
||||
SUBDIR += sgmltools
|
||||
|
30
textproc/sed_inplace/Makefile
Normal file
30
textproc/sed_inplace/Makefile
Normal file
@ -0,0 +1,30 @@
|
||||
# New ports collection makefile for: sed_inplace
|
||||
# Date created: 17 June 2002
|
||||
# Whom: Maxim Sobolev <sobomax@FreeBSD.org>
|
||||
#
|
||||
# $FreeBSD$
|
||||
#
|
||||
|
||||
PORTNAME= sed_inplace
|
||||
PORTVERSION= 2002.06.17
|
||||
CATEGORIES= textproc
|
||||
MASTER_SITES= #
|
||||
DISTFILES= #
|
||||
|
||||
MAINTAINER= sobomax@FreeBSD.org
|
||||
|
||||
NO_WRKSUBDIR= yes
|
||||
|
||||
MAKEFILE= ${PKGDIR}/src/Makefile
|
||||
MAKE_ENV= PKGDIR="${PKGDIR}"
|
||||
|
||||
.include <bsd.port.pre.mk>
|
||||
|
||||
.if ${OSVERSION} > 500033
|
||||
IGNORE= "is in the base system"
|
||||
.endif
|
||||
|
||||
do-install:
|
||||
${INSTALL_PROGRAM} ${WRKSRC}/sed_inplace ${PREFIX}/bin
|
||||
|
||||
.include <bsd.port.post.mk>
|
1
textproc/sed_inplace/distinfo
Normal file
1
textproc/sed_inplace/distinfo
Normal file
@ -0,0 +1 @@
|
||||
|
1
textproc/sed_inplace/pkg-comment
Normal file
1
textproc/sed_inplace/pkg-comment
Normal file
@ -0,0 +1 @@
|
||||
A modified version of the sed(1) which can do in-place editing of files
|
2
textproc/sed_inplace/pkg-descr
Normal file
2
textproc/sed_inplace/pkg-descr
Normal file
@ -0,0 +1,2 @@
|
||||
Modified version of the FreeBSD sed(1) command which can do in-place editing
|
||||
of files.
|
1
textproc/sed_inplace/pkg-plist
Normal file
1
textproc/sed_inplace/pkg-plist
Normal file
@ -0,0 +1 @@
|
||||
bin/sed_inplace
|
12
textproc/sed_inplace/src/Makefile
Normal file
12
textproc/sed_inplace/src/Makefile
Normal file
@ -0,0 +1,12 @@
|
||||
# $FreeBSD$
|
||||
|
||||
.PATH: ${PKGDIR}/src
|
||||
|
||||
PROG= sed_inplace
|
||||
SRCS= compile.c main.c misc.c process.c
|
||||
|
||||
CFLAGS+= -I${PKGDIR}/src
|
||||
|
||||
MAN1=
|
||||
|
||||
.include <bsd.prog.mk>
|
868
textproc/sed_inplace/src/compile.c
Normal file
868
textproc/sed_inplace/src/compile.c
Normal file
@ -0,0 +1,868 @@
|
||||
/*-
|
||||
* Copyright (c) 1992 Diomidis Spinellis.
|
||||
* Copyright (c) 1992, 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to Berkeley by
|
||||
* Diomidis Spinellis of Imperial College, University of London.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. All advertising materials mentioning features or use of this software
|
||||
* must display the following acknowledgement:
|
||||
* This product includes software developed by the University of
|
||||
* California, Berkeley and its contributors.
|
||||
* 4. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD: /tmp/pcvs/ports/textproc/sed_inplace/src/Attic/compile.c,v 1.1 2002-06-17 08:46:11 sobomax Exp $");
|
||||
|
||||
#ifndef lint
|
||||
static const char sccsid[] = "@(#)compile.c 8.1 (Berkeley) 6/6/93";
|
||||
#endif
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <ctype.h>
|
||||
#include <err.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <regex.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "defs.h"
|
||||
#include "extern.h"
|
||||
|
||||
#define LHSZ 128
|
||||
#define LHMASK (LHSZ - 1)
|
||||
static struct labhash {
|
||||
struct labhash *lh_next;
|
||||
u_int lh_hash;
|
||||
struct s_command *lh_cmd;
|
||||
int lh_ref;
|
||||
} *labels[LHSZ];
|
||||
|
||||
static char *compile_addr(char *, struct s_addr *);
|
||||
static char *compile_ccl(char **, char *);
|
||||
static char *compile_delimited(char *, char *);
|
||||
static char *compile_flags(char *, struct s_subst *);
|
||||
static char *compile_re(char *, regex_t **);
|
||||
static char *compile_subst(char *, struct s_subst *);
|
||||
static char *compile_text(void);
|
||||
static char *compile_tr(char *, char **);
|
||||
static struct s_command
|
||||
**compile_stream(struct s_command **);
|
||||
static char *duptoeol(char *, const char *);
|
||||
static void enterlabel(struct s_command *);
|
||||
static struct s_command
|
||||
*findlabel(char *);
|
||||
static void fixuplabel(struct s_command *, struct s_command *);
|
||||
static void uselabel(void);
|
||||
|
||||
/*
|
||||
* Command specification. This is used to drive the command parser.
|
||||
*/
|
||||
struct s_format {
|
||||
char code; /* Command code */
|
||||
int naddr; /* Number of address args */
|
||||
enum e_args args; /* Argument type */
|
||||
};
|
||||
|
||||
static struct s_format cmd_fmts[] = {
|
||||
{'{', 2, GROUP},
|
||||
{'}', 0, ENDGROUP},
|
||||
{'a', 1, TEXT},
|
||||
{'b', 2, BRANCH},
|
||||
{'c', 2, TEXT},
|
||||
{'d', 2, EMPTY},
|
||||
{'D', 2, EMPTY},
|
||||
{'g', 2, EMPTY},
|
||||
{'G', 2, EMPTY},
|
||||
{'h', 2, EMPTY},
|
||||
{'H', 2, EMPTY},
|
||||
{'i', 1, TEXT},
|
||||
{'l', 2, EMPTY},
|
||||
{'n', 2, EMPTY},
|
||||
{'N', 2, EMPTY},
|
||||
{'p', 2, EMPTY},
|
||||
{'P', 2, EMPTY},
|
||||
{'q', 1, EMPTY},
|
||||
{'r', 1, RFILE},
|
||||
{'s', 2, SUBST},
|
||||
{'t', 2, BRANCH},
|
||||
{'w', 2, WFILE},
|
||||
{'x', 2, EMPTY},
|
||||
{'y', 2, TR},
|
||||
{'!', 2, NONSEL},
|
||||
{':', 0, LABEL},
|
||||
{'#', 0, COMMENT},
|
||||
{'=', 1, EMPTY},
|
||||
{'\0', 0, COMMENT},
|
||||
};
|
||||
|
||||
/* The compiled program. */
|
||||
struct s_command *prog;
|
||||
|
||||
/*
|
||||
* Compile the program into prog.
|
||||
* Initialise appends.
|
||||
*/
|
||||
void
|
||||
compile()
|
||||
{
|
||||
*compile_stream(&prog) = NULL;
|
||||
fixuplabel(prog, NULL);
|
||||
uselabel();
|
||||
if (appendnum == 0)
|
||||
appends = NULL;
|
||||
else if ((appends = malloc(sizeof(struct s_appends) * appendnum)) ==
|
||||
NULL)
|
||||
err(1, "malloc");
|
||||
if ((match = malloc((maxnsub + 1) * sizeof(regmatch_t))) == NULL)
|
||||
err(1, "malloc");
|
||||
}
|
||||
|
||||
#define EATSPACE() do { \
|
||||
if (p) \
|
||||
while (*p && isspace((unsigned char)*p)) \
|
||||
p++; \
|
||||
} while (0)
|
||||
|
||||
static struct s_command **
|
||||
compile_stream(link)
|
||||
struct s_command **link;
|
||||
{
|
||||
char *p;
|
||||
static char lbuf[_POSIX2_LINE_MAX + 1]; /* To save stack */
|
||||
struct s_command *cmd, *cmd2, *stack;
|
||||
struct s_format *fp;
|
||||
int naddr; /* Number of addresses */
|
||||
|
||||
stack = 0;
|
||||
for (;;) {
|
||||
if ((p = cu_fgets(lbuf, sizeof(lbuf), NULL)) == NULL) {
|
||||
if (stack != 0)
|
||||
errx(1, "%lu: %s: unexpected EOF (pending }'s)",
|
||||
linenum, fname);
|
||||
return (link);
|
||||
}
|
||||
|
||||
semicolon: EATSPACE();
|
||||
if (p && (*p == '#' || *p == '\0'))
|
||||
continue;
|
||||
if ((*link = cmd = malloc(sizeof(struct s_command))) == NULL)
|
||||
err(1, "malloc");
|
||||
link = &cmd->next;
|
||||
cmd->nonsel = cmd->inrange = 0;
|
||||
/* First parse the addresses */
|
||||
naddr = 0;
|
||||
|
||||
/* Valid characters to start an address */
|
||||
#define addrchar(c) (strchr("0123456789/\\$", (c)))
|
||||
if (addrchar(*p)) {
|
||||
naddr++;
|
||||
if ((cmd->a1 = malloc(sizeof(struct s_addr))) == NULL)
|
||||
err(1, "malloc");
|
||||
p = compile_addr(p, cmd->a1);
|
||||
EATSPACE(); /* EXTENSION */
|
||||
if (*p == ',') {
|
||||
p++;
|
||||
EATSPACE(); /* EXTENSION */
|
||||
naddr++;
|
||||
if ((cmd->a2 = malloc(sizeof(struct s_addr)))
|
||||
== NULL)
|
||||
err(1, "malloc");
|
||||
p = compile_addr(p, cmd->a2);
|
||||
EATSPACE();
|
||||
} else
|
||||
cmd->a2 = 0;
|
||||
} else
|
||||
cmd->a1 = cmd->a2 = 0;
|
||||
|
||||
nonsel: /* Now parse the command */
|
||||
if (!*p)
|
||||
errx(1, "%lu: %s: command expected", linenum, fname);
|
||||
cmd->code = *p;
|
||||
for (fp = cmd_fmts; fp->code; fp++)
|
||||
if (fp->code == *p)
|
||||
break;
|
||||
if (!fp->code)
|
||||
errx(1, "%lu: %s: invalid command code %c", linenum, fname, *p);
|
||||
if (naddr > fp->naddr)
|
||||
errx(1,
|
||||
"%lu: %s: command %c expects up to %d address(es), found %d",
|
||||
linenum, fname, *p, fp->naddr, naddr);
|
||||
switch (fp->args) {
|
||||
case NONSEL: /* ! */
|
||||
p++;
|
||||
EATSPACE();
|
||||
cmd->nonsel = ! cmd->nonsel;
|
||||
goto nonsel;
|
||||
case GROUP: /* { */
|
||||
p++;
|
||||
EATSPACE();
|
||||
cmd->next = stack;
|
||||
stack = cmd;
|
||||
link = &cmd->u.c;
|
||||
if (*p)
|
||||
goto semicolon;
|
||||
break;
|
||||
case ENDGROUP:
|
||||
/*
|
||||
* Short-circuit command processing, since end of
|
||||
* group is really just a noop.
|
||||
*/
|
||||
cmd->nonsel = 1;
|
||||
if (stack == 0)
|
||||
errx(1, "%lu: %s: unexpected }", linenum, fname);
|
||||
cmd2 = stack;
|
||||
stack = cmd2->next;
|
||||
cmd2->next = cmd;
|
||||
/*FALLTHROUGH*/
|
||||
case EMPTY: /* d D g G h H l n N p P q x = \0 */
|
||||
p++;
|
||||
EATSPACE();
|
||||
if (*p == ';') {
|
||||
p++;
|
||||
link = &cmd->next;
|
||||
goto semicolon;
|
||||
}
|
||||
if (*p)
|
||||
errx(1, "%lu: %s: extra characters at the end of %c command",
|
||||
linenum, fname, cmd->code);
|
||||
break;
|
||||
case TEXT: /* a c i */
|
||||
p++;
|
||||
EATSPACE();
|
||||
if (*p != '\\')
|
||||
errx(1,
|
||||
"%lu: %s: command %c expects \\ followed by text", linenum, fname, cmd->code);
|
||||
p++;
|
||||
EATSPACE();
|
||||
if (*p)
|
||||
errx(1,
|
||||
"%lu: %s: extra characters after \\ at the end of %c command",
|
||||
linenum, fname, cmd->code);
|
||||
cmd->t = compile_text();
|
||||
break;
|
||||
case COMMENT: /* \0 # */
|
||||
break;
|
||||
case WFILE: /* w */
|
||||
p++;
|
||||
EATSPACE();
|
||||
if (*p == '\0')
|
||||
errx(1, "%lu: %s: filename expected", linenum, fname);
|
||||
cmd->t = duptoeol(p, "w command");
|
||||
if (aflag)
|
||||
cmd->u.fd = -1;
|
||||
else if ((cmd->u.fd = open(p,
|
||||
O_WRONLY|O_APPEND|O_CREAT|O_TRUNC,
|
||||
DEFFILEMODE)) == -1)
|
||||
err(1, "%s", p);
|
||||
break;
|
||||
case RFILE: /* r */
|
||||
p++;
|
||||
EATSPACE();
|
||||
if (*p == '\0')
|
||||
errx(1, "%lu: %s: filename expected", linenum, fname);
|
||||
else
|
||||
cmd->t = duptoeol(p, "read command");
|
||||
break;
|
||||
case BRANCH: /* b t */
|
||||
p++;
|
||||
EATSPACE();
|
||||
if (*p == '\0')
|
||||
cmd->t = NULL;
|
||||
else
|
||||
cmd->t = duptoeol(p, "branch");
|
||||
break;
|
||||
case LABEL: /* : */
|
||||
p++;
|
||||
EATSPACE();
|
||||
cmd->t = duptoeol(p, "label");
|
||||
if (strlen(p) == 0)
|
||||
errx(1, "%lu: %s: empty label", linenum, fname);
|
||||
enterlabel(cmd);
|
||||
break;
|
||||
case SUBST: /* s */
|
||||
p++;
|
||||
if (*p == '\0' || *p == '\\')
|
||||
errx(1,
|
||||
"%lu: %s: substitute pattern can not be delimited by newline or backslash",
|
||||
linenum, fname);
|
||||
if ((cmd->u.s = malloc(sizeof(struct s_subst))) == NULL)
|
||||
err(1, "malloc");
|
||||
p = compile_re(p, &cmd->u.s->re);
|
||||
if (p == NULL)
|
||||
errx(1,
|
||||
"%lu: %s: unterminated substitute pattern", linenum, fname);
|
||||
--p;
|
||||
p = compile_subst(p, cmd->u.s);
|
||||
p = compile_flags(p, cmd->u.s);
|
||||
EATSPACE();
|
||||
if (*p == ';') {
|
||||
p++;
|
||||
link = &cmd->next;
|
||||
goto semicolon;
|
||||
}
|
||||
break;
|
||||
case TR: /* y */
|
||||
p++;
|
||||
p = compile_tr(p, (char **)&cmd->u.y);
|
||||
EATSPACE();
|
||||
if (*p == ';') {
|
||||
p++;
|
||||
link = &cmd->next;
|
||||
goto semicolon;
|
||||
}
|
||||
if (*p)
|
||||
errx(1,
|
||||
"%lu: %s: extra text at the end of a transform command", linenum, fname);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Get a delimited string. P points to the delimeter of the string; d points
|
||||
* to a buffer area. Newline and delimiter escapes are processed; other
|
||||
* escapes are ignored.
|
||||
*
|
||||
* Returns a pointer to the first character after the final delimiter or NULL
|
||||
* in the case of a non-terminated string. The character array d is filled
|
||||
* with the processed string.
|
||||
*/
|
||||
static char *
|
||||
compile_delimited(p, d)
|
||||
char *p, *d;
|
||||
{
|
||||
char c;
|
||||
|
||||
c = *p++;
|
||||
if (c == '\0')
|
||||
return (NULL);
|
||||
else if (c == '\\')
|
||||
errx(1, "%lu: %s: \\ can not be used as a string delimiter",
|
||||
linenum, fname);
|
||||
else if (c == '\n')
|
||||
errx(1, "%lu: %s: newline can not be used as a string delimiter",
|
||||
linenum, fname);
|
||||
while (*p) {
|
||||
if (*p == '[') {
|
||||
if ((d = compile_ccl(&p, d)) == NULL)
|
||||
errx(1, "%lu: %s: unbalanced brackets ([])", linenum, fname);
|
||||
continue;
|
||||
} else if (*p == '\\' && p[1] == '[') {
|
||||
*d++ = *p++;
|
||||
} else if (*p == '\\' && p[1] == c)
|
||||
p++;
|
||||
else if (*p == '\\' && p[1] == 'n') {
|
||||
*d++ = '\n';
|
||||
p += 2;
|
||||
continue;
|
||||
} else if (*p == '\\' && p[1] == '\\')
|
||||
*d++ = *p++;
|
||||
else if (*p == c) {
|
||||
*d = '\0';
|
||||
return (p + 1);
|
||||
}
|
||||
*d++ = *p++;
|
||||
}
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
|
||||
/* compile_ccl: expand a POSIX character class */
|
||||
static char *
|
||||
compile_ccl(sp, t)
|
||||
char **sp;
|
||||
char *t;
|
||||
{
|
||||
int c, d;
|
||||
char *s = *sp;
|
||||
|
||||
*t++ = *s++;
|
||||
if (*s == '^')
|
||||
*t++ = *s++;
|
||||
if (*s == ']')
|
||||
*t++ = *s++;
|
||||
for (; *s && (*t = *s) != ']'; s++, t++)
|
||||
if (*s == '[' && ((d = *(s+1)) == '.' || d == ':' || d == '=')) {
|
||||
*++t = *++s, t++, s++;
|
||||
for (c = *s; (*t = *s) != ']' || c != d; s++, t++)
|
||||
if ((c = *s) == '\0')
|
||||
return NULL;
|
||||
} else if (*s == '\\' && s[1] == 'n')
|
||||
*t = '\n', s++;
|
||||
return (*s == ']') ? *sp = ++s, ++t : NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get a regular expression. P points to the delimiter of the regular
|
||||
* expression; repp points to the address of a regexp pointer. Newline
|
||||
* and delimiter escapes are processed; other escapes are ignored.
|
||||
* Returns a pointer to the first character after the final delimiter
|
||||
* or NULL in the case of a non terminated regular expression. The regexp
|
||||
* pointer is set to the compiled regular expression.
|
||||
* Cflags are passed to regcomp.
|
||||
*/
|
||||
static char *
|
||||
compile_re(p, repp)
|
||||
char *p;
|
||||
regex_t **repp;
|
||||
{
|
||||
int eval;
|
||||
char re[_POSIX2_LINE_MAX + 1];
|
||||
|
||||
p = compile_delimited(p, re);
|
||||
if (p && strlen(re) == 0) {
|
||||
*repp = NULL;
|
||||
return (p);
|
||||
}
|
||||
if ((*repp = malloc(sizeof(regex_t))) == NULL)
|
||||
err(1, "malloc");
|
||||
if (p && (eval = regcomp(*repp, re, rflags)) != 0)
|
||||
errx(1, "%lu: %s: RE error: %s",
|
||||
linenum, fname, strregerror(eval, *repp));
|
||||
if (maxnsub < (*repp)->re_nsub)
|
||||
maxnsub = (*repp)->re_nsub;
|
||||
return (p);
|
||||
}
|
||||
|
||||
/*
|
||||
* Compile the substitution string of a regular expression and set res to
|
||||
* point to a saved copy of it. Nsub is the number of parenthesized regular
|
||||
* expressions.
|
||||
*/
|
||||
static char *
|
||||
compile_subst(p, s)
|
||||
char *p;
|
||||
struct s_subst *s;
|
||||
{
|
||||
static char lbuf[_POSIX2_LINE_MAX + 1];
|
||||
int asize, size;
|
||||
u_char ref;
|
||||
char c, *text, *op, *sp;
|
||||
int more = 1, sawesc = 0;
|
||||
|
||||
c = *p++; /* Terminator character */
|
||||
if (c == '\0')
|
||||
return (NULL);
|
||||
|
||||
s->maxbref = 0;
|
||||
s->linenum = linenum;
|
||||
asize = 2 * _POSIX2_LINE_MAX + 1;
|
||||
if ((text = malloc(asize)) == NULL)
|
||||
err(1, "malloc");
|
||||
size = 0;
|
||||
do {
|
||||
op = sp = text + size;
|
||||
for (; *p; p++) {
|
||||
if (*p == '\\' || sawesc) {
|
||||
/*
|
||||
* If this is a continuation from the last
|
||||
* buffer, we won't have a character to
|
||||
* skip over.
|
||||
*/
|
||||
if (sawesc)
|
||||
sawesc = 0;
|
||||
else
|
||||
p++;
|
||||
|
||||
if (*p == '\0') {
|
||||
/*
|
||||
* This escaped character is continued
|
||||
* in the next part of the line. Note
|
||||
* this fact, then cause the loop to
|
||||
* exit w/ normal EOL case and reenter
|
||||
* above with the new buffer.
|
||||
*/
|
||||
sawesc = 1;
|
||||
p--;
|
||||
continue;
|
||||
} else if (strchr("123456789", *p) != NULL) {
|
||||
*sp++ = '\\';
|
||||
ref = *p - '0';
|
||||
if (s->re != NULL &&
|
||||
ref > s->re->re_nsub)
|
||||
errx(1, "%lu: %s: \\%c not defined in the RE",
|
||||
linenum, fname, *p);
|
||||
if (s->maxbref < ref)
|
||||
s->maxbref = ref;
|
||||
} else if (*p == '&' || *p == '\\')
|
||||
*sp++ = '\\';
|
||||
} else if (*p == c) {
|
||||
if (*++p == '\0' && more) {
|
||||
if (cu_fgets(lbuf, sizeof(lbuf), &more))
|
||||
p = lbuf;
|
||||
}
|
||||
*sp++ = '\0';
|
||||
size += sp - op;
|
||||
if ((s->new = realloc(text, size)) == NULL)
|
||||
err(1, "realloc");
|
||||
return (p);
|
||||
} else if (*p == '\n') {
|
||||
errx(1,
|
||||
"%lu: %s: unescaped newline inside substitute pattern", linenum, fname);
|
||||
/* NOTREACHED */
|
||||
}
|
||||
*sp++ = *p;
|
||||
}
|
||||
size += sp - op;
|
||||
if (asize - size < _POSIX2_LINE_MAX + 1) {
|
||||
asize *= 2;
|
||||
if ((text = realloc(text, asize)) == NULL)
|
||||
err(1, "realloc");
|
||||
}
|
||||
} while (cu_fgets(p = lbuf, sizeof(lbuf), &more));
|
||||
errx(1, "%lu: %s: unterminated substitute in regular expression",
|
||||
linenum, fname);
|
||||
/* NOTREACHED */
|
||||
}
|
||||
|
||||
/*
|
||||
* Compile the flags of the s command
|
||||
*/
|
||||
static char *
|
||||
compile_flags(p, s)
|
||||
char *p;
|
||||
struct s_subst *s;
|
||||
{
|
||||
int gn; /* True if we have seen g or n */
|
||||
char wfile[_POSIX2_LINE_MAX + 1], *q;
|
||||
|
||||
s->n = 1; /* Default */
|
||||
s->p = 0;
|
||||
s->wfile = NULL;
|
||||
s->wfd = -1;
|
||||
for (gn = 0;;) {
|
||||
EATSPACE(); /* EXTENSION */
|
||||
switch (*p) {
|
||||
case 'g':
|
||||
if (gn)
|
||||
errx(1,
|
||||
"%lu: %s: more than one number or 'g' in substitute flags", linenum, fname);
|
||||
gn = 1;
|
||||
s->n = 0;
|
||||
break;
|
||||
case '\0':
|
||||
case '\n':
|
||||
case ';':
|
||||
return (p);
|
||||
case 'p':
|
||||
s->p = 1;
|
||||
break;
|
||||
case '1': case '2': case '3':
|
||||
case '4': case '5': case '6':
|
||||
case '7': case '8': case '9':
|
||||
if (gn)
|
||||
errx(1,
|
||||
"%lu: %s: more than one number or 'g' in substitute flags", linenum, fname);
|
||||
gn = 1;
|
||||
/* XXX Check for overflow */
|
||||
s->n = (int)strtol(p, &p, 10);
|
||||
break;
|
||||
case 'w':
|
||||
p++;
|
||||
#ifdef HISTORIC_PRACTICE
|
||||
if (*p != ' ') {
|
||||
warnx("%lu: %s: space missing before w wfile", linenum, fname);
|
||||
return (p);
|
||||
}
|
||||
#endif
|
||||
EATSPACE();
|
||||
q = wfile;
|
||||
while (*p) {
|
||||
if (*p == '\n')
|
||||
break;
|
||||
*q++ = *p++;
|
||||
}
|
||||
*q = '\0';
|
||||
if (q == wfile)
|
||||
errx(1, "%lu: %s: no wfile specified", linenum, fname);
|
||||
s->wfile = strdup(wfile);
|
||||
if (!aflag && (s->wfd = open(wfile,
|
||||
O_WRONLY|O_APPEND|O_CREAT|O_TRUNC,
|
||||
DEFFILEMODE)) == -1)
|
||||
err(1, "%s", wfile);
|
||||
return (p);
|
||||
default:
|
||||
errx(1, "%lu: %s: bad flag in substitute command: '%c'",
|
||||
linenum, fname, *p);
|
||||
break;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Compile a translation set of strings into a lookup table.
|
||||
*/
|
||||
static char *
|
||||
compile_tr(p, transtab)
|
||||
char *p;
|
||||
char **transtab;
|
||||
{
|
||||
int i;
|
||||
char *lt, *op, *np;
|
||||
char old[_POSIX2_LINE_MAX + 1];
|
||||
char new[_POSIX2_LINE_MAX + 1];
|
||||
|
||||
if (*p == '\0' || *p == '\\')
|
||||
errx(1,
|
||||
"%lu: %s: transform pattern can not be delimited by newline or backslash",
|
||||
linenum, fname);
|
||||
p = compile_delimited(p, old);
|
||||
if (p == NULL)
|
||||
errx(1, "%lu: %s: unterminated transform source string",
|
||||
linenum, fname);
|
||||
p = compile_delimited(--p, new);
|
||||
if (p == NULL)
|
||||
errx(1, "%lu: %s: unterminated transform target string",
|
||||
linenum, fname);
|
||||
EATSPACE();
|
||||
if (strlen(new) != strlen(old))
|
||||
errx(1, "%lu: %s: transform strings are not the same length",
|
||||
linenum, fname);
|
||||
/* We assume characters are 8 bits */
|
||||
if ((lt = malloc(UCHAR_MAX)) == NULL)
|
||||
err(1, "malloc");
|
||||
for (i = 0; i <= UCHAR_MAX; i++)
|
||||
lt[i] = (char)i;
|
||||
for (op = old, np = new; *op; op++, np++)
|
||||
lt[(u_char)*op] = *np;
|
||||
*transtab = lt;
|
||||
return (p);
|
||||
}
|
||||
|
||||
/*
|
||||
* Compile the text following an a or i command.
|
||||
*/
|
||||
static char *
|
||||
compile_text()
|
||||
{
|
||||
int asize, esc_nl, size;
|
||||
char *text, *p, *op, *s;
|
||||
char lbuf[_POSIX2_LINE_MAX + 1];
|
||||
|
||||
asize = 2 * _POSIX2_LINE_MAX + 1;
|
||||
if ((text = malloc(asize)) == NULL)
|
||||
err(1, "malloc");
|
||||
size = 0;
|
||||
while (cu_fgets(lbuf, sizeof(lbuf), NULL)) {
|
||||
op = s = text + size;
|
||||
p = lbuf;
|
||||
EATSPACE();
|
||||
for (esc_nl = 0; *p != '\0'; p++) {
|
||||
if (*p == '\\' && p[1] != '\0' && *++p == '\n')
|
||||
esc_nl = 1;
|
||||
*s++ = *p;
|
||||
}
|
||||
size += s - op;
|
||||
if (!esc_nl) {
|
||||
*s = '\0';
|
||||
break;
|
||||
}
|
||||
if (asize - size < _POSIX2_LINE_MAX + 1) {
|
||||
asize *= 2;
|
||||
if ((text = realloc(text, asize)) == NULL)
|
||||
err(1, "realloc");
|
||||
}
|
||||
}
|
||||
text[size] = '\0';
|
||||
if ((p = realloc(text, size + 1)) == NULL)
|
||||
err(1, "realloc");
|
||||
return (p);
|
||||
}
|
||||
|
||||
/*
|
||||
* Get an address and return a pointer to the first character after
|
||||
* it. Fill the structure pointed to according to the address.
|
||||
*/
|
||||
static char *
|
||||
compile_addr(p, a)
|
||||
char *p;
|
||||
struct s_addr *a;
|
||||
{
|
||||
char *end;
|
||||
|
||||
switch (*p) {
|
||||
case '\\': /* Context address */
|
||||
++p;
|
||||
/* FALLTHROUGH */
|
||||
case '/': /* Context address */
|
||||
p = compile_re(p, &a->u.r);
|
||||
if (p == NULL)
|
||||
errx(1, "%lu: %s: unterminated regular expression", linenum, fname);
|
||||
a->type = AT_RE;
|
||||
return (p);
|
||||
|
||||
case '$': /* Last line */
|
||||
a->type = AT_LAST;
|
||||
return (p + 1);
|
||||
/* Line number */
|
||||
case '0': case '1': case '2': case '3': case '4':
|
||||
case '5': case '6': case '7': case '8': case '9':
|
||||
a->type = AT_LINE;
|
||||
a->u.l = strtol(p, &end, 10);
|
||||
return (end);
|
||||
default:
|
||||
errx(1, "%lu: %s: expected context address", linenum, fname);
|
||||
return (NULL);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* duptoeol --
|
||||
* Return a copy of all the characters up to \n or \0.
|
||||
*/
|
||||
static char *
|
||||
duptoeol(s, ctype)
|
||||
char *s;
|
||||
const char *ctype;
|
||||
{
|
||||
size_t len;
|
||||
int ws;
|
||||
char *p, *start;
|
||||
|
||||
ws = 0;
|
||||
for (start = s; *s != '\0' && *s != '\n'; ++s)
|
||||
ws = isspace((unsigned char)*s);
|
||||
*s = '\0';
|
||||
if (ws)
|
||||
warnx("%lu: %s: whitespace after %s", linenum, fname, ctype);
|
||||
len = s - start + 1;
|
||||
if ((p = malloc(len)) == NULL)
|
||||
err(1, "malloc");
|
||||
return (memmove(p, start, len));
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert goto label names to addresses, and count a and r commands, in
|
||||
* the given subset of the script. Free the memory used by labels in b
|
||||
* and t commands (but not by :).
|
||||
*
|
||||
* TODO: Remove } nodes
|
||||
*/
|
||||
static void
|
||||
fixuplabel(cp, end)
|
||||
struct s_command *cp, *end;
|
||||
{
|
||||
|
||||
for (; cp != end; cp = cp->next)
|
||||
switch (cp->code) {
|
||||
case 'a':
|
||||
case 'r':
|
||||
appendnum++;
|
||||
break;
|
||||
case 'b':
|
||||
case 't':
|
||||
/* Resolve branch target. */
|
||||
if (cp->t == NULL) {
|
||||
cp->u.c = NULL;
|
||||
break;
|
||||
}
|
||||
if ((cp->u.c = findlabel(cp->t)) == NULL)
|
||||
errx(1, "%lu: %s: undefined label '%s'", linenum, fname, cp->t);
|
||||
free(cp->t);
|
||||
break;
|
||||
case '{':
|
||||
/* Do interior commands. */
|
||||
fixuplabel(cp->u.c, cp->next);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Associate the given command label for later lookup.
|
||||
*/
|
||||
static void
|
||||
enterlabel(cp)
|
||||
struct s_command *cp;
|
||||
{
|
||||
struct labhash **lhp, *lh;
|
||||
u_char *p;
|
||||
u_int h, c;
|
||||
|
||||
for (h = 0, p = (u_char *)cp->t; (c = *p) != 0; p++)
|
||||
h = (h << 5) + h + c;
|
||||
lhp = &labels[h & LHMASK];
|
||||
for (lh = *lhp; lh != NULL; lh = lh->lh_next)
|
||||
if (lh->lh_hash == h && strcmp(cp->t, lh->lh_cmd->t) == 0)
|
||||
errx(1, "%lu: %s: duplicate label '%s'", linenum, fname, cp->t);
|
||||
if ((lh = malloc(sizeof *lh)) == NULL)
|
||||
err(1, "malloc");
|
||||
lh->lh_next = *lhp;
|
||||
lh->lh_hash = h;
|
||||
lh->lh_cmd = cp;
|
||||
lh->lh_ref = 0;
|
||||
*lhp = lh;
|
||||
}
|
||||
|
||||
/*
|
||||
* Find the label contained in the command l in the command linked
|
||||
* list cp. L is excluded from the search. Return NULL if not found.
|
||||
*/
|
||||
static struct s_command *
|
||||
findlabel(name)
|
||||
char *name;
|
||||
{
|
||||
struct labhash *lh;
|
||||
u_char *p;
|
||||
u_int h, c;
|
||||
|
||||
for (h = 0, p = (u_char *)name; (c = *p) != 0; p++)
|
||||
h = (h << 5) + h + c;
|
||||
for (lh = labels[h & LHMASK]; lh != NULL; lh = lh->lh_next) {
|
||||
if (lh->lh_hash == h && strcmp(name, lh->lh_cmd->t) == 0) {
|
||||
lh->lh_ref = 1;
|
||||
return (lh->lh_cmd);
|
||||
}
|
||||
}
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Warn about any unused labels. As a side effect, release the label hash
|
||||
* table space.
|
||||
*/
|
||||
static void
|
||||
uselabel()
|
||||
{
|
||||
struct labhash *lh, *next;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < LHSZ; i++) {
|
||||
for (lh = labels[i]; lh != NULL; lh = next) {
|
||||
next = lh->lh_next;
|
||||
if (!lh->lh_ref)
|
||||
warnx("%lu: %s: unused label '%s'",
|
||||
linenum, fname, lh->lh_cmd->t);
|
||||
free(lh);
|
||||
}
|
||||
}
|
||||
}
|
136
textproc/sed_inplace/src/defs.h
Normal file
136
textproc/sed_inplace/src/defs.h
Normal file
@ -0,0 +1,136 @@
|
||||
/*-
|
||||
* Copyright (c) 1992 Diomidis Spinellis.
|
||||
* Copyright (c) 1992, 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to Berkeley by
|
||||
* Diomidis Spinellis of Imperial College, University of London.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. All advertising materials mentioning features or use of this software
|
||||
* must display the following acknowledgement:
|
||||
* This product includes software developed by the University of
|
||||
* California, Berkeley and its contributors.
|
||||
* 4. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* @(#)defs.h 8.1 (Berkeley) 6/6/93
|
||||
*/
|
||||
|
||||
/*
|
||||
* Types of address specifications
|
||||
*/
|
||||
enum e_atype {
|
||||
AT_RE, /* Line that match RE */
|
||||
AT_LINE, /* Specific line */
|
||||
AT_LAST, /* Last line */
|
||||
};
|
||||
|
||||
/*
|
||||
* Format of an address
|
||||
*/
|
||||
struct s_addr {
|
||||
enum e_atype type; /* Address type */
|
||||
union {
|
||||
u_long l; /* Line number */
|
||||
regex_t *r; /* Regular expression */
|
||||
} u;
|
||||
};
|
||||
|
||||
/*
|
||||
* Substitution command
|
||||
*/
|
||||
struct s_subst {
|
||||
int n; /* Occurrence to subst. */
|
||||
int p; /* True if p flag */
|
||||
char *wfile; /* NULL if no wfile */
|
||||
int wfd; /* Cached file descriptor */
|
||||
regex_t *re; /* Regular expression */
|
||||
int maxbref; /* Largest backreference. */
|
||||
u_long linenum; /* Line number. */
|
||||
char *new; /* Replacement text */
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* An internally compiled command.
|
||||
* Initialy, label references are stored in t, on a second pass they
|
||||
* are updated to pointers.
|
||||
*/
|
||||
struct s_command {
|
||||
struct s_command *next; /* Pointer to next command */
|
||||
struct s_addr *a1, *a2; /* Start and end address */
|
||||
char *t; /* Text for : a c i r w */
|
||||
union {
|
||||
struct s_command *c; /* Command(s) for b t { */
|
||||
struct s_subst *s; /* Substitute command */
|
||||
u_char *y; /* Replace command array */
|
||||
int fd; /* File descriptor for w */
|
||||
} u;
|
||||
char code; /* Command code */
|
||||
u_int nonsel:1; /* True if ! */
|
||||
u_int inrange:1; /* True if in range */
|
||||
};
|
||||
|
||||
/*
|
||||
* Types of command arguments recognised by the parser
|
||||
*/
|
||||
enum e_args {
|
||||
EMPTY, /* d D g G h H l n N p P q x = \0 */
|
||||
TEXT, /* a c i */
|
||||
NONSEL, /* ! */
|
||||
GROUP, /* { */
|
||||
ENDGROUP, /* } */
|
||||
COMMENT, /* # */
|
||||
BRANCH, /* b t */
|
||||
LABEL, /* : */
|
||||
RFILE, /* r */
|
||||
WFILE, /* w */
|
||||
SUBST, /* s */
|
||||
TR /* y */
|
||||
};
|
||||
|
||||
/*
|
||||
* Structure containing things to append before a line is read
|
||||
*/
|
||||
struct s_appends {
|
||||
enum {AP_STRING, AP_FILE} type;
|
||||
char *s;
|
||||
size_t len;
|
||||
};
|
||||
|
||||
enum e_spflag {
|
||||
APPEND, /* Append to the contents. */
|
||||
REPLACE, /* Replace the contents. */
|
||||
};
|
||||
|
||||
/*
|
||||
* Structure for a space (process, hold, otherwise).
|
||||
*/
|
||||
typedef struct {
|
||||
char *space; /* Current space pointer. */
|
||||
size_t len; /* Current length. */
|
||||
int deleted; /* If deleted. */
|
||||
char *back; /* Backing memory. */
|
||||
size_t blen; /* Backing memory length. */
|
||||
} SPACE;
|
58
textproc/sed_inplace/src/extern.h
Normal file
58
textproc/sed_inplace/src/extern.h
Normal file
@ -0,0 +1,58 @@
|
||||
/*-
|
||||
* Copyright (c) 1992 Diomidis Spinellis.
|
||||
* Copyright (c) 1992, 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to Berkeley by
|
||||
* Diomidis Spinellis of Imperial College, University of London.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. All advertising materials mentioning features or use of this software
|
||||
* must display the following acknowledgement:
|
||||
* This product includes software developed by the University of
|
||||
* California, Berkeley and its contributors.
|
||||
* 4. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* @(#)extern.h 8.1 (Berkeley) 6/6/93
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
extern struct s_command *prog;
|
||||
extern struct s_appends *appends;
|
||||
extern regmatch_t *match;
|
||||
extern size_t maxnsub;
|
||||
extern u_long linenum;
|
||||
extern int appendnum;
|
||||
extern int lastline;
|
||||
extern int aflag, eflag, nflag;
|
||||
extern const char *fname;
|
||||
extern int rflags; /* regex flags to use */
|
||||
|
||||
void cfclose(struct s_command *, struct s_command *);
|
||||
void compile(void);
|
||||
void cspace(SPACE *, char *, size_t, enum e_spflag);
|
||||
char *cu_fgets(char *, int, int *);
|
||||
int mf_fgets(SPACE *, enum e_spflag);
|
||||
void process(void);
|
||||
char *strregerror(int, regex_t *);
|
466
textproc/sed_inplace/src/main.c
Normal file
466
textproc/sed_inplace/src/main.c
Normal file
@ -0,0 +1,466 @@
|
||||
/*-
|
||||
* Copyright (c) 1992 Diomidis Spinellis.
|
||||
* Copyright (c) 1992, 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to Berkeley by
|
||||
* Diomidis Spinellis of Imperial College, University of London.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. All advertising materials mentioning features or use of this software
|
||||
* must display the following acknowledgement:
|
||||
* This product includes software developed by the University of
|
||||
* California, Berkeley and its contributors.
|
||||
* 4. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD: /tmp/pcvs/ports/textproc/sed_inplace/src/Attic/main.c,v 1.1 2002-06-17 08:46:11 sobomax Exp $");
|
||||
|
||||
#ifndef lint
|
||||
static const char copyright[] =
|
||||
"@(#) Copyright (c) 1992, 1993\n\
|
||||
The Regents of the University of California. All rights reserved.\n";
|
||||
#endif
|
||||
|
||||
#ifndef lint
|
||||
static const char sccsid[] = "@(#)main.c 8.2 (Berkeley) 1/3/94";
|
||||
#endif
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <locale.h>
|
||||
#include <regex.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "defs.h"
|
||||
#include "extern.h"
|
||||
|
||||
/*
|
||||
* Linked list of units (strings and files) to be compiled
|
||||
*/
|
||||
struct s_compunit {
|
||||
struct s_compunit *next;
|
||||
enum e_cut {CU_FILE, CU_STRING} type;
|
||||
char *s; /* Pointer to string or fname */
|
||||
};
|
||||
|
||||
/*
|
||||
* Linked list pointer to compilation units and pointer to current
|
||||
* next pointer.
|
||||
*/
|
||||
static struct s_compunit *script, **cu_nextp = &script;
|
||||
|
||||
/*
|
||||
* Linked list of files to be processed
|
||||
*/
|
||||
struct s_flist {
|
||||
char *fname;
|
||||
struct s_flist *next;
|
||||
};
|
||||
|
||||
/*
|
||||
* Linked list pointer to files and pointer to current
|
||||
* next pointer.
|
||||
*/
|
||||
static struct s_flist *files, **fl_nextp = &files;
|
||||
|
||||
int aflag, eflag, nflag;
|
||||
int rflags = 0;
|
||||
static int rval; /* Exit status */
|
||||
|
||||
/*
|
||||
* Current file and line number; line numbers restart across compilation
|
||||
* units, but span across input files.
|
||||
*/
|
||||
const char *fname; /* File name. */
|
||||
const char *inplace; /* Inplace edit file extension. */
|
||||
u_long linenum;
|
||||
int lastline; /* TRUE on the last line of the last file */
|
||||
|
||||
static void add_compunit(enum e_cut, char *);
|
||||
static void add_file(char *);
|
||||
static int inplace_edit(char **);
|
||||
static void usage(void);
|
||||
|
||||
int
|
||||
main(argc, argv)
|
||||
int argc;
|
||||
char *argv[];
|
||||
{
|
||||
int c, fflag;
|
||||
char *temp_arg;
|
||||
|
||||
(void) setlocale(LC_ALL, "");
|
||||
|
||||
fflag = 0;
|
||||
inplace = NULL;
|
||||
|
||||
while ((c = getopt(argc, argv, "Eae:f:i:n")) != -1)
|
||||
switch (c) {
|
||||
case 'E':
|
||||
rflags = REG_EXTENDED;
|
||||
break;
|
||||
case 'a':
|
||||
aflag = 1;
|
||||
break;
|
||||
case 'e':
|
||||
eflag = 1;
|
||||
if ((temp_arg = malloc(strlen(optarg) + 2)) == NULL)
|
||||
err(1, "malloc");
|
||||
strcpy(temp_arg, optarg);
|
||||
strcat(temp_arg, "\n");
|
||||
add_compunit(CU_STRING, temp_arg);
|
||||
break;
|
||||
case 'f':
|
||||
fflag = 1;
|
||||
add_compunit(CU_FILE, optarg);
|
||||
break;
|
||||
case 'i':
|
||||
inplace = optarg;
|
||||
break;
|
||||
case 'n':
|
||||
nflag = 1;
|
||||
break;
|
||||
default:
|
||||
case '?':
|
||||
usage();
|
||||
}
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
|
||||
/* First usage case; script is the first arg */
|
||||
if (!eflag && !fflag && *argv) {
|
||||
add_compunit(CU_STRING, *argv);
|
||||
argv++;
|
||||
}
|
||||
|
||||
compile();
|
||||
|
||||
/* Continue with first and start second usage */
|
||||
if (*argv)
|
||||
for (; *argv; argv++)
|
||||
add_file(*argv);
|
||||
else
|
||||
add_file(NULL);
|
||||
process();
|
||||
cfclose(prog, NULL);
|
||||
if (fclose(stdout))
|
||||
err(1, "stdout");
|
||||
exit(rval);
|
||||
}
|
||||
|
||||
static void
|
||||
usage()
|
||||
{
|
||||
(void)fprintf(stderr, "%s\n%s\n",
|
||||
"usage: sed script [-Ean] [-i extension] [file ...]",
|
||||
" sed [-an] [-i extension] [-e script] ... [-f script_file] ... [file ...]");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Like fgets, but go through the chain of compilation units chaining them
|
||||
* together. Empty strings and files are ignored.
|
||||
*/
|
||||
char *
|
||||
cu_fgets(buf, n, more)
|
||||
char *buf;
|
||||
int n;
|
||||
int *more;
|
||||
{
|
||||
static enum {ST_EOF, ST_FILE, ST_STRING} state = ST_EOF;
|
||||
static FILE *f; /* Current open file */
|
||||
static char *s; /* Current pointer inside string */
|
||||
static char string_ident[30];
|
||||
char *p;
|
||||
|
||||
again:
|
||||
switch (state) {
|
||||
case ST_EOF:
|
||||
if (script == NULL) {
|
||||
if (more != NULL)
|
||||
*more = 0;
|
||||
return (NULL);
|
||||
}
|
||||
linenum = 0;
|
||||
switch (script->type) {
|
||||
case CU_FILE:
|
||||
if ((f = fopen(script->s, "r")) == NULL)
|
||||
err(1, "%s", script->s);
|
||||
fname = script->s;
|
||||
state = ST_FILE;
|
||||
goto again;
|
||||
case CU_STRING:
|
||||
if ((snprintf(string_ident,
|
||||
sizeof(string_ident), "\"%s\"", script->s)) >=
|
||||
sizeof(string_ident) - 1)
|
||||
(void)strcpy(string_ident +
|
||||
sizeof(string_ident) - 6, " ...\"");
|
||||
fname = string_ident;
|
||||
s = script->s;
|
||||
state = ST_STRING;
|
||||
goto again;
|
||||
}
|
||||
case ST_FILE:
|
||||
if ((p = fgets(buf, n, f)) != NULL) {
|
||||
linenum++;
|
||||
if (linenum == 1 && buf[0] == '#' && buf[1] == 'n')
|
||||
nflag = 1;
|
||||
if (more != NULL)
|
||||
*more = !feof(f);
|
||||
return (p);
|
||||
}
|
||||
script = script->next;
|
||||
(void)fclose(f);
|
||||
state = ST_EOF;
|
||||
goto again;
|
||||
case ST_STRING:
|
||||
if (linenum == 0 && s[0] == '#' && s[1] == 'n')
|
||||
nflag = 1;
|
||||
p = buf;
|
||||
for (;;) {
|
||||
if (n-- <= 1) {
|
||||
*p = '\0';
|
||||
linenum++;
|
||||
if (more != NULL)
|
||||
*more = 1;
|
||||
return (buf);
|
||||
}
|
||||
switch (*s) {
|
||||
case '\0':
|
||||
state = ST_EOF;
|
||||
if (s == script->s) {
|
||||
script = script->next;
|
||||
goto again;
|
||||
} else {
|
||||
script = script->next;
|
||||
*p = '\0';
|
||||
linenum++;
|
||||
if (more != NULL)
|
||||
*more = 0;
|
||||
return (buf);
|
||||
}
|
||||
case '\n':
|
||||
*p++ = '\n';
|
||||
*p = '\0';
|
||||
s++;
|
||||
linenum++;
|
||||
if (more != NULL)
|
||||
*more = 0;
|
||||
return (buf);
|
||||
default:
|
||||
*p++ = *s++;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* NOTREACHED */
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Like fgets, but go through the list of files chaining them together.
|
||||
* Set len to the length of the line.
|
||||
*/
|
||||
int
|
||||
mf_fgets(sp, spflag)
|
||||
SPACE *sp;
|
||||
enum e_spflag spflag;
|
||||
{
|
||||
static FILE *f; /* Current open file */
|
||||
size_t len;
|
||||
char *p;
|
||||
int c;
|
||||
static int firstfile;
|
||||
|
||||
if (f == NULL) {
|
||||
/* stdin? */
|
||||
if (files->fname == NULL) {
|
||||
if (inplace != NULL)
|
||||
errx(1, "-i may not be used with stdin");
|
||||
f = stdin;
|
||||
fname = "stdin";
|
||||
}
|
||||
firstfile = 1;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
if (f != NULL && (c = getc(f)) != EOF) {
|
||||
(void)ungetc(c, f);
|
||||
break;
|
||||
}
|
||||
/* If we are here then either eof or no files are open yet */
|
||||
if (f == stdin) {
|
||||
sp->len = 0;
|
||||
lastline = 1;
|
||||
return (0);
|
||||
}
|
||||
if (f != NULL) {
|
||||
fclose(f);
|
||||
}
|
||||
if (firstfile == 0) {
|
||||
files = files->next;
|
||||
} else
|
||||
firstfile = 0;
|
||||
if (files == NULL) {
|
||||
sp->len = 0;
|
||||
lastline = 1;
|
||||
return (0);
|
||||
}
|
||||
if (inplace != NULL) {
|
||||
if (inplace_edit(&files->fname) == -1)
|
||||
continue;
|
||||
}
|
||||
fname = files->fname;
|
||||
if ((f = fopen(fname, "r")) == NULL) {
|
||||
warn("%s", fname);
|
||||
rval = 1;
|
||||
continue;
|
||||
}
|
||||
if (inplace != NULL && *inplace == '\0')
|
||||
unlink(fname);
|
||||
}
|
||||
/*
|
||||
* We are here only when f is open and we still have something to
|
||||
* read from it.
|
||||
*
|
||||
* Use fgetln so that we can handle essentially infinite input data.
|
||||
* Can't use the pointer into the stdio buffer as the process space
|
||||
* because the ungetc() can cause it to move.
|
||||
*/
|
||||
p = fgetln(f, &len);
|
||||
if (ferror(f))
|
||||
errx(1, "%s: %s", fname, strerror(errno ? errno : EIO));
|
||||
cspace(sp, p, len, spflag);
|
||||
|
||||
linenum++;
|
||||
if (files->next == NULL) {
|
||||
if ((c = getc(f)) != EOF) {
|
||||
(void)ungetc(c, f);
|
||||
} else {
|
||||
lastline = 1;
|
||||
}
|
||||
}
|
||||
|
||||
return (1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Add a compilation unit to the linked list
|
||||
*/
|
||||
static void
|
||||
add_compunit(type, s)
|
||||
enum e_cut type;
|
||||
char *s;
|
||||
{
|
||||
struct s_compunit *cu;
|
||||
|
||||
if ((cu = malloc(sizeof(struct s_compunit))) == NULL)
|
||||
err(1, "malloc");
|
||||
cu->type = type;
|
||||
cu->s = s;
|
||||
cu->next = NULL;
|
||||
*cu_nextp = cu;
|
||||
cu_nextp = &cu->next;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add a file to the linked list
|
||||
*/
|
||||
static void
|
||||
add_file(s)
|
||||
char *s;
|
||||
{
|
||||
struct s_flist *fp;
|
||||
|
||||
if ((fp = malloc(sizeof(struct s_flist))) == NULL)
|
||||
err(1, "malloc");
|
||||
fp->next = NULL;
|
||||
*fl_nextp = fp;
|
||||
fp->fname = s;
|
||||
fl_nextp = &fp->next;
|
||||
}
|
||||
|
||||
/*
|
||||
* Modify a pointer to a filename for inplace editing and reopen stdout
|
||||
*/
|
||||
static int
|
||||
inplace_edit(filename)
|
||||
char **filename;
|
||||
{
|
||||
struct stat orig;
|
||||
int input, output;
|
||||
char backup[MAXPATHLEN];
|
||||
char *buffer;
|
||||
|
||||
if (lstat(*filename, &orig) == -1)
|
||||
err(1, "lstat");
|
||||
if ((orig.st_mode & S_IFREG) == 0) {
|
||||
warnx("cannot inplace edit %s, not a regular file", *filename);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (*inplace == '\0') {
|
||||
char template[] = "/tmp/sed.XXXXXXXXXX";
|
||||
|
||||
output = mkstemp(template);
|
||||
if (output == -1)
|
||||
err(1, "mkstemp");
|
||||
strlcpy(backup, template, MAXPATHLEN);
|
||||
} else {
|
||||
strlcpy(backup, *filename, MAXPATHLEN);
|
||||
strlcat(backup, inplace, MAXPATHLEN);
|
||||
output = open(backup, O_WRONLY | O_CREAT | O_TRUNC);
|
||||
if (output == -1)
|
||||
err(1, "open(%s)", backup);
|
||||
}
|
||||
|
||||
input = open(*filename, O_RDONLY);
|
||||
if (input == -1)
|
||||
err(1, "open(%s)", *filename);
|
||||
if (fchmod(output, orig.st_mode & ~S_IFMT) == -1)
|
||||
err(1, "chmod");
|
||||
buffer = (char *)mmap(0, orig.st_size, PROT_READ, MAP_SHARED, input, 0);
|
||||
if (buffer == MAP_FAILED)
|
||||
err(1, "mmap(%s)", *filename);
|
||||
if (write(output, buffer, orig.st_size) == -1)
|
||||
err(1, "write(%s)", backup);
|
||||
if (munmap(buffer, orig.st_size) == -1)
|
||||
err(1, "munmap(%s)", *filename);
|
||||
close(input);
|
||||
close(output);
|
||||
freopen(*filename, "w", stdout);
|
||||
*filename = strdup(backup);
|
||||
return 0;
|
||||
}
|
76
textproc/sed_inplace/src/misc.c
Normal file
76
textproc/sed_inplace/src/misc.c
Normal file
@ -0,0 +1,76 @@
|
||||
/*-
|
||||
* Copyright (c) 1992 Diomidis Spinellis.
|
||||
* Copyright (c) 1992, 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to Berkeley by
|
||||
* Diomidis Spinellis of Imperial College, University of London.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. All advertising materials mentioning features or use of this software
|
||||
* must display the following acknowledgement:
|
||||
* This product includes software developed by the University of
|
||||
* California, Berkeley and its contributors.
|
||||
* 4. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD: /tmp/pcvs/ports/textproc/sed_inplace/src/Attic/misc.c,v 1.1 2002-06-17 08:46:11 sobomax Exp $");
|
||||
|
||||
#ifndef lint
|
||||
static const char sccsid[] = "@(#)misc.c 8.1 (Berkeley) 6/6/93";
|
||||
#endif
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <err.h>
|
||||
#include <regex.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "defs.h"
|
||||
#include "extern.h"
|
||||
|
||||
/*
|
||||
* Return a string for a regular expression error passed. This is a overkill,
|
||||
* because of the silly semantics of regerror (we can never know the size of
|
||||
* the buffer).
|
||||
*/
|
||||
char *
|
||||
strregerror(errcode, preg)
|
||||
int errcode;
|
||||
regex_t *preg;
|
||||
{
|
||||
static char *oe;
|
||||
size_t s;
|
||||
|
||||
if (oe != NULL)
|
||||
free(oe);
|
||||
s = regerror(errcode, preg, "", 0);
|
||||
if ((oe = malloc(s)) == NULL)
|
||||
err(1, "malloc");
|
||||
(void)regerror(errcode, preg, oe, s);
|
||||
return (oe);
|
||||
}
|
646
textproc/sed_inplace/src/process.c
Normal file
646
textproc/sed_inplace/src/process.c
Normal file
@ -0,0 +1,646 @@
|
||||
/*-
|
||||
* Copyright (c) 1992 Diomidis Spinellis.
|
||||
* Copyright (c) 1992, 1993, 1994
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to Berkeley by
|
||||
* Diomidis Spinellis of Imperial College, University of London.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. All advertising materials mentioning features or use of this software
|
||||
* must display the following acknowledgement:
|
||||
* This product includes software developed by the University of
|
||||
* California, Berkeley and its contributors.
|
||||
* 4. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD: /tmp/pcvs/ports/textproc/sed_inplace/src/Attic/process.c,v 1.1 2002-06-17 08:46:11 sobomax Exp $");
|
||||
|
||||
#ifndef lint
|
||||
static const char sccsid[] = "@(#)process.c 8.6 (Berkeley) 4/20/94";
|
||||
#endif
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/uio.h>
|
||||
|
||||
#include <ctype.h>
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <regex.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "defs.h"
|
||||
#include "extern.h"
|
||||
|
||||
static SPACE HS, PS, SS;
|
||||
#define pd PS.deleted
|
||||
#define ps PS.space
|
||||
#define psl PS.len
|
||||
#define hs HS.space
|
||||
#define hsl HS.len
|
||||
|
||||
static inline int applies(struct s_command *);
|
||||
static void flush_appends(void);
|
||||
static void lputs(char *);
|
||||
static inline int regexec_e(regex_t *, const char *, int, int, size_t);
|
||||
static void regsub(SPACE *, char *, char *);
|
||||
static int substitute(struct s_command *);
|
||||
|
||||
struct s_appends *appends; /* Array of pointers to strings to append. */
|
||||
static int appendx; /* Index into appends array. */
|
||||
int appendnum; /* Size of appends array. */
|
||||
|
||||
static int lastaddr; /* Set by applies if last address of a range. */
|
||||
static int sdone; /* If any substitutes since last line input. */
|
||||
/* Iov structure for 'w' commands. */
|
||||
static regex_t *defpreg;
|
||||
size_t maxnsub;
|
||||
regmatch_t *match;
|
||||
|
||||
#define OUT(s) { fwrite(s, sizeof(u_char), psl, stdout); }
|
||||
|
||||
void
|
||||
process()
|
||||
{
|
||||
struct s_command *cp;
|
||||
SPACE tspace;
|
||||
size_t len, oldpsl = 0;
|
||||
char *p;
|
||||
|
||||
for (linenum = 0; mf_fgets(&PS, REPLACE);) {
|
||||
pd = 0;
|
||||
top:
|
||||
cp = prog;
|
||||
redirect:
|
||||
while (cp != NULL) {
|
||||
if (!applies(cp)) {
|
||||
cp = cp->next;
|
||||
continue;
|
||||
}
|
||||
switch (cp->code) {
|
||||
case '{':
|
||||
cp = cp->u.c;
|
||||
goto redirect;
|
||||
case 'a':
|
||||
if (appendx >= appendnum)
|
||||
if ((appends = realloc(appends,
|
||||
sizeof(struct s_appends) *
|
||||
(appendnum *= 2))) == NULL)
|
||||
err(1, "realloc");
|
||||
appends[appendx].type = AP_STRING;
|
||||
appends[appendx].s = cp->t;
|
||||
appends[appendx].len = strlen(cp->t);
|
||||
appendx++;
|
||||
break;
|
||||
case 'b':
|
||||
cp = cp->u.c;
|
||||
goto redirect;
|
||||
case 'c':
|
||||
pd = 1;
|
||||
psl = 0;
|
||||
if (cp->a2 == NULL || lastaddr)
|
||||
(void)printf("%s", cp->t);
|
||||
break;
|
||||
case 'd':
|
||||
pd = 1;
|
||||
goto new;
|
||||
case 'D':
|
||||
if (pd)
|
||||
goto new;
|
||||
if (psl == 0 ||
|
||||
(p = memchr(ps, '\n', psl - 1)) == NULL) {
|
||||
pd = 1;
|
||||
goto new;
|
||||
} else {
|
||||
psl -= (p + 1) - ps;
|
||||
memmove(ps, p + 1, psl);
|
||||
goto top;
|
||||
}
|
||||
case 'g':
|
||||
cspace(&PS, hs, hsl, REPLACE);
|
||||
break;
|
||||
case 'G':
|
||||
if (hs == NULL)
|
||||
cspace(&HS, "\n", 1, REPLACE);
|
||||
cspace(&PS, hs, hsl, 0);
|
||||
break;
|
||||
case 'h':
|
||||
cspace(&HS, ps, psl, REPLACE);
|
||||
break;
|
||||
case 'H':
|
||||
cspace(&HS, ps, psl, 0);
|
||||
break;
|
||||
case 'i':
|
||||
(void)printf("%s", cp->t);
|
||||
break;
|
||||
case 'l':
|
||||
lputs(ps);
|
||||
break;
|
||||
case 'n':
|
||||
if (!nflag && !pd)
|
||||
OUT(ps)
|
||||
flush_appends();
|
||||
if (!mf_fgets(&PS, REPLACE))
|
||||
exit(0);
|
||||
pd = 0;
|
||||
break;
|
||||
case 'N':
|
||||
flush_appends();
|
||||
if (!mf_fgets(&PS, 0)) {
|
||||
if (!nflag && !pd)
|
||||
OUT(ps)
|
||||
exit(0);
|
||||
}
|
||||
break;
|
||||
case 'p':
|
||||
if (pd)
|
||||
break;
|
||||
OUT(ps)
|
||||
break;
|
||||
case 'P':
|
||||
if (pd)
|
||||
break;
|
||||
if (psl != 0 &&
|
||||
(p = memchr(ps, '\n', psl - 1)) != NULL) {
|
||||
oldpsl = psl;
|
||||
psl = (p + 1) - ps;
|
||||
}
|
||||
OUT(ps)
|
||||
if (p != NULL)
|
||||
psl = oldpsl;
|
||||
break;
|
||||
case 'q':
|
||||
if (!nflag && !pd)
|
||||
OUT(ps)
|
||||
flush_appends();
|
||||
exit(0);
|
||||
case 'r':
|
||||
if (appendx >= appendnum)
|
||||
if ((appends = realloc(appends,
|
||||
sizeof(struct s_appends) *
|
||||
(appendnum *= 2))) == NULL)
|
||||
err(1, "realloc");
|
||||
appends[appendx].type = AP_FILE;
|
||||
appends[appendx].s = cp->t;
|
||||
appends[appendx].len = strlen(cp->t);
|
||||
appendx++;
|
||||
break;
|
||||
case 's':
|
||||
sdone |= substitute(cp);
|
||||
break;
|
||||
case 't':
|
||||
if (sdone) {
|
||||
sdone = 0;
|
||||
cp = cp->u.c;
|
||||
goto redirect;
|
||||
}
|
||||
break;
|
||||
case 'w':
|
||||
if (pd)
|
||||
break;
|
||||
if (cp->u.fd == -1 && (cp->u.fd = open(cp->t,
|
||||
O_WRONLY|O_APPEND|O_CREAT|O_TRUNC,
|
||||
DEFFILEMODE)) == -1)
|
||||
err(1, "%s", cp->t);
|
||||
if (write(cp->u.fd, ps, psl) != psl)
|
||||
err(1, "%s", cp->t);
|
||||
break;
|
||||
case 'x':
|
||||
if (hs == NULL)
|
||||
cspace(&HS, "\n", 1, REPLACE);
|
||||
tspace = PS;
|
||||
PS = HS;
|
||||
HS = tspace;
|
||||
break;
|
||||
case 'y':
|
||||
if (pd || psl == 0)
|
||||
break;
|
||||
for (p = ps, len = psl; --len; ++p)
|
||||
*p = cp->u.y[(unsigned char)*p];
|
||||
break;
|
||||
case ':':
|
||||
case '}':
|
||||
break;
|
||||
case '=':
|
||||
(void)printf("%lu\n", linenum);
|
||||
}
|
||||
cp = cp->next;
|
||||
} /* for all cp */
|
||||
|
||||
new: if (!nflag && !pd)
|
||||
OUT(ps)
|
||||
flush_appends();
|
||||
} /* for all lines */
|
||||
}
|
||||
|
||||
/*
|
||||
* TRUE if the address passed matches the current program state
|
||||
* (lastline, linenumber, ps).
|
||||
*/
|
||||
#define MATCH(a) \
|
||||
(a)->type == AT_RE ? regexec_e((a)->u.r, ps, 0, 1, psl) : \
|
||||
(a)->type == AT_LINE ? linenum == (a)->u.l : lastline
|
||||
|
||||
/*
|
||||
* Return TRUE if the command applies to the current line. Sets the inrange
|
||||
* flag to process ranges. Interprets the non-select (``!'') flag.
|
||||
*/
|
||||
static inline int
|
||||
applies(cp)
|
||||
struct s_command *cp;
|
||||
{
|
||||
int r;
|
||||
|
||||
lastaddr = 0;
|
||||
if (cp->a1 == NULL && cp->a2 == NULL)
|
||||
r = 1;
|
||||
else if (cp->a2)
|
||||
if (cp->inrange) {
|
||||
if (MATCH(cp->a2)) {
|
||||
cp->inrange = 0;
|
||||
lastaddr = 1;
|
||||
}
|
||||
r = 1;
|
||||
} else if (MATCH(cp->a1)) {
|
||||
/*
|
||||
* If the second address is a number less than or
|
||||
* equal to the line number first selected, only
|
||||
* one line shall be selected.
|
||||
* -- POSIX 1003.2
|
||||
*/
|
||||
if (cp->a2->type == AT_LINE &&
|
||||
linenum >= cp->a2->u.l)
|
||||
lastaddr = 1;
|
||||
else
|
||||
cp->inrange = 1;
|
||||
r = 1;
|
||||
} else
|
||||
r = 0;
|
||||
else
|
||||
r = MATCH(cp->a1);
|
||||
return (cp->nonsel ? ! r : r);
|
||||
}
|
||||
|
||||
/*
|
||||
* substitute --
|
||||
* Do substitutions in the pattern space. Currently, we build a
|
||||
* copy of the new pattern space in the substitute space structure
|
||||
* and then swap them.
|
||||
*/
|
||||
static int
|
||||
substitute(cp)
|
||||
struct s_command *cp;
|
||||
{
|
||||
SPACE tspace;
|
||||
regex_t *re;
|
||||
size_t re_off, slen;
|
||||
int lastempty, n;
|
||||
char *s;
|
||||
|
||||
s = ps;
|
||||
re = cp->u.s->re;
|
||||
if (re == NULL) {
|
||||
if (defpreg != NULL && cp->u.s->maxbref > defpreg->re_nsub) {
|
||||
linenum = cp->u.s->linenum;
|
||||
errx(1, "%lu: %s: \\%d not defined in the RE",
|
||||
linenum, fname, cp->u.s->maxbref);
|
||||
}
|
||||
}
|
||||
if (!regexec_e(re, s, 0, 0, psl))
|
||||
return (0);
|
||||
|
||||
SS.len = 0; /* Clean substitute space. */
|
||||
slen = psl;
|
||||
n = cp->u.s->n;
|
||||
lastempty = 1;
|
||||
|
||||
switch (n) {
|
||||
case 0: /* Global */
|
||||
do {
|
||||
if (lastempty || match[0].rm_so != match[0].rm_eo) {
|
||||
/* Locate start of replaced string. */
|
||||
re_off = match[0].rm_so;
|
||||
/* Copy leading retained string. */
|
||||
cspace(&SS, s, re_off, APPEND);
|
||||
/* Add in regular expression. */
|
||||
regsub(&SS, s, cp->u.s->new);
|
||||
}
|
||||
|
||||
/* Move past this match. */
|
||||
if (match[0].rm_so != match[0].rm_eo) {
|
||||
s += match[0].rm_eo;
|
||||
slen -= match[0].rm_eo;
|
||||
lastempty = 0;
|
||||
} else if (match[0].rm_so == slen) {
|
||||
s += match[0].rm_so;
|
||||
slen = 0;
|
||||
} else {
|
||||
if (match[0].rm_so == 0)
|
||||
cspace(&SS, s, match[0].rm_so + 1,
|
||||
APPEND);
|
||||
else
|
||||
cspace(&SS, s + match[0].rm_so, 1,
|
||||
APPEND);
|
||||
s += match[0].rm_so + 1;
|
||||
slen -= match[0].rm_so + 1;
|
||||
lastempty = 1;
|
||||
}
|
||||
} while (slen > 0 && regexec_e(re, s, REG_NOTBOL, 0, slen));
|
||||
/* Copy trailing retained string. */
|
||||
if (slen > 0)
|
||||
cspace(&SS, s, slen, APPEND);
|
||||
break;
|
||||
default: /* Nth occurrence */
|
||||
while (--n) {
|
||||
s += match[0].rm_eo;
|
||||
slen -= match[0].rm_eo;
|
||||
if (!regexec_e(re, s, REG_NOTBOL, 0, slen))
|
||||
return (0);
|
||||
}
|
||||
/* FALLTHROUGH */
|
||||
case 1: /* 1st occurrence */
|
||||
/* Locate start of replaced string. */
|
||||
re_off = match[0].rm_so + (s - ps);
|
||||
/* Copy leading retained string. */
|
||||
cspace(&SS, ps, re_off, APPEND);
|
||||
/* Add in regular expression. */
|
||||
regsub(&SS, s, cp->u.s->new);
|
||||
/* Copy trailing retained string. */
|
||||
s += match[0].rm_eo;
|
||||
slen -= match[0].rm_eo;
|
||||
cspace(&SS, s, slen, APPEND);
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Swap the substitute space and the pattern space, and make sure
|
||||
* that any leftover pointers into stdio memory get lost.
|
||||
*/
|
||||
tspace = PS;
|
||||
PS = SS;
|
||||
SS = tspace;
|
||||
SS.space = SS.back;
|
||||
|
||||
/* Handle the 'p' flag. */
|
||||
if (cp->u.s->p)
|
||||
OUT(ps)
|
||||
|
||||
/* Handle the 'w' flag. */
|
||||
if (cp->u.s->wfile && !pd) {
|
||||
if (cp->u.s->wfd == -1 && (cp->u.s->wfd = open(cp->u.s->wfile,
|
||||
O_WRONLY|O_APPEND|O_CREAT|O_TRUNC, DEFFILEMODE)) == -1)
|
||||
err(1, "%s", cp->u.s->wfile);
|
||||
if (write(cp->u.s->wfd, ps, psl) != psl)
|
||||
err(1, "%s", cp->u.s->wfile);
|
||||
}
|
||||
return (1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Flush append requests. Always called before reading a line,
|
||||
* therefore it also resets the substitution done (sdone) flag.
|
||||
*/
|
||||
static void
|
||||
flush_appends()
|
||||
{
|
||||
FILE *f;
|
||||
int count, i;
|
||||
char buf[8 * 1024];
|
||||
|
||||
for (i = 0; i < appendx; i++)
|
||||
switch (appends[i].type) {
|
||||
case AP_STRING:
|
||||
fwrite(appends[i].s, sizeof(char), appends[i].len,
|
||||
stdout);
|
||||
break;
|
||||
case AP_FILE:
|
||||
/*
|
||||
* Read files probably shouldn't be cached. Since
|
||||
* it's not an error to read a non-existent file,
|
||||
* it's possible that another program is interacting
|
||||
* with the sed script through the filesystem. It
|
||||
* would be truly bizarre, but possible. It's probably
|
||||
* not that big a performance win, anyhow.
|
||||
*/
|
||||
if ((f = fopen(appends[i].s, "r")) == NULL)
|
||||
break;
|
||||
while ((count = fread(buf, sizeof(char), sizeof(buf), f)))
|
||||
(void)fwrite(buf, sizeof(char), count, stdout);
|
||||
(void)fclose(f);
|
||||
break;
|
||||
}
|
||||
if (ferror(stdout))
|
||||
errx(1, "stdout: %s", strerror(errno ? errno : EIO));
|
||||
appendx = sdone = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
lputs(s)
|
||||
char *s;
|
||||
{
|
||||
int count;
|
||||
char *escapes, *p;
|
||||
struct winsize win;
|
||||
static int termwidth = -1;
|
||||
|
||||
if (termwidth == -1) {
|
||||
if ((p = getenv("COLUMNS")) && *p != '\0')
|
||||
termwidth = atoi(p);
|
||||
else if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &win) == 0 &&
|
||||
win.ws_col > 0)
|
||||
termwidth = win.ws_col;
|
||||
else
|
||||
termwidth = 60;
|
||||
}
|
||||
|
||||
for (count = 0; *s; ++s) {
|
||||
if (count + 5 >= termwidth) {
|
||||
(void)printf("\\\n");
|
||||
count = 0;
|
||||
}
|
||||
if (isprint((unsigned char)*s) && *s != '\\') {
|
||||
(void)putchar(*s);
|
||||
count++;
|
||||
} else if (*s != '\n') {
|
||||
escapes = "\\\a\b\f\r\t\v";
|
||||
(void)putchar('\\');
|
||||
if ((p = strchr(escapes, *s))) {
|
||||
(void)putchar("\\abfrtv"[p - escapes]);
|
||||
count += 2;
|
||||
} else {
|
||||
(void)printf("%03o", *(u_char *)s);
|
||||
count += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
(void)putchar('$');
|
||||
(void)putchar('\n');
|
||||
if (ferror(stdout))
|
||||
errx(1, "stdout: %s", strerror(errno ? errno : EIO));
|
||||
}
|
||||
|
||||
static inline int
|
||||
regexec_e(preg, string, eflags, nomatch, slen)
|
||||
regex_t *preg;
|
||||
const char *string;
|
||||
int eflags, nomatch;
|
||||
size_t slen;
|
||||
{
|
||||
int eval;
|
||||
|
||||
if (preg == NULL) {
|
||||
if (defpreg == NULL)
|
||||
errx(1, "first RE may not be empty");
|
||||
} else
|
||||
defpreg = preg;
|
||||
|
||||
/* Set anchors, discounting trailing newline (if any). */
|
||||
if (slen > 0 && string[slen - 1] == '\n')
|
||||
slen--;
|
||||
match[0].rm_so = 0;
|
||||
match[0].rm_eo = slen;
|
||||
|
||||
eval = regexec(defpreg, string,
|
||||
nomatch ? 0 : maxnsub + 1, match, eflags | REG_STARTEND);
|
||||
switch(eval) {
|
||||
case 0:
|
||||
return (1);
|
||||
case REG_NOMATCH:
|
||||
return (0);
|
||||
}
|
||||
errx(1, "RE error: %s", strregerror(eval, defpreg));
|
||||
/* NOTREACHED */
|
||||
}
|
||||
|
||||
/*
|
||||
* regsub - perform substitutions after a regexp match
|
||||
* Based on a routine by Henry Spencer
|
||||
*/
|
||||
static void
|
||||
regsub(sp, string, src)
|
||||
SPACE *sp;
|
||||
char *string, *src;
|
||||
{
|
||||
int len, no;
|
||||
char c, *dst;
|
||||
|
||||
#define NEEDSP(reqlen) \
|
||||
if (sp->len >= sp->blen - (reqlen) - 1) { \
|
||||
sp->blen += (reqlen) + 1024; \
|
||||
if ((sp->space = sp->back = realloc(sp->back, sp->blen)) \
|
||||
== NULL) \
|
||||
err(1, "realloc"); \
|
||||
dst = sp->space + sp->len; \
|
||||
}
|
||||
|
||||
dst = sp->space + sp->len;
|
||||
while ((c = *src++) != '\0') {
|
||||
if (c == '&')
|
||||
no = 0;
|
||||
else if (c == '\\' && isdigit((unsigned char)*src))
|
||||
no = *src++ - '0';
|
||||
else
|
||||
no = -1;
|
||||
if (no < 0) { /* Ordinary character. */
|
||||
if (c == '\\' && (*src == '\\' || *src == '&'))
|
||||
c = *src++;
|
||||
NEEDSP(1);
|
||||
*dst++ = c;
|
||||
++sp->len;
|
||||
} else if (match[no].rm_so != -1 && match[no].rm_eo != -1) {
|
||||
len = match[no].rm_eo - match[no].rm_so;
|
||||
NEEDSP(len);
|
||||
memmove(dst, string + match[no].rm_so, len);
|
||||
dst += len;
|
||||
sp->len += len;
|
||||
}
|
||||
}
|
||||
NEEDSP(1);
|
||||
*dst = '\0';
|
||||
}
|
||||
|
||||
/*
|
||||
* aspace --
|
||||
* Append the source space to the destination space, allocating new
|
||||
* space as necessary.
|
||||
*/
|
||||
void
|
||||
cspace(sp, p, len, spflag)
|
||||
SPACE *sp;
|
||||
char *p;
|
||||
size_t len;
|
||||
enum e_spflag spflag;
|
||||
{
|
||||
size_t tlen;
|
||||
|
||||
/* Make sure SPACE has enough memory and ramp up quickly. */
|
||||
tlen = sp->len + len + 1;
|
||||
if (tlen > sp->blen) {
|
||||
sp->blen = tlen + 1024;
|
||||
if ((sp->space = sp->back = realloc(sp->back, sp->blen)) ==
|
||||
NULL)
|
||||
err(1, "realloc");
|
||||
}
|
||||
|
||||
if (spflag == REPLACE)
|
||||
sp->len = 0;
|
||||
|
||||
memmove(sp->space + sp->len, p, len);
|
||||
|
||||
sp->space[sp->len += len] = '\0';
|
||||
}
|
||||
|
||||
/*
|
||||
* Close all cached opened files and report any errors
|
||||
*/
|
||||
void
|
||||
cfclose(cp, end)
|
||||
struct s_command *cp, *end;
|
||||
{
|
||||
|
||||
for (; cp != end; cp = cp->next)
|
||||
switch(cp->code) {
|
||||
case 's':
|
||||
if (cp->u.s->wfd != -1 && close(cp->u.s->wfd))
|
||||
err(1, "%s", cp->u.s->wfile);
|
||||
cp->u.s->wfd = -1;
|
||||
break;
|
||||
case 'w':
|
||||
if (cp->u.fd != -1 && close(cp->u.fd))
|
||||
err(1, "%s", cp->t);
|
||||
cp->u.fd = -1;
|
||||
break;
|
||||
case '{':
|
||||
cfclose(cp->u.c, cp->next);
|
||||
break;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user