fix packman sort col, and make sort case-insensitive
[minix3.git] / commands / simple / sed.c
blob4f11bfa643bd824518ac5a2c089ac9dbf01e98b2
1 /* sed - stream editor Author: Eric S. Raymond */
3 /* This used to be three different files with the following makefile:
4 * (Note the chmem).
6 CFLAGS= -F -T.
8 OBJS= sedcomp.s sedexec.s
10 sed: $(OBJS)
11 cc -T. -o sed $(OBJS)
12 @chmem =13312 sed
14 $(OBJS): sed.h
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.
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>
28 /*+++++++++++++++*/
30 /* Sed.h -- types and constants for the stream editor */
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 */
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 */
64 struct 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;
81 typedef struct cmd_t sedcmd; /* use this name for declarations */
83 #define BAD ((char *) -1) /* guaranteed not a string ptr */
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 */
102 /* Sed.h ends here */
104 #ifndef CMASK
105 #define CMASK 0xFF /* some char type should have been unsigned
106 * char? */
107 #endif
109 /*+++++++++++++++*/
111 /* Sed - stream editor Author: Eric S. Raymond */
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.
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.
126 The operation of execute() is described in its source module.
129 /* #include <stdio.h> */
130 /* #include "sed.h" */
132 /* Imported functions */
134 /***** public stuff ******/
136 #define MAXCMDS 500 /* maximum number of compiled commands */
137 #define MAXLINES 256 /* max # numeric addresses to compile */
139 /* Main data areas */
140 char linebuf[MAXBUF + 1]; /* current-line buffer */
141 sedcmd cmds[MAXCMDS + 1]; /* hold compiled commands */
142 long linenum[MAXLINES]; /* numeric-addresses table */
144 /* Miscellaneous shared variables */
145 int nflag; /* -n option flag */
146 int eargc; /* scratch copy of argument count */
147 char **eargv; /* scratch copy of argument list */
148 char bits[] = {1, 2, 4, 8, 16, 32, 64, 128};
150 /***** module common stuff *****/
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 */
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 */
162 /* Error messages */
163 static char AGMSG[] = "sed: garbled address %s\n";
164 static char CGMSG[] = "sed: garbled command %s\n";
165 static char TMTXT[] = "sed: too much text: %s\n";
166 static char AD1NG[] = "sed: no addresses allowed for %s\n";
167 static char AD2NG[] = "sed: only one address allowed for %s\n";
168 static char TMCDS[] = "sed: too many commands, last was %s\n";
169 static char COCFI[] = "sed: cannot open command-file %s\n";
170 static char UFLAG[] = "sed: unknown flag %c\n";
171 static char CCOFI[] = "sed: cannot create %s\n";
172 static char ULABL[] = "sed: undefined label %s\n";
173 static char TMLBR[] = "sed: too many {'s\n";
174 static char FRENL[] = "sed: first RE must be non-null\n";
175 static char NSCAX[] = "sed: no such command as %s\n";
176 static char TMRBR[] = "sed: too many }'s\n";
177 static char DLABL[] = "sed: duplicate label %s\n";
178 static char TMLAB[] = "sed: too many labels: %s\n";
179 static char TMWFI[] = "sed: too many w files\n";
180 static char REITL[] = "sed: RE too long: %s\n";
181 static char TMLNR[] = "sed: too many line numbers\n";
182 static char TRAIL[] = "sed: command \"%s\" has trailing garbage\n";
184 typedef 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 */
190 label;
192 /* Label handling */
193 static label labels[MAXLABS]; /* here's the label table */
194 static label *lab = labels + 1; /* pointer to current label */
195 static label *lablst = labels; /* header for search list */
197 /* String pool for regular expressions, append text, etc. etc. */
198 static char pool[POOLSIZE]; /* the pool */
199 static char *fp = pool; /* current pool pointer */
200 static char *poolend = pool + POOLSIZE; /* pointer past pool end */
202 /* Compilation state */
203 static FILE *cmdf = NULL; /* current command source */
204 static char *cp = linebuf; /* compile pointer */
205 static sedcmd *cmdp = cmds; /* current compiled-cmd ptr */
206 static char *lastre = NULL; /* old RE pointer */
207 static int bdepth = 0; /* current {}-nesting level */
208 static int bcount = 0; /* # tagged patterns in current RE */
210 /* Compilation flags */
211 static int eflag; /* -e option flag */
212 static int gflag; /* -g option flag */
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));
243 int main(argc, argv)
244 /* Main sequence of the stream editor */
245 int argc;
246 char *argv[];
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);
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;
281 if (cmdp == cmds) { /* no commands have been compiled */
282 eargv--;
283 eargc++;
284 eflag++;
285 compile();
286 eflag = 0;
287 eargv++;
288 eargc--;
290 if (bdepth) /* we have unbalanced squigglies */
291 ABORT(TMLBR);
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);
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 */
305 /* Indirect through this to get command internal code, if it exists */
306 static char cmdmask[] =
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,
319 static void compile()
320 /* Precompile sed commands out of a file */
322 char ccode;
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;
330 SKIPWS(cp);
331 if (*cp == '\0') /* empty */
332 continue;
333 if (*cp == '#') { /* comment */
334 while (*cp) ++cp;
335 continue;
337 if (*cp == ';') { /* ; separates cmds */
338 cp++;
339 continue;
342 /* Compile first address */
343 if (fp > poolend)
344 ABORT(TMTXT);
345 else if ((fp = address(cmdp->addr1 = fp)) == BAD)
346 ABORT(AGMSG);
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 */
370 if (fp > poolend) ABORT(TMTXT);
372 SKIPWS(cp); /* discard whitespace after address */
373 IFEQ(cp, '!') cmdp->flags.allbut = 1;
375 SKIPWS(cp); /* get cmd char, range-check it */
376 if ((*cp < LOWCMD) || (*cp > '~')
377 || ((ccode = cmdmask[*cp - LOWCMD]) == 0))
378 ABORT(NSCAX);
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 */
386 if (++cmdp >= cmds + MAXCMDS) ABORT(TMCDS);
388 SKIPWS(cp); /* look for trailing stuff */
389 if (*cp != '\0' && *cp != ';' && *cp != '#') ABORT(TRAIL);
393 static int cmdcomp(cchar)
394 /* Compile a single command */
395 register char cchar; /* character name of command */
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 */
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);
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);
420 case '=': /* print current source line number */
421 case 'q': /* exit the stream editor */
422 if (cmdp->addr2) ABORT(AD2NG);
423 break;
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);
435 lpt->address = cmdp;
436 return(1);
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;
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;
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);
466 break;
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;
477 case 'D': /* delete current line in hold space */
478 cmdp->u.link = cmds;
479 break;
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;
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 */
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);
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);
519 fout[nwfiles++] = cmdp->fout;
520 break;
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;
528 return(0); /* succeeded in interpreting one command */
531 static char *rhscomp(rhsp, delim) /* uses bcount */
532 /* Generate replacement string for substitute command right hand side */
533 register char *rhsp; /* place to compile expression to */
534 register char delim; /* regular-expression end-mark to look for */
536 register char *p = cp; /* strictly for speed */
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);
553 static char *recomp(expbuf, redelim) /* uses cp, bcount */
554 /* Compile a regular expression to internal form */
555 char *expbuf; /* place to compile it to */
556 char redelim; /* RE end-marker to look for */
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 */
569 if (*cp == redelim) /* if first char is RE endmarker */
570 return(cp++, expbuf); /* leave existing RE unchanged */
572 lastep = NULL; /* there's no previous RE */
573 brnestp = brnest; /* initialize ptr to brnest array */
574 tags = bcount = 0; /* initialize counters */
576 if (*ep++ = (*sp == '^')) /* check for start-of-line syntax */
577 sp++;
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 */
589 if (c != '*') /* if we're a postfix op */
590 lastep = ep; /* get ready to match last */
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;
623 case '\0': /* ignore nuls */
624 continue;
626 case '\n': /* trailing pattern delimiter is missing */
627 return(cp = sp, BAD);
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;
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;
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);
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];
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';
668 /* Enter (possibly translated) char in set */
669 ep[c >> 3] |= bits[c & 7];
670 } while
671 ((c = *sp++) != ']');
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;
680 defchar: /* match literal character */
681 default: /* which is what we'd do by default */
682 *ep++ = CCHR; /* insert character mark */
683 *ep++ = c;
688 static int cmdline(cbuf) /* uses eflag, eargc, cmdf */
689 /* Read next command from -e argument or command file */
690 register char *cbuf;
692 register int inc; /* not char because must hold EOF */
694 *cbuf-- = 0; /* so pre-increment points us at cbuf */
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 */
701 if (eflag > 0) { /* there are pending -e arguments */
702 eflag = -1;
703 if (eargc-- <= 0) quit(2); /* if no arguments, barf */
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... */
719 /* Found end-of-string; can advance to next argument */
720 return(savep = NULL, 1);
722 if ((p = savep) == NULL) return(-1);
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);
734 return(savep = NULL, 1);
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 */
744 return(*++cbuf = '\0', -1); /* end-of-file, no more chars */
747 static char *address(expbuf) /* uses cp, linenum */
748 /* Expand an address at *cp... into expbuf, return ptr at following char */
749 register char *expbuf;
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 */
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 */
761 if (*cp == '/' || *cp == '\\') { /* start of regular-expression match */
762 if (*cp == '\\') cp++;
763 return(recomp(expbuf, *cp++)); /* compile the RE */
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 */
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 */
781 return(NULL); /* no legal address was found */
784 static char *gettext(txp) /* uses global cp */
785 /* Accept multiline input from *cp..., discarding leading whitespace */
786 register char *txp; /* where to put the text */
788 register char *p = cp; /* this is for speed */
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);
803 static label *search(ptr) /* uses global lablst */
804 /* Find the label matching *ptr, return NULL if none */
805 register label *ptr;
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);
814 static void resolve()
815 { /* uses global lablst */
816 /* Write label links into the compiled-command space */
817 register label *lptr;
818 register sedcmd *rptr, *trptr;
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;
831 rptr->u.link = lptr->address;
835 static char *ycomp(ep, delim)
836 /* Compile a y (transliterate) command */
837 register char *ep; /* where to compile to */
838 char delim; /* end delimiter to look for */
840 register char *tp, *sp;
841 register int c;
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);
848 tp++; /* tp now points at first char of 'to'
849 * section */
851 /* Now rescan the 'from' section */
852 while ((c = *sp++ & 0x7F) != delim) {
853 if (c == '\\' && *sp == 'n') {
854 sp++;
855 c = '\n';
857 if ((ep[c] = *tp++) == '\\' && *tp == 'n') {
858 ep[c] = '\n';
859 tp++;
861 if ((ep[c] == delim) || (ep[c] == '\0')) return(BAD);
864 if (*tp != delim) /* 'to', 'from' parts have unequal lengths */
865 return(BAD);
867 cp = ++tp; /* point compile ptr past translit */
869 for (c = 0; c < 128; c++) /* fill in self-map entries in table */
870 if (ep[c] == 0) ep[c] = c;
872 return(ep + 0x80); /* return first free location past table end */
875 void quit(n)
876 int n;
878 /* Flush buffers and exit. Now a historical relic. Rely on exit to flush
879 * the buffers.
881 exit(n);
884 /*+++++++++++++++*/
887 sedexec.c -- execute compiled form of stream editor commands
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.
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.
900 /* #include <stdio.h> */
901 /* #include <ctype.h> */
902 /* #include "sed.h" */
904 /***** shared variables imported from the main ******/
906 /* Main data areas */
907 extern char linebuf[]; /* current-line buffer */
908 extern sedcmd cmds[]; /* hold compiled commands */
909 extern long linenum[]; /* numeric-addresses table */
911 /* Miscellaneous shared variables */
912 extern int nflag; /* -n option flag */
913 extern int eargc; /* scratch copy of argument count */
914 extern char **eargv; /* scratch copy of argument list */
915 extern char bits[]; /* the bits table */
917 /***** end of imported stuff *****/
919 #define MAXHOLD MAXBUF /* size of the hold space */
920 #define GENSIZ MAXBUF /* maximum genbuf size */
922 #define TRUE 1
923 #define FALSE 0
925 static char LTLMSG[] = "sed: line too long\n";
927 static char *spend; /* current end-of-line-buffer pointer */
928 static long lnum = 0L; /* current source line number */
930 /* Append buffer maintenance */
931 static sedcmd *appends[MAXAPPENDS]; /* array of ptrs to a,i,c commands */
932 static sedcmd **aptr = appends; /* ptr to current append */
934 /* Genbuf and its pointers */
935 static char genbuf[GENSIZ];
936 static char *loc1;
937 static char *loc2;
938 static char *locs;
940 /* Command-logic flags */
941 static int lastline; /* do-line flag */
942 static int jump; /* jump to cmd's link address if set */
943 static int delete; /* delete command flag */
945 /* Tagged-pattern tracking */
946 static char *bracend[MAXTAGS]; /* tagged pattern start pointers */
947 static char *brastart[MAXTAGS]; /* tagged pattern end pointers */
949 static int anysub; /* true if any s on current line succeeded */
952 void execute()
953 /* Execute the compiled commands in cmds[] */
955 register char *p1; /* dummy copy ptrs */
956 register sedcmd *ipc; /* ptr to current command */
957 char *execp; /* ptr to source */
960 initget();
962 /* Here's the main command-execution loop */
963 for (;;) {
965 /* Get next line to filter */
966 if ((execp = getline(linebuf)) == BAD) return;
967 spend = execp;
968 anysub = FALSE;
970 /* Loop through compiled commands, executing them */
971 for (ipc = cmds; ipc->command;) {
972 if (!selected(ipc)) {
973 ipc++;
974 continue;
976 command(ipc); /* execute the command pointed at */
978 if (delete) /* if delete flag is set */
979 break; /* don't exec rest of compiled cmds */
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;
987 } else /* normal goto next command */
988 ipc++;
991 /* We've now done all modification commands on the line */
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);
999 /* If we've been set up for append, emit the text from it */
1000 if (aptr > appends) readout();
1002 delete = FALSE; /* clear delete flag; about to get next cmd */
1006 static int selected(ipc)
1007 /* Is current command selected */
1008 sedcmd *ipc;
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 */
1015 if (!p1) /* No addresses: always selected */
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;
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;
1040 return ipc->flags.allbut ? !sel : sel;
1043 static int match(expbuf, gf) /* uses genbuf */
1044 /* Match RE at expbuf against linebuf; if gf set, copy linebuf from genbuf */
1045 char *expbuf;
1046 int gf;
1048 register char *p1, *p2, c;
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;
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 */
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 */
1081 /* Else try for unanchored match of the pattern */
1082 do {
1083 if (advance(p1, p2)) return(loc1 = p1, 1);
1084 } while
1085 (*p1++);
1087 /* If got here, didn't match either way */
1088 return(FALSE);
1091 static int advance(lp, ep)
1092 /* Attempt to advance match pointer by one pattern element */
1093 register char *lp; /* source (linebuf) ptr */
1094 register char *ep; /* regular expression element ptr */
1096 register char *curlp; /* save ptr for closures */
1097 char c; /* scratch character holder */
1098 char *bbeg;
1099 int ct;
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 */
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 */
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 */
1118 case CEOF: /* end-of-address mark */
1119 loc2 = lp; /* set second loc */
1120 return(TRUE); /* return true */
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 */
1128 return(FALSE); /* else return false */
1130 case CBRA: /* start of tagged pattern */
1131 brastart[*ep++] = lp; /* mark it */
1132 continue; /* and go */
1134 case CKET: /* end of tagged pattern */
1135 bracend[*ep++] = lp; /* mark it */
1136 continue; /* and go */
1138 case CBACK:
1139 bbeg = brastart[*ep];
1140 ct = bracend[*ep++] - bbeg;
1142 if (Memcmp(bbeg, lp, ct)) {
1143 lp += ct;
1144 continue;
1146 return(FALSE);
1148 case CBACK | STAR:
1149 bbeg = brastart[*ep];
1150 ct = bracend[*ep++] - bbeg;
1151 curlp = lp;
1152 while (Memcmp(bbeg, lp, ct)) lp += ct;
1154 while (lp >= curlp) {
1155 if (advance(lp, ep)) return(TRUE);
1156 lp -= ct;
1158 return(FALSE);
1161 case CDOT | STAR: /* match .* */
1162 curlp = lp; /* save closure start loc */
1163 while (*lp++); /* match anything */
1164 goto star; /* now look for followers */
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 */
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 */
1181 star: /* the recursion part of a * or + match */
1182 if (--lp == curlp) /* 0 matches */
1183 continue;
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);
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);
1203 do {
1204 if (lp == locs) break;
1205 if (advance(lp, ep)) return (TRUE);
1206 } while
1207 (lp-- > curlp);
1208 return(FALSE);
1210 default:
1211 fprintf(stderr, "sed: RE error, %o\n", *--ep);
1212 quit(2);
1216 static int substitute(ipc)
1217 /* Perform s command */
1218 sedcmd *ipc; /* ptr to s command struct */
1220 int nullmatch;
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 */
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 */
1237 return(TRUE); /* we succeeded */
1240 static void dosub(rhsbuf) /* uses linebuf, genbuf, spend */
1241 /* Generate substituted right-hand side (of s command) */
1242 char *rhsbuf; /* where to put the result */
1244 register char *lp, *sp, *rp;
1245 int c;
1247 /* Copy linebuf to genbuf up to location 1 */
1248 lp = linebuf;
1249 sp = genbuf;
1250 while (lp < loc1) *sp++ = *lp++;
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;
1260 *sp++ = c & 0177;
1261 if (sp >= genbuf + MAXBUF) fprintf(stderr, LTLMSG);
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;
1273 static char *place(asp, al1, al2) /* uses genbuf */
1274 /* Place chars at *al1...*(al1 - 1) at asp... in genbuf[] */
1275 register char *asp, *al1, *al2;
1277 while (al1 < al2) {
1278 *asp++ = *al1++;
1279 if (asp >= genbuf + MAXBUF) fprintf(stderr, LTLMSG);
1281 return(asp);
1284 static void listto(p1, fp)
1285 /* Write a hex dump expansion of *p1... to fp */
1286 register char *p1; /* the source */
1287 FILE *fp; /* output stream to write to */
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);
1315 putc('\n', fp);
1318 static void truncated(h)
1319 int h;
1321 static long last = 0L;
1323 if (lnum == last) return;
1324 last = lnum;
1326 fprintf(stderr, "sed: ");
1327 fprintf(stderr, h ? "hold space" : "line %ld", lnum);
1328 fprintf(stderr, " truncated to %d characters\n", MAXBUF);
1331 static void command(ipc)
1332 /* Execute compiled command pointed at by ipc */
1333 sedcmd *ipc;
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 */
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;
1350 case CCMD: /* change pattern space */
1351 delete = TRUE;
1352 if (!ipc->flags.inrange || lastline) printf("%s\n", ipc->u.lhs);
1353 break;
1355 case DCMD: /* delete pattern space */
1356 delete++;
1357 break;
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;
1369 case EQCMD: /* show current line number */
1370 fprintf(stdout, "%ld\n", lnum);
1371 break;
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;
1380 case CGCMD: /* append hold space to pattern space */
1381 *spend++ = '\n';
1382 p1 = spend;
1383 p2 = holdsp;
1385 if (p1 > linebuf + MAXBUF) {
1386 truncated(0);
1387 p1[-1] = 0;
1388 break;
1390 while (*p1++ = *p2++);
1392 spend = p1 - 1;
1393 break;
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;
1402 case CHCMD: /* append pattern space to hold space */
1403 *hspend++ = '\n';
1404 p1 = hspend;
1405 p2 = linebuf;
1407 if (p1 > holdsp + MAXBUF) {
1408 truncated(1);
1409 p1[-1] = 0;
1410 break;
1412 while (*p1++ = *p2++);
1414 hspend = p1 - 1;
1415 break;
1417 case ICMD: /* insert text */
1418 printf("%s\n", ipc->u.lhs);
1419 break;
1421 case BCMD: /* branch to label */
1422 jump = TRUE;
1423 break;
1425 case LCMD: /* list text */
1426 listto(linebuf, (ipc->fout != NULL) ? ipc->fout : stdout);
1427 break;
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;
1436 spend = execp;
1437 anysub = FALSE;
1438 break;
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;
1447 spend = execp;
1448 anysub = FALSE;
1449 break;
1451 case PCMD: /* print pattern space */
1452 puts(linebuf);
1453 break;
1455 case CPCMD: /* print one line from pattern space */
1456 cpcom: /* so s command can jump here */
1457 for (p1 = linebuf; *p1 != '\n' && *p1 != '\0';) putc(*p1++, stdout);
1458 putc('\n', stdout);
1459 break;
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);
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;
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;
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;
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;
1500 case WCMD: /* write pattern space to file */
1501 fprintf(ipc->fout, "%s\n", linebuf);
1502 break;
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;
1518 case YCMD:
1519 p1 = linebuf;
1520 p2 = ipc->u.lhs;
1521 while (*p1 = p2[*p1]) p1++;
1522 break;
1526 static void openfile(file)
1527 char *file;
1528 /* Replace stdin by given file */
1530 if (freopen(file, "r", stdin) == NULL) {
1531 fprintf(stderr, "sed: can't open %s\n", file);
1532 quit(1);
1536 static int c; /* Will be the next char to read, a kind of
1537 * lookahead */
1539 static void get()
1540 /* Read next character into c treating all argument files as run through cat */
1542 while ((c = getchar()) == EOF && --eargc >= 0) openfile(*eargv++);
1545 static void initget()
1546 /* Initialise character input */
1548 if (--eargc >= 0) openfile(*eargv++); /* else input == stdin */
1549 get();
1552 static char *getline(buf)
1553 /* Get next line of text to be edited, return pointer to end */
1554 register char *buf; /* where to send the input */
1556 if (c == EOF) return BAD;
1558 lnum++; /* we can read a new line */
1560 do {
1561 if (c == '\n') {
1562 get();
1563 break;
1565 if (buf <= linebuf + MAXBUF) *buf++ = c;
1566 get();
1567 } while (c != EOF);
1569 if (c == EOF) lastline = TRUE;
1571 if (buf > linebuf + MAXBUF) {
1572 truncated(0);
1573 --buf;
1575 *buf = 0;
1576 return buf;
1579 static int Memcmp(a, b, count)
1580 /* Return TRUE if *a... == *b... for count chars, FALSE otherwise */
1581 register char *a, *b;
1582 int count;
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 */
1590 static void readout()
1591 /* Write file indicated by r command to output */
1593 register int t; /* hold input char or EOF */
1594 FILE *fi; /* ptr to file to be read */
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;
1606 while ((t = getc(fi)) != EOF) putc((char) t, stdout);
1607 fclose(fi);
1609 aptr = appends; /* reset the append ptr */
1610 *aptr = 0;
1613 /* Sedexec.c ends here */