fixed undo within a global command (would corrupt the buffer)
[unix-history] / bin / ed / ed.c
CommitLineData
54a7a3ed
AM
1/* ed.c: This file contains the main control and user-interface routines
2 for the ed line editor. */
3/*-
4 * Copyright (c) 1993 The Regents of the University of California.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to Berkeley
8 * by Andrew Moore, Talke Studio.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the University of
21 * California, Berkeley and its contributors.
22 * 4. Neither the name of the University nor the names of its contributors
23 * may be used to endorse or promote products derived from this software
24 * without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 * SUCH DAMAGE.
37 */
38/*-
39 * Kernighan/Plauger, "Software Tools in Pascal," (c) 1981 by
40 * Addison-Wesley Publishing Company, Inc. Reprinted with permission of
41 * the publisher.
42 */
43
44#ifndef lint
45char copyright1[] =
46"@(#) Copyright (c) 1993 The Regents of the University of California.\n\
47 All rights reserved.\n";
48char copyright2[] =
49"@(#) Kernighan/Plauger, Software Tools in Pascal, (c) 1981 by\n\
50 Addison-Wesley Publishing Company, Inc. Reprinted with permission of\n\
51 the publisher.\n";
52#endif /* not lint */
53
54#ifndef lint
55static char sccsid[] = "@(#)ed.c 5.5 (Berkeley) 3/28/93";
56#endif /* not lint */
57
58/*
59 * CREDITS
60 * The buf.c algorithm is attributed to Rodney Ruddock of
61 * the University of Guelph, Guelph, Ontario.
62 *
63 * The cbc.c encryption code is adapted from
64 * the bdes program by Matt Bishop of Dartmouth College,
65 * Hanover, NH.
66 *
67 * Addison-Wesley Publishing Company generously granted
68 * permission to distribute this program over Internet.
69 *
70 */
71
72#include <unistd.h>
73#include <stdio.h>
74#include <stdlib.h>
75#include <string.h>
76#include <ctype.h>
77#include <setjmp.h>
78#include <pwd.h>
79#include <sys/ioctl.h>
80
81#include "ed.h"
82
83#ifdef _POSIX_SOURCE
84sigjmp_buf env;
85#else
86jmp_buf env;
87#endif
88
89/* static buffers */
90char *shcmd; /* shell command buffer */
91int shcmdsz; /* shell command buffer size */
92int shcmdi; /* shell command buffer index */
93char *cvbuf; /* global command buffer */
94int cvbufsz; /* global command buffer size */
95char *lhbuf; /* lhs buffer */
96int lhbufsz; /* lhs buffer size */
97char *rhbuf; /* rhs buffer */
98int rhbufsz; /* rhs buffer size */
99int rhbufi; /* rhs buffer index */
100char *rbuf; /* regsub buffer */
101int rbufsz; /* regsub buffer size */
102char *sbuf; /* file i/o buffer */
103int sbufsz; /* file i/o buffer size */
104char *ibuf; /* ed command-line buffer */
105int ibufsz; /* ed command-line buffer size */
106char *ibufp; /* pointer to ed command-line buffer */
107
108/* global flags */
109int isbinary; /* if set, buffer contains ASCII NULs */
110int modified; /* if set, buffer modified since last write */
111int garrulous = 0; /* if set, print all error messages */
112int scripted = 0; /* if set, suppress diagnostics */
113int des = 0; /* if set, use crypt(3) for i/o */
114int mutex = 0; /* if set, signals set "sigflags" */
115int sigflags = 0; /* if set, signals received while mutex set */
116int sigactive = 0; /* if set, signal handlers are enabled */
117int red = 0; /* if set, restrict shell/directory access */
118
119char dfn[MAXFNAME + 1] = ""; /* default filename */
120long curln; /* current address */
121long lastln; /* last address */
122int lineno; /* script line number */
123char *prompt; /* command-line prompt */
124char *dps = "*"; /* default command-line prompt */
125
126char *usage = "usage: %s [-] [-sx] [-p string] [name]\n";
127
128extern char errmsg[];
129extern int optind;
130extern char *optarg;
131
132/* ed: line editor */
133main(argc, argv)
134 int argc;
135 char **argv;
136{
137 int c, n;
138 long status = 0;
139
140 red = (n = strlen(argv[0])) > 2 && argv[0][n - 3] == 'r';
141top:
142 while ((c = getopt(argc, argv, "p:sx")) != EOF)
143 switch(c) {
144 case 'p': /* set prompt */
145 prompt = optarg;
146 break;
147 case 's': /* run script */
148 scripted = 1;
149 break;
150 case 'x': /* use crypt */
151#ifdef DES
152 des = getkey();
153#else
154 fprintf(stderr, "crypt unavailable\n?\n");
155#endif
156 break;
157
158 default:
159 fprintf(stderr, usage, argv[0]);
160 exit(1);
161 }
162 argv += optind;
163 argc -= optind;
164 if (argc && **argv == '-') {
165 scripted = 1;
166 if (argc > 1) {
167 optind = 1;
168 goto top;
169 }
170 argv++;
171 argc--;
172 }
173 /* assert: reliable signals! */
174#ifdef SIGWINCH
175 dowinch(SIGWINCH);
176 if (isatty(0)) signal(SIGWINCH, dowinch);
177#endif
178 signal(SIGHUP, onhup);
179 signal(SIGQUIT, SIG_IGN);
180 signal(SIGINT, onintr);
181#ifdef _POSIX_SOURCE
182 if (status = sigsetjmp(env, 1))
183#else
184 if (status = setjmp(env))
185#endif
186 {
187 fputs("\n?\n", stderr);
188 sprintf(errmsg, "interrupt");
189 } else {
190 init_buf();
191 sigactive = 1; /* enable signal handlers */
192 if (argc && **argv && ckfn(*argv)) {
193 if (doread(0, *argv) < 0 && !isatty(0))
194 quit(2);
195 else if (**argv != '!')
196 strcpy(dfn, *argv);
197 } else if (argc) {
198 fputs("?\n", stderr);
199 if (**argv == '\0')
200 sprintf(errmsg, "invalid filename");
201 if (!isatty(0))
202 quit(2);
203 }
204 }
205 for (;;) {
206 if (status < 0 && garrulous)
207 fprintf(stderr, "%s\n", errmsg);
208 if (prompt) {
209 printf("%s", prompt);
210 fflush(stdout);
211 }
212 if ((n = getline()) < 0) {
213 status = ERR;
214 continue;
215 } else if (n == 0) {
216 if (modified && !scripted) {
217 fputs("?\n", stderr);
218 sprintf(errmsg, "warning: file modified");
219 if (!isatty(0)) {
220 fprintf(stderr, garrulous ? "script, line %d: %s\n"
221 : "", lineno, errmsg);
222 quit(2);
223 }
224 clearerr(stdin);
225 modified = 0;
226 status = EMOD;
227 continue;
228 } else
229 quit(0);
230 } else if (ibuf[n - 1] != '\n') {
231 /* discard line */
232 sprintf(errmsg, "unexpected end-of-file");
233 clearerr(stdin);
234 status = ERR;
235 continue;
236 }
237 if ((n = getlist()) >= 0 && (status = ckglob()) != 0) {
238 if (status > 0 && (status = doglob(status)) >= 0) {
239 curln = status;
240 continue;
241 }
242 } else if ((status = n) >= 0 && (status = docmd(0)) >= 0) {
243 if (!status || status
244 && (status = doprint(curln, curln, status)) >= 0)
245 continue;
246 }
247 switch (status) {
248 case EOF:
249 quit(0);
250 case EMOD:
251 modified = 0;
252 fputs("?\n", stderr); /* give warning */
253 sprintf(errmsg, "warning: file modified");
254 if (!isatty(0)) {
255 fprintf(stderr, garrulous ? "script, line %d: %s\n" : "", lineno, errmsg);
256 quit(2);
257 }
258 break;
259 case FATAL:
260 if (!isatty(0))
261 fprintf(stderr, garrulous ? "script, line %d: %s\n" : "", lineno, errmsg);
262 else
263 fprintf(stderr, garrulous ? "%s\n" : "", errmsg);
264 quit(3);
265 default:
266 fputs("?\n", stderr);
267 if (!isatty(0)) {
268 fprintf(stderr, garrulous ? "script, line %d: %s\n" : "", lineno, errmsg);
269 quit(2);
270 }
271 break;
272 }
273 }
274 /*NOTREACHED*/
275}
276
277
278long line1, line2, nlines;
279
280/* getlist: get line numbers from the command buffer until an illegal
281 address is seen. return range status */
282getlist()
283{
284 long num;
285
286 nlines = line2 = 0;
287 while ((num = getone()) >= 0) {
288 line1 = line2;
289 line2 = num;
290 nlines++;
291 if (*ibufp != ',' && *ibufp != ';')
292 break;
293 else if (*ibufp++ == ';')
294 curln = num;
295 }
296 nlines = min(nlines, 2);
297 if (nlines == 0)
298 line2 = curln;
299 if (nlines <= 1)
300 line1 = line2;
301 return (num == ERR) ? ERR : nlines;
302}
303
304
305/* getone: return the next line number in the command buffer */
306long
307getone()
308{
309 int c;
310 long i, num;
311
312 if ((num = getnum(1)) < 0)
313 return num;
314 for (;;) {
315 c = isspace(*ibufp);
316 skipblanks();
317 c = c && isdigit(*ibufp);
318 if (!c && *ibufp != '+' && *ibufp != '-' && *ibufp != '^')
319 break;
320 c = c ? '+' : *ibufp++;
321 if ((i = getnum(0)) < 0) {
322 sprintf(errmsg, "invalid address");
323 return i;
324 }
325 if (c == '+')
326 num += i;
327 else num -= i;
328 }
329 if (num > lastln || num < 0) {
330 sprintf(errmsg, "invalid address");
331 return ERR;
332 }
333 return num;
334}
335
336
337#define MAXMARK 26 /* max number of marks */
338
339line_t *mark[MAXMARK]; /* line markers */
340int markno; /* line marker count */
341
342/* getnum: return a relative line number from the command buffer */
343long
344getnum(first)
345 int first;
346{
347 pattern_t *pat;
348 char c;
349
350 skipblanks();
351 if (isdigit(*ibufp))
352 return strtol(ibufp, &ibufp, 10);
353 switch(c = *ibufp) {
354 case '.':
355 ibufp++;
356 return first ? curln : ERR;
357 case '$':
358 ibufp++;
359 return first ? lastln : ERR;
360 case '/':
361 case '?':
362 if ((pat = optpat()) == NULL)
363 return ERR;
364 else if (*ibufp == c)
365 ibufp++;
366 return first ? patscan(pat, (c == '/') ? 1 : 0) : ERR;
367 case '^':
368 case '-':
369 case '+':
370 return first ? curln : 1;
371 case '\'':
372 ibufp++;
373 return (first && islower(*ibufp)) ? getaddr(mark[*ibufp++ - 'a']) : ERR;
374 case '%':
375 case ',':
376 case ';':
377 if (first) {
378 ibufp++;
379 line2 = (c == ';') ? curln : 1;
380 nlines++;
381 return lastln;
382 }
383 return 1;
384 default:
385 return first ? EOF : 1;
386 }
387}
388
389
390/* gflags */
391#define GLB 001 /* global command */
392#define GPR 002 /* print after command */
393#define GLS 004 /* list after command */
394#define GNP 010 /* enumerate after command */
395#define GSG 020 /* global substitute */
396
397
398/* VRFYCMD: verify the command suffix in the command buffer */
399#define VRFYCMD() { \
400 int done = 0; \
401 do { \
402 switch(*ibufp) { \
403 case 'p': \
404 gflag |= GPR, ibufp++; \
405 break; \
406 case 'l': \
407 gflag |= GLS, ibufp++; \
408 break; \
409 case 'n': \
410 gflag |= GNP, ibufp++; \
411 break; \
412 default: \
413 done++; \
414 } \
415 } while (!done); \
416 if (*ibufp++ != '\n') { \
417 sprintf(errmsg, "invalid command suffix"); \
418 return ERR; \
419 } \
420}
421
422
423/* ckglob: set lines matching a pattern in the command buffer; return
424 global status */
425ckglob()
426{
427 pattern_t *pat;
428 char c, delim;
429 char *s;
430 int nomatch;
431 long n;
432 line_t *lp;
433 int gflag = 0; /* print suffix of interactive cmd */
434
435 if ((c = *ibufp) == 'V' || c == 'G')
436 gflag = GLB;
437 else if (c != 'g' && c != 'v')
438 return 0;
439 if (ckrange(1, lastln) < 0)
440 return ERR;
441 else if ((delim = *++ibufp) == ' ' || delim == '\n') {
442 sprintf(errmsg, "invalid pattern delimiter");
443 return ERR;
444 } else if ((pat = optpat()) == NULL)
445 return ERR;
446 else if (*ibufp == delim)
447 ibufp++;
448 if (gflag)
449 VRFYCMD(); /* get print suffix */
450 for (lp = getlp(n = 1); n <= lastln; n++, lp = lp->next) {
451 if ((s = gettxt(lp)) == NULL)
452 return ERR;
453 lp->len &= ~ACTV; /* zero ACTV bit */
454 if (isbinary)
455 s = nultonl(s, lp->len & ~ACTV);
456 if (line1 <= n && n <= line2
457 && (!(nomatch = regexec(pat, s, 0, NULL, 0))
458 && (c == 'g' || c == 'G')
459 || nomatch && (c == 'v' || c == 'V')))
460 lp->len |= ACTV;
461 }
462 return gflag | GSG;
463}
464
465
466/* doglob: apply command list in the command buffer to the active
467 lines in a range; return command status */
468long
469doglob(gflag)
470 int gflag;
471{
472 static char *ocmd = NULL;
473 static int ocmdsz = 0;
474
475 line_t *lp = NULL;
476 long lc;
477 int status;
478 int n;
479 int interact = gflag & ~GSG; /* GLB & gflag ? */
480 char *cmd = NULL;
481
482#ifdef BACKWARDS
483 if (!interact)
484 if (!strcmp(ibufp, "\n"))
485 cmd = "p\n"; /* null cmd-list == `p' */
486 else if ((cmd = getcmdv(&n, 0)) == NULL)
487 return ERR;
488#else
489 if (!interact && (cmd = getcmdv(&n, 0)) == NULL)
490 return ERR;
491#endif
492 ureset();
493 for (;;) {
494 for (lp = getlp(lc = 1); lc <= lastln; lc++, lp = lp->next)
495 if (lp->len & ACTV) /* active line */
496 break;
497 if (lc > lastln)
498 break;
499 lp->len ^= ACTV; /* zero ACTV bit */
500 curln = lc;
501 if (interact) {
502 /* print curln and get a command in global syntax */
503 if (doprint(curln, curln, 0) < 0)
504 return ERR;
505 while ((n = getline()) > 0
506 && ibuf[n - 1] != '\n')
507 clearerr(stdin);
508 if (n < 0)
509 return ERR;
510 else if (n == 0) {
511 sprintf(errmsg, "unexpected end-of-file");
512 return ERR;
513 } else if (n == 1 && !strcmp(ibuf, "\n"))
514 continue;
515 else if (n == 2 && !strcmp(ibuf, "&\n")) {
516 if (cmd == NULL) {
517 sprintf(errmsg, "no previous command");
518 return ERR;
519 } else cmd = ocmd;
520 } else if ((cmd = getcmdv(&n, 0)) == NULL)
521 return ERR;
522 else {
523 CKBUF(ocmd, ocmdsz, n + 1, ERR);
524 memcpy(ocmd, cmd, n + 1);
525 cmd = ocmd;
526 }
527
528 }
529 ibufp = cmd;
530 for (; *ibufp;)
531 if ((status = getlist()) < 0
532 || (status = docmd(1)) < 0
533 || (status > 0
534 && (status = doprint(curln, curln, status)) < 0))
535 return status;
536 }
537 return ((interact & ~GLB ) && doprint(curln, curln, interact) < 0) ? ERR : curln;
538}
539
540
541#ifdef BACKWARDS
542/* GETLINE3: get a legal address from the command buffer */
543#define GETLINE3(num) \
544{ \
545 long ol1, ol2; \
546\
547 ol1 = line1, ol2 = line2; \
548 if (getlist() < 0) \
549 return ERR; \
550 else if (nlines == 0) { \
551 sprintf(errmsg, "destination expected"); \
552 return ERR; \
553 } else if (line2 < 0 || lastln < line2) { \
554 sprintf(errmsg, "invalid address"); \
555 return ERR; \
556 } \
557 num = line2; \
558 line1 = ol1, line2 = ol2; \
559}
560#else /* BACKWARDS */
561/* GETLINE3: get a legal address from the command buffer */
562#define GETLINE3(num) \
563{ \
564 long ol1, ol2; \
565\
566 ol1 = line1, ol2 = line2; \
567 if (getlist() < 0) \
568 return ERR; \
569 if (line2 < 0 || lastln < line2) { \
570 sprintf(errmsg, "invalid address"); \
571 return ERR; \
572 } \
573 num = line2; \
574 line1 = ol1, line2 = ol2; \
575}
576#endif
577
578/* sgflags */
579#define SGG 001 /* complement previous global substitute suffix */
580#define SGP 002 /* complement previous print suffix */
581#define SGR 004 /* use last regex instead of last pat */
582#define SGF 010 /* newline found */
583
584long ucurln = -1; /* if >= 0, undo enabled */
585long ulastln = -1; /* if >= 0, undo enabled */
54a7a3ed
AM
586int patlock = 0; /* if set, pattern not released by optpat() */
587
588long rows = 22; /* scroll length: ws_row - 2 */
589
590/* docmd: execute the next command in command buffer; return print
591 request, if any */
592docmd(glob)
593 int glob;
594{
595 static pattern_t *pat = NULL;
596 static int sgflag = 0;
597
598 pattern_t *tpat;
599 char *fnp;
600 int gflag = 0;
601 int sflags = 0;
602 long num = 0;
603 int n = 0;
604 int c;
605
606 skipblanks();
607 switch(c = *ibufp++) {
608 case 'a':
609 VRFYCMD();
610 if (!glob) ureset();
611 if (append(line2, glob) < 0)
612 return ERR;
613 break;
614 case 'c':
615 if (ckrange(curln, curln) < 0)
616 return ERR;
617 VRFYCMD();
618 if (!glob) ureset();
619 if (lndelete(line1, line2) < 0 || append(curln, glob) < 0)
620 return ERR;
621 break;
622 case 'd':
623 if (ckrange(curln, curln) < 0)
624 return ERR;
625 VRFYCMD();
626 if (!glob) ureset();
627 if (lndelete(line1, line2) < 0)
628 return ERR;
629 else if (nextln(curln, lastln) != 0)
630 curln = nextln(curln, lastln);
631 modified = 1;
632 break;
633 case 'e':
634 if (modified && !scripted)
635 return EMOD;
636 /* fall through */
637 case 'E':
638 if (nlines > 0) {
639 sprintf(errmsg, "unexpected address");
640 return ERR;
641 } else if (!isspace(*ibufp)) {
642 sprintf(errmsg, "unexpected command suffix");
643 return ERR;
644 } else if ((fnp = getfn()) == NULL)
645 return ERR;
646 VRFYCMD();
647 memset(mark, 0, sizeof mark);
e8bd1b9a
AM
648 if (lndelete(1, lastln) < 0)
649 return ERR;
54a7a3ed
AM
650 ureset();
651 if (sbclose() < 0)
652 return ERR;
653 else if (sbopen() < 0)
654 return FATAL;
655 if (*fnp && *fnp != '!') strcpy(dfn, fnp);
656#ifdef BACKWARDS
657 if (*fnp == '\0' && *dfn == '\0') {
658 sprintf(errmsg, "no current filename");
659 return ERR;
660 }
661#endif
662 if (doread(0, *fnp ? fnp : dfn) < 0)
663 return ERR;
664 ureset();
665 modified = 0;
666 ucurln = ulastln = -1;
667 break;
668 case 'f':
669 if (nlines > 0) {
670 sprintf(errmsg, "unexpected address");
671 return ERR;
672 } else if (!isspace(*ibufp)) {
673 sprintf(errmsg, "unexpected command suffix");
674 return ERR;
675 } else if ((fnp = getfn()) == NULL)
676 return ERR;
677 else if (*fnp == '!') {
678 sprintf(errmsg, "invalid redirection");
679 return ERR;
680 }
681 VRFYCMD();
682 if (*fnp) strcpy(dfn, fnp);
683 printf("%s\n", esctos(dfn));
684 break;
685 case 'g':
686 case 'G':
687 sprintf(errmsg, "cannot nest global commands");
688 return ERR;
689 case 'h':
690 if (nlines > 0) {
691 sprintf(errmsg, "unexpected address");
692 return ERR;
693 }
694 VRFYCMD();
695 if (*errmsg) fprintf(stderr, "%s\n", errmsg);
696 break;
697 case 'H':
698 if (nlines > 0) {
699 sprintf(errmsg, "unexpected address");
700 return ERR;
701 }
702 VRFYCMD();
703 if ((garrulous = 1 - garrulous) && *errmsg)
704 fprintf(stderr, "%s\n", errmsg);
705 break;
706 case 'i':
707 if (line2 == 0) {
708 sprintf(errmsg, "invalid address");
709 return ERR;
710 }
711 VRFYCMD();
712 if (!glob) ureset();
713 if (append(prevln(line2, lastln), glob) < 0)
714 return ERR;
715 break;
716 case 'j':
717 if (ckrange(curln, curln + 1) < 0)
718 return ERR;
719 VRFYCMD();
720 if (!glob) ureset();
721 if (line1 != line2 && join(line1, line2) < 0)
722 return ERR;
723 break;
724 case 'k':
725 c = *ibufp++;
726 if (line2 == 0) {
727 sprintf(errmsg, "invalid address");
728 return ERR;
729 } else if (!islower(c)) {
730 sprintf(errmsg, "invalid mark character");
731 return ERR;
732 }
733 VRFYCMD();
734 if (!mark[c - 'a']) markno++;
735 mark[c - 'a'] = getlp(line2);
736 break;
737 case 'l':
738 if (ckrange(curln, curln) < 0)
739 return ERR;
740 VRFYCMD();
741 if (doprint(line1, line2, gflag | GLS) < 0)
742 return ERR;
743 gflag = 0;
744 break;
745 case 'm':
746 if (ckrange(curln, curln) < 0)
747 return ERR;
748 GETLINE3(num);
749 if (line1 <= num && num < line2) {
750 sprintf(errmsg, "invalid destination");
751 return ERR;
752 }
753 VRFYCMD();
754 if (!glob) ureset();
e8bd1b9a 755 if (move(num, glob) < 0)
54a7a3ed
AM
756 return ERR;
757 else
758 modified = 1;
759 break;
760 case 'n':
761 if (ckrange(curln, curln) < 0)
762 return ERR;
763 VRFYCMD();
764 if (doprint(line1, line2, gflag | GNP) < 0)
765 return ERR;
766 gflag = 0;
767 break;
768 case 'p':
769 if (ckrange(curln, curln) < 0)
770 return ERR;
771 VRFYCMD();
772 if (doprint(line1, line2, gflag | GPR) < 0)
773 return ERR;
774 gflag = 0;
775 break;
776 case 'P':
777 if (nlines > 0) {
778 sprintf(errmsg, "unexpected address");
779 return ERR;
780 }
781 VRFYCMD();
782 prompt = prompt ? NULL : optarg ? optarg : dps;
783 break;
784 case 'q':
785 case 'Q':
786 if (nlines > 0) {
787 sprintf(errmsg, "unexpected address");
788 return ERR;
789 }
790 VRFYCMD();
791 gflag = (modified && !scripted && c == 'q') ? EMOD : EOF;
792 break;
793 case 'r':
794 if (!isspace(*ibufp)) {
795 sprintf(errmsg, "unexpected command suffix");
796 return ERR;
797 } else if (nlines == 0)
798 line2 = lastln;
799 if ((fnp = getfn()) == NULL)
800 return ERR;
801 VRFYCMD();
802 if (!glob) ureset();
803 if (*dfn == '\0' && *fnp != '!') strcpy(dfn, fnp);
804#ifdef BACKWARDS
805 if (*fnp == '\0' && *dfn == '\0') {
806 sprintf(errmsg, "no current filename");
807 return ERR;
808 }
809#endif
810 if ((num = doread(line2, *fnp ? fnp : dfn)) < 0)
811 return ERR;
812 else if (num && num != lastln)
813 modified = 1;
814 break;
815 case 's':
816 do {
817 switch(*ibufp) {
818 case '\n':
819 sflags |=SGF;
820 break;
821 case 'g':
822 sflags |= SGG;
823 ibufp++;
824 break;
825 case 'p':
826 sflags |= SGP;
827 ibufp++;
828 break;
829 case 'r':
830 sflags |= SGR;
831 ibufp++;
832 break;
833 default:
834 if (sflags) {
835 sprintf(errmsg, "invalid command suffix");
836 return ERR;
837 }
838 }
839 } while (sflags && *ibufp != '\n');
840 if (sflags && !pat) {
841 sprintf(errmsg, "no previous substitution");
842 return ERR;
843 } else if (!(sflags & SGF))
844 sgflag &= 0xff;
845 tpat = pat;
846 spl1();
847 if ((!sflags || (sflags & SGR))
848 && (tpat = optpat()) == NULL)
849 return ERR;
850 else if (tpat != pat) {
851 if (pat) {
852 regfree(pat);
853 free(pat);
854 }
855 pat = tpat;
856 patlock = 1; /* reserve pattern */
857 } else if (pat == NULL) {
858 /* NOTREACHED */
859 sprintf(errmsg, "no previous substitution");
860 return ERR;
861 }
862 spl0();
863 if (!sflags && (sgflag = getrhs(glob)) < 0)
864 return ERR;
865 else if (glob)
866 sgflag |= GLB;
867 else
868 sgflag &= ~GLB;
869 if (sflags & SGG)
870 sgflag ^= GSG;
871 if (sflags & SGP)
872 sgflag ^= GPR, sgflag &= ~(GLS | GNP);
873 do {
874 switch(*ibufp) {
875 case 'p':
876 sgflag |= GPR, ibufp++;
877 break;
878 case 'l':
879 sgflag |= GLS, ibufp++;
880 break;
881 case 'n':
882 sgflag |= GNP, ibufp++;
883 break;
884 default:
885 n++;
886 }
887 } while (!n);
888 if (ckrange(curln, curln) < 0)
889 return ERR;
890 VRFYCMD();
891 if (!glob) ureset();
892 if ((n = subst(pat, sgflag)) < 0)
893 return ERR;
894 else if (n)
895 modified = 1;
896 break;
897 case 't':
898 if (ckrange(curln, curln) < 0)
899 return ERR;
900 GETLINE3(num);
901 VRFYCMD();
902 if (!glob) ureset();
903 if (transfer(num) < 0)
904 return ERR;
905 modified = 1;
906 break;
907 case 'u':
908 if (nlines > 0) {
909 sprintf(errmsg, "unexpected address");
910 return ERR;
911 }
912 VRFYCMD();
e8bd1b9a 913 if (undo(glob) < 0)
54a7a3ed
AM
914 return ERR;
915 break;
916 case 'v':
917 case 'V':
918 sprintf(errmsg, "cannot nest global commands");
919 return ERR;
920 case 'w':
921 case 'W':
922 if ((n = *ibufp) == 'q' || n == 'Q') {
923 gflag = EOF;
924 ibufp++;
925 }
926 if (!isspace(*ibufp)) {
927 sprintf(errmsg, "unexpected command suffix");
928 return ERR;
929 } else if ((fnp = getfn()) == NULL)
930 return ERR;
931 if (nlines == 0 && !lastln)
932 line1 = line2 = 0;
933 else if (ckrange(1, lastln) < 0)
934 return ERR;
935 VRFYCMD();
936 if (*dfn == '\0' && *fnp != '!') strcpy(dfn, fnp);
937#ifdef BACKWARDS
938 if (*fnp == '\0' && *dfn == '\0') {
939 sprintf(errmsg, "no current filename");
940 return ERR;
941 }
942#endif
943 if ((num = dowrite(line1, line2, *fnp ? fnp : dfn, (c == 'W') ? "a" : "w")) < 0)
944 return ERR;
945 else if (num == lastln)
946 modified = 0;
947 else if (modified && !scripted && n == 'q')
948 gflag = EMOD;
949 break;
950 case 'x':
951 if (nlines > 0) {
952 sprintf(errmsg, "unexpected address");
953 return ERR;
954 }
955 VRFYCMD();
956#ifdef DES
957 des = getkey();
958#else
959 sprintf(errmsg, "crypt unavailable");
960 return ERR;
961#endif
962 break;
963 case 'z':
964#ifdef BACKWARDS
965 if (ckrange(line1 = 1, curln + 1) < 0)
966#else
967 if (ckrange(line1 = 1, curln + !glob) < 0)
968#endif
969 return ERR;
970 else if ('0' < *ibufp && *ibufp <= '9')
971 rows = strtol(ibufp, &ibufp, 10);
972 VRFYCMD();
973 if (doprint(line2, min(lastln, line2 + rows - 1), gflag) < 0)
974 return ERR;
975 gflag = 0;
976 break;
977 case '=':
978 VRFYCMD();
979 printf("%d\n", nlines ? line2 : lastln);
980 break;
981 case '!':
982#ifndef VI_BANG
983 if (nlines > 0) {
984 sprintf(errmsg, "unexpected address");
985 return ERR;
986 }
987#endif
988 if ((sflags = getshcmd()) < 0)
989 return ERR;
990 VRFYCMD();
991 if (sflags) printf("%s\n", shcmd + 1);
992#ifdef VI_BANG
993 if (nlines == 0) {
994#endif
995 system(shcmd + 1);
996 if (!scripted) printf("!\n");
997 break;
998#ifdef VI_BANG
999 }
1000 if (!lastln && !line1 && !line2) {
1001 if (!glob) ureset();
1002 } else if (ckrange(curln, curln) < 0)
1003 return ERR;
1004 else {
1005 if (!glob) ureset();
1006 if (lndelete(line1, line2) < 0)
1007 return ERR;
1008 line2 = curln;
1009 modified = 1;
1010 }
1011 if ((num = doread(line2, shcmd)) < 0)
1012 return ERR;
1013 else if (num && num != lastln)
1014 modified = 1;
1015 break;
1016#endif
1017 case '\n':
1018#ifdef BACKWARDS
1019 if (ckrange(line1 = 1, curln + 1) < 0
1020#else
1021 if (ckrange(line1 = 1, curln + !glob) < 0
1022#endif
1023 || doprint(line2, line2, 0) < 0)
1024 return ERR;
1025 break;
1026 default:
1027 sprintf(errmsg, "unknown command");
1028 return ERR;
1029 }
1030 return gflag;
1031}
1032
1033
1034/* ckrange: return status of line number range check */
1035ckrange(def1, def2)
1036 long def1, def2;
1037{
1038 if (nlines == 0) {
1039 line1 = def1;
1040 line2 = def2;
1041 }
1042 if (line1 > line2 || 1 > line1 || line2 > lastln) {
1043 sprintf(errmsg, "invalid address");
1044 return ERR;
1045 }
1046 return 0;
1047}
1048
1049
1050/* patscan: return the number of the next line matching a pattern in a
1051 given direction. wrap around begin/end of line queue if necessary */
1052long
1053patscan(pat, dir)
1054 pattern_t *pat;
1055 int dir;
1056{
1057 char *s;
1058 long n = curln;
1059 line_t *lp;
1060
1061 do {
1062 if (n = dir ? nextln(n, lastln) : prevln(n, lastln)) {
1063 if ((s = gettxt(lp = getlp(n))) == NULL)
1064 return ERR;
1065 if (isbinary)
1066 s = nultonl(s, lp->len & ~ACTV);
1067 if (!regexec(pat, s, 0, NULL, 0))
1068 return n;
1069 }
1070 } while (n != curln);
1071 sprintf(errmsg, "no match");
1072 return ERR;
1073}
1074
1075
1076/* getfn: return pointer to copy of filename in the command buffer */
1077char *
1078getfn()
1079{
1080 static char *file = NULL;
1081 static int filesz = 0;
1082
1083 int n;
1084
1085 if (*ibufp != '\n') {
1086 skipblanks();
1087 if (*ibufp == '\n') {
1088 sprintf(errmsg, "invalid filename");
1089 return NULL;
1090 } else if ((ibufp = getcmdv(&n, 1)) == NULL)
1091 return NULL;
1092#ifdef VI_BANG
1093 else if (*ibufp == '!') {
1094 ibufp++;
1095 if ((n = getshcmd()) < 0)
1096 return NULL;
1097 if (n) printf("%s\n", shcmd + 1);
1098 return shcmd;
1099 }
1100#endif
1101 else if (n - 1 > MAXFNAME) {
1102 sprintf(errmsg, "filename too long");
1103 return NULL;
1104 }
1105 }
1106#ifndef BACKWARDS
1107 else if (*dfn == '\0') {
1108 sprintf(errmsg, "no current filename");
1109 return NULL;
1110 }
1111#endif
1112 CKBUF(file, filesz, MAXFNAME + 1, NULL);
1113 for (n = 0; *ibufp != '\n';)
1114 file[n++] = *ibufp++;
1115 file[n] = '\0';
1116 return ckfn(file);
1117}
1118
1119
1120/* getrhs: extract substitution template from the command buffer */
1121getrhs(glob)
1122 int glob;
1123{
1124 char delim;
1125
1126 if ((delim = *ibufp) == '\n') {
1127 rhbufi = 0;
1128 return GPR;
1129 } else if (makesub(glob) == NULL)
1130 return ERR;
1131 else if (*ibufp == '\n')
1132 return GPR;
1133 else if (*ibufp == delim)
1134 ibufp++;
1135 if ('1' <= *ibufp && *ibufp <= '9')
1136 return (int) strtol(ibufp, &ibufp, 10) << 8;
1137 else if (*ibufp == 'g') {
1138 ibufp++;
1139 return GSG;
1140 }
1141 return 0;
1142}
1143
1144
1145/* makesub: return pointer to copy of substitution template in the command
1146 buffer */
1147char *
1148makesub(glob)
1149 int glob;
1150{
1151 int n = 0;
1152 int i = 0;
1153 char delim = *ibufp++;
1154 char c;
1155
1156 if (*ibufp == '%' && *(ibufp + 1) == delim) {
1157 ibufp++;
1158 if (!rhbuf) sprintf(errmsg, "no previous substitution");
1159 return rhbuf;
1160 }
1161 while (*ibufp != delim) {
1162 CKBUF(rhbuf, rhbufsz, i + 2, NULL);
1163 if ((c = rhbuf[i++] = *ibufp++) == '\n' && *ibufp == '\0') {
1164 i--, ibufp--;
1165 break;
1166 } else if (c != '\\')
1167 ;
1168 else if ((rhbuf[i++] = *ibufp++) != '\n')
1169 ;
1170 else if (!glob) {
1171 while ((n = getline()) == 0
1172 || n > 0 && ibuf[n - 1] != '\n')
1173 clearerr(stdin);
1174 if (n < 0)
1175 return NULL;
1176 } else
1177 /*NOTREACHED*/
1178 ;
1179 }
1180 CKBUF(rhbuf, rhbufsz, i + 1, NULL);
1181 rhbuf[rhbufi = i] = '\0';
1182 return rhbuf;
1183}
1184
1185
1186/* getshcmd: read a shell command up a maximum size from stdin; return
1187 substitution status */
1188int
1189getshcmd()
1190{
1191 static char *buf = NULL;
1192 static int n = 0;
1193
1194 char *s; /* substitution char pointer */
1195 int i = 0;
1196 int j = 0;
1197
1198 if (red) {
1199 sprintf(errmsg, "shell access restricted");
1200 return ERR;
1201 } else if ((s = ibufp = getcmdv(&j, 1)) == NULL)
1202 return ERR;
1203 CKBUF(buf, n, j + 1, ERR);
1204 buf[i++] = '!'; /* prefix command w/ bang */
1205 while (*ibufp != '\n')
1206 switch (*ibufp) {
1207 default:
1208 CKBUF(buf, n, i + 2, ERR);
1209 buf[i++] = *ibufp;
1210 if (*ibufp++ == '\\')
1211 buf[i++] = *ibufp++;
1212 break;
1213 case '!':
1214 if (s != ibufp) {
1215 CKBUF(buf, n, i + 1, ERR);
1216 buf[i++] = *ibufp++;
1217 }
1218#ifdef BACKWARDS
1219 else if (shcmd == NULL || *(shcmd + 1) == '\0')
1220#else
1221 else if (shcmd == NULL)
1222#endif
1223 {
1224 sprintf(errmsg, "no previous command");
1225 return ERR;
1226 } else {
1227 CKBUF(buf, n, i + shcmdi, ERR);
1228 for (s = shcmd + 1; s < shcmd + shcmdi;)
1229 buf[i++] = *s++;
1230 s = ibufp++;
1231 }
1232 break;
1233 case '%':
1234 if (*dfn == '\0') {
1235 sprintf(errmsg, "no current filename");
1236 return ERR;
1237 }
1238 j = strlen(s = esctos(dfn));
1239 CKBUF(buf, n, i + j, ERR);
1240 while (j--)
1241 buf[i++] = *s++;
1242 s = ibufp++;
1243 break;
1244 }
1245 CKBUF(shcmd, shcmdsz, i + 1, ERR);
1246 memcpy(shcmd, buf, i);
1247 shcmd[shcmdi = i] = '\0';
1248 return *s == '!' || *s == '%';
1249}
1250
1251
1252/* append: insert text from stdin to after line n; stop when either a
1253 single period is read or EOF; return status */
1254append(n, glob)
1255 long n;
1256 int glob;
1257{
1258 int l;
1259 char *lp = ibuf;
1260 char *eot;
1261 undo_t *up = NULL;
1262
1263 for (curln = n;;) {
1264 if (!glob) {
1265 if ((l = getline()) < 0)
1266 return ERR;
1267 else if (l == 0 || ibuf[l - 1] != '\n') {
1268 clearerr(stdin);
1269 return l ? EOF : 0;
1270 }
1271 lp = ibuf;
1272 } else if (*(lp = ibufp) == '\0')
1273 return 0;
1274 else {
1275 while (*ibufp++ != '\n')
1276 ;
1277 l = ibufp - lp;
1278 }
1279 if (l == 2 && lp[0] == '.' && lp[1] == '\n') {
1280 return 0;
1281 }
1282 eot = lp + l;
1283 spl1();
1284 do {
1285 if ((lp = puttxt(lp)) == NULL) {
1286 spl0();
1287 return ERR;
1288 } else if (up)
1289 up->t = getlp(curln);
1290 else if ((up = upush(UADD, curln, curln)) == NULL) {
1291 spl0();
1292 return ERR;
1293 }
1294 } while (lp != eot);
1295 spl0();
1296 modified = 1;
1297 }
1298}
1299
1300
54a7a3ed
AM
1301/* subst: change all text matching a pattern in a range of lines according to
1302 a substitution template; return status */
1303subst(pat, gflag)
1304 pattern_t *pat;
1305 int gflag;
1306{
1307 undo_t *up;
1308 char *txt;
1309 char *eot;
1310 long lc;
1311 int nsubs = 0;
1312 line_t *lp;
1313 int len;
1314
1315 curln = prevln(line1, lastln);
1316 for (lc = 0; lc <= line2 - line1; lc++) {
1317 lp = getlp(curln = nextln(curln, lastln));
1318 if ((len = regsub(pat, lp, gflag)) < 0)
1319 return ERR;
1320 else if (len) {
1321 up = NULL;
e8bd1b9a
AM
1322 if (lndelete(curln, curln) < 0)
1323 return ERR;
54a7a3ed
AM
1324 txt = rbuf;
1325 eot = rbuf + len;
1326 spl1();
1327 do {
1328 if ((txt = puttxt(txt)) == NULL) {
1329 spl0();
1330 return ERR;
1331 } else if (up)
1332 up->t = getlp(curln);
1333 else if ((up = upush(UADD, curln, curln)) == NULL) {
1334 spl0();
1335 return ERR;
1336 }
1337 } while (txt != eot);
1338 spl0();
1339 nsubs++;
1340 }
1341 }
1342 if (nsubs == 0 && !(gflag & GLB)) {
1343 sprintf(errmsg, "no match");
1344 return ERR;
1345 } else if ((gflag & (GPR | GLS | GNP))
1346 && doprint(curln, curln, gflag) < 0)
1347 return ERR;
1348 return 1;
1349}
54a7a3ed
AM
1350
1351
1352/* regsub: replace text matched by a pattern according to a substitution
1353 template; return pointer to the modified text */
1354regsub(pat, lp, gflag)
1355 pattern_t *pat;
1356 line_t *lp;
1357 int gflag;
1358{
1359 int off = 0;
1360 int kth = gflag >> 8; /* substitute kth match only */
1361 int chngd = 0;
1362 int matchno = 0;
1363 int len;
1364 int i = 0;
1365 regmatch_t rm[SE_MAX];
1366 char *txt;
1367 char *eot;
1368
1369 if ((txt = gettxt(lp)) == NULL)
1370 return ERR;
1371 len = lp->len & ~ACTV;
1372 eot = txt + len;
1373 if (isbinary) txt = nultonl(txt, len);
1374 if (!regexec(pat, txt, SE_MAX, rm, 0)) {
1375 do {
1376 if (!kth || kth == ++matchno) {
1377 chngd++;
1378 i = rm[0].rm_so;
1379 CKBUF(rbuf, rbufsz, off + i, ERR);
1380 if (isbinary) txt = nltonul(txt, rm[0].rm_eo);
1381 memcpy(rbuf + off, txt, i);
1382 if ((off = catsub(txt, rm, off += i)) < 0)
1383 return ERR;
1384 } else {
1385 i = rm[0].rm_eo;
1386 CKBUF(rbuf, rbufsz, off + i, ERR);
1387 if (isbinary) txt = nltonul(txt, i);
1388 memcpy(rbuf + off, txt, i);
1389 off += i;
1390 }
1391 txt += rm[0].rm_eo;
1392 } while (*txt && (!chngd || (gflag & GSG) && rm[0].rm_eo)
1393 && !regexec(pat, txt, SE_MAX, rm, REG_NOTBOL));
1394 i = eot - txt;
1395 CKBUF(rbuf, rbufsz, off + i + 2, ERR);
1396 if (i > 0 && !rm[0].rm_eo && (gflag & GSG)) {
1397 sprintf(errmsg, "infinite substitution loop");
1398 return ERR;
1399 }
1400 if (isbinary) txt = nltonul(txt, i);
1401 memcpy(rbuf + off, txt, i);
1402 memcpy(rbuf + off + i, "\n", 2);
1403 }
1404 return chngd ? off + i + 1 : 0;
1405}
1406
1407
1408/* join: replace a range of lines with the joined text of those lines */
1409join(from, to)
1410 long from;
1411 long to;
1412{
1413 static char *buf = NULL;
1414 static int n;
1415
1416 char *s;
1417 int len = 0;
1418 int size = 0;
1419 line_t *bp, *ep;
1420
1421 ep = getlp(nextln(to, lastln));
1422 for (bp = getlp(from); bp != ep; bp = bp->next, size += len) {
1423 if ((s = gettxt(bp)) == NULL)
1424 return ERR;
1425 len = bp->len & ~ACTV;
1426 CKBUF(buf, n, size + len, ERR);
1427 memcpy(buf + size, s, len);
1428 }
1429 CKBUF(buf, n, size + 2, ERR);
1430 memcpy(buf + size, "\n", 2);
e8bd1b9a
AM
1431 if (lndelete(from, to) < 0)
1432 return ERR;
54a7a3ed
AM
1433 curln = from - 1;
1434 spl1();
1435 if (puttxt(buf) == NULL
1436 || upush(UADD, curln, curln) == NULL) {
1437 spl0();
1438 return ERR;
1439 }
1440 spl0();
1441 modified = 1;
1442 return 0;
1443}
1444
1445
1446/* move: move a range of lines */
e8bd1b9a 1447move(num, glob)
54a7a3ed
AM
1448 long num;
1449{
e8bd1b9a 1450 line_t *b1, *a1, *b2, *a2, *lp;
54a7a3ed
AM
1451 long n = nextln(line2, lastln);
1452 long p = prevln(line1, lastln);
e8bd1b9a 1453 int done = (num == line1 - 1 || num == line2);
54a7a3ed
AM
1454
1455 spl1();
e8bd1b9a
AM
1456 if (done) {
1457 a2 = getlp(n);
1458 b2 = getlp(p);
1459 curln = line2;
1460 } else if (upush(UMOV, p, n) == NULL
54a7a3ed 1461 || upush(UMOV, num, nextln(num, lastln)) == NULL) {
e8bd1b9a
AM
1462 spl0();
1463 return ERR;
1464 } else {
1465 a1 = getlp(n);
1466 if (num < line1)
1467 b1 = getlp(p), b2 = getlp(num); /* this getlp last! */
1468 else b2 = getlp(num), b1 = getlp(p); /* this getlp last! */
1469 a2 = b2->next;
1470 requeue(b2, b1->next);
1471 requeue(a1->prev, a2);
1472 requeue(b1, a1);
1473 curln = num + ((num < line1) ? line2 - line1 + 1 : 0);
54a7a3ed 1474 }
e8bd1b9a
AM
1475 if (glob)
1476 for (lp = b2->next; lp != a2; lp = lp->next)
1477 lp->len &= ~ACTV; /* zero ACTV bit */
54a7a3ed
AM
1478 spl0();
1479 return 0;
1480}
1481
1482
1483/* transfer: copy a range of lines; return status */
1484transfer(num)
1485 long num;
1486{
1487 line_t *lp;
1488 long nl, nt, lc;
1489 long mid = (num < line2) ? num : line2;
1490 undo_t *up = NULL;
1491
1492 curln = num;
1493 for (nt = 0, nl = line1; nl <= mid; nl++, nt++) {
1494 spl1();
1495 if ((lp = lpdup(getlp(nl))) == NULL) {
1496 spl0();
1497 return ERR;
1498 }
1499 lpqueue(lp);
1500 if (up)
1501 up->t = lp;
1502 else if ((up = upush(UADD, curln, curln)) == NULL) {
1503 spl0();
1504 return ERR;
1505 }
1506 spl0();
1507 }
1508 for (nl += nt, lc = line2 + nt; nl <= lc; nl += 2, lc++) {
1509 spl1();
1510 if ((lp = lpdup(getlp(nl))) == NULL) {
1511 spl0();
1512 return ERR;
1513 }
1514 lpqueue(lp);
1515 if (up)
1516 up->t = lp;
1517 else if ((up = upush(UADD, curln, curln)) == NULL) {
1518 spl0();
1519 return ERR;
1520 }
1521 spl0();
1522 }
1523 return 0;
1524}
1525
1526
1527/* lndelete: delete a range of lines */
1528lndelete(from, to)
1529 long from, to;
1530{
1531 line_t *before, *after;
1532
1533 spl1();
1534 if (upush(UDEL, from, to) == NULL) {
1535 spl0();
1536 return ERR;
1537 }
1538 after = getlp(nextln(to, lastln));
1539 before = getlp(prevln(from, lastln)); /* this getlp last! */
1540 requeue(before, after);
1541 lastln -= to - from + 1;
1542 curln = prevln(from, lastln);
1543 spl0();
1544 return 0;
1545}
1546
1547
1548/* catsub: modify text according to a substitution template;
1549 return offset to end of modified text */
1550catsub(boln, rm, off)
1551 char *boln;
1552 regmatch_t *rm;
1553 int off;
1554{
1555 int j = 0;
1556 int k = 0;
1557 char *sub = rhbuf;
1558
1559 for (; sub - rhbuf < rhbufi; sub++)
1560 if (*sub == '&') {
1561 j = rm[0].rm_so;
1562 k = rm[0].rm_eo;
1563 CKBUF(rbuf, rbufsz, off + k - j, ERR);
1564 while (j < k)
1565 rbuf[off++] = boln[j++];
1566 } else if (*sub == '\\' && '1' <= *++sub && *sub <= '9'
1567 && rm[*sub - '0'].rm_so >= 0
1568 && rm[*sub - '0'].rm_eo >= 0) {
1569 j = rm[*sub - '0'].rm_so;
1570 k = rm[*sub - '0'].rm_eo;
1571 CKBUF(rbuf, rbufsz, off + k - j, ERR);
1572 while (j < k)
1573 rbuf[off++] = boln[j++];
1574 } else {
1575 CKBUF(rbuf, rbufsz, off + 1, ERR);
1576 rbuf[off++] = *sub;
1577 }
1578 CKBUF(rbuf, rbufsz, off + 1, ERR);
1579 rbuf[off] = '\0';
1580 return off;
1581}
1582
1583/* doprint: print a range of lines to stdout */
1584doprint(from, to, gflag)
1585 long from;
1586 long to;
1587 int gflag;
1588{
1589 line_t *bp;
1590 line_t *ep;
1591 char *s;
1592
1593 if (!from) {
1594 sprintf(errmsg, "invalid address");
1595 return ERR;
1596 }
1597 ep = getlp(nextln(to, lastln));
1598 for (bp = getlp(from); bp != ep; bp = bp->next) {
1599 if ((s = gettxt(bp)) == NULL)
1600 return ERR;
1601 putstr(s, bp->len & ~ACTV, curln = from++, gflag);
1602 }
1603 return 0;
1604}
1605
1606
1607int cols = 72; /* wrap column: ws_col - 8 */
1608
1609/* putstr: print text to stdout */
1610void
1611putstr(s, l, n, gflag)
1612 char *s;
1613 int l;
1614 long n;
1615 int gflag;
1616{
1617 int col = 0;
1618
1619 if (gflag & GNP) {
1620 printf("%ld\t", n);
1621 col = 8;
1622 }
1623 for (; l--; s++) {
1624 if ((gflag & GLS) && ++col > cols) {
1625 fputs("\\\n", stdout);
1626 col = 1;
1627 }
1628 if (gflag & GLS) {
1629 switch (*s) {
1630 case '\b':
1631 fputs("\\b", stdout);
1632 break;
1633 case '\f':
1634 fputs("\\f", stdout);
1635 break;
1636 case '\n':
1637 fputs("\\n", stdout);
1638 break;
1639 case '\r':
1640 fputs("\\r", stdout);
1641 break;
1642 case '\t':
1643 fputs("\\t", stdout);
1644 break;
1645 case '\v':
1646 fputs("\\v", stdout);
1647 break;
1648 default:
1649 if (*s < 32 || 126 < *s) {
1650 putchar('\\');
1651 putchar((((unsigned char) *s & 0300) >> 6) + '0');
1652 putchar((((unsigned char) *s & 070) >> 3) + '0');
1653 putchar(((unsigned char) *s & 07) + '0');
1654 col += 2;
1655 } else if (*s == '\\')
1656 fputs("\\\\", stdout);
1657 else {
1658 putchar(*s);
1659 col--;
1660 }
1661 }
1662 col++;
1663 } else
1664 putchar(*s);
1665 }
1666#ifndef BACKWARDS
1667 if (gflag & GLS)
1668 putchar('$');
1669#endif
1670 putchar('\n');
1671}
1672
1673
1674int newline_added; /* set if newline appended to input file */
1675
1676/* doread: read a text file into the editor buffer; return line count */
1677long
1678doread(n, fn)
1679 long n;
1680 char *fn;
1681{
1682 FILE *fp;
1683 line_t *lp = getlp(n);
1684 unsigned long size = 0;
1685 undo_t *up = NULL;
1686 int len;
1687
1688 isbinary = newline_added = 0;
1689 if ((fp = (*fn == '!') ? popen(fn + 1, "r") : fopen(esctos(fn), "r")) == NULL) {
1690 fprintf(stderr, "%s: %s\n", fn, strerror(errno));
1691 sprintf(errmsg, "cannot open input file");
1692 return ERR;
1693 } else if (des)
1694 desinit();
1695 for (curln = n; (len = sgetline(fp)) > 0; size += len) {
1696 spl1();
1697 if (puttxt(sbuf) == NULL) {
1698 spl0();
1699 return ERR;
1700 }
1701 lp = lp->next;
1702 if (up)
1703 up->t = lp;
1704 else if ((up = upush(UADD, curln, curln)) == NULL) {
1705 spl0();
1706 return ERR;
1707 }
1708 spl0();
1709 }
1710 if (((*fn == '!') ? pclose(fp) : fclose(fp)) < 0) {
1711 fprintf(stderr, "%s: %s\n", fn, strerror(errno));
1712 sprintf(errmsg, "cannot close input file");
1713 return ERR;
1714 }
1715 if (newline_added && !isbinary)
1716 fputs("newline appended\n", stderr);
1717 if (des) size += 8 - size % 8;
1718 fprintf(stderr, !scripted ? "%lu\n" : "", size);
1719 return (len < 0) ? ERR : curln - n;
1720}
1721
1722
1723/* dowrite: write the text of a range of lines to a file; return line count */
1724long
1725dowrite(n, m, fn, mode)
1726 long n;
1727 long m;
1728 char *fn;
1729 char *mode;
1730{
1731 FILE *fp;
1732 line_t *lp;
1733 unsigned long size = 0;
1734 long lc = n ? m - n + 1 : 0;
1735 char *s = NULL;
1736 int len;
1737 int ct;
1738
1739 if ((fp = ((*fn == '!') ? popen(fn + 1, "w") : fopen(esctos(fn), mode))) == NULL) {
1740 fprintf(stderr, "%s: %s\n", fn, strerror(errno));
1741 sprintf(errmsg, "cannot open output file");
1742 return ERR;
1743 } else if (des)
1744 desinit();
1745 if (n && !des)
1746 for (lp = getlp(n); n <= m; n++, lp = lp->next) {
1747 if ((s = gettxt(lp)) == NULL)
1748 return ERR;
1749 len = lp->len & ~ACTV;
1750 if (n != lastln || !isbinary || !newline_added)
1751 s[len++] = '\n';
1752 if ((ct = fwrite(s, sizeof(char), len, fp)) < 0 || ct != len) {
1753 fprintf(stderr, "%s: %s\n", fn, strerror(errno));
1754 sprintf(errmsg, "cannot write file");
1755 return ERR;
1756 }
1757 size += len;
1758 }
1759 else if (n)
1760 for (lp = getlp(n); n <= m; n++, lp = lp->next) {
1761 if ((s = gettxt(lp)) == NULL)
1762 return ERR;
1763 len = lp->len & ~ACTV;
1764 while (len--) {
1765 if (desputc(*s++, fp) == EOF && ferror(fp)) {
1766 fprintf(stderr, "%s: %s\n", fn, strerror(errno));
1767 sprintf(errmsg, "cannot write file");
1768 return ERR;
1769 }
1770 }
1771 if (n != lastln || !isbinary || !newline_added) {
1772 if (desputc('\n', fp) < 0) {
1773 fprintf(stderr, "%s: %s\n", fn, strerror(errno));
1774 sprintf(errmsg, "cannot write file");
1775 return ERR;
1776 }
1777 size++; /* for '\n' */
1778 }
1779 size += (lp->len & ~ACTV);
1780 }
1781 if (des) {
1782 desflush(fp); /* flush buffer */
1783 size += 8 - size % 8; /* adjust DES size */
1784 }
1785 if (((*fn == '!') ? pclose(fp) : fclose(fp)) < 0) {
1786 fprintf(stderr, "%s: %s\n", fn, strerror(errno));
1787 sprintf(errmsg, "cannot close output file");
1788 return ERR;
1789 }
1790 fprintf(stderr, !scripted ? "%lu\n" : "", size);
1791 return lc;
1792}
1793
1794
1795#define USIZE 100 /* undo stack size */
1796undo_t *ustack = NULL; /* undo stack */
1797long usize = 0; /* stack size variable */
1798long u_p = 0; /* undo stack pointer */
1799
54a7a3ed
AM
1800/* upush: return pointer to intialized undo node */
1801undo_t *
1802upush(type, from, to)
1803 int type;
1804 long from;
1805 long to;
1806{
1807 undo_t *t;
1808
1809#if defined(sun) || defined(NO_REALLOC_NULL)
1810 if (ustack == NULL
1811 && (ustack = (undo_t *) malloc((usize = USIZE) * sizeof(undo_t))) == NULL) {
1812 fprintf(stderr, "%s\n", strerror(errno));
1813 sprintf(errmsg, "out of memory");
1814 return NULL;
1815 }
1816#endif
1817 t = ustack;
1818 if (u_p < usize
1819 || (t = (undo_t *) realloc(ustack, (usize += USIZE) * sizeof(undo_t))) != NULL) {
1820 ustack = t;
1821 ustack[u_p].type = type;
1822 ustack[u_p].t = getlp(to);
1823 ustack[u_p].h = getlp(from);
1824 return ustack + u_p++;
1825 }
1826 /* out of memory - release undo stack */
1827 fprintf(stderr, "%s\n", strerror(errno));
1828 sprintf(errmsg, "out of memory");
1829 ureset();
1830 free(ustack);
1831 ustack = NULL;
1832 usize = 0;
1833 return NULL;
1834}
1835
e8bd1b9a
AM
1836
1837/* USWAP: swap undo nodes */
1838#define USWAP(x,y) { \
1839 undo_t utmp; \
1840 utmp = x, x = y, y = utmp; \
1841}
1842
1843
54a7a3ed 1844/* undo: undo last change to the editor buffer */
e8bd1b9a
AM
1845undo(glob)
1846 int glob;
54a7a3ed 1847{
e8bd1b9a 1848 long n;
54a7a3ed
AM
1849 long ocurln = curln;
1850 long olastln = lastln;
e8bd1b9a 1851 line_t *lp, *np;
54a7a3ed
AM
1852
1853 if (ucurln == -1 || ulastln == -1) {
1854 sprintf(errmsg, "nothing to undo");
1855 return ERR;
1856 } else if (u_p)
1857 modified = 1;
1858 getlp(0); /* this getlp last! */
1859 spl1();
e8bd1b9a
AM
1860 for (n = u_p; n-- > 0;) {
1861 switch(ustack[n].type) {
54a7a3ed
AM
1862 case UADD:
1863 requeue(ustack[n].h->prev, ustack[n].t->next);
1864 break;
1865 case UDEL:
1866 requeue(ustack[n].h->prev, ustack[n].h);
1867 requeue(ustack[n].t, ustack[n].t->next);
1868 break;
1869 case UMOV:
1870 case VMOV:
e8bd1b9a
AM
1871 requeue(ustack[n - 1].h, ustack[n].h->next);
1872 requeue(ustack[n].t->prev, ustack[n - 1].t);
54a7a3ed 1873 requeue(ustack[n].h, ustack[n].t);
e8bd1b9a 1874 n--;
54a7a3ed
AM
1875 break;
1876 default:
1877 /*NOTREACHED*/
1878 ;
1879 }
e8bd1b9a
AM
1880 ustack[n].type ^= 1;
1881 }
1882 /* reverse undo order */
1883 for (n = u_p; n-- > (u_p + 1)/ 2;)
1884 USWAP(ustack[n], ustack[u_p - 1 - n]);
1885 if (glob)
1886 for (lp = np = getlp(0); (lp = lp->next) != np;)
1887 lp->len &= ~ACTV; /* zero ACTV bit */
54a7a3ed
AM
1888 curln = ucurln, ucurln = ocurln;
1889 lastln = ulastln, ulastln = olastln;
1890 spl0();
1891 return 0;
1892}
1893
1894
1895/* ureset: clear the undo stack */
1896void
1897ureset()
1898{
1899 line_t *lp, *ep, *tl;
1900 int i;
1901
1902 while (u_p--)
e8bd1b9a 1903 if ((ustack[u_p].type) == UDEL) {
54a7a3ed
AM
1904 ep = ustack[u_p].t->next;
1905 for (lp = ustack[u_p].h; lp != ep; lp = tl) {
1906 if (markno)
1907 for (i = 0; i < MAXMARK; i++)
1908 if (mark[i] == lp) {
1909 mark[i] = NULL;
1910 markno--;
1911 }
1912 tl = lp->next;
1913 free(lp);
1914 }
1915 }
e8bd1b9a 1916 u_p = 0;
54a7a3ed
AM
1917 ucurln = curln;
1918 ulastln = lastln;
1919}
1920
1921
1922
1923/* sgetline: read a line of text up a maximum size from a file; return
1924 line length */
1925sgetline(fp)
1926 FILE *fp;
1927{
1928 register int c;
1929 register int i = 0;
1930
1931 while (((c = des ? desgetc(fp) : getc(fp)) != EOF || !feof(fp) && !ferror(fp)) && c != '\n') {
1932 CKBUF(sbuf, sbufsz, i + 1, ERR);
1933 if (!(sbuf[i++] = c)) isbinary = 1;
1934 }
1935 CKBUF(sbuf, sbufsz, i + 2, ERR);
1936 if (c == '\n')
1937 sbuf[i++] = c;
1938 else if (feof(fp) && i) {
1939 sbuf[i++] = '\n';
1940 newline_added = 1;
1941 } else if (ferror(fp)) {
1942 fprintf(stderr, "%s\n", strerror(errno));
1943 sprintf(errmsg, "cannot read input file");
1944 return ERR;
1945 }
1946 sbuf[i] = '\0';
1947 return (isbinary && newline_added && i) ? --i : i;
1948}
1949
1950
1951/* getline: read a line of text up a maximum size from stdin; return
1952 line length */
1953getline()
1954{
1955 register int i = 0;
1956 register int oi = 0;
1957 char c;
1958
1959 /* Read one character at a time to avoid i/o contention with shell
1960 escapes invoked by nonterminal input, e.g.,
1961 ed - <<EOF
1962 !cat
1963 hello, world
1964 EOF */
1965 for (;;)
1966 switch (read(0, &c, 1)) {
1967 default:
1968 oi = 0;
1969 CKBUF(ibuf, ibufsz, i + 2, ERR);
1970 if (!(ibuf[i++] = c)) isbinary = 1;
1971 if (c != '\n')
1972 continue;
1973 lineno++; /* script line no. */
1974 ibuf[i] = '\0';
1975 ibufp = ibuf;
1976 return i;
1977 case 0:
1978 if (i != oi) {
1979 oi = i;
1980 continue;
1981 } else if (i)
1982 ibuf[i] = '\0';
1983 ibufp = ibuf;
1984 return i;
1985 case -1:
1986 fprintf(stderr, "%s\n", strerror(errno));
1987 sprintf(errmsg, "cannot read standard input");
1988 clearerr(stdin);
1989 ibufp = NULL;
1990 return ERR;
1991 }
1992}
1993
1994
1995/* getcmdv: get a command vector */
1996char *
1997getcmdv(sizep, nonl)
1998 int *sizep;
1999 int nonl;
2000{
2001 int l, n;
2002 char *t = ibufp;
2003
2004 while (*t++ != '\n')
2005 ;
2006 if ((l = t - ibufp) < 2 || !oddesc(ibufp, ibufp + l - 1)) {
2007 *sizep = l;
2008 return ibufp;
2009 }
2010 *sizep = -1;
2011 CKBUF(cvbuf, cvbufsz, l, NULL);
2012 memcpy(cvbuf, ibufp, l);
2013 *(cvbuf + --l - 1) = '\n'; /* strip trailing esc */
2014 if (nonl) l--; /* strip newline */
2015 for (;;) {
2016 if ((n = getline()) < 0)
2017 return NULL;
2018 else if (n == 0 || ibuf[n - 1] != '\n') {
2019 sprintf(errmsg, "unexpected end-of-file");
2020 return NULL;
2021 }
2022 CKBUF(cvbuf, cvbufsz, l + n, NULL);
2023 memcpy(cvbuf + l, ibuf, n);
2024 l += n;
2025 if (n < 2 || !oddesc(cvbuf, cvbuf + l - 1))
2026 break;
2027 *(cvbuf + --l - 1) = '\n'; /* strip trailing esc */
2028 if (nonl) l--; /* strip newline */
2029 }
2030 CKBUF(cvbuf, cvbufsz, l + 1, NULL);
2031 cvbuf[l] = '\0';
2032 *sizep = l;
2033 return cvbuf;
2034}
2035
2036
2037/* lpdup: return a pointer to a copy of a line node */
2038line_t *
2039lpdup(lp)
2040 line_t *lp;
2041{
2042 line_t *np;
2043
2044 if ((np = (line_t *) malloc(sizeof(line_t))) == NULL) {
2045 fprintf(stderr, "%s\n", strerror(errno));
2046 sprintf(errmsg, "out of memory");
2047 return NULL;
2048 }
2049 np->seek = lp->seek;
2050 np->len = (lp->len & ~ACTV); /* zero ACTV bit */
2051 return np;
2052}
2053
2054
2055/* oddesc: return the parity of escapes preceding a character in a
2056 string */
2057oddesc(s, t)
2058 char *s;
2059 char *t;
2060{
2061 return (s == t || *(t - 1) != '\\') ? 0 : !oddesc(s, t - 1);
2062}
2063
2064
2065/* esctos: return copy of escaped string */
2066char *
2067esctos(s)
2068 char *s;
2069{
2070 static char *file = NULL;
2071 static int filesz = 0;
2072
2073 int i = 0;
2074
2075 CKBUF(file, filesz, MAXFNAME + 1, NULL);
2076 /* assert: no trailing escape */
2077 while (file[i++] = (*s == '\\') ? *++s : *s)
2078 s++;
2079 return file;
2080}
2081
2082
2083void
2084onhup(signo)
2085 int signo;
2086{
2087 if (mutex)
2088 sigflags |= (1 << signo);
2089 else dohup(signo);
2090}
2091
2092
2093void
2094onintr(signo)
2095 int signo;
2096{
2097 if (mutex)
2098 sigflags |= (1 << signo);
2099 else dointr(signo);
2100}
2101
2102
2103
2104void
2105dohup(signo)
2106 int signo;
2107{
2108 char *hup = NULL; /* hup filename */
2109 char *s;
2110 int n;
2111
2112 if (!sigactive)
2113 quit(1);
2114 sigflags &= ~(1 << signo);
2115 if (lastln && dowrite(1, lastln, "ed.hup", "w") < 0
2116 && (s = getenv("HOME")) != NULL
2117 && (n = strlen(s)) + 8 <= MAXFNAME /* "ed.hup" + '/' */
2118 && (hup = (char *) malloc(n + 10)) != NULL) {
2119 strcpy(hup, s);
2120 if (hup[n - 1] != '/')
2121 hup[n] = '/', hup[n+1] = '\0';
2122 strcat(hup, "ed.hup");
2123 dowrite(1, lastln, hup, "w");
2124 }
2125 quit(2);
2126}
2127
2128
2129void
2130dointr(signo)
2131 int signo;
2132{
2133 if (!sigactive)
2134 quit(1);
2135 sigflags &= ~(1 << signo);
2136#ifdef _POSIX_SOURCE
2137 siglongjmp(env, -1);
2138#else
2139 longjmp(env, -1);
2140#endif
2141}
2142
2143
2144struct winsize ws; /* window size structure */
2145
2146void
2147dowinch(signo)
2148 int signo;
2149{
2150 sigflags &= ~(1 << signo);
2151 if (ioctl(0, TIOCGWINSZ, (char *) &ws) >= 0) {
2152 if (ws.ws_row > 2) rows = ws.ws_row - 2;
2153 if (ws.ws_col > 8) cols = ws.ws_col - 8;
2154 }
2155}
2156
2157
2158unsigned char ctab[256]; /* character translation table */
2159
2160/* translit: translate characters in a string */
2161char *
2162translit(s, len, from, to)
2163 char *s;
2164 int len;
2165 int from;
2166 int to;
2167{
2168 static int i = 0;
2169
2170 unsigned char *us;
2171
2172 ctab[i] = i; /* restore table to initial state */
2173 ctab[i = from] = to;
2174 for (us = (unsigned char *) s; len-- > 0; us++)
2175 *us = ctab[*us];
2176 return s;
2177}
2178
2179
2180line_t line0; /* initial node of line queue */
2181
2182/* init_buf: open scratch buffer; initialize line queue */
2183void
2184init_buf()
2185{
2186 int i = 0;
2187
2188 if (sbopen() < 0)
2189 quit(2);
2190 requeue(&line0, &line0);
2191 for (i = 0; i < 256; i++)
2192 ctab[i] = i;
2193}
2194
2195
2196/* ckfn: return a legal filename */
2197char *
2198ckfn(s)
2199 char *s;
2200{
2201 if (red && (*s == '!' || !strcmp(s, "..") || strchr(s, '/'))) {
2202 sprintf(errmsg, "shell access restricted");
2203 return NULL;
2204 }
2205 return s;
2206}