source: trunk/minix/commands/simple/sed.c@ 9

Last change on this file since 9 was 9, checked in by Mattia Monga, 13 years ago

Minix 3.1.2a

File size: 45.0 KB
RevLine 
[9]1/* sed - stream editor Author: Eric S. Raymond */
2
3/* This used to be three different files with the following makefile:
4 * (Note the chmem).
5
6CFLAGS= -F -T.
7
8OBJS= sedcomp.s sedexec.s
9
10sed: $(OBJS)
11 cc -T. -o sed $(OBJS)
12 @chmem =13312 sed
13
14$(OBJS): sed.h
15
16 * If you want longer lines: increase MAXBUF.
17 * If you want scripts with more text: increase POOLSIZE.
18 * If you want more commands per script: increase MAXCMDS.
19 */
20
21#include <ctype.h>
22#include <sys/types.h>
23#include <stdlib.h>
24#include <string.h>
25#include <unistd.h>
26#include <stdio.h>
27
28/*+++++++++++++++*/
29
30/* Sed.h -- types and constants for the stream editor */
31
32/* Data area sizes used by both modules */
33#define MAXBUF 4000 /* current line buffer size */
34#define MAXAPPENDS 20 /* maximum number of appends */
35#define MAXTAGS 9 /* tagged patterns are \1 to \9 */
36
37/* Constants for compiled-command representation */
38#define EQCMD 0x01 /* = -- print current line number */
39#define ACMD 0x02 /* a -- append text after current line */
40#define BCMD 0x03 /* b -- branch to label */
41#define CCMD 0x04 /* c -- change current line */
42#define DCMD 0x05 /* d -- delete all of pattern space */
43#define CDCMD 0x06 /* D -- delete first line of pattern space */
44#define GCMD 0x07 /* g -- copy hold space to pattern space */
45#define CGCMD 0x08 /* G -- append hold space to pattern space */
46#define HCMD 0x09 /* h -- copy pattern space to hold space */
47#define CHCMD 0x0A /* H -- append pattern space to hold space */
48#define ICMD 0x0B /* i -- insert text before current line */
49#define LCMD 0x0C /* l -- print pattern space in escaped form */
50#define NCMD 0x0D /* n -- get next line into pattern space */
51#define CNCMD 0x0E /* N -- append next line to pattern space */
52#define PCMD 0x0F /* p -- print pattern space to output */
53#define CPCMD 0x10 /* P -- print first line of pattern space */
54#define QCMD 0x11 /* q -- exit the stream editor */
55#define RCMD 0x12 /* r -- read in a file after current line */
56#define SCMD 0x13 /* s -- regular-expression substitute */
57#define TCMD 0x14 /* t -- branch on any substitute successful */
58#define CTCMD 0x15 /* T -- branch on any substitute failed */
59#define WCMD 0x16 /* w -- write pattern space to file */
60#define CWCMD 0x17 /* W -- write first line of pattern space */
61#define XCMD 0x18 /* x -- exhange pattern and hold spaces */
62#define YCMD 0x19 /* y -- transliterate text */
63
64struct cmd_t { /* compiled-command representation */
65 char *addr1; /* first address for command */
66 char *addr2; /* second address for command */
67 union {
68 char *lhs; /* s command lhs */
69 struct cmd_t *link; /* label link */
70 } u;
71 char command; /* command code */
72 char *rhs; /* s command replacement string */
73 FILE *fout; /* associated output file descriptor */
74 struct {
75 char allbut; /* was negation specified? */
76 char global; /* was g postfix specified? */
77 char print; /* was p postfix specified? */
78 char inrange; /* in an address range? */
79 } flags;
80};
81typedef struct cmd_t sedcmd; /* use this name for declarations */
82
83#define BAD ((char *) -1) /* guaranteed not a string ptr */
84
85
86
87/* Address and regular expression compiled-form markers */
88#define STAR 1 /* marker for Kleene star */
89#define CCHR 2 /* non-newline character to be matched
90 * follows */
91#define CDOT 4 /* dot wild-card marker */
92#define CCL 6 /* character class follows */
93#define CNL 8 /* match line start */
94#define CDOL 10 /* match line end */
95#define CBRA 12 /* tagged pattern start marker */
96#define CKET 14 /* tagged pattern end marker */
97#define CBACK 16 /* backslash-digit pair marker */
98#define CLNUM 18 /* numeric-address index follows */
99#define CEND 20 /* symbol for end-of-source */
100#define CEOF 22 /* end-of-field mark */
101
102/* Sed.h ends here */
103
104#ifndef CMASK
105#define CMASK 0xFF /* some char type should have been unsigned
106 * char? */
107#endif
108
109/*+++++++++++++++*/
110
111/* Sed - stream editor Author: Eric S. Raymond */
112
113/*
114 The stream editor compiles its command input (from files or -e options)
115 into an internal form using compile() then executes the compiled form using
116 execute(). Main() just initializes data structures, interprets command line
117 options, and calls compile() and execute() in appropriate sequence.
118
119 The data structure produced by compile() is an array of compiled-command
120 structures (type sedcmd). These contain several pointers into pool[], the
121 regular-expression and text-data pool, plus a command code and g & p flags.
122 In the special case that the command is a label the struct will hold a ptr
123 into the labels array labels[] during most of the compile, until resolve()
124 resolves references at the end.
125
126 The operation of execute() is described in its source module.
127*/
128
129/* #include <stdio.h> */
130/* #include "sed.h" */
131
132/* Imported functions */
133
134/***** public stuff ******/
135
136#define MAXCMDS 500 /* maximum number of compiled commands */
137#define MAXLINES 256 /* max # numeric addresses to compile */
138
139/* Main data areas */
140char linebuf[MAXBUF + 1]; /* current-line buffer */
141sedcmd cmds[MAXCMDS + 1]; /* hold compiled commands */
142long linenum[MAXLINES]; /* numeric-addresses table */
143
144/* Miscellaneous shared variables */
145int nflag; /* -n option flag */
146int eargc; /* scratch copy of argument count */
147char **eargv; /* scratch copy of argument list */
148char bits[] = {1, 2, 4, 8, 16, 32, 64, 128};
149
150/***** module common stuff *****/
151
152#define POOLSIZE 20000 /* size of string-pool space */
153#define WFILES 10 /* max # w output files that can be compiled */
154#define RELIMIT 256 /* max chars in compiled RE */
155#define MAXDEPTH 20 /* maximum {}-nesting level */
156#define MAXLABS 50 /* max # of labels that can be handled */
157
158#define SKIPWS(pc) while ((*pc==' ') || (*pc=='\t')) pc++
159#define ABORT(msg) (fprintf(stderr, msg, linebuf), quit(2))
160#define IFEQ(x, v) if (*x == v) x++ , /* do expression */
161
162/* Error messages */
163static char AGMSG[] = "sed: garbled address %s\n";
164static char CGMSG[] = "sed: garbled command %s\n";
165static char TMTXT[] = "sed: too much text: %s\n";
166static char AD1NG[] = "sed: no addresses allowed for %s\n";
167static char AD2NG[] = "sed: only one address allowed for %s\n";
168static char TMCDS[] = "sed: too many commands, last was %s\n";
169static char COCFI[] = "sed: cannot open command-file %s\n";
170static char UFLAG[] = "sed: unknown flag %c\n";
171static char CCOFI[] = "sed: cannot create %s\n";
172static char ULABL[] = "sed: undefined label %s\n";
173static char TMLBR[] = "sed: too many {'s\n";
174static char FRENL[] = "sed: first RE must be non-null\n";
175static char NSCAX[] = "sed: no such command as %s\n";
176static char TMRBR[] = "sed: too many }'s\n";
177static char DLABL[] = "sed: duplicate label %s\n";
178static char TMLAB[] = "sed: too many labels: %s\n";
179static char TMWFI[] = "sed: too many w files\n";
180static char REITL[] = "sed: RE too long: %s\n";
181static char TMLNR[] = "sed: too many line numbers\n";
182static char TRAIL[] = "sed: command \"%s\" has trailing garbage\n";
183
184typedef struct { /* represent a command label */
185 char *name; /* the label name */
186 sedcmd *last; /* it's on the label search list */
187 sedcmd *address; /* pointer to the cmd it labels */
188}
189
190 label;
191
192/* Label handling */
193static label labels[MAXLABS]; /* here's the label table */
194static label *lab = labels + 1; /* pointer to current label */
195static label *lablst = labels; /* header for search list */
196
197/* String pool for regular expressions, append text, etc. etc. */
198static char pool[POOLSIZE]; /* the pool */
199static char *fp = pool; /* current pool pointer */
200static char *poolend = pool + POOLSIZE; /* pointer past pool end */
201
202/* Compilation state */
203static FILE *cmdf = NULL; /* current command source */
204static char *cp = linebuf; /* compile pointer */
205static sedcmd *cmdp = cmds; /* current compiled-cmd ptr */
206static char *lastre = NULL; /* old RE pointer */
207static int bdepth = 0; /* current {}-nesting level */
208static int bcount = 0; /* # tagged patterns in current RE */
209
210/* Compilation flags */
211static int eflag; /* -e option flag */
212static int gflag; /* -g option flag */
213
214_PROTOTYPE(int main, (int argc, char **argv));
215_PROTOTYPE(static void compile, (void));
216_PROTOTYPE(static int cmdcomp, (int cchar));
217_PROTOTYPE(static char *rhscomp, (char *rhsp, int delim));
218_PROTOTYPE(static char *recomp, (char *expbuf, int redelim));
219_PROTOTYPE(static int cmdline, (char *cbuf));
220_PROTOTYPE(static char *address, (char *expbuf));
221_PROTOTYPE(static char *gettext, (char *txp));
222_PROTOTYPE(static label *search, (label *ptr));
223_PROTOTYPE(static void resolve, (void));
224_PROTOTYPE(static char *ycomp, (char *ep, int delim));
225_PROTOTYPE(void quit, (int n));
226_PROTOTYPE(void execute, (void));
227_PROTOTYPE(static int selected, (sedcmd *ipc));
228_PROTOTYPE(static int match, (char *expbuf, int gf));
229_PROTOTYPE(static int advance, (char *lp, char *ep));
230_PROTOTYPE(static int substitute, (sedcmd *ipc));
231_PROTOTYPE(static void dosub, (char *rhsbuf));
232_PROTOTYPE(static char *place, (char *asp, char *al1, char *al2));
233_PROTOTYPE(static void listto, (char *p1, FILE *fp));
234_PROTOTYPE(static void truncated, (int h));
235_PROTOTYPE(static void command, (sedcmd *ipc));
236_PROTOTYPE(static void openfile, (char *file));
237_PROTOTYPE(static void get, (void));
238_PROTOTYPE(static void initget, (void));
239_PROTOTYPE(static char *getline, (char *buf));
240_PROTOTYPE(static int Memcmp, (char *a, char *b, int count));
241_PROTOTYPE(static void readout, (void));
242
243int main(argc, argv)
244/* Main sequence of the stream editor */
245int argc;
246char *argv[];
247{
248 eargc = argc; /* set local copy of argument count */
249 eargv = argv; /* set local copy of argument list */
250 cmdp->addr1 = pool; /* 1st addr expand will be at pool start */
251 if (eargc == 1) quit(0); /* exit immediately if no arguments */
252 /* Scan through the arguments, interpreting each one */
253 while ((--eargc > 0) && (**++eargv == '-')) switch (eargv[0][1]) {
254 case 'e':
255 eflag++;
256 compile(); /* compile with e flag on */
257 eflag = 0;
258 continue; /* get another argument */
259 case 'f':
260 if (eargc-- <= 0) /* barf if no -f file */
261 quit(2);
262 if ((cmdf = fopen(*++eargv, "r")) == NULL) {
263 fprintf(stderr, COCFI, *eargv);
264 quit(2);
265 }
266 compile(); /* file is O.K., compile it */
267 fclose(cmdf);
268 continue; /* go back for another argument */
269 case 'g':
270 gflag++; /* set global flag on all s cmds */
271 continue;
272 case 'n':
273 nflag++; /* no print except on p flag or w */
274 continue;
275 default:
276 fprintf(stdout, UFLAG, eargv[0][1]);
277 continue;
278 }
279
280
281 if (cmdp == cmds) { /* no commands have been compiled */
282 eargv--;
283 eargc++;
284 eflag++;
285 compile();
286 eflag = 0;
287 eargv++;
288 eargc--;
289 }
290 if (bdepth) /* we have unbalanced squigglies */
291 ABORT(TMLBR);
292
293 lablst->address = cmdp; /* set up header of label linked list */
294 resolve(); /* resolve label table indirections */
295 execute(); /* execute commands */
296 quit(0); /* everything was O.K. if we got here */
297 return(0);
298}
299
300
301#define H 0x80 /* 128 bit, on if there's really code for
302 * command */
303#define LOWCMD 56 /* = '8', lowest char indexed in cmdmask */
304
305/* Indirect through this to get command internal code, if it exists */
306static char cmdmask[] =
307{
308 0, 0, H, 0, 0, H + EQCMD, 0, 0,
309 0, 0, 0, 0, H + CDCMD, 0, 0, CGCMD,
310 CHCMD, 0, 0, 0, 0, 0, CNCMD, 0,
311 CPCMD, 0, 0, 0, H + CTCMD, 0, 0, H + CWCMD,
312 0, 0, 0, 0, 0, 0, 0, 0,
313 0, H + ACMD, H + BCMD, H + CCMD, DCMD, 0, 0, GCMD,
314 HCMD, H + ICMD, 0, 0, H + LCMD, 0, NCMD, 0,
315 PCMD, H + QCMD, H + RCMD, H + SCMD, H + TCMD, 0, 0, H + WCMD,
316 XCMD, H + YCMD, 0, H + BCMD, 0, H, 0, 0,
317};
318
319static void compile()
320/* Precompile sed commands out of a file */
321{
322 char ccode;
323
324
325 for (;;) { /* main compilation loop */
326 if (*cp == '\0') { /* get a new command line */
327 *linebuf = '\0'; /* K.H */
328 if (cmdline(cp = linebuf) < 0) break;
329 }
330 SKIPWS(cp);
331 if (*cp == '\0') /* empty */
332 continue;
333 if (*cp == '#') { /* comment */
334 while (*cp) ++cp;
335 continue;
336 }
337 if (*cp == ';') { /* ; separates cmds */
338 cp++;
339 continue;
340 }
341
342 /* Compile first address */
343 if (fp > poolend)
344 ABORT(TMTXT);
345 else if ((fp = address(cmdp->addr1 = fp)) == BAD)
346 ABORT(AGMSG);
347
348 if (fp == cmdp->addr1) {/* if empty RE was found */
349 if (lastre) /* if there was previous RE */
350 cmdp->addr1 = lastre; /* use it */
351 else
352 ABORT(FRENL);
353 } else if (fp == NULL) {/* if fp was NULL */
354 fp = cmdp->addr1; /* use current pool location */
355 cmdp->addr1 = NULL;
356 } else {
357 lastre = cmdp->addr1;
358 if (*cp == ',' || *cp == ';') { /* there's 2nd addr */
359 cp++;
360 if (fp > poolend) ABORT(TMTXT);
361 fp = address(cmdp->addr2 = fp);
362 if (fp == BAD || fp == NULL) ABORT(AGMSG);
363 if (fp == cmdp->addr2)
364 cmdp->addr2 = lastre;
365 else
366 lastre = cmdp->addr2;
367 } else
368 cmdp->addr2 = NULL; /* no 2nd address */
369 }
370 if (fp > poolend) ABORT(TMTXT);
371
372 SKIPWS(cp); /* discard whitespace after address */
373 IFEQ(cp, '!') cmdp->flags.allbut = 1;
374
375 SKIPWS(cp); /* get cmd char, range-check it */
376 if ((*cp < LOWCMD) || (*cp > '~')
377 || ((ccode = cmdmask[*cp - LOWCMD]) == 0))
378 ABORT(NSCAX);
379
380 cmdp->command = ccode & ~H; /* fill in command value */
381 if ((ccode & H) == 0) /* if no compile-time code */
382 cp++; /* discard command char */
383 else if (cmdcomp(*cp++))/* execute it; if ret = 1 */
384 continue; /* skip next line read */
385
386 if (++cmdp >= cmds + MAXCMDS) ABORT(TMCDS);
387
388 SKIPWS(cp); /* look for trailing stuff */
389 if (*cp != '\0' && *cp != ';' && *cp != '#') ABORT(TRAIL);
390 }
391}
392
393static int cmdcomp(cchar)
394/* Compile a single command */
395register char cchar; /* character name of command */
396{
397 static sedcmd **cmpstk[MAXDEPTH]; /* current cmd stack for {} */
398 static char *fname[WFILES]; /* w file name pointers */
399 static FILE *fout[WFILES]; /* w file file ptrs */
400 static int nwfiles = 1; /* count of open w files */
401 int i; /* indexing dummy used in w */
402 sedcmd *sp1, *sp2; /* temps for label searches */
403 label *lpt;
404 char redelim; /* current RE delimiter */
405
406 fout[0] = stdout;
407 switch (cchar) {
408 case '{': /* start command group */
409 cmdp->flags.allbut = !cmdp->flags.allbut;
410 cmpstk[bdepth++] = &(cmdp->u.link);
411 if (++cmdp >= cmds + MAXCMDS) ABORT(TMCDS);
412 return(1);
413
414 case '}': /* end command group */
415 if (cmdp->addr1) ABORT(AD1NG); /* no addresses allowed */
416 if (--bdepth < 0) ABORT(TMRBR); /* too many right braces */
417 *cmpstk[bdepth] = cmdp; /* set the jump address */
418 return(1);
419
420 case '=': /* print current source line number */
421 case 'q': /* exit the stream editor */
422 if (cmdp->addr2) ABORT(AD2NG);
423 break;
424
425 case ':': /* label declaration */
426 if (cmdp->addr1) ABORT(AD1NG); /* no addresses allowed */
427 fp = gettext(lab->name = fp); /* get the label name */
428 if (lpt = search(lab)) {/* does it have a double? */
429 if (lpt->address) ABORT(DLABL); /* yes, abort */
430 } else { /* check that it doesn't overflow label table */
431 lab->last = NULL;
432 lpt = lab;
433 if (++lab >= labels + MAXLABS) ABORT(TMLAB);
434 }
435 lpt->address = cmdp;
436 return(1);
437
438 case 'b': /* branch command */
439 case 't': /* branch-on-succeed command */
440 case 'T': /* branch-on-fail command */
441 SKIPWS(cp);
442 if (*cp == '\0') { /* if branch is to start of cmds... */
443 /* Add current command to end of label last */
444 if (sp1 = lablst->last) {
445 while (sp2 = sp1->u.link) sp1 = sp2;
446 sp1->u.link = cmdp;
447 } else /* lablst->last == NULL */
448 lablst->last = cmdp;
449 break;
450 }
451 fp = gettext(lab->name = fp); /* else get label into pool */
452 if (lpt = search(lab)) {/* enter branch to it */
453 if (lpt->address)
454 cmdp->u.link = lpt->address;
455 else {
456 sp1 = lpt->last;
457 while (sp2 = sp1->u.link) sp1 = sp2;
458 sp1->u.link = cmdp;
459 }
460 } else { /* matching named label not found */
461 lab->last = cmdp; /* add the new label */
462 lab->address = NULL; /* it's forward of here */
463 if (++lab >= labels + MAXLABS) /* overflow if last */
464 ABORT(TMLAB);
465 }
466 break;
467
468 case 'a': /* append text */
469 case 'i': /* insert text */
470 case 'r': /* read file into stream */
471 if (cmdp->addr2) ABORT(AD2NG);
472 case 'c': /* change text */
473 if ((*cp == '\\') && (*++cp == '\n')) cp++;
474 fp = gettext(cmdp->u.lhs = fp);
475 break;
476
477 case 'D': /* delete current line in hold space */
478 cmdp->u.link = cmds;
479 break;
480
481 case 's': /* substitute regular expression */
482 redelim = *cp++; /* get delimiter from 1st ch */
483 if ((fp = recomp(cmdp->u.lhs = fp, redelim)) == BAD) ABORT(CGMSG);
484 if (fp == cmdp->u.lhs) /* if compiled RE zero len */
485 cmdp->u.lhs = lastre; /* use the previous one */
486 else /* otherwise */
487 lastre = cmdp->u.lhs; /* save the one just found */
488 if ((cmdp->rhs = fp) > poolend) ABORT(TMTXT);
489 if ((fp = rhscomp(cmdp->rhs, redelim)) == BAD) ABORT(CGMSG);
490 if (gflag) cmdp->flags.global ++;
491 while (*cp == 'g' || *cp == 'p' || *cp == 'P') {
492 IFEQ(cp, 'g') cmdp->flags.global ++;
493 IFEQ(cp, 'p') cmdp->flags.print = 1;
494 IFEQ(cp, 'P') cmdp->flags.print = 2;
495 }
496
497 case 'l': /* list pattern space */
498 if (*cp == 'w')
499 cp++; /* and execute a w command! */
500 else
501 break; /* s or l is done */
502
503 case 'w': /* write-pattern-space command */
504 case 'W': /* write-first-line command */
505 if (nwfiles >= WFILES) ABORT(TMWFI);
506 fp = gettext(fname[nwfiles] = fp); /* filename will be in pool */
507 for (i = nwfiles - 1; i >= 0; i--) /* match it in table */
508 if ((fname[i] != NULL) &&
509 (strcmp(fname[nwfiles], fname[i]) == 0)) {
510 cmdp->fout = fout[i];
511 return(0);
512 }
513
514 /* If didn't find one, open new out file */
515 if ((cmdp->fout = fopen(fname[nwfiles], "w")) == NULL) {
516 fprintf(stderr, CCOFI, fname[nwfiles]);
517 quit(2);
518 }
519 fout[nwfiles++] = cmdp->fout;
520 break;
521
522 case 'y': /* transliterate text */
523 fp = ycomp(cmdp->u.lhs = fp, *cp++); /* compile translit */
524 if (fp == BAD) ABORT(CGMSG); /* fail on bad form */
525 if (fp > poolend) ABORT(TMTXT); /* fail on overflow */
526 break;
527 }
528 return(0); /* succeeded in interpreting one command */
529}
530
531static char *rhscomp(rhsp, delim) /* uses bcount */
532 /* Generate replacement string for substitute command right hand side */
533register char *rhsp; /* place to compile expression to */
534register char delim; /* regular-expression end-mark to look for */
535{
536 register char *p = cp; /* strictly for speed */
537
538 for (;;)
539 if ((*rhsp = *p++) == '\\') { /* copy; if it's a \, */
540 *rhsp = *p++; /* copy escaped char */
541 /* Check validity of pattern tag */
542 if (*rhsp > bcount + '0' && *rhsp <= '9') return(BAD);
543 *rhsp++ |= 0x80;/* mark the good ones */
544 continue;
545 } else if (*rhsp == delim) { /* found RE end, hooray... */
546 *rhsp++ = '\0'; /* cap the expression string */
547 cp = p;
548 return(rhsp); /* pt at 1 past the RE */
549 } else if (*rhsp++ == '\0') /* last ch not RE end, help! */
550 return(BAD);
551}
552
553static char *recomp(expbuf, redelim) /* uses cp, bcount */
554 /* Compile a regular expression to internal form */
555char *expbuf; /* place to compile it to */
556char redelim; /* RE end-marker to look for */
557{
558 register char *ep = expbuf; /* current-compiled-char pointer */
559 register char *sp = cp; /* source-character ptr */
560 register int c; /* current-character pointer */
561 char negclass; /* all-but flag */
562 char *lastep; /* ptr to last expr compiled */
563 char *svclass; /* start of current char class */
564 char brnest[MAXTAGS]; /* bracket-nesting array */
565 char *brnestp; /* ptr to current bracket-nest */
566 int classct; /* class element count */
567 int tags; /* # of closed tags */
568
569 if (*cp == redelim) /* if first char is RE endmarker */
570 return(cp++, expbuf); /* leave existing RE unchanged */
571
572 lastep = NULL; /* there's no previous RE */
573 brnestp = brnest; /* initialize ptr to brnest array */
574 tags = bcount = 0; /* initialize counters */
575
576 if (*ep++ = (*sp == '^')) /* check for start-of-line syntax */
577 sp++;
578
579 for (;;) {
580 if (ep >= expbuf + RELIMIT) /* match is too large */
581 return(cp = sp, BAD);
582 if ((c = *sp++) == redelim) { /* found the end of the RE */
583 cp = sp;
584 if (brnestp != brnest) /* \(, \) unbalanced */
585 return(BAD);
586 *ep++ = CEOF; /* write end-of-pattern mark */
587 return(ep); /* return ptr to compiled RE */
588 }
589 if (c != '*') /* if we're a postfix op */
590 lastep = ep; /* get ready to match last */
591
592 switch (c) {
593 case '\\':
594 if ((c = *sp++) == '(') { /* start tagged section */
595 if (bcount >= MAXTAGS) return(cp = sp, BAD);
596 *brnestp++ = bcount; /* update tag stack */
597 *ep++ = CBRA; /* enter tag-start */
598 *ep++ = bcount++; /* bump tag count */
599 continue;
600 } else if (c == ')') { /* end tagged section */
601 if (brnestp <= brnest) /* extra \) */
602 return(cp = sp, BAD);
603 *ep++ = CKET; /* enter end-of-tag */
604 *ep++ = *--brnestp; /* pop tag stack */
605 tags++; /* count closed tags */
606 continue;
607 } else if (c >= '1' && c <= '9') { /* tag use */
608 if ((c -= '1') >= tags) /* too few */
609 return(BAD);
610 *ep++ = CBACK; /* enter tag mark */
611 *ep++ = c; /* and the number */
612 continue;
613 } else if (c == '\n') /* escaped newline no good */
614 return(cp = sp, BAD);
615 else if (c == 'n') /* match a newline */
616 c = '\n';
617 else if (c == 't') /* match a tab */
618 c = '\t';
619 else if (c == 'r') /* match a return */
620 c = '\r';
621 goto defchar;
622
623 case '\0': /* ignore nuls */
624 continue;
625
626 case '\n': /* trailing pattern delimiter is missing */
627 return(cp = sp, BAD);
628
629 case '.': /* match any char except newline */
630 *ep++ = CDOT;
631 continue;
632 case '*': /* 0..n repeats of previous pattern */
633 if (lastep == NULL) /* if * isn't first on line */
634 goto defchar; /* match a literal * */
635 if (*lastep == CKET) /* can't iterate a tag */
636 return(cp = sp, BAD);
637 *lastep |= STAR;/* flag previous pattern */
638 continue;
639
640 case '$': /* match only end-of-line */
641 if (*sp != redelim) /* if we're not at end of RE */
642 goto defchar; /* match a literal $ */
643 *ep++ = CDOL; /* insert end-symbol mark */
644 continue;
645
646 case '[': /* begin character set pattern */
647 if (ep + 17 >= expbuf + RELIMIT) ABORT(REITL);
648 *ep++ = CCL; /* insert class mark */
649 if (negclass = ((c = *sp++) == '^')) c = *sp++;
650 svclass = sp; /* save ptr to class start */
651 do {
652 if (c == '\0') ABORT(CGMSG);
653
654 /* Handle character ranges */
655 if (c == '-' && sp > svclass && *sp != ']')
656 for (c = sp[-2]; c < *sp; c++)
657 ep[c >> 3] |= bits[c & 7];
658
659 /* Handle escape sequences in sets */
660 if (c == '\\')
661 if ((c = *sp++) == 'n')
662 c = '\n';
663 else if (c == 't')
664 c = '\t';
665 else if (c == 'r')
666 c = '\r';
667
668 /* Enter (possibly translated) char in set */
669 ep[c >> 3] |= bits[c & 7];
670 } while
671 ((c = *sp++) != ']');
672
673 /* Invert the bitmask if all-but was specified */
674 if (negclass) for (classct = 0; classct < 16; classct++)
675 ep[classct] ^= 0xFF;
676 ep[0] &= 0xFE; /* never match ASCII 0 */
677 ep += 16; /* advance ep past set mask */
678 continue;
679
680 defchar: /* match literal character */
681 default: /* which is what we'd do by default */
682 *ep++ = CCHR; /* insert character mark */
683 *ep++ = c;
684 }
685 }
686}
687
688static int cmdline(cbuf) /* uses eflag, eargc, cmdf */
689 /* Read next command from -e argument or command file */
690register char *cbuf;
691{
692 register int inc; /* not char because must hold EOF */
693
694 *cbuf-- = 0; /* so pre-increment points us at cbuf */
695
696 /* E command flag is on */
697 if (eflag) {
698 register char *p; /* ptr to current -e argument */
699 static char *savep; /* saves previous value of p */
700
701 if (eflag > 0) { /* there are pending -e arguments */
702 eflag = -1;
703 if (eargc-- <= 0) quit(2); /* if no arguments, barf */
704
705 /* Else transcribe next e argument into cbuf */
706 p = *++eargv;
707 while (*++cbuf = *p++)
708 if (*cbuf == '\\') {
709 if ((*++cbuf = *p++) == '\0')
710 return(savep = NULL, -1);
711 else
712 continue;
713 } else if (*cbuf == '\n') { /* end of 1 cmd line */
714 *cbuf = '\0';
715 return(savep = p, 1);
716 /* We'll be back for the rest... */
717 }
718
719 /* Found end-of-string; can advance to next argument */
720 return(savep = NULL, 1);
721 }
722 if ((p = savep) == NULL) return(-1);
723
724 while (*++cbuf = *p++)
725 if (*cbuf == '\\') {
726 if ((*++cbuf = *p++) == '0')
727 return(savep = NULL, -1);
728 else
729 continue;
730 } else if (*cbuf == '\n') {
731 *cbuf = '\0';
732 return(savep = p, 1);
733 }
734 return(savep = NULL, 1);
735 }
736
737 /* If no -e flag read from command file descriptor */
738 while ((inc = getc(cmdf)) != EOF) /* get next char */
739 if ((*++cbuf = inc) == '\\') /* if it's escape */
740 *++cbuf = inc = getc(cmdf); /* get next char */
741 else if (*cbuf == '\n') /* end on newline */
742 return(*cbuf = '\0', 1); /* cap the string */
743
744 return(*++cbuf = '\0', -1); /* end-of-file, no more chars */
745}
746
747static char *address(expbuf) /* uses cp, linenum */
748 /* Expand an address at *cp... into expbuf, return ptr at following char */
749register char *expbuf;
750{
751 static int numl = 0; /* current ind in addr-number table */
752 register char *rcp; /* temp compile ptr for forwd look */
753 long lno; /* computed value of numeric address */
754
755 if (*cp == '$') { /* end-of-source address */
756 *expbuf++ = CEND; /* write symbolic end address */
757 *expbuf++ = CEOF; /* and the end-of-address mark (!) */
758 cp++; /* go to next source character */
759 return(expbuf); /* we're done */
760 }
761 if (*cp == '/' || *cp == '\\') { /* start of regular-expression match */
762 if (*cp == '\\') cp++;
763 return(recomp(expbuf, *cp++)); /* compile the RE */
764 }
765
766 rcp = cp;
767 lno = 0; /* now handle a numeric address */
768 while (*rcp >= '0' && *rcp <= '9') /* collect digits */
769 lno = lno * 10 + *rcp++ - '0'; /* compute their value */
770
771 if (rcp > cp) { /* if we caught a number... */
772 *expbuf++ = CLNUM; /* put a numeric-address marker */
773 *expbuf++ = numl; /* and the address table index */
774 linenum[numl++] = lno; /* and set the table entry */
775 if (numl >= MAXLINES) /* oh-oh, address table overflow */
776 ABORT(TMLNR); /* abort with error message */
777 *expbuf++ = CEOF; /* write the end-of-address marker */
778 cp = rcp; /* point compile past the address */
779 return(expbuf); /* we're done */
780 }
781 return(NULL); /* no legal address was found */
782}
783
784static char *gettext(txp) /* uses global cp */
785 /* Accept multiline input from *cp..., discarding leading whitespace */
786register char *txp; /* where to put the text */
787{
788 register char *p = cp; /* this is for speed */
789
790 SKIPWS(p); /* discard whitespace */
791 do {
792 if ((*txp = *p++) == '\\') /* handle escapes */
793 *txp = *p++;
794 if (*txp == '\0') /* we're at end of input */
795 return(cp = --p, ++txp);
796 else if (*txp == '\n') /* also SKIPWS after newline */
797 SKIPWS(p);
798 } while
799 (txp++); /* keep going till we find that nul */
800 return(txp);
801}
802
803static label *search(ptr) /* uses global lablst */
804 /* Find the label matching *ptr, return NULL if none */
805register label *ptr;
806{
807 register label *rp;
808 for (rp = lablst; rp < ptr; rp++)
809 if ((rp->name != NULL) && (strcmp(rp->name, ptr->name) == 0))
810 return(rp);
811 return(NULL);
812}
813
814static void resolve()
815{ /* uses global lablst */
816 /* Write label links into the compiled-command space */
817 register label *lptr;
818 register sedcmd *rptr, *trptr;
819
820 /* Loop through the label table */
821 for (lptr = lablst; lptr < lab; lptr++)
822 if (lptr->address == NULL) { /* barf if not defined */
823 fprintf(stderr, ULABL, lptr->name);
824 quit(2);
825 } else if (lptr->last) {/* if last is non-null */
826 rptr = lptr->last; /* chase it */
827 while (trptr = rptr->u.link) { /* resolve refs */
828 rptr->u.link = lptr->address;
829 rptr = trptr;
830 }
831 rptr->u.link = lptr->address;
832 }
833}
834
835static char *ycomp(ep, delim)
836/* Compile a y (transliterate) command */
837register char *ep; /* where to compile to */
838char delim; /* end delimiter to look for */
839{
840 register char *tp, *sp;
841 register int c;
842
843 /* Scan the 'from' section for invalid chars */
844 for (sp = tp = cp; *tp != delim; tp++) {
845 if (*tp == '\\') tp++;
846 if ((*tp == '\n') || (*tp == '\0')) return (BAD);
847 }
848 tp++; /* tp now points at first char of 'to'
849 * section */
850
851 /* Now rescan the 'from' section */
852 while ((c = *sp++ & 0x7F) != delim) {
853 if (c == '\\' && *sp == 'n') {
854 sp++;
855 c = '\n';
856 }
857 if ((ep[c] = *tp++) == '\\' && *tp == 'n') {
858 ep[c] = '\n';
859 tp++;
860 }
861 if ((ep[c] == delim) || (ep[c] == '\0')) return(BAD);
862 }
863
864 if (*tp != delim) /* 'to', 'from' parts have unequal lengths */
865 return(BAD);
866
867 cp = ++tp; /* point compile ptr past translit */
868
869 for (c = 0; c < 128; c++) /* fill in self-map entries in table */
870 if (ep[c] == 0) ep[c] = c;
871
872 return(ep + 0x80); /* return first free location past table end */
873}
874
875void quit(n)
876int n;
877{
878/* Flush buffers and exit. Now a historical relic. Rely on exit to flush
879 * the buffers.
880 */
881 exit(n);
882}
883
884/*+++++++++++++++*/
885
886/*
887 sedexec.c -- execute compiled form of stream editor commands
888
889 The single entry point of this module is the function execute(). It
890 may take a string argument (the name of a file to be used as text) or
891 the argument NULL which tells it to filter standard input. It executes
892 the compiled commands in cmds[] on each line in turn.
893
894 The function command() does most of the work. Match() and advance()
895 are used for matching text against precompiled regular expressions and
896 dosub() does right-hand-side substitution. Getline() does text input;
897 readout() and Memcmp() are output and string-comparison utilities.
898*/
899
900/* #include <stdio.h> */
901/* #include <ctype.h> */
902/* #include "sed.h" */
903
904/***** shared variables imported from the main ******/
905
906/* Main data areas */
907extern char linebuf[]; /* current-line buffer */
908extern sedcmd cmds[]; /* hold compiled commands */
909extern long linenum[]; /* numeric-addresses table */
910
911/* Miscellaneous shared variables */
912extern int nflag; /* -n option flag */
913extern int eargc; /* scratch copy of argument count */
914extern char **eargv; /* scratch copy of argument list */
915extern char bits[]; /* the bits table */
916
917/***** end of imported stuff *****/
918
919#define MAXHOLD MAXBUF /* size of the hold space */
920#define GENSIZ MAXBUF /* maximum genbuf size */
921
922#define TRUE 1
923#define FALSE 0
924
925static char LTLMSG[] = "sed: line too long\n";
926
927static char *spend; /* current end-of-line-buffer pointer */
928static long lnum = 0L; /* current source line number */
929
930/* Append buffer maintenance */
931static sedcmd *appends[MAXAPPENDS]; /* array of ptrs to a,i,c commands */
932static sedcmd **aptr = appends; /* ptr to current append */
933
934/* Genbuf and its pointers */
935static char genbuf[GENSIZ];
936static char *loc1;
937static char *loc2;
938static char *locs;
939
940/* Command-logic flags */
941static int lastline; /* do-line flag */
942static int jump; /* jump to cmd's link address if set */
943static int delete; /* delete command flag */
944
945/* Tagged-pattern tracking */
946static char *bracend[MAXTAGS]; /* tagged pattern start pointers */
947static char *brastart[MAXTAGS]; /* tagged pattern end pointers */
948
949static int anysub; /* true if any s on current line succeeded */
950
951
952void execute()
953/* Execute the compiled commands in cmds[] */
954{
955 register char *p1; /* dummy copy ptrs */
956 register sedcmd *ipc; /* ptr to current command */
957 char *execp; /* ptr to source */
958
959
960 initget();
961
962 /* Here's the main command-execution loop */
963 for (;;) {
964
965 /* Get next line to filter */
966 if ((execp = getline(linebuf)) == BAD) return;
967 spend = execp;
968 anysub = FALSE;
969
970 /* Loop through compiled commands, executing them */
971 for (ipc = cmds; ipc->command;) {
972 if (!selected(ipc)) {
973 ipc++;
974 continue;
975 }
976 command(ipc); /* execute the command pointed at */
977
978 if (delete) /* if delete flag is set */
979 break; /* don't exec rest of compiled cmds */
980
981 if (jump) { /* if jump set, follow cmd's link */
982 jump = FALSE;
983 if ((ipc = ipc->u.link) == 0) {
984 ipc = cmds;
985 break;
986 }
987 } else /* normal goto next command */
988 ipc++;
989 }
990
991 /* We've now done all modification commands on the line */
992
993 /* Here's where the transformed line is output */
994 if (!nflag && !delete) {
995 for (p1 = linebuf; p1 < spend; p1++) putc(*p1, stdout);
996 putc('\n', stdout);
997 }
998
999 /* If we've been set up for append, emit the text from it */
1000 if (aptr > appends) readout();
1001
1002 delete = FALSE; /* clear delete flag; about to get next cmd */
1003 }
1004}
1005
1006static int selected(ipc)
1007/* Is current command selected */
1008sedcmd *ipc;
1009{
1010 register char *p1 = ipc->addr1; /* point p1 at first address */
1011 register char *p2 = ipc->addr2; /* and p2 at second */
1012 int c;
1013 int sel = TRUE; /* select by default */
1014
1015 if (!p1) /* No addresses: always selected */
1016 ;
1017 else if (ipc->flags.inrange) {
1018 if (*p2 == CEND);
1019 else if (*p2 == CLNUM) {
1020 c = p2[1] & CMASK;
1021 if (lnum >= linenum[c]) {
1022 ipc->flags.inrange = FALSE;
1023 if (lnum > linenum[c]) sel = FALSE;
1024 }
1025 } else if (match(p2, 0))
1026 ipc->flags.inrange = FALSE;
1027 } else if (*p1 == CEND) {
1028 if (!lastline) sel = FALSE;
1029 } else if (*p1 == CLNUM) {
1030 c = p1[1] & CMASK;
1031 if (lnum != linenum[c])
1032 sel = FALSE;
1033 else if (p2)
1034 ipc->flags.inrange = TRUE;
1035 } else if (match(p1, 0)) {
1036 if (p2) ipc->flags.inrange = TRUE;
1037 } else
1038 sel = FALSE;
1039
1040 return ipc->flags.allbut ? !sel : sel;
1041}
1042
1043static int match(expbuf, gf) /* uses genbuf */
1044 /* Match RE at expbuf against linebuf; if gf set, copy linebuf from genbuf */
1045char *expbuf;
1046int gf;
1047{
1048 register char *p1, *p2, c;
1049
1050 if (gf) {
1051 if (*expbuf) return(FALSE);
1052 p1 = linebuf;
1053 p2 = genbuf;
1054 while (*p1++ = *p2++);
1055 locs = p1 = loc2;
1056 } else {
1057 p1 = linebuf;
1058 locs = FALSE;
1059 }
1060
1061 p2 = expbuf;
1062 if (*p2++) {
1063 loc1 = p1;
1064 if (*p2 == CCHR && p2[1] != *p1) /* 1st char is wrong */
1065 return(FALSE); /* so fail */
1066 return(advance(p1, p2));/* else try to match rest */
1067 }
1068
1069 /* Quick check for 1st character if it's literal */
1070 if (*p2 == CCHR) {
1071 c = p2[1]; /* pull out character to search for */
1072 do {
1073 if (*p1 != c) continue; /* scan the source string */
1074 if (advance(p1, p2)) /* found it, match the rest */
1075 return(loc1 = p1, 1);
1076 } while
1077 (*p1++);
1078 return(FALSE); /* didn't find that first char */
1079 }
1080
1081 /* Else try for unanchored match of the pattern */
1082 do {
1083 if (advance(p1, p2)) return(loc1 = p1, 1);
1084 } while
1085 (*p1++);
1086
1087 /* If got here, didn't match either way */
1088 return(FALSE);
1089}
1090
1091static int advance(lp, ep)
1092/* Attempt to advance match pointer by one pattern element */
1093register char *lp; /* source (linebuf) ptr */
1094register char *ep; /* regular expression element ptr */
1095{
1096 register char *curlp; /* save ptr for closures */
1097 char c; /* scratch character holder */
1098 char *bbeg;
1099 int ct;
1100
1101 for (;;) switch (*ep++) {
1102 case CCHR: /* literal character */
1103 if (*ep++ == *lp++) /* if chars are equal */
1104 continue; /* matched */
1105 return(FALSE); /* else return false */
1106
1107 case CDOT: /* anything but newline */
1108 if (*lp++) /* first NUL is at EOL */
1109 continue; /* keep going if didn't find */
1110 return(FALSE); /* else return false */
1111
1112 case CNL: /* start-of-line */
1113 case CDOL: /* end-of-line */
1114 if (*lp == 0) /* found that first NUL? */
1115 continue; /* yes, keep going */
1116 return(FALSE); /* else return false */
1117
1118 case CEOF: /* end-of-address mark */
1119 loc2 = lp; /* set second loc */
1120 return(TRUE); /* return true */
1121
1122 case CCL: /* a closure */
1123 c = *lp++ & 0177;
1124 if (ep[c >> 3] & bits[c & 07]) { /* is char in set? */
1125 ep += 16; /* then skip rest of bitmask */
1126 continue; /* and keep going */
1127 }
1128 return(FALSE); /* else return false */
1129
1130 case CBRA: /* start of tagged pattern */
1131 brastart[*ep++] = lp; /* mark it */
1132 continue; /* and go */
1133
1134 case CKET: /* end of tagged pattern */
1135 bracend[*ep++] = lp; /* mark it */
1136 continue; /* and go */
1137
1138 case CBACK:
1139 bbeg = brastart[*ep];
1140 ct = bracend[*ep++] - bbeg;
1141
1142 if (Memcmp(bbeg, lp, ct)) {
1143 lp += ct;
1144 continue;
1145 }
1146 return(FALSE);
1147
1148 case CBACK | STAR:
1149 bbeg = brastart[*ep];
1150 ct = bracend[*ep++] - bbeg;
1151 curlp = lp;
1152 while (Memcmp(bbeg, lp, ct)) lp += ct;
1153
1154 while (lp >= curlp) {
1155 if (advance(lp, ep)) return(TRUE);
1156 lp -= ct;
1157 }
1158 return(FALSE);
1159
1160
1161 case CDOT | STAR: /* match .* */
1162 curlp = lp; /* save closure start loc */
1163 while (*lp++); /* match anything */
1164 goto star; /* now look for followers */
1165
1166 case CCHR | STAR: /* match <literal char>* */
1167 curlp = lp; /* save closure start loc */
1168 while (*lp++ == *ep); /* match many of that char */
1169 ep++; /* to start of next element */
1170 goto star; /* match it and followers */
1171
1172 case CCL | STAR: /* match [...]* */
1173 curlp = lp; /* save closure start loc */
1174 do {
1175 c = *lp++ & 0x7F; /* match any in set */
1176 } while
1177 (ep[c >> 3] & bits[c & 07]);
1178 ep += 16; /* skip past the set */
1179 goto star; /* match followers */
1180
1181 star: /* the recursion part of a * or + match */
1182 if (--lp == curlp) /* 0 matches */
1183 continue;
1184
1185 if (*ep == CCHR) {
1186 c = ep[1];
1187 do {
1188 if (*lp != c) continue;
1189 if (advance(lp, ep)) return (TRUE);
1190 } while
1191 (lp-- > curlp);
1192 return(FALSE);
1193 }
1194 if (*ep == CBACK) {
1195 c = *(brastart[ep[1]]);
1196 do {
1197 if (*lp != c) continue;
1198 if (advance(lp, ep)) return (TRUE);
1199 } while
1200 (lp-- > curlp);
1201 return(FALSE);
1202 }
1203 do {
1204 if (lp == locs) break;
1205 if (advance(lp, ep)) return (TRUE);
1206 } while
1207 (lp-- > curlp);
1208 return(FALSE);
1209
1210 default:
1211 fprintf(stderr, "sed: RE error, %o\n", *--ep);
1212 quit(2);
1213 }
1214}
1215
1216static int substitute(ipc)
1217/* Perform s command */
1218sedcmd *ipc; /* ptr to s command struct */
1219{
1220 int nullmatch;
1221
1222 if (match(ipc->u.lhs, 0)) { /* if no match */
1223 nullmatch = (loc1 == loc2);
1224 dosub(ipc->rhs); /* perform it once */
1225 } else
1226 return(FALSE); /* command fails */
1227
1228 if (ipc->flags.global) /* if global flag enabled */
1229 while (*loc2) { /* cycle through possibles */
1230 if (nullmatch) loc2++;
1231 if (match(ipc->u.lhs, 1)) { /* found another */
1232 nullmatch = (loc1 == loc2);
1233 dosub(ipc->rhs); /* so substitute */
1234 } else /* otherwise, */
1235 break; /* we're done */
1236 }
1237 return(TRUE); /* we succeeded */
1238}
1239
1240static void dosub(rhsbuf) /* uses linebuf, genbuf, spend */
1241 /* Generate substituted right-hand side (of s command) */
1242char *rhsbuf; /* where to put the result */
1243{
1244 register char *lp, *sp, *rp;
1245 int c;
1246
1247 /* Copy linebuf to genbuf up to location 1 */
1248 lp = linebuf;
1249 sp = genbuf;
1250 while (lp < loc1) *sp++ = *lp++;
1251
1252 for (rp = rhsbuf; c = *rp++;) {
1253 if (c == '&') {
1254 sp = place(sp, loc1, loc2);
1255 continue;
1256 } else if (c & 0200 && (c &= 0177) >= '1' && c < MAXTAGS + '1') {
1257 sp = place(sp, brastart[c - '1'], bracend[c - '1']);
1258 continue;
1259 }
1260 *sp++ = c & 0177;
1261 if (sp >= genbuf + MAXBUF) fprintf(stderr, LTLMSG);
1262 }
1263 lp = loc2;
1264 loc2 = sp - genbuf + linebuf;
1265 while (*sp++ = *lp++)
1266 if (sp >= genbuf + MAXBUF) fprintf(stderr, LTLMSG);
1267 lp = linebuf;
1268 sp = genbuf;
1269 while (*lp++ = *sp++);
1270 spend = lp - 1;
1271}
1272
1273static char *place(asp, al1, al2) /* uses genbuf */
1274 /* Place chars at *al1...*(al1 - 1) at asp... in genbuf[] */
1275register char *asp, *al1, *al2;
1276{
1277 while (al1 < al2) {
1278 *asp++ = *al1++;
1279 if (asp >= genbuf + MAXBUF) fprintf(stderr, LTLMSG);
1280 }
1281 return(asp);
1282}
1283
1284static void listto(p1, fp)
1285/* Write a hex dump expansion of *p1... to fp */
1286register char *p1; /* the source */
1287FILE *fp; /* output stream to write to */
1288{
1289 p1--;
1290 while (*p1++)
1291 if (isprint(*p1))
1292 putc(*p1, fp); /* pass it through */
1293 else {
1294 putc('\\', fp); /* emit a backslash */
1295 switch (*p1) {
1296 case '\b':
1297 putc('b', fp);
1298 break; /* BS */
1299 case '\t':
1300 putc('t', fp);
1301 break; /* TAB */
1302 case '\n':
1303 putc('n', fp);
1304 break; /* NL */
1305 case '\r':
1306 putc('r', fp);
1307 break; /* CR */
1308 case '\33':
1309 putc('e', fp);
1310 break; /* ESC */
1311 default:
1312 fprintf(fp, "%02x", *p1 & 0xFF);
1313 }
1314 }
1315 putc('\n', fp);
1316}
1317
1318static void truncated(h)
1319int h;
1320{
1321 static long last = 0L;
1322
1323 if (lnum == last) return;
1324 last = lnum;
1325
1326 fprintf(stderr, "sed: ");
1327 fprintf(stderr, h ? "hold space" : "line %ld", lnum);
1328 fprintf(stderr, " truncated to %d characters\n", MAXBUF);
1329}
1330
1331static void command(ipc)
1332/* Execute compiled command pointed at by ipc */
1333sedcmd *ipc;
1334{
1335 static char holdsp[MAXHOLD + 1]; /* the hold space */
1336 static char *hspend = holdsp; /* hold space end pointer */
1337 register char *p1, *p2;
1338 char *execp;
1339 int didsub; /* true if last s succeeded */
1340
1341 switch (ipc->command) {
1342 case ACMD: /* append */
1343 *aptr++ = ipc;
1344 if (aptr >= appends + MAXAPPENDS) fprintf(stderr,
1345 "sed: too many appends after line %ld\n",
1346 lnum);
1347 *aptr = 0;
1348 break;
1349
1350 case CCMD: /* change pattern space */
1351 delete = TRUE;
1352 if (!ipc->flags.inrange || lastline) printf("%s\n", ipc->u.lhs);
1353 break;
1354
1355 case DCMD: /* delete pattern space */
1356 delete++;
1357 break;
1358
1359 case CDCMD: /* delete a line in hold space */
1360 p1 = p2 = linebuf;
1361 while (*p1 != '\n')
1362 if (delete = (*p1++ == 0)) return;
1363 p1++;
1364 while (*p2++ = *p1++) continue;
1365 spend = p2 - 1;
1366 jump++;
1367 break;
1368
1369 case EQCMD: /* show current line number */
1370 fprintf(stdout, "%ld\n", lnum);
1371 break;
1372
1373 case GCMD: /* copy hold space to pattern space */
1374 p1 = linebuf;
1375 p2 = holdsp;
1376 while (*p1++ = *p2++);
1377 spend = p1 - 1;
1378 break;
1379
1380 case CGCMD: /* append hold space to pattern space */
1381 *spend++ = '\n';
1382 p1 = spend;
1383 p2 = holdsp;
1384 do
1385 if (p1 > linebuf + MAXBUF) {
1386 truncated(0);
1387 p1[-1] = 0;
1388 break;
1389 }
1390 while (*p1++ = *p2++);
1391
1392 spend = p1 - 1;
1393 break;
1394
1395 case HCMD: /* copy pattern space to hold space */
1396 p1 = holdsp;
1397 p2 = linebuf;
1398 while (*p1++ = *p2++);
1399 hspend = p1 - 1;
1400 break;
1401
1402 case CHCMD: /* append pattern space to hold space */
1403 *hspend++ = '\n';
1404 p1 = hspend;
1405 p2 = linebuf;
1406 do
1407 if (p1 > holdsp + MAXBUF) {
1408 truncated(1);
1409 p1[-1] = 0;
1410 break;
1411 }
1412 while (*p1++ = *p2++);
1413
1414 hspend = p1 - 1;
1415 break;
1416
1417 case ICMD: /* insert text */
1418 printf("%s\n", ipc->u.lhs);
1419 break;
1420
1421 case BCMD: /* branch to label */
1422 jump = TRUE;
1423 break;
1424
1425 case LCMD: /* list text */
1426 listto(linebuf, (ipc->fout != NULL) ? ipc->fout : stdout);
1427 break;
1428
1429 case NCMD: /* read next line into pattern space */
1430 if (!nflag) puts(linebuf); /* flush out the current line */
1431 if (aptr > appends) readout(); /* do pending a, r commands */
1432 if ((execp = getline(linebuf)) == BAD) {
1433 delete = TRUE;
1434 break;
1435 }
1436 spend = execp;
1437 anysub = FALSE;
1438 break;
1439
1440 case CNCMD: /* append next line to pattern space */
1441 if (aptr > appends) readout();
1442 *spend++ = '\n';
1443 if ((execp = getline(spend)) == BAD) {
1444 *--spend = 0;
1445 break;
1446 }
1447 spend = execp;
1448 anysub = FALSE;
1449 break;
1450
1451 case PCMD: /* print pattern space */
1452 puts(linebuf);
1453 break;
1454
1455 case CPCMD: /* print one line from pattern space */
1456cpcom: /* so s command can jump here */
1457 for (p1 = linebuf; *p1 != '\n' && *p1 != '\0';) putc(*p1++, stdout);
1458 putc('\n', stdout);
1459 break;
1460
1461 case QCMD: /* quit the stream editor */
1462 if (!nflag) puts(linebuf); /* flush out the current line */
1463 if (aptr > appends)
1464 readout(); /* do any pending a and r commands */
1465 quit(0);
1466
1467 case RCMD: /* read a file into the stream */
1468 *aptr++ = ipc;
1469 if (aptr >= appends + MAXAPPENDS) fprintf(stderr,
1470 "sed: too many reads after line %ld\n",
1471 lnum);
1472 *aptr = 0;
1473 break;
1474
1475 case SCMD: /* substitute RE */
1476 didsub = substitute(ipc);
1477 if (didsub) anysub = TRUE;
1478 if (ipc->flags.print && didsub)
1479 if (ipc->flags.print == TRUE)
1480 puts(linebuf);
1481 else
1482 goto cpcom;
1483 if (didsub && ipc->fout) fprintf(ipc->fout, "%s\n", linebuf);
1484 break;
1485
1486 case TCMD: /* branch on any s successful */
1487 case CTCMD: /* branch on any s failed */
1488 if (anysub == (ipc->command == CTCMD))
1489 break; /* no branch if any s failed, else */
1490 anysub = FALSE;
1491 jump = TRUE; /* set up to jump to assoc'd label */
1492 break;
1493
1494 case CWCMD: /* write one line from pattern space */
1495 for (p1 = linebuf; *p1 != '\n' && *p1 != '\0';)
1496 putc(*p1++, ipc->fout);
1497 putc('\n', ipc->fout);
1498 break;
1499
1500 case WCMD: /* write pattern space to file */
1501 fprintf(ipc->fout, "%s\n", linebuf);
1502 break;
1503
1504 case XCMD: /* exchange pattern and hold spaces */
1505 p1 = linebuf;
1506 p2 = genbuf;
1507 while (*p2++ = *p1++) continue;
1508 p1 = holdsp;
1509 p2 = linebuf;
1510 while (*p2++ = *p1++) continue;
1511 spend = p2 - 1;
1512 p1 = genbuf;
1513 p2 = holdsp;
1514 while (*p2++ = *p1++) continue;
1515 hspend = p2 - 1;
1516 break;
1517
1518 case YCMD:
1519 p1 = linebuf;
1520 p2 = ipc->u.lhs;
1521 while (*p1 = p2[*p1]) p1++;
1522 break;
1523 }
1524}
1525
1526static void openfile(file)
1527char *file;
1528/* Replace stdin by given file */
1529{
1530 if (freopen(file, "r", stdin) == NULL) {
1531 fprintf(stderr, "sed: can't open %s\n", file);
1532 quit(1);
1533 }
1534}
1535
1536static int c; /* Will be the next char to read, a kind of
1537 * lookahead */
1538
1539static void get()
1540/* Read next character into c treating all argument files as run through cat */
1541{
1542 while ((c = getchar()) == EOF && --eargc >= 0) openfile(*eargv++);
1543}
1544
1545static void initget()
1546/* Initialise character input */
1547{
1548 if (--eargc >= 0) openfile(*eargv++); /* else input == stdin */
1549 get();
1550}
1551
1552static char *getline(buf)
1553/* Get next line of text to be edited, return pointer to end */
1554register char *buf; /* where to send the input */
1555{
1556 if (c == EOF) return BAD;
1557
1558 lnum++; /* we can read a new line */
1559
1560 do {
1561 if (c == '\n') {
1562 get();
1563 break;
1564 }
1565 if (buf <= linebuf + MAXBUF) *buf++ = c;
1566 get();
1567 } while (c != EOF);
1568
1569 if (c == EOF) lastline = TRUE;
1570
1571 if (buf > linebuf + MAXBUF) {
1572 truncated(0);
1573 --buf;
1574 }
1575 *buf = 0;
1576 return buf;
1577}
1578
1579static int Memcmp(a, b, count)
1580/* Return TRUE if *a... == *b... for count chars, FALSE otherwise */
1581register char *a, *b;
1582int count;
1583{
1584 while (count--) /* look at count characters */
1585 if (*a++ != *b++) /* if any are nonequal */
1586 return(FALSE); /* return FALSE for false */
1587 return(TRUE); /* compare succeeded */
1588}
1589
1590static void readout()
1591/* Write file indicated by r command to output */
1592{
1593 register int t; /* hold input char or EOF */
1594 FILE *fi; /* ptr to file to be read */
1595
1596 aptr = appends - 1; /* arrange for pre-increment to work right */
1597 while (*++aptr)
1598 if ((*aptr)->command == ACMD) /* process "a" cmd */
1599 printf("%s\n", (*aptr)->u.lhs);
1600 else { /* process "r" cmd */
1601 if ((fi = fopen((*aptr)->u.lhs, "r")) == NULL) {
1602 fprintf(stderr, "sed: can't open %s\n",
1603 (*aptr)->u.lhs);
1604 continue;
1605 }
1606 while ((t = getc(fi)) != EOF) putc((char) t, stdout);
1607 fclose(fi);
1608 }
1609 aptr = appends; /* reset the append ptr */
1610 *aptr = 0;
1611}
1612
1613/* Sedexec.c ends here */
Note: See TracBrowser for help on using the repository browser.