1 /* sed - stream editor Author: Eric S. Raymond */
3 /* This used to be three different files with the following makefile:
8 OBJS= sedcomp.s sedexec.s
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.
22 #include <sys/types.h>
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 */
68 char *lhs
; /* s command lhs */
69 struct cmd_t
*link
; /* label link */
71 char command
; /* command code */
72 char *rhs
; /* s command replacement string */
73 FILE *fout
; /* associated output file descriptor */
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? */
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
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 */
105 #define CMASK 0xFF /* some char type should have been unsigned
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 */
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 */
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));
244 /* Main sequence of the stream editor */
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]) {
256 compile(); /* compile with e flag on */
258 continue; /* get another argument */
260 if (eargc
-- <= 0) /* barf if no -f file */
262 if ((cmdf
= fopen(*++eargv
, "r")) == NULL
) {
263 fprintf(stderr
, COCFI
, *eargv
);
266 compile(); /* file is O.K., compile it */
268 continue; /* go back for another argument */
270 gflag
++; /* set global flag on all s cmds */
273 nflag
++; /* no print except on p flag or w */
276 fprintf(stdout
, UFLAG
, eargv
[0][1]);
281 if (cmdp
== cmds
) { /* no commands have been compiled */
290 if (bdepth
) /* we have unbalanced squigglies */
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 */
301 #define H 0x80 /* 128 bit, on if there's really code for
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 */
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;
331 if (*cp
== '\0') /* empty */
333 if (*cp
== '#') { /* comment */
337 if (*cp
== ';') { /* ; separates cmds */
342 /* Compile first address */
345 else if ((fp
= address(cmdp
->addr1
= fp
)) == BAD
)
348 if (fp
== cmdp
->addr1
) {/* if empty RE was found */
349 if (lastre
) /* if there was previous RE */
350 cmdp
->addr1
= lastre
; /* use it */
353 } else if (fp
== NULL
) {/* if fp was NULL */
354 fp
= cmdp
->addr1
; /* use current pool location */
357 lastre
= cmdp
->addr1
;
358 if (*cp
== ',' || *cp
== ';') { /* there's 2nd addr */
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
;
366 lastre
= cmdp
->addr2
;
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))
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 */
404 char redelim
; /* current RE delimiter */
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
);
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 */
420 case '=': /* print current source line number */
421 case 'q': /* exit the stream editor */
422 if (cmdp
->addr2
) ABORT(AD2NG
);
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 */
433 if (++lab
>= labels
+ MAXLABS
) ABORT(TMLAB
);
438 case 'b': /* branch command */
439 case 't': /* branch-on-succeed command */
440 case 'T': /* branch-on-fail command */
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
;
447 } else /* lablst->last == NULL */
451 fp
= gettext(lab
->name
= fp
); /* else get label into pool */
452 if (lpt
= search(lab
)) {/* enter branch to it */
454 cmdp
->u
.link
= lpt
->address
;
457 while (sp2
= sp1
->u
.link
) sp1
= sp2
;
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 */
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
);
477 case 'D': /* delete current line in hold space */
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 */
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 */
499 cp
++; /* and execute a w command! */
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
];
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
]);
519 fout
[nwfiles
++] = cmdp
->fout
;
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 */
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 */
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 */
545 } else if (*rhsp
== delim
) { /* found RE end, hooray... */
546 *rhsp
++ = '\0'; /* cap the expression string */
548 return(rhsp
); /* pt at 1 past the RE */
549 } else if (*rhsp
++ == '\0') /* last ch not RE end, help! */
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 */
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 */
584 if (brnestp
!= brnest
) /* \(, \) unbalanced */
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 */
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 */
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 */
607 } else if (c
>= '1' && c
<= '9') { /* tag use */
608 if ((c
-= '1') >= tags
) /* too few */
610 *ep
++ = CBACK
; /* enter tag mark */
611 *ep
++ = c
; /* and the number */
613 } else if (c
== '\n') /* escaped newline no good */
614 return(cp
= sp
, BAD
);
615 else if (c
== 'n') /* match a newline */
617 else if (c
== 't') /* match a tab */
619 else if (c
== 'r') /* match a return */
623 case '\0': /* ignore nuls */
626 case '\n': /* trailing pattern delimiter is missing */
627 return(cp
= sp
, BAD
);
629 case '.': /* match any char except newline */
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 */
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 */
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 */
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 */
661 if ((c
= *sp
++) == 'n')
668 /* Enter (possibly translated) char in set */
669 ep
[c
>> 3] |= bits
[c
& 7];
671 ((c
= *sp
++) != ']');
673 /* Invert the bitmask if all-but was specified */
674 if (negclass
) for (classct
= 0; classct
< 16; classct
++)
676 ep
[0] &= 0xFE; /* never match ASCII 0 */
677 ep
+= 16; /* advance ep past set mask */
680 defchar
: /* match literal character */
681 default: /* which is what we'd do by default */
682 *ep
++ = CCHR
; /* insert character mark */
688 static int cmdline(cbuf
) /* uses eflag, eargc, cmdf */
689 /* Read next command from -e argument or command file */
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 */
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 */
703 if (eargc
-- <= 0) quit(2); /* if no arguments, barf */
705 /* Else transcribe next e argument into cbuf */
707 while (*++cbuf
= *p
++)
709 if ((*++cbuf
= *p
++) == '\0')
710 return(savep
= NULL
, -1);
713 } else if (*cbuf
== '\n') { /* end of 1 cmd line */
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
++)
726 if ((*++cbuf
= *p
++) == '0')
727 return(savep
= NULL
, -1);
730 } else if (*cbuf
== '\n') {
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 */
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 */
792 if ((*txp
= *p
++) == '\\') /* handle escapes */
794 if (*txp
== '\0') /* we're at end of input */
795 return(cp
= --p
, ++txp
);
796 else if (*txp
== '\n') /* also SKIPWS after newline */
799 (txp
++); /* keep going till we find that nul */
803 static label
*search(ptr
) /* uses global lablst */
804 /* Find the label matching *ptr, return NULL if none */
808 for (rp
= lablst
; rp
< ptr
; rp
++)
809 if ((rp
->name
!= NULL
) && (strcmp(rp
->name
, ptr
->name
) == 0))
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
);
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
;
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
;
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'
851 /* Now rescan the 'from' section */
852 while ((c
= *sp
++ & 0x7F) != delim
) {
853 if (c
== '\\' && *sp
== 'n') {
857 if ((ep
[c
] = *tp
++) == '\\' && *tp
== 'n') {
861 if ((ep
[c
] == delim
) || (ep
[c
] == '\0')) return(BAD
);
864 if (*tp
!= delim
) /* 'to', 'from' parts have unequal lengths */
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 */
878 /* Flush buffers and exit. Now a historical relic. Rely on exit to flush
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 */
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
];
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 */
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 */
962 /* Here's the main command-execution loop */
965 /* Get next line to filter */
966 if ((execp
= getline(linebuf
)) == BAD
) return;
970 /* Loop through compiled commands, executing them */
971 for (ipc
= cmds
; ipc
->command
;) {
972 if (!selected(ipc
)) {
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 */
983 if ((ipc
= ipc
->u
.link
) == 0) {
987 } else /* normal goto next command */
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
);
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 */
1010 register char *p1
= ipc
->addr1
; /* point p1 at first address */
1011 register char *p2
= ipc
->addr2
; /* and p2 at second */
1013 int sel
= TRUE
; /* select by default */
1015 if (!p1
) /* No addresses: always selected */
1017 else if (ipc
->flags
.inrange
) {
1019 else if (*p2
== CLNUM
) {
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
) {
1031 if (lnum
!= linenum
[c
])
1034 ipc
->flags
.inrange
= TRUE
;
1035 } else if (match(p1
, 0)) {
1036 if (p2
) ipc
->flags
.inrange
= TRUE
;
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 */
1048 register char *p1
, *p2
, c
;
1051 if (*expbuf
) return(FALSE
);
1054 while (*p1
++ = *p2
++);
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 */
1071 c
= p2
[1]; /* pull out character to search for */
1073 if (*p1
!= c
) continue; /* scan the source string */
1074 if (advance(p1
, p2
)) /* found it, match the rest */
1075 return(loc1
= p1
, 1);
1078 return(FALSE
); /* didn't find that first char */
1081 /* Else try for unanchored match of the pattern */
1083 if (advance(p1
, p2
)) return(loc1
= p1
, 1);
1087 /* If got here, didn't match either way */
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 */
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 */
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 */
1139 bbeg
= brastart
[*ep
];
1140 ct
= bracend
[*ep
++] - bbeg
;
1142 if (Memcmp(bbeg
, lp
, ct
)) {
1149 bbeg
= brastart
[*ep
];
1150 ct
= bracend
[*ep
++] - bbeg
;
1152 while (Memcmp(bbeg
, lp
, ct
)) lp
+= ct
;
1154 while (lp
>= curlp
) {
1155 if (advance(lp
, ep
)) return(TRUE
);
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 */
1175 c
= *lp
++ & 0x7F; /* match any in set */
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 */
1188 if (*lp
!= c
) continue;
1189 if (advance(lp
, ep
)) return (TRUE
);
1195 c
= *(brastart
[ep
[1]]);
1197 if (*lp
!= c
) continue;
1198 if (advance(lp
, ep
)) return (TRUE
);
1204 if (lp
== locs
) break;
1205 if (advance(lp
, ep
)) return (TRUE
);
1211 fprintf(stderr
, "sed: RE error, %o\n", *--ep
);
1216 static int substitute(ipc
)
1217 /* Perform s command */
1218 sedcmd
*ipc
; /* ptr to s command struct */
1222 if (match(ipc
->u
.lhs
, 0)) { /* if no match */
1223 nullmatch
= (loc1
== loc2
);
1224 dosub(ipc
->rhs
); /* perform it once */
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
;
1247 /* Copy linebuf to genbuf up to location 1 */
1250 while (lp
< loc1
) *sp
++ = *lp
++;
1252 for (rp
= rhsbuf
; c
= *rp
++;) {
1254 sp
= place(sp
, loc1
, loc2
);
1256 } else if (c
& 0200 && (c
&= 0177) >= '1' && c
< MAXTAGS
+ '1') {
1257 sp
= place(sp
, brastart
[c
- '1'], bracend
[c
- '1']);
1261 if (sp
>= genbuf
+ MAXBUF
) fprintf(stderr
, LTLMSG
);
1264 loc2
= sp
- genbuf
+ linebuf
;
1265 while (*sp
++ = *lp
++)
1266 if (sp
>= genbuf
+ MAXBUF
) fprintf(stderr
, LTLMSG
);
1269 while (*lp
++ = *sp
++);
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
;
1279 if (asp
>= genbuf
+ MAXBUF
) fprintf(stderr
, LTLMSG
);
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 */
1292 putc(*p1
, fp
); /* pass it through */
1294 putc('\\', fp
); /* emit a backslash */
1312 fprintf(fp
, "%02x", *p1
& 0xFF);
1318 static void truncated(h
)
1321 static long last
= 0L;
1323 if (lnum
== last
) return;
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 */
1335 static char holdsp
[MAXHOLD
+ 1]; /* the hold space */
1336 static char *hspend
= holdsp
; /* hold space end pointer */
1337 register char *p1
, *p2
;
1339 int didsub
; /* true if last s succeeded */
1341 switch (ipc
->command
) {
1342 case ACMD
: /* append */
1344 if (aptr
>= appends
+ MAXAPPENDS
) fprintf(stderr
,
1345 "sed: too many appends after line %ld\n",
1350 case CCMD
: /* change pattern space */
1352 if (!ipc
->flags
.inrange
|| lastline
) printf("%s\n", ipc
->u
.lhs
);
1355 case DCMD
: /* delete pattern space */
1359 case CDCMD
: /* delete a line in hold space */
1362 if (delete = (*p1
++ == 0)) return;
1364 while (*p2
++ = *p1
++) continue;
1369 case EQCMD
: /* show current line number */
1370 fprintf(stdout
, "%ld\n", lnum
);
1373 case GCMD
: /* copy hold space to pattern space */
1376 while (*p1
++ = *p2
++);
1380 case CGCMD
: /* append hold space to pattern space */
1385 if (p1
> linebuf
+ MAXBUF
) {
1390 while (*p1
++ = *p2
++);
1395 case HCMD
: /* copy pattern space to hold space */
1398 while (*p1
++ = *p2
++);
1402 case CHCMD
: /* append pattern space to hold space */
1407 if (p1
> holdsp
+ MAXBUF
) {
1412 while (*p1
++ = *p2
++);
1417 case ICMD
: /* insert text */
1418 printf("%s\n", ipc
->u
.lhs
);
1421 case BCMD
: /* branch to label */
1425 case LCMD
: /* list text */
1426 listto(linebuf
, (ipc
->fout
!= NULL
) ? ipc
->fout
: stdout
);
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
) {
1440 case CNCMD
: /* append next line to pattern space */
1441 if (aptr
> appends
) readout();
1443 if ((execp
= getline(spend
)) == BAD
) {
1451 case PCMD
: /* print pattern space */
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
);
1461 case QCMD
: /* quit the stream editor */
1462 if (!nflag
) puts(linebuf
); /* flush out the current line */
1464 readout(); /* do any pending a and r commands */
1467 case RCMD
: /* read a file into the stream */
1469 if (aptr
>= appends
+ MAXAPPENDS
) fprintf(stderr
,
1470 "sed: too many reads after line %ld\n",
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
)
1483 if (didsub
&& ipc
->fout
) fprintf(ipc
->fout
, "%s\n", linebuf
);
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 */
1491 jump
= TRUE
; /* set up to jump to assoc'd label */
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
);
1500 case WCMD
: /* write pattern space to file */
1501 fprintf(ipc
->fout
, "%s\n", linebuf
);
1504 case XCMD
: /* exchange pattern and hold spaces */
1507 while (*p2
++ = *p1
++) continue;
1510 while (*p2
++ = *p1
++) continue;
1514 while (*p2
++ = *p1
++) continue;
1521 while (*p1
= p2
[*p1
]) p1
++;
1526 static void openfile(file
)
1528 /* Replace stdin by given file */
1530 if (freopen(file
, "r", stdin
) == NULL
) {
1531 fprintf(stderr
, "sed: can't open %s\n", file
);
1536 static int c
; /* Will be the next char to read, a kind of
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 */
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 */
1565 if (buf
<= linebuf
+ MAXBUF
) *buf
++ = c
;
1569 if (c
== EOF
) lastline
= TRUE
;
1571 if (buf
> linebuf
+ MAXBUF
) {
1579 static int Memcmp(a
, b
, count
)
1580 /* Return TRUE if *a... == *b... for count chars, FALSE otherwise */
1581 register char *a
, *b
;
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 */
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",
1606 while ((t
= getc(fi
)) != EOF
) putc((char) t
, stdout
);
1609 aptr
= appends
; /* reset the append ptr */
1613 /* Sedexec.c ends here */