* 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.
* %sccs.include.redist.c%
static char sccsid
[] = "@(#)process.c 8.1 (Berkeley) %G%";
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, size_t));
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. */
#define OUT(s) { fwrite(s, sizeof(u_char), psl, stdout); }
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
;
appends
[appendx
].len
= strlen(cp
->t
);
if (cp
->a2
== NULL
|| lastaddr
)
(void)printf("%s", cp
->t
);
if ((p
= memchr(ps
, '\n', psl
)) == NULL
)
cspace(&PS
, hs
, hsl
, REPLACE
);
cspace(&HS
, ps
, psl
, REPLACE
);
(void)printf("%s", cp
->t
);
r
= mf_fgets(&PS
, REPLACE
);
if ((p
= memchr(ps
, '\n', psl
)) != NULL
) {
if (appendx
>= appendnum
)
appends
= xrealloc(appends
,
sizeof(struct s_appends
) *
appends
[appendx
].type
= AP_FILE
;
appends
[appendx
].s
= cp
->t
;
appends
[appendx
].len
= strlen(cp
->t
);
if (cp
->u
.fd
== -1 && (cp
->u
.fd
= open(cp
->t
,
O_WRONLY
|O_APPEND
|O_CREAT
|O_TRUNC
,
if (write(cp
->u
.fd
, ps
, psl
) != psl
)
cspace(&HS
, "", 0, REPLACE
);
for (p
= ps
, len
= psl
; --len
; ++p
)
(void)printf("%lu\n", linenum
);
* 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, 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.
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, psl
))
SS
.len
= 0; /* Clean substitute space. */
/* Locate start of replaced string. */
/* 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(regexec_e(re
, s
, REG_NOTBOL
, 0, slen
));
/* Copy trailing retained string. */
cspace(&SS
, s
, slen
, APPEND
);
default: /* Nth occurrence */
if (!regexec_e(re
, s
, REG_NOTBOL
, 0, slen
))
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
, slen
, 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. */
/* 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 (write(cp
->u
.s
->wfd
, ps
, psl
) != psl
)
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
) {
fwrite(appends
[i
].s
, sizeof(char), appends
[i
].len
,
* 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
, sizeof(char), sizeof(buf
), f
))
(void)fwrite(buf
, sizeof(char), 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
, slen
)
err(FATAL
, "first RE may not be empty");
/* Set anchors, discounting trailing newline (if any). */
if (slen
> 0 && string
[slen
- 1] == '\n')
eval
= regexec(defpreg
, string
,
nomatch
? 0 : maxnsub
+ 1, match
, eflags
| REG_STARTEND
);
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. */
tlen
= sp
->len
+ len
+ 1;
sp
->space
= sp
->back
= xrealloc(sp
->back
, sp
->blen
);
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
);