toolbox: add OpenADK toolbox for very small systems, thx Thorsten Glaser
[openadk.git] / package / toolbox / src / sed / process.c
blobb2b2a87174e83144e6af127ad99c15acd6dc6ca7
1 /* $OpenBSD: process.c,v 1.32 2017/02/22 14:09:09 tom Exp $ */
3 /*-
4 * Copyright (c) 2015
5 * mirabilos <m@mirbsd.org>
6 * Copyright (c) 1992 Diomidis Spinellis.
7 * Copyright (c) 1992, 1993
8 * The Regents of the University of California. All rights reserved.
10 * This code is derived from software contributed to Berkeley by
11 * Diomidis Spinellis of Imperial College, University of London.
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
15 * are met:
16 * 1. Redistributions of source code must retain the above copyright
17 * notice, this list of conditions and the following disclaimer.
18 * 2. Redistributions in binary form must reproduce the above copyright
19 * notice, this list of conditions and the following disclaimer in the
20 * documentation and/or other materials provided with the distribution.
21 * 3. Neither the name of the University nor the names of its contributors
22 * may be used to endorse or promote products derived from this software
23 * without specific prior written permission.
25 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 * SUCH DAMAGE.
38 #include <sys/types.h>
39 #include <sys/stat.h>
40 #include <sys/uio.h>
42 __SCCSID("@(#)process.c 8.1 (Berkeley) 6/6/93");
43 __RCSID("$MirOS: src/usr.bin/sed/process.c,v 1.5 2017/11/20 01:23:57 tg Exp $");
45 #include <ctype.h>
46 #include <errno.h>
47 #include <fcntl.h>
48 #include <limits.h>
49 #include <regex.h>
50 #include <stdio.h>
51 #include <stdlib.h>
52 #include <string.h>
53 #include <unistd.h>
55 #include "defs.h"
56 #include "extern.h"
58 static SPACE HS, PS, SS;
59 #define pd PS.deleted
60 #define ps PS.space
61 #define psl PS.len
62 #define psanl PS.append_newline
63 #define hs HS.space
64 #define hsl HS.len
66 static inline int applies(struct s_command *);
67 static void flush_appends(void);
68 static void lputs(char *);
69 static inline int regexec_e(regex_t *, const char *, int, int, size_t,
70 size_t);
71 static void regsub(SPACE *, char *, char *);
72 static int substitute(struct s_command *);
74 struct s_appends *appends; /* Array of pointers to strings to append. */
75 static size_t appendx; /* Index into appends array. */
76 size_t appendnum; /* Size of appends array. */
78 static int lastaddr; /* Set by applies if last address of a range. */
79 static int sdone; /* If any substitutes since last line input. */
80 /* Iov structure for 'w' commands. */
81 static regex_t *defpreg;
82 size_t maxnsub;
83 regmatch_t *match;
85 #define OUT() do {\
86 fwrite(ps, 1, psl, outfile);\
87 if (psanl) fputc('\n', outfile);\
88 } while (0)
90 void
91 process(void)
93 struct s_command *cp;
94 SPACE tspace;
95 size_t len, oldpsl;
96 char *p;
98 for (linenum = 0; mf_fgets(&PS, REPLACE);) {
99 pd = 0;
100 top:
101 cp = prog;
102 redirect:
103 while (cp != NULL) {
104 if (!applies(cp)) {
105 cp = cp->next;
106 continue;
108 switch (cp->code) {
109 case '{':
110 cp = cp->u.c;
111 goto redirect;
112 case 'a':
113 if (appendx >= appendnum) {
114 appends = xreallocarray(appends,
115 appendnum,
116 2 * sizeof(struct s_appends));
117 appendnum *= 2;
119 appends[appendx].type = AP_STRING;
120 appends[appendx].s = cp->t;
121 appends[appendx].len = strlen(cp->t);
122 appendx++;
123 break;
124 case 'b':
125 cp = cp->u.c;
126 goto redirect;
127 case 'c':
128 pd = 1;
129 psl = 0;
130 if (cp->a2 == NULL || lastaddr || lastline())
131 (void)fprintf(outfile, "%s", cp->t);
132 break;
133 case 'd':
134 pd = 1;
135 goto new;
136 case 'D':
137 if (pd)
138 goto new;
139 if (psl == 0 ||
140 (p = memchr(ps, '\n', psl)) == NULL) {
141 pd = 1;
142 goto new;
143 } else {
144 psl -= (p + 1) - ps;
145 memmove(ps, p + 1, psl);
146 goto top;
148 case 'g':
149 cspace(&PS, hs, hsl, REPLACE);
150 break;
151 case 'G':
152 cspace(&PS, "\n", 1, 0);
153 cspace(&PS, hs, hsl, 0);
154 break;
155 case 'h':
156 cspace(&HS, ps, psl, REPLACE);
157 break;
158 case 'H':
159 cspace(&HS, "\n", 1, 0);
160 cspace(&HS, ps, psl, 0);
161 break;
162 case 'i':
163 (void)fprintf(outfile, "%s", cp->t);
164 break;
165 case 'l':
166 lputs(ps);
167 break;
168 case 'n':
169 if (!nflag && !pd)
170 OUT();
171 flush_appends();
172 if (!mf_fgets(&PS, REPLACE))
173 exit(0);
174 pd = 0;
175 break;
176 case 'N':
177 flush_appends();
178 cspace(&PS, "\n", 1, 0);
179 if (!mf_fgets(&PS, 0))
180 exit(0);
181 break;
182 case 'p':
183 if (pd)
184 break;
185 OUT();
186 break;
187 case 'P':
188 if (pd)
189 break;
190 if ((p = memchr(ps, '\n', psl)) != NULL) {
191 oldpsl = psl;
192 psl = p - ps;
193 psanl = 1;
194 OUT();
195 psl = oldpsl;
196 } else {
197 OUT();
199 break;
200 case 'q':
201 if (!nflag && !pd)
202 OUT();
203 flush_appends();
204 exit(0);
205 case 'r':
206 if (appendx >= appendnum) {
207 appends = xreallocarray(appends,
208 appendnum,
209 2 * sizeof(struct s_appends));
210 appendnum *= 2;
212 appends[appendx].type = AP_FILE;
213 appends[appendx].s = cp->t;
214 appends[appendx].len = strlen(cp->t);
215 appendx++;
216 break;
217 case 's':
218 sdone |= substitute(cp);
219 break;
220 case 't':
221 if (sdone) {
222 sdone = 0;
223 cp = cp->u.c;
224 goto redirect;
226 break;
227 case 'w':
228 if (pd)
229 break;
230 if (cp->u.fd == -1 && (cp->u.fd = open(cp->t,
231 O_WRONLY|O_APPEND|O_CREAT|O_TRUNC,
232 DEFFILEMODE)) == -1)
233 error(FATAL, "%s: %s",
234 cp->t, strerror(errno));
235 if ((size_t)write(cp->u.fd, ps, psl) != psl ||
236 write(cp->u.fd, "\n", 1) != 1)
237 error(FATAL, "%s: %s",
238 cp->t, strerror(errno));
239 break;
240 case 'x':
241 if (hs == NULL)
242 cspace(&HS, "", 0, REPLACE);
243 tspace = PS;
244 PS = HS;
245 psanl = tspace.append_newline;
246 HS = tspace;
247 break;
248 case 'y':
249 if (pd || psl == 0)
250 break;
251 for (p = ps, len = psl; len--; ++p)
252 *p = cp->u.y[(unsigned char)*p];
253 break;
254 case ':':
255 case '}':
256 break;
257 case '=':
258 (void)fprintf(outfile, "%lu\n", linenum);
260 cp = cp->next;
261 } /* for all cp */
263 new: if (!nflag && !pd)
264 OUT();
265 flush_appends();
266 } /* for all lines */
270 * TRUE if the address passed matches the current program state
271 * (lastline, linenumber, ps).
273 #define MATCH(a) \
274 (a)->type == AT_RE ? regexec_e((a)->u.r, ps, 0, 1, 0, psl) : \
275 (a)->type == AT_LINE ? linenum == (a)->u.l : lastline()
278 * Return TRUE if the command applies to the current line. Sets the inrange
279 * flag to process ranges. Interprets the non-select (``!'') flag.
281 static inline int
282 applies(struct s_command *cp)
284 int r;
286 lastaddr = 0;
287 if (cp->a1 == NULL && cp->a2 == NULL)
288 r = 1;
289 else if (cp->a2)
290 if (cp->inrange) {
291 if (MATCH(cp->a2)) {
292 cp->inrange = 0;
293 lastaddr = 1;
295 r = 1;
296 } else if (MATCH(cp->a1)) {
298 * If the second address is a number less than or
299 * equal to the line number first selected, only
300 * one line shall be selected.
301 * -- POSIX 1003.2
303 if (cp->a2->type == AT_LINE &&
304 linenum >= cp->a2->u.l)
305 lastaddr = 1;
306 else
307 cp->inrange = 1;
308 r = 1;
309 } else
310 r = 0;
311 else
312 r = MATCH(cp->a1);
313 return (cp->nonsel ? !r : r);
317 * Reset all inrange markers.
319 void
320 resetranges(void)
322 struct s_command *cp;
324 for (cp = prog; cp; cp = cp->code == '{' ? cp->u.c : cp->next)
325 if (cp->a2)
326 cp->inrange = 0;
330 * substitute --
331 * Do substitutions in the pattern space. Currently, we build a
332 * copy of the new pattern space in the substitute space structure
333 * and then swap them.
335 static int
336 substitute(struct s_command *cp)
338 SPACE tspace;
339 regex_t *re;
340 regoff_t slen;
341 int n, lastempty;
342 size_t le = 0;
343 char *s;
345 s = ps;
346 re = cp->u.s->re;
347 if (re == NULL) {
348 if (defpreg != NULL && cp->u.s->maxbref > defpreg->re_nsub) {
349 linenum = cp->u.s->linenum;
350 error(COMPILE, "\\%zu not defined in the RE",
351 cp->u.s->maxbref);
354 if (!regexec_e(re, ps, 0, 0, 0, psl))
355 return (0);
357 SS.len = 0; /* Clean substitute space. */
358 slen = psl;
359 n = cp->u.s->n;
360 lastempty = 1;
362 do {
363 /* Copy the leading retained string. */
364 if (n <= 1 && (match[0].rm_so > le))
365 cspace(&SS, s, match[0].rm_so - le, APPEND);
367 /* Skip zero-length matches right after other matches. */
368 if (lastempty || (match[0].rm_so - le) ||
369 match[0].rm_so != match[0].rm_eo) {
370 if (n <= 1) {
371 /* Want this match: append replacement. */
372 regsub(&SS, ps, cp->u.s->new);
373 if (n == 1)
374 n = -1;
375 } else {
376 /* Want a later match: append original. */
377 if (match[0].rm_eo - le)
378 cspace(&SS, s, match[0].rm_eo - le,
379 APPEND);
380 n--;
384 /* Move past this match. */
385 s = ps + match[0].rm_eo;
386 slen = psl - match[0].rm_eo;
387 le = match[0].rm_eo;
390 * After a zero-length match, advance one byte,
391 * and at the end of the line, terminate.
393 if (match[0].rm_so == match[0].rm_eo) {
394 if (*s == '\0' || *s == '\n')
395 slen = -1;
396 else
397 slen--;
398 if (*s != '\0') {
399 cspace(&SS, s++, 1, APPEND);
400 le++;
402 lastempty = 1;
403 } else
404 lastempty = 0;
406 } while (n >= 0 && slen >= 0 &&
407 regexec_e(re, ps, REG_NOTBOL, 0, le, psl));
409 /* Did not find the requested number of matches. */
410 if (n > 0)
411 return (0);
413 /* Copy the trailing retained string. */
414 if (slen > 0)
415 cspace(&SS, s, slen, APPEND);
418 * Swap the substitute space and the pattern space, and make sure
419 * that any leftover pointers into stdio memory get lost.
421 tspace = PS;
422 PS = SS;
423 psanl = tspace.append_newline;
424 SS = tspace;
425 SS.space = SS.back;
427 /* Handle the 'p' flag. */
428 if (cp->u.s->p)
429 OUT();
431 /* Handle the 'w' flag. */
432 if (cp->u.s->wfile && !pd) {
433 if (cp->u.s->wfd == -1 && (cp->u.s->wfd = open(cp->u.s->wfile,
434 O_WRONLY|O_APPEND|O_CREAT|O_TRUNC, DEFFILEMODE)) == -1)
435 error(FATAL, "%s: %s", cp->u.s->wfile, strerror(errno));
436 if ((size_t)write(cp->u.s->wfd, ps, psl) != psl ||
437 write(cp->u.s->wfd, "\n", 1) != 1)
438 error(FATAL, "%s: %s", cp->u.s->wfile, strerror(errno));
440 return (1);
444 * Flush append requests. Always called before reading a line,
445 * therefore it also resets the substitution done (sdone) flag.
447 static void
448 flush_appends(void)
450 FILE *f;
451 int count;
452 size_t i;
453 char buf[8 * 1024];
455 for (i = 0; i < appendx; i++)
456 switch (appends[i].type) {
457 case AP_STRING:
458 fwrite(appends[i].s, sizeof(char), appends[i].len,
459 outfile);
460 break;
461 case AP_FILE:
463 * Read files probably shouldn't be cached. Since
464 * it's not an error to read a non-existent file,
465 * it's possible that another program is interacting
466 * with the sed script through the filesystem. It
467 * would be truly bizarre, but possible. It's probably
468 * not that big a performance win, anyhow.
470 if ((f = fopen(appends[i].s, "r")) == NULL)
471 break;
472 while ((count = fread(buf, sizeof(char), sizeof(buf), f)))
473 (void)fwrite(buf, sizeof(char), count, outfile);
474 (void)fclose(f);
475 break;
477 if (ferror(outfile))
478 error(FATAL, "%s: %s", outfname, strerror(errno ? errno : EIO));
479 appendx = sdone = 0;
482 static void
483 lputs(char *s)
485 int count;
486 extern int termwidth;
487 const char *escapes;
488 char *p;
490 for (count = 0; *s; ++s) {
491 if (count >= termwidth) {
492 (void)fprintf(outfile, "\\\n");
493 count = 0;
495 if (isascii((unsigned char)*s) && isprint((unsigned char)*s)
496 && *s != '\\') {
497 (void)fputc(*s, outfile);
498 count++;
499 } else {
500 escapes = "\\\a\b\f\n\r\t\v";
501 if (*s == '\n' && s[1] == '\0') {
502 /* omit trailing newline */
503 break;
505 (void)fputc('\\', outfile);
506 if ((p = strchr(escapes, *s))) {
507 (void)fputc("\\abfnrtv"[p - escapes], outfile);
508 count += 2;
509 } else {
510 (void)fprintf(outfile, "%03o", *(u_char *)s);
511 count += 4;
515 (void)fputc('$', outfile);
516 (void)fputc('\n', outfile);
517 if (ferror(outfile))
518 error(FATAL, "%s: %s", outfname, strerror(errno ? errno : EIO));
521 static inline int
522 regexec_e(regex_t *preg, const char *string, int eflags,
523 int nomatch, size_t start, size_t stop)
525 int eval;
527 if (preg == NULL) {
528 if (defpreg == NULL)
529 error(FATAL, "first RE may not be empty");
530 } else
531 defpreg = preg;
533 /* Set anchors */
534 match[0].rm_so = start;
535 match[0].rm_eo = stop;
537 eval = regexec(defpreg, string,
538 nomatch ? 0 : maxnsub + 1, match, eflags | REG_STARTEND);
539 switch (eval) {
540 case 0:
541 return (1);
542 case REG_NOMATCH:
543 return (0);
545 error(FATAL, "RE error: %s", strregerror(eval, defpreg));
549 * regsub - perform substitutions after a regexp match
550 * Based on a routine by Henry Spencer
552 static void
553 regsub(SPACE *sp, char *string, char *src)
555 int len, no;
556 char c, *dst;
558 #define NEEDSP(reqlen) \
559 if (sp->len + (reqlen) + 1 >= sp->blen) { \
560 size_t newlen = sp->blen + (reqlen) + 1024; \
561 sp->space = sp->back = xrealloc(sp->back, newlen); \
562 sp->blen = newlen; \
563 dst = sp->space + sp->len; \
566 dst = sp->space + sp->len;
567 while ((c = *src++) != '\0') {
568 if (c == '&')
569 no = 0;
570 else if (c == '\\' && isdigit((unsigned char)*src))
571 no = *src++ - '0';
572 else
573 no = -1;
574 if (no < 0) { /* Ordinary character. */
575 if (c == '\\' && (*src == '\\' || *src == '&'))
576 c = *src++;
577 NEEDSP(1);
578 *dst++ = c;
579 ++sp->len;
580 } else if (match[no].rm_so != -1 && match[no].rm_eo != -1) {
581 len = match[no].rm_eo - match[no].rm_so;
582 NEEDSP(len);
583 memmove(dst, string + match[no].rm_so, len);
584 dst += len;
585 sp->len += len;
588 NEEDSP(1);
589 *dst = '\0';
593 * aspace --
594 * Append the source space to the destination space, allocating new
595 * space as necessary.
597 void
598 cspace(SPACE *sp, const char *p, size_t len, enum e_spflag spflag)
600 size_t tlen;
602 /* Make sure SPACE has enough memory and ramp up quickly. */
603 tlen = sp->len + len + 1;
604 if (tlen > sp->blen) {
605 size_t newlen = tlen + 1024;
606 sp->space = sp->back = xrealloc(sp->back, newlen);
607 sp->blen = newlen;
610 if (spflag == REPLACE)
611 sp->len = 0;
613 memmove(sp->space + sp->len, p, len);
615 sp->space[sp->len += len] = '\0';
619 * Close all cached opened files and report any errors
621 void
622 cfclose(struct s_command *cp, struct s_command *end)
625 for (; cp != end; cp = cp->next)
626 switch (cp->code) {
627 case 's':
628 if (cp->u.s->wfd != -1 && close(cp->u.s->wfd))
629 error(FATAL,
630 "%s: %s", cp->u.s->wfile, strerror(errno));
631 cp->u.s->wfd = -1;
632 break;
633 case 'w':
634 if (cp->u.fd != -1 && close(cp->u.fd))
635 error(FATAL, "%s: %s", cp->t, strerror(errno));
636 cp->u.fd = -1;
637 break;
638 case '{':
639 cfclose(cp->u.c, cp->next);
640 break;