* Copyright (c) 1992 Diomidis Spinellis.
* Copyright (c) 1992 The Regents of the University of California.
* 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
* 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
static char sccsid
[] = "@(#)process.c 5.10 (Berkeley) 12/2/92";
static SPACE HS
= {""}, PS
, SS
;
static inline int applies
__P((struct s_command
*));
static void flush_appends
__P((void));
static void lputs
__P((char *));
static inline int regexec_e
__P((regex_t
*, const char *, int, int));
static void regsub
__P((SPACE
*, char *, char *));
static int substitute
__P((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 struct iovec iov
[2] = { NULL
, 0, "\n", 1 };
for (linenum
= 0; mf_fgets(&PS
, REPLACE
);) {
if (appendx
>= appendnum
)
appends
= xrealloc(appends
,
sizeof(struct s_appends
) *
appends
[appendx
].type
= AP_STRING
;
appends
[appendx
].s
= cp
->t
;
if (cp
->a2
== NULL
|| lastaddr
)
(void)printf("%s", cp
->t
);
if ((p
= strchr(ps
, '\n')) == NULL
)
cspace(&PS
, hs
, hsl
, REPLACE
);
cspace(&PS
, hs
, hsl
, APPENDNL
);
cspace(&HS
, ps
, psl
, REPLACE
);
cspace(&HS
, ps
, psl
, APPENDNL
);
(void)printf("%s", cp
->t
);
(void)printf("%s\n", ps
);
r
= mf_fgets(&PS
, REPLACE
);
if (!mf_fgets(&PS
, APPENDNL
)) {
(void)printf("%s\n", ps
);
(void)printf("%s\n", ps
);
if ((p
= strchr(ps
, '\n')) != NULL
) {
(void)printf("%s\n", ps
);
(void)printf("%s\n", ps
);
if (appendx
>= appendnum
)
appends
= xrealloc(appends
,
sizeof(struct s_appends
) *
appends
[appendx
].type
= AP_FILE
;
appends
[appendx
].s
= cp
->t
;
if (cp
->u
.fd
== -1 && (cp
->u
.fd
= open(cp
->t
,
O_WRONLY
|O_APPEND
|O_CREAT
|O_TRUNC
,
if (writev(cp
->u
.fd
, iov
, 2) != psl
+ 1)
for (p
= ps
, len
= psl
; len
--; ++p
)
(void)printf("%lu\n", linenum
);
(void)printf("%s\n", ps
);
* TRUE if the address passed matches the current program state
* (lastline, linenumber, ps).
(a)->type == AT_RE ? regexec_e((a)->u.r, ps, 0, 1) : \
(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.
if (cp
->a1
== NULL
&& cp
->a2
== NULL
)
} 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.
if (cp
->a2
->type
== AT_LINE
&&
return (cp
->nonsel
? ! r
: r
);
* Do substitutions in the pattern space. Currently, we build a
* copy of the new pattern space in the substitute space structure
if (defpreg
!= NULL
&& cp
->u
.s
->maxbref
> defpreg
->re_nsub
) {
linenum
= cp
->u
.s
->linenum
;
err(COMPILE
, "\\%d not defined in the RE",
if (!regexec_e(re
, s
, 0, 0))
SS
.len
= 0; /* Clean substitute space. */
/* Locate start of replaced string. */
re_eoff
= match
[0].rm_eo
;
/* 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. */
} while(*s
&& re_eoff
&& regexec_e(re
, s
, REG_NOTBOL
, 0));
if (eos
- s
> 0 && !re_eoff
)
err(FATAL
, "infinite substitution loop");
/* Copy trailing retained string. */
cspace(&SS
, s
, strlen(s
), APPEND
);
default: /* Nth occurrence */
if (!regexec_e(re
, s
, REG_NOTBOL
, 0))
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. */
cspace(&SS
, s
, strlen(s
), APPEND
);
* Swap the substitute space and the pattern space, and make sure
* that any leftover pointers into stdio memory get lost.
/* Handle the 'p' flag. */
(void)printf("%s\n", 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(FATAL
, "%s: %s\n", cp
->u
.s
->wfile
, strerror(errno
));
if (writev(cp
->u
.s
->wfd
, iov
, 2) != psl
+ 1)
err(FATAL
, "%s: %s\n", cp
->u
.s
->wfile
, strerror(errno
));
* Flush append requests. Always called before reading a line,
* therefore it also resets the substitution done (sdone) flag.
for (i
= 0; i
< appendx
; i
++)
switch (appends
[i
].type
) {
(void)printf("%s", appends
[i
].s
);
* 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 file system. 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
)
while (count
= fread(buf
, 1, sizeof(buf
), f
))
(void)fwrite(buf
, 1, count
, stdout
);
err(FATAL
, "stdout: %s", strerror(errno
? errno
: EIO
));
register char *escapes
, *p
;
static int termwidth
= -1;
if (p
= getenv("COLUMNS"))
else if (ioctl(STDOUT_FILENO
, TIOCGWINSZ
, &win
) == 0 &&
for (count
= 0; *s
; ++s
) {
if (count
>= termwidth
) {
if (isascii(*s
) && isprint(*s
) && *s
!= '\\') {
escapes
= "\\\a\b\f\n\r\t\v";
if (p
= strchr(escapes
, *s
)) {
(void)putchar("\\abfnrtv"[p
- escapes
]);
(void)printf("%03o", (u_char
)*s
);
err(FATAL
, "stdout: %s", strerror(errno
? errno
: EIO
));
regexec_e(preg
, string
, eflags
, nomatch
)
err(FATAL
, "first RE may not be empty");
eval
= regexec(defpreg
, string
,
nomatch
? 0 : maxnsub
+ 1, match
, eflags
);
err(FATAL
, "RE error: %s", strregerror(eval
, defpreg
));
* regsub - perform substitutions after a regexp match
* Based on a routine by Henry Spencer
if (sp->len >= sp->blen - (reqlen) - 1) { \
sp->blen += (reqlen) + 1024; \
sp->space = sp->back = xrealloc(sp->back, sp->blen); \
dst = sp->space + sp->len; \
dst
= sp
->space
+ sp
->len
;
while ((c
= *src
++) != '\0') {
else if (c
== '\\' && isdigit(*src
))
if (no
< 0) { /* Ordinary character. */
if (c
== '\\' && (*src
== '\\' || *src
== '&'))
} else if (match
[no
].rm_so
!= -1 && match
[no
].rm_eo
!= -1) {
len
= match
[no
].rm_eo
- match
[no
].rm_so
;
memmove(dst
, string
+ match
[no
].rm_so
, len
);
* Append the source space to the destination space, allocating new
cspace(sp
, p
, len
, spflag
)
* Make sure SPACE has enough memory and ramp up quickly. Appends
* need two extra bytes, one for the newline, one for a terminating
/* tlen = sp->len + len + spflag == APPENDNL ? 2 : 1; */
tlen
= sp
->len
+ len
+ (spflag
== APPENDNL
? 2 : 1); /* XXX */
sp
->space
= sp
->back
= xrealloc(sp
->back
, sp
->blen
);
sp
->space
[sp
->len
++] = '\n';
else if (spflag
== REPLACE
)
memmove(sp
->space
+ sp
->len
, p
, len
);
sp
->space
[sp
->len
+= len
] = '\0';
* Close all cached opened files and report any errors
register struct s_command
*cp
, *end
;
for (; cp
!= end
; cp
= cp
->next
)
if (cp
->u
.s
->wfd
!= -1 && close(cp
->u
.s
->wfd
))
"%s: %s", cp
->u
.s
->wfile
, strerror(errno
));
if (cp
->u
.fd
!= -1 && close(cp
->u
.fd
))
err(FATAL
, "%s: %s", cp
->t
, strerror(errno
));
cfclose(cp
->u
.c
, cp
->next
);