Unleashed v1.4
[unleashed.git] / usr / src / cmd / ed / ed.c
blob95703961b96e442258e9f058d8bb3aef32044929
1 /*
2 * CDDL HEADER START
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
19 * CDDL HEADER END
23 * Copyright (c) 1988, 2010, Oracle and/or its affiliates. All rights reserved.
26 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
27 /* All Rights Reserved */
30 * Editor
33 #include <crypt.h>
34 #include <libgen.h>
35 #include <wait.h>
36 #include <string.h>
37 #include <sys/types.h>
38 #include <locale.h>
39 #include <regexpr.h>
40 #include <regex.h>
41 #include <errno.h>
42 #include <paths.h>
44 static const char *msgtab[] =
46 "write or open on pipe failed", /* 0 */
47 "warning: expecting `w'", /* 1 */
48 "mark not lower case ascii", /* 2 */
49 "Cannot open input file", /* 3 */
50 "PWB spec problem", /* 4 */
51 "nothing to undo", /* 5 */
52 "restricted shell", /* 6 */
53 "cannot create output file", /* 7 */
54 "filesystem out of space!", /* 8 */
55 "cannot open file", /* 9 */
56 "cannot link", /* 10 */
57 "Range endpoint too large", /* 11 */
58 "unknown command", /* 12 */
59 "search string not found", /* 13 */
60 "-", /* 14 */
61 "line out of range", /* 15 */
62 "bad number", /* 16 */
63 "bad range", /* 17 */
64 "Illegal address count", /* 18 */
65 "incomplete global expression", /* 19 */
66 "illegal suffix", /* 20 */
67 "illegal or missing filename", /* 21 */
68 "no space after command", /* 22 */
69 "fork failed - try again", /* 23 */
70 "maximum of 64 characters in file names", /* 24 */
71 "`\\digit' out of range", /* 25 */
72 "interrupt", /* 26 */
73 "line too long", /* 27 */
74 "illegal character in input file", /* 28 */
75 "write error", /* 29 */
76 "out of memory for append", /* 30 */
77 "temp file too big", /* 31 */
78 "I/O error on temp file", /* 32 */
79 "multiple globals not allowed", /* 33 */
80 "global too long", /* 34 */
81 "no match", /* 35 */
82 "illegal or missing delimiter", /* 36 */
83 "-", /* 37 */
84 "replacement string too long", /* 38 */
85 "illegal move destination", /* 39 */
86 "-", /* 40 */
87 "no remembered search string", /* 41 */
88 "'\\( \\)' imbalance", /* 42 */
89 "Too many `\\(' s", /* 43 */
90 "more than 2 numbers given", /* 44 */
91 "'\\}' expected", /* 45 */
92 "first number exceeds second", /* 46 */
93 "incomplete substitute", /* 47 */
94 "newline unexpected", /* 48 */
95 "'[ ]' imbalance", /* 49 */
96 "regular expression overflow", /* 50 */
97 "regular expression error", /* 51 */
98 "command expected", /* 52 */
99 "a, i, or c not allowed in G", /* 53 */
100 "end of line expected", /* 54 */
101 "no remembered replacement string", /* 55 */
102 "no remembered command", /* 56 */
103 "illegal redirection", /* 57 */
104 "possible concurrent update", /* 58 */
105 "-", /* 59 */
106 "the x command has become X (upper case)", /* 60 */
107 "Warning: 'w' may destroy input file "
108 "(due to `illegal char' read earlier)",
109 /* 61 */
110 "Caution: 'q' may lose data in buffer;"
111 " 'w' may destroy input file",
112 /* 62 */
113 "Encryption of string failed", /* 63 */
114 "Encryption facility not available", /* 64 */
115 "Cannot encrypt temporary file", /* 65 */
116 "Enter key:", /* 66 */
117 "Illegal byte sequence", /* 67 */
118 "File does not exist", /* 68 */
119 "tempnam failed", /* 69 */
120 "Cannot open temporary file", /* 70 */
124 #include <stdlib.h>
125 #include <limits.h>
126 #include <stdio.h>
127 #include <signal.h>
128 #include <sys/types.h>
129 #include <sys/stat.h>
130 #include <sys/statvfs.h>
131 #include <unistd.h>
132 #include <termio.h>
133 #include <ctype.h>
134 #include <setjmp.h>
135 #include <fcntl.h>
136 #include <wchar.h> /* I18N */
137 #include <wctype.h> /* I18N */
138 #include <widec.h> /* I18N */
140 #define FTYPE(A) (A.st_mode)
141 #define FMODE(A) (A.st_mode)
142 #define IDENTICAL(A, B) (A.st_dev == B.st_dev && A.st_ino == B.st_ino)
143 #define ISBLK(A) ((A.st_mode & S_IFMT) == S_IFBLK)
144 #define ISCHR(A) ((A.st_mode & S_IFMT) == S_IFCHR)
145 #define ISDIR(A) ((A.st_mode & S_IFMT) == S_IFDIR)
146 #define ISFIFO(A) ((A.st_mode & S_IFMT) == S_IFIFO)
147 #define ISREG(A) ((A.st_mode & S_IFMT) == S_IFREG)
149 #define PUTM() if (xcode >= 0) puts(gettext(msgtab[xcode]))
150 #define UNGETC(c) (peekc = c)
151 #define FNSIZE PATH_MAX
152 #define LBSIZE LINE_MAX
154 /* size of substitution replacement pattern buffer */
155 #define RHSIZE (LINE_MAX*2)
157 #define KSIZE 8
159 #define READ 0
160 #define WRITE 1
162 extern char *optarg; /* Value of argument */
163 extern int optind; /* Indicator of argument */
164 extern int __xpg4; /* defined in xpg4.c; 0 if not xpg4-compiled program */
166 struct Fspec {
167 char Ftabs[22];
168 char Fdel;
169 unsigned char Flim;
170 char Fmov;
171 char Ffill;
173 static struct Fspec fss;
175 static char *fsp;
176 static int fsprtn;
177 static char line[70];
178 static char *linp = line;
179 static int sig;
180 static int Xqt = 0;
181 static int lastc;
182 static char savedfile[FNSIZE];
183 static char file[FNSIZE];
184 static char funny[FNSIZE];
185 static int funlink = 0;
186 static char linebuf[LBSIZE];
187 static char *tstring = linebuf;
189 static char *expbuf;
191 static char rhsbuf[RHSIZE];
192 struct lin {
193 long cur;
194 long sav;
196 typedef struct lin *LINE;
197 static LINE zero;
198 static LINE dot;
199 static LINE dol;
200 static LINE endcore;
201 static LINE fendcore;
202 static LINE addr1;
203 static LINE addr2;
204 static LINE savdol, savdot;
205 static int globflg;
206 static int initflg;
207 static char genbuf[LBSIZE];
208 static long count;
209 static int numpass; /* Number of passes thru dosub(). */
210 static int gsubf; /* Occurrence value. LBSIZE-1=all. */
211 static int ocerr1; /* Allows lines NOT changed by dosub() to NOT be put */
212 /* out. Retains last line changed as current line. */
213 static int ocerr2; /* Flags if ANY line changed by substitute(). 0=nc. */
214 static char *nextip;
215 static char *linebp;
216 static int ninbuf;
217 static int peekc;
218 static int io;
219 static void (*oldhup)(), (*oldintr)();
220 static void (*oldquit)(), (*oldpipe)();
221 static void quit(int) __NORETURN;
222 static int vflag = 1;
223 static int xflag;
224 static int xtflag;
225 static int kflag;
226 static int crflag;
227 /* Flag for determining if file being read is encrypted */
228 static int hflag;
229 static int xcode = -1;
230 static char crbuf[LBSIZE];
231 static int perm[2];
232 static int tperm[2];
233 static int permflag;
234 static int tpermflag;
235 static int col;
236 static char *globp;
237 static int tfile = -1;
238 static int tline;
239 static char *tfname;
240 extern char *locs;
241 static char ibuff[LBSIZE];
242 static int iblock = -1;
243 static char obuff[LBSIZE];
244 static int oblock = -1;
245 static int ichanged;
246 static int nleft;
247 static long savnames[26], names[26];
248 static int anymarks;
249 static long subnewa;
250 static int fchange;
251 static int nline;
252 static int fflg, shflg;
253 static char prompt[16] = "*";
254 static int rflg;
255 static int readflg;
256 static int eflg;
257 static int qflg = 0;
258 static int ncflg;
259 static int listn;
260 static int listf;
261 static int pflag;
262 static int flag28 = 0; /* Prevents write after a partial read */
263 static int save28 = 0; /* Flag whether buffer empty at start of read */
264 static long savtime;
265 static char *name = "SHELL";
266 static char *rshell = "/usr/lib/rsh";
267 static char *val;
268 static char *home;
269 static int nodelim;
271 int makekey(int *);
272 int _mbftowc(char *, wchar_t *, int (*)(), int *);
273 static int error(int code);
274 static void tlist(struct Fspec *);
275 static void tstd(struct Fspec *);
276 static void gdelete(void);
277 static void delete(void);
278 static void exfile(void);
279 static void filename(int comm);
280 static void newline(void);
281 static int gettty(void);
282 static void commands(void);
283 static void undo(void);
284 static void save(void);
285 static void strcopy(char *source, char *dest);
286 static int strequal(char **scan1, char *str);
287 static int stdtab(char *, char *);
288 static int lenchk(char *, struct Fspec *);
289 static void clear(struct Fspec *);
290 static int expnd(char *, char *, int *, struct Fspec *);
291 static void tincr(int, struct Fspec *);
292 static void targ(struct Fspec *);
293 static int numb(void);
294 static int fspec(char *, struct Fspec *, int);
295 static void red(char *);
296 static void newtime(void);
297 static void chktime(void);
298 static void getime(void);
299 static void mkfunny(void);
300 static int eopen(char *, int);
301 static void eclose(int f);
302 static void globaln(int);
303 static char *getkey(const char *);
304 static int execute(int, LINE);
305 static void error1(int);
306 static int getcopy(void);
307 static void move(int);
308 static void dosub(void);
309 static int getsub(void);
310 static int compsub(void);
311 static void substitute(int);
312 static void join(void);
313 static void global(int);
314 static void init(void);
315 static void rdelete(LINE, LINE);
316 static void append(int (*)(void), LINE);
317 static int getfile(void);
318 static void putfile(void);
319 static void onpipe(int);
320 static void onhup(int);
321 static void onintr(int);
322 static void setdot(void);
323 static void setall(void);
324 static void setnoaddr(void);
325 static void nonzero(void);
326 static void setzeroasone(void);
327 static long putline(void);
328 static LINE address(void);
329 static char *getaline(long);
330 static char *getblock(long, long);
331 static char *place(char *, char *, char *);
332 static void comple(wchar_t);
333 static void putchr(unsigned char);
334 static void putwchr(wchar_t);
335 static int getchr(void);
336 static void unixcom(void);
337 static void blkio(int, char *, ssize_t (*)());
338 static void reverse(LINE, LINE);
339 static void putd();
340 static wchar_t get_wchr(void);
342 static struct stat Fl, Tf;
343 #ifndef RESEARCH
344 static struct statvfs U;
345 static int Short = 0;
346 static mode_t oldmask; /* No umask while writing */
347 #endif
348 static jmp_buf savej;
350 #ifdef NULLS
351 int nulls; /* Null count */
352 #endif
353 static long ccount;
355 static int errcnt = 0;
358 static void
359 onpipe(int sig)
361 (int)error(0);
365 main(int argc, char **argv)
367 char *p1, *p2;
368 int c;
370 (void) setlocale(LC_ALL, "");
371 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
372 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */
373 #endif
374 (void) textdomain(TEXT_DOMAIN);
376 oldquit = signal(SIGQUIT, SIG_IGN);
377 oldhup = signal(SIGHUP, SIG_IGN);
378 oldintr = signal(SIGINT, SIG_IGN);
379 oldpipe = signal(SIGPIPE, onpipe);
380 if (signal(SIGTERM, SIG_IGN) != SIG_IGN)
381 signal(SIGTERM, quit);
382 p1 = *argv;
383 while (*p1++)
385 while (--p1 >= *argv)
386 if (*p1 == '/')
387 break;
388 *argv = p1 + 1;
389 /* if SHELL set in environment and is /usr/lib/rsh, set rflg */
390 if ((val = getenv(name)) != NULL)
391 if (strcmp(val, rshell) == 0)
392 rflg++;
393 if (**argv == 'r')
394 rflg++;
395 home = getenv("HOME");
396 while (1) {
397 while ((c = getopt(argc, argv, "sp:qxC")) != EOF) {
398 switch (c) {
400 case 's':
401 vflag = 0;
402 break;
404 case 'p':
405 strncpy(prompt, optarg, sizeof (prompt)-1);
406 shflg = 1;
407 break;
409 case 'q':
410 signal(SIGQUIT, SIG_DFL);
411 vflag = 1;
412 break;
414 case 'x':
415 crflag = -1;
416 xflag = 1;
417 break;
419 case 'C':
420 crflag = 1;
421 xflag = 1;
422 break;
424 case '?':
425 (void) fprintf(stderr, gettext(
426 "Usage: ed [- | -s] [-p string] [-x] [-C] [file]\n"
427 " red [- | -s] [-p string] [-x] [-C] [file]\n"));
428 exit(2);
431 if (argv[optind] && strcmp(argv[optind], "-") == 0 &&
432 strcmp(argv[optind-1], "--") != 0) {
433 vflag = 0;
434 optind++;
435 continue;
437 break;
439 argc = argc - optind;
440 argv = &argv[optind];
442 if (xflag) {
443 if (permflag)
444 crypt_close(perm);
445 permflag = 1;
446 kflag = run_setkey(&perm[0], getkey(msgtab[66]));
447 if (kflag == -1) {
448 puts(gettext(msgtab[64]));
449 xflag = 0;
450 kflag = 0;
452 if (kflag == 0)
453 crflag = 0;
456 if (argc > 0) {
457 p1 = *argv;
458 if (strlen(p1) >= (size_t)FNSIZE) {
459 puts(gettext("file name too long"));
460 if (kflag)
461 crypt_close(perm);
462 exit(2);
464 p2 = savedfile;
465 while (*p2++ = *p1++)
467 globp = "e";
468 fflg++;
469 } else /* editing with no file so set savtime to 0 */
470 savtime = 0;
471 eflg++;
472 if ((tfname = tempnam("", "ea")) == NULL) {
473 puts(gettext(msgtab[69]));
474 exit(2);
477 fendcore = (LINE)sbrk(0);
478 init();
479 if (oldintr != SIG_IGN)
480 signal(SIGINT, onintr);
481 if (oldhup != SIG_IGN)
482 signal(SIGHUP, onhup);
483 setjmp(savej);
484 commands();
485 quit(sig);
486 return (0);
489 static void
490 commands(void)
492 LINE a1;
493 int c;
494 char *p1, *p2;
495 int fsave, m, n;
497 for (;;) {
498 nodelim = 0;
499 if (pflag) {
500 pflag = 0;
501 addr1 = addr2 = dot;
502 goto print;
504 if (shflg && globp == 0)
505 write(1, gettext(prompt), strlen(gettext(prompt)));
506 addr1 = 0;
507 addr2 = 0;
508 if ((c = getchr()) == ',') {
509 addr1 = zero + 1;
510 addr2 = dol;
511 #ifdef XPG6
512 /* XPG4 - it was an error if the second address was */
513 /* input and the first address was ommitted */
514 /* Parse second address */
515 if ((a1 = address()) != 0) {
516 addr2 = a1;
518 #endif
519 c = getchr();
520 goto swch;
521 } else if (c == ';') {
522 addr1 = dot;
523 addr2 = dol;
524 #ifdef XPG6
525 /* XPG4 - it was an error if the second address was */
526 /* input and the first address was ommitted */
527 /* Parse second address */
528 if ((a1 = address()) != 0) {
529 addr2 = a1;
531 #endif
532 c = getchr();
533 goto swch;
534 } else
535 peekc = c;
536 do {
537 addr1 = addr2;
538 if ((a1 = address()) == 0) {
539 c = getchr();
540 break;
542 addr2 = a1;
543 if ((c = getchr()) == ';') {
544 c = ',';
545 dot = a1;
547 } while (c == ',');
548 if (addr1 == 0)
549 addr1 = addr2;
550 swch:
551 switch (c) {
553 case 'a':
554 setdot();
555 newline();
556 if (!globflg) save();
557 append(gettty, addr2);
558 continue;
560 case 'c':
561 #ifdef XPG6
562 setzeroasone();
563 #endif
564 delete();
565 append(gettty, addr1-1);
567 /* XPG4 - If no new lines are inserted, then the current */
568 /* line becomes the line after the lines deleted. */
570 if (((linebuf[0] != '.') || (dot == (addr1-1))) &&
571 (addr2 <= dol))
572 dot = addr1;
573 continue;
575 case 'd':
576 delete();
577 continue;
579 case 'E':
580 fchange = 0;
581 c = 'e';
582 /* FALLTHROUGH */
583 case 'e':
584 fflg++;
585 setnoaddr();
586 if (vflag && fchange) {
587 fchange = 0;
588 (void) error(1);
590 filename(c);
591 eflg++;
592 init();
593 addr2 = zero;
594 goto caseread;
596 case 'f':
597 setnoaddr();
598 filename(c);
599 if (!ncflg) /* there is a filename */
600 getime();
601 else
602 ncflg--;
603 puts(savedfile);
604 continue;
606 case 'g':
607 global(1);
608 continue;
609 case 'G':
610 globaln(1);
611 continue;
613 case 'h':
614 newline();
615 setnoaddr();
616 PUTM();
617 continue;
619 case 'H':
620 newline();
621 setnoaddr();
622 if (!hflag) {
623 hflag = 1;
624 PUTM();
626 else
627 hflag = 0;
628 continue;
630 case 'i':
631 #ifdef XPG6
632 setzeroasone();
633 #endif
634 setdot();
635 nonzero();
636 newline();
637 if (!globflg) save();
638 append(gettty, addr2-1);
639 if (dot == addr2-1)
640 dot += 1;
641 continue;
643 case 'j':
644 if (addr2 == 0) {
645 addr1 = dot;
646 addr2 = dot+1;
648 setdot();
649 newline();
650 nonzero();
651 if (!globflg) save();
652 join();
653 continue;
655 case 'k':
656 if ((c = getchr()) < 'a' || c > 'z')
657 (void) error(2);
658 newline();
659 setdot();
660 nonzero();
661 names[c-'a'] = addr2->cur & ~01;
662 anymarks |= 01;
663 continue;
665 case 'm':
666 move(0);
667 continue;
669 case '\n':
670 if (addr2 == 0)
671 addr2 = dot+1;
672 addr1 = addr2;
673 goto print;
675 case 'n':
676 listn++;
677 newline();
678 goto print;
680 case 'l':
681 listf++;
682 /* FALLTHROUGH */
683 case 'p':
684 newline();
685 print:
686 setdot();
687 nonzero();
688 a1 = addr1;
689 do {
690 if (listn) {
691 count = a1 - zero;
692 putd();
693 putchr('\t');
695 puts(getaline((a1++)->cur));
696 } while (a1 <= addr2);
697 dot = addr2;
698 pflag = 0;
699 listn = 0;
700 listf = 0;
701 continue;
703 case 'Q':
704 fchange = 0;
705 /* FALLTHROUGH */
706 case 'q':
707 setnoaddr();
708 newline();
709 quit(sig);
711 case 'r':
712 filename(c);
713 caseread:
714 readflg = 1;
715 save28 = (dol != fendcore);
716 if (crflag == 2 || crflag == -2)
717 crflag = -1; /* restore crflag for next file */
718 errno = 0;
719 if ((io = eopen(file, O_RDONLY)) < 0) {
720 lastc = '\n';
721 /* if first entering editor and file does not exist */
722 /* set saved access time to 0 */
723 if (eflg) {
724 savtime = 0;
725 eflg = 0;
726 if (c == 'e' && vflag == 0)
727 qflg = 1;
729 if (errno == ENOENT) {
730 (void) error(68);
731 } else {
732 (void) error(3);
735 /* get last mod time of file */
736 /* eflg - entered editor with ed or e */
737 if (eflg) {
738 eflg = 0;
739 getime();
741 setall();
742 ninbuf = 0;
743 n = zero != dol;
744 #ifdef NULLS
745 nulls = 0;
746 #endif
747 if (!globflg && (c == 'r')) save();
748 append(getfile, addr2);
749 exfile();
750 readflg = 0;
751 fchange = n;
752 continue;
754 case 's':
755 setdot();
756 nonzero();
757 if (!globflg) save();
758 substitute(globp != 0);
759 continue;
761 case 't':
762 move(1);
763 continue;
765 case 'u':
766 setdot();
767 newline();
768 if (!initflg)
769 undo();
770 else
771 (void) error(5);
772 fchange = 1;
773 continue;
775 case 'v':
776 global(0);
777 continue;
778 case 'V':
779 globaln(0);
780 continue;
782 case 'W':
783 case 'w':
784 if (flag28) {
785 flag28 = 0;
786 fchange = 0;
787 (void) error(61);
789 setall();
791 /* on NULL-RE condition do not generate error */
793 if ((linebuf[0] != '.') && (zero != dol) &&
794 (addr1 <= zero || addr2 > dol))
795 (void) error(15);
796 filename(c);
797 if (Xqt) {
798 io = eopen(file, O_WRONLY);
799 n = 1; /* set n so newtime will not execute */
800 } else {
801 struct stat lFl;
802 fstat(tfile, &Tf);
803 if (stat(file, &Fl) < 0) {
804 if ((io = creat(file, S_IRUSR|S_IWUSR|S_IRGRP
805 |S_IWGRP|S_IROTH|S_IWOTH)) < 0)
806 (void) error(7);
807 fstat(io, &Fl);
808 Fl.st_mtime = 0;
809 lFl = Fl;
810 close(io);
811 } else {
812 #ifndef RESEARCH
813 oldmask = umask(0);
815 * Must determine if file is
816 * a symbolic link
818 lstat(file, &lFl);
819 #endif
821 #ifndef RESEARCH
823 * Determine if there are enough free blocks on system
825 if (!Short && statvfs(file, &U) == 0 &&
826 U.f_bfree < ((Tf.st_size/U.f_frsize) + 100)) {
827 Short = 1;
828 (void) error(8);
830 Short = 0;
831 #endif
832 p1 = savedfile; /* The current filename */
833 p2 = file;
834 m = strcmp(p1, p2);
835 if (c == 'w' && Fl.st_nlink == 1 && ISREG(lFl)) {
836 if (close(open(file, O_WRONLY)) < 0)
837 (void) error(9);
838 if (!(n = m))
839 chktime();
840 mkfunny();
842 * If funlink equals one it means that
843 * funny points to a valid file which must
844 * be unlinked when interrupted.
847 funlink = 1;
848 if ((io = creat(funny, FMODE(Fl))) >= 0) {
849 chown(funny, Fl.st_uid, Fl.st_gid);
850 chmod(funny, FMODE(Fl));
851 putfile();
852 exfile();
854 if (rename(funny, file))
855 (void) error(10);
856 funlink = 0;
857 /* if filenames are the same */
858 if (!n)
859 newtime();
860 /* check if entire buffer was written */
861 fsave = fchange;
862 if (((addr1 == zero) ||
863 (addr1 == (zero + 1))) &&
864 (addr2 == dol))
865 fchange = 0;
866 else
867 fchange = 1;
868 if (fchange == 1 && m != 0)
869 fchange = fsave;
870 continue;
872 } else {
873 n = 1; /* set n so newtime will not execute */
875 if ((io = open(file,
876 (c == 'w') ? O_WRONLY|O_CREAT|O_TRUNC
877 : O_WRONLY|O_CREAT|O_APPEND, S_IRUSR|S_IWUSR
878 |S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)) < 0)
879 (void) error(7);
881 putfile();
882 exfile();
883 if (!n)
884 newtime();
885 fsave = fchange;
886 fchange = (((addr1 == zero) || (addr1 == (zero + 1))) &&
887 (addr2 == dol)) ? 0 : 1;
888 /* Leave fchange alone if partial write was to another file */
889 if (fchange == 1 && m != 0) fchange = fsave;
890 continue;
892 case 'C':
893 crflag = 1;
895 * C is same as X, but always assume input files are
896 * ciphertext
898 goto encrypt;
900 case 'X':
901 crflag = -1;
902 encrypt:
903 setnoaddr();
904 newline();
905 xflag = 1;
906 if (permflag)
907 (void) crypt_close(perm);
908 permflag = 1;
909 if ((kflag = run_setkey(&perm[0], getkey(msgtab[66]))) == -1) {
910 xflag = 0;
911 kflag = 0;
912 crflag = 0;
913 (void) error(64);
915 if (kflag == 0)
916 crflag = 0;
917 continue;
919 case '=':
920 setall();
921 newline();
922 count = (addr2-zero)&077777;
923 putd();
924 putchr('\n');
925 continue;
927 case '!':
928 unixcom();
929 continue;
931 case EOF:
932 return;
934 case 'P':
935 setnoaddr();
936 newline();
937 if (shflg)
938 shflg = 0;
939 else
940 shflg++;
941 continue;
943 if (c == 'x')
944 (void) error(60);
945 else
946 (void) error(12);
950 LINE
951 address(void)
953 int minus, c;
954 LINE a1;
955 int n, relerr, retval;
957 minus = 0;
958 a1 = 0;
959 for (;;) {
960 c = getchr();
961 if ('0' <= c && c <= '9') {
962 n = 0;
963 do {
964 n *= 10;
965 n += c - '0';
966 } while ((c = getchr()) >= '0' && c <= '9');
967 peekc = c;
968 if (a1 == 0)
969 a1 = zero;
970 if (minus < 0)
971 n = -n;
972 a1 += n;
973 minus = 0;
974 continue;
976 relerr = 0;
977 if (a1 || minus)
978 relerr++;
979 switch (c) {
980 case ' ':
981 case '\t':
982 continue;
984 case '+':
985 minus++;
986 if (a1 == 0)
987 a1 = dot;
988 continue;
990 case '-':
991 case '^':
992 minus--;
993 if (a1 == 0)
994 a1 = dot;
995 continue;
997 case '?':
998 case '/':
999 comple(c);
1000 a1 = dot;
1001 for (;;) {
1002 if (c == '/') {
1003 a1++;
1004 if (a1 > dol)
1005 a1 = zero;
1006 } else {
1007 a1--;
1008 if (a1 < zero)
1009 a1 = dol;
1012 if (execute(0, a1))
1013 break;
1014 if (a1 == dot)
1015 (void) error(13);
1017 break;
1019 case '$':
1020 a1 = dol;
1021 break;
1023 case '.':
1024 a1 = dot;
1025 break;
1027 case '\'':
1028 if ((c = getchr()) < 'a' || c > 'z')
1029 (void) error(2);
1030 for (a1 = zero; a1 <= dol; a1++)
1031 if (names[c-'a'] == (a1->cur & ~01))
1032 break;
1033 break;
1035 default:
1036 peekc = c;
1037 if (a1 == 0)
1038 return (0);
1039 a1 += minus;
1041 /* on NULL-RE condition do not generate error */
1043 if ((linebuf[0] != '.') && (a1 < zero || a1 > dol))
1044 (void) error(15);
1045 return (a1);
1047 if (relerr)
1048 (void) error(16);
1052 static void
1053 setdot(void)
1055 if (addr2 == 0)
1056 addr1 = addr2 = dot;
1057 if (addr1 > addr2)
1058 (void) error(17);
1061 static void
1062 setall(void)
1064 if (addr2 == 0) {
1065 addr1 = zero+1;
1066 addr2 = dol;
1067 if (dol == zero)
1068 addr1 = zero;
1070 setdot();
1073 static void
1074 setnoaddr(void)
1076 if (addr2)
1077 (void) error(18);
1080 static void
1081 nonzero(void)
1083 /* on NULL-RE condition do not generate error */
1085 if ((linebuf[0] != '.') && (addr1 <= zero || addr2 > dol))
1086 (void) error(15);
1089 static void
1090 setzeroasone(void)
1092 /* for the c and i commands 0 equal to 1 address */
1093 if (addr1 == zero) {
1094 addr1 = zero+1;
1096 if (addr2 == zero) {
1097 addr2 = zero+1;
1102 static void
1103 newline(void)
1105 int c;
1107 if ((c = getchr()) == '\n')
1108 return;
1109 if (c == 'p' || c == 'l' || c == 'n') {
1110 pflag++;
1111 if (c == 'l') listf++;
1112 if (c == 'n') listn++;
1113 if ((c = getchr()) == '\n')
1114 return;
1116 (void) error(20);
1119 static void
1120 filename(int comm)
1122 char *p1, *p2;
1123 int c;
1124 int i = 0;
1126 count = 0;
1127 c = getchr();
1128 if (c == '\n' || c == EOF) {
1129 p1 = savedfile;
1130 if (*p1 == 0 && comm != 'f')
1131 (void) error(21);
1132 /* ncflg set means do not get mod time of file */
1133 /* since no filename followed f */
1134 if (comm == 'f')
1135 ncflg++;
1136 p2 = file;
1137 while (*p2++ = *p1++)
1139 red(savedfile);
1140 return;
1142 if (c != ' ')
1143 (void) error(22);
1144 while ((c = getchr()) == ' ')
1146 if (c == '!')
1147 ++Xqt, c = getchr();
1148 if (c == '\n')
1149 (void) error(21);
1150 p1 = file;
1151 do {
1152 if (++i >= FNSIZE)
1153 (void) error(24);
1154 *p1++ = c;
1155 if (c == EOF || (c == ' ' && !Xqt))
1156 (void) error(21);
1157 } while ((c = getchr()) != '\n');
1158 *p1++ = 0;
1159 if (Xqt)
1160 if (comm == 'f') {
1161 --Xqt;
1162 (void) error(57);
1164 else
1165 return;
1166 if (savedfile[0] == 0 || comm == 'e' || comm == 'f') {
1167 p1 = savedfile;
1168 p2 = file;
1169 while (*p1++ = *p2++)
1172 red(file);
1176 static void
1177 exfile(void)
1179 #ifdef NULLS
1180 int c;
1181 #endif
1183 #ifndef RESEARCH
1184 if (oldmask) {
1185 umask(oldmask);
1186 oldmask = 0;
1188 #endif
1189 eclose(io);
1190 io = -1;
1191 if (vflag) {
1192 putd();
1193 putchr('\n');
1194 #ifdef NULLS
1195 if (nulls) {
1196 c = count;
1197 count = nulls;
1198 nulls = 0;
1199 putd();
1200 puts(gettext(" nulls replaced by '\\0'"));
1201 count = c;
1203 #endif
1207 static void
1208 onintr(int sig)
1210 signal(SIGINT, onintr);
1211 putchr('\n');
1212 lastc = '\n';
1213 globflg = 0;
1214 if (funlink) unlink(funny); /* remove tmp file */
1215 /* if interrupted a read, only part of file may be in buffer */
1216 if (readflg) {
1217 sprintf(tstring, "\007read may be incomplete - beware!\007");
1218 puts(gettext(tstring));
1219 fchange = 0;
1221 (void) error(26);
1224 static void
1225 onhup(int sig)
1227 signal(SIGINT, SIG_IGN);
1228 signal(SIGHUP, SIG_IGN);
1230 * if there are lines in file and file was not written
1231 * since last update, save in ed.hup, or $HOME/ed.hup
1233 if (dol > zero && fchange == 1) {
1234 addr1 = zero+1;
1235 addr2 = dol;
1236 io = creat("ed.hup",
1237 S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
1238 if (io < 0 && home) {
1239 char *fn;
1241 fn = (char *)calloc(strlen(home) + 8, sizeof (char));
1242 if (fn) {
1243 strcpy(fn, home);
1244 strcat(fn, "/ed.hup");
1245 io = creat(fn, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP
1246 |S_IROTH|S_IWOTH);
1247 free(fn);
1250 if (io > 0)
1251 putfile();
1253 fchange = 0;
1254 ++errcnt;
1255 quit(sig);
1258 static int
1259 error(int code)
1261 int c;
1263 if (code == 28 && save28 == 0) {
1264 fchange = 0;
1265 flag28++;
1267 readflg = 0;
1268 ++errcnt;
1269 listf = listn = 0;
1270 pflag = 0;
1271 #ifndef RESEARCH
1272 if (oldmask) {
1273 umask(oldmask);
1274 oldmask = 0;
1276 #endif
1277 #ifdef NULLS /* Not really nulls, but close enough */
1278 /* This is a bug because of buffering */
1279 if (code == 28) /* illegal char. */
1280 putd();
1281 #endif
1282 /* Cant open file or file does not exist */
1283 if ((code == 3) || (code == 68)) {
1284 if (qflg == 0) {
1285 putchr('?');
1286 puts(file);
1288 else
1289 qflg = 0;
1291 else
1293 putchr('?');
1294 putchr('\n');
1296 count = 0;
1297 lseek(0, (long)0, 2);
1298 if (globp)
1299 lastc = '\n';
1300 globp = 0;
1301 peekc = lastc;
1302 if (lastc)
1303 while ((c = getchr()) != '\n' && c != EOF)
1305 if (io) {
1306 eclose(io);
1307 io = -1;
1309 xcode = code;
1310 if (hflag)
1311 PUTM();
1312 if (code == 4)
1313 return (0); /* Non-fatal error. */
1314 longjmp(savej, 1);
1315 /* NOTREACHED */
1318 static int
1319 getchr(void)
1321 char c;
1322 if (lastc = peekc) {
1323 peekc = 0;
1324 return (lastc);
1326 if (globp) {
1327 if ((lastc = (unsigned char)*globp++) != 0)
1328 return (lastc);
1329 globp = 0;
1330 return (EOF);
1332 if (read(0, &c, 1) <= 0)
1333 return (lastc = EOF);
1334 lastc = (unsigned char)c;
1335 return (lastc);
1338 static int
1339 gettty(void)
1341 int c;
1342 char *gf;
1343 char *p;
1345 p = linebuf;
1346 gf = globp;
1347 while ((c = getchr()) != '\n') {
1348 if (c == EOF) {
1349 if (gf)
1350 peekc = c;
1351 return (c);
1353 if (c == 0)
1354 continue;
1355 *p++ = c;
1357 if (p > &linebuf[LBSIZE-1])
1358 (void) error(27);
1360 *p++ = 0;
1361 if (linebuf[0] == '.' && linebuf[1] == 0)
1362 return (EOF);
1365 * POSIX.2/XPG4 explicitly says no to this:
1367 * in Solaris backslash followed by special character "." is
1368 * special character "." itself; (so terminating input mode can be
1369 * "\.\n").
1371 * however, POSIX2/XPG4 says, input mode is terminated by
1372 * entering line consisting of only 2 characters: ".\n"
1374 * if (linebuf[0]=='\\' && linebuf[1]=='.' && linebuf[2]==0) {
1375 * linebuf[0] = '.';
1376 * linebuf[1] = 0;
1379 return (0);
1382 static int
1383 getfile(void)
1385 char c;
1386 char *lp, *fp;
1388 lp = linebuf;
1389 fp = nextip;
1390 do {
1391 if (--ninbuf < 0) {
1392 if ((ninbuf = read(io, genbuf, LBSIZE)-1) < 0)
1393 if (lp > linebuf) {
1394 puts(gettext("'\\n' appended"));
1395 *genbuf = '\n';
1397 else
1398 return (EOF);
1399 if (crflag == -1) {
1400 if (isencrypt(genbuf, ninbuf + 1))
1401 crflag = 2;
1402 else
1403 crflag = -2;
1405 fp = genbuf;
1406 if (crflag > 0)
1407 if (run_crypt(count, genbuf, ninbuf+1, perm) == -1)
1408 (void) error(63);
1410 if (lp >= &linebuf[LBSIZE]) {
1411 lastc = '\n';
1412 (void) error(27);
1414 if ((*lp++ = c = *fp++) == 0) {
1415 #ifdef NULLS
1416 lp[-1] = '\\';
1417 *lp++ = '0';
1418 nulls++;
1419 #else
1420 lp--;
1421 continue;
1422 #endif
1424 count++;
1425 } while (c != '\n');
1426 *--lp = 0;
1427 nextip = fp;
1428 if (fss.Ffill && fss.Flim && lenchk(linebuf, &fss) < 0) {
1429 write(1, gettext("line too long: lno = "),
1430 strlen(gettext("line too long: lno = ")));
1431 ccount = count;
1432 count = (++dot-zero)&077777;
1433 dot--;
1434 putd();
1435 count = ccount;
1436 putchr('\n');
1438 return (0);
1441 static void
1442 putfile(void)
1444 int n;
1445 LINE a1;
1446 char *fp, *lp;
1447 int nib;
1449 nib = LBSIZE;
1450 fp = genbuf;
1451 a1 = addr1;
1452 do {
1453 lp = getaline(a1++->cur);
1454 if (fss.Ffill && fss.Flim && lenchk(linebuf, &fss) < 0) {
1455 write(1, gettext("line too long: lno = "),
1456 strlen(gettext("line too long: lno = ")));
1457 ccount = count;
1458 count = (a1-zero-1)&077777;
1459 putd();
1460 count = ccount;
1461 putchr('\n');
1463 for (;;) {
1464 if (--nib < 0) {
1465 n = fp-genbuf;
1466 if (kflag)
1467 if (run_crypt(count-n, genbuf, n, perm) == -1)
1468 (void) error(63);
1469 if (write(io, genbuf, n) != n)
1470 (void) error(29);
1471 nib = LBSIZE - 1;
1472 fp = genbuf;
1474 if (dol->cur == 0L)break; /* Allow write of null file */
1475 count++;
1476 if ((*fp++ = *lp++) == 0) {
1477 fp[-1] = '\n';
1478 break;
1481 } while (a1 <= addr2);
1482 n = fp-genbuf;
1483 if (kflag)
1484 if (run_crypt(count-n, genbuf, n, perm) == -1)
1485 (void) error(63);
1486 if (write(io, genbuf, n) != n)
1487 (void) error(29);
1490 static void
1491 append(int (*f)(void), LINE a)
1493 LINE a1, a2, rdot;
1494 long tl;
1496 nline = 0;
1497 dot = a;
1498 while ((*f)() == 0) {
1499 if (dol >= endcore) {
1500 if ((int)sbrk(512 * sizeof (struct lin)) == -1) {
1501 lastc = '\n';
1502 (void) error(30);
1504 endcore += 512;
1506 tl = putline();
1507 nline++;
1508 a1 = ++dol;
1509 a2 = a1+1;
1510 rdot = ++dot;
1511 while (a1 > rdot)
1512 (--a2)->cur = (--a1)->cur;
1513 rdot->cur = tl;
1517 static void
1518 unixcom(void)
1520 void (*savint)();
1521 pid_t pid, rpid;
1522 int retcode;
1523 static char savcmd[LBSIZE]; /* last command */
1524 char curcmd[LBSIZE]; /* current command */
1525 char *psavcmd, *pcurcmd, *psavedfile;
1526 int endflg = 1, shflg = 0;
1527 wchar_t c;
1528 int len;
1530 setnoaddr();
1531 if (rflg)
1532 (void) error(6);
1533 pcurcmd = curcmd;
1534 /* read command til end */
1537 * a '!' found in beginning of command is replaced with the saved
1538 * command. a '%' found in command is replaced with the current
1539 * filename
1542 c = getchr();
1543 if (c == '!') {
1544 if (savcmd[0] == 0)
1545 (void) error(56);
1546 else {
1547 psavcmd = savcmd;
1548 while (*pcurcmd++ = *psavcmd++)
1550 --pcurcmd;
1551 shflg = 1;
1553 } else
1554 UNGETC(c); /* put c back */
1555 while (endflg == 1) {
1556 while ((c = get_wchr()) != '\n' && c != '%' && c != '\\') {
1557 if ((len = wctomb(pcurcmd, c)) <= 0) {
1558 *pcurcmd = (unsigned char)c;
1559 len = 1;
1561 pcurcmd += len;
1564 if (c == '%') {
1565 if (savedfile[0] == 0)
1566 (void) error(21);
1567 else {
1568 psavedfile = savedfile;
1569 while (pcurcmd < curcmd + LBSIZE &&
1570 (*pcurcmd++ = *psavedfile++))
1572 --pcurcmd;
1573 shflg = 1;
1575 } else if (c == '\\') {
1576 c = get_wchr();
1577 if (c != '%')
1578 *pcurcmd++ = '\\';
1579 if ((len = wctomb(pcurcmd, c)) <= 0) {
1580 *pcurcmd = (unsigned char)c;
1581 len = 1;
1583 pcurcmd += len;
1585 else
1586 /* end of command hit */
1587 endflg = 0;
1589 *pcurcmd++ = 0;
1590 if (shflg == 1)
1591 puts(curcmd);
1592 /* save command */
1593 strcpy(savcmd, curcmd);
1595 if ((pid = fork()) == 0) {
1596 signal(SIGHUP, oldhup);
1597 signal(SIGQUIT, oldquit);
1598 close(tfile);
1599 execlp(_PATH_BSHELL, "sh", "-c", curcmd, NULL);
1600 exit(0100);
1602 savint = signal(SIGINT, SIG_IGN);
1603 while ((rpid = wait(&retcode)) != pid && rpid != (pid_t)-1)
1605 signal(SIGINT, savint);
1606 if (vflag) puts("!");
1609 static void
1610 quit(int sig)
1612 if (vflag && fchange) {
1613 fchange = 0;
1614 if (flag28) {
1615 flag28 = 0;
1616 (void) error(62);
1620 * For case where user reads in BOTH a good
1621 * file & a bad file
1623 (void) error(1);
1625 unlink(tfname);
1626 if (kflag)
1627 crypt_close(perm);
1628 if (xtflag)
1629 crypt_close(tperm);
1630 exit(errcnt? 2: 0);
1633 static void
1634 delete(void)
1636 setdot();
1637 newline();
1638 nonzero();
1639 if (!globflg)
1640 save();
1641 rdelete(addr1, addr2);
1644 static void
1645 rdelete(LINE ad1, LINE ad2)
1647 LINE a1, a2, a3;
1649 a1 = ad1;
1650 a2 = ad2+1;
1651 a3 = dol;
1652 dol -= a2 - a1;
1653 do {
1654 (a1++)->cur = (a2++)->cur;
1655 } while (a2 <= a3);
1656 a1 = ad1;
1657 if (a1 > dol)
1658 a1 = dol;
1659 dot = a1;
1660 fchange = 1;
1663 static void
1664 gdelete(void)
1666 LINE a1, a2, a3;
1668 a3 = dol;
1669 for (a1 = zero+1; (a1->cur&01) == 0; a1++)
1670 if (a1 >= a3)
1671 return;
1672 for (a2 = a1 + 1; a2 <= a3; ) {
1673 if (a2->cur & 01) {
1674 a2++;
1675 dot = a1;
1676 } else
1677 (a1++)->cur = (a2++)->cur;
1679 dol = a1-1;
1680 if (dot > dol)
1681 dot = dol;
1682 fchange = 1;
1685 static char *
1686 getaline(long tl)
1688 char *bp, *lp;
1689 int nl;
1691 lp = linebuf;
1692 bp = getblock(tl, READ);
1693 nl = nleft;
1694 tl &= ~0377;
1695 while (*lp++ = *bp++)
1696 if (--nl == 0) {
1697 bp = getblock(tl += 0400, READ);
1698 nl = nleft;
1700 return (linebuf);
1703 static long
1704 putline(void)
1706 char *bp, *lp;
1707 int nl;
1708 long tl;
1710 fchange = 1;
1711 lp = linebuf;
1712 tl = tline;
1713 bp = getblock(tl, WRITE);
1714 nl = nleft;
1715 tl &= ~0377;
1716 while (*bp = *lp++) {
1717 if (*bp++ == '\n') {
1718 *--bp = 0;
1719 linebp = lp;
1720 break;
1722 if (--nl == 0) {
1723 bp = getblock(tl += 0400, WRITE);
1724 nl = nleft;
1727 nl = tline;
1728 tline += (((lp-linebuf)+03)>>1)&077776;
1729 return (nl);
1732 static char *
1733 getblock(long atl, long iof)
1735 int bno, off;
1736 char *p1, *p2;
1737 int n;
1739 bno = atl >> 8;
1740 off = (atl<<1)&0774;
1742 /* bno is limited to 16 bits */
1743 if (bno >= 65535) {
1744 lastc = '\n';
1745 (void) error(31);
1747 nleft = 512 - off;
1748 if (bno == iblock) {
1749 ichanged |= iof;
1750 return (ibuff+off);
1752 if (bno == oblock)
1753 return (obuff+off);
1754 if (iof == READ) {
1755 if (ichanged) {
1756 if (xtflag)
1757 if (run_crypt(0L, ibuff, 512, tperm) == -1)
1758 (void) error(63);
1759 blkio(iblock, ibuff, write);
1761 ichanged = 0;
1762 iblock = bno;
1763 blkio(bno, ibuff, read);
1764 if (xtflag)
1765 if (run_crypt(0L, ibuff, 512, tperm) == -1)
1766 (void) error(63);
1767 return (ibuff+off);
1769 if (oblock >= 0) {
1770 if (xtflag) {
1771 p1 = obuff;
1772 p2 = crbuf;
1773 n = 512;
1774 while (n--)
1775 *p2++ = *p1++;
1776 if (run_crypt(0L, crbuf, 512, tperm) == -1)
1777 (void) error(63);
1778 blkio(oblock, crbuf, write);
1779 } else
1780 blkio(oblock, obuff, write);
1782 oblock = bno;
1783 return (obuff+off);
1786 static void
1787 blkio(int b, char *buf, ssize_t (*iofcn)())
1789 lseek(tfile, (long)b<<9, 0);
1790 if ((*iofcn)(tfile, buf, 512) != 512) {
1791 if (dol != zero)
1792 (void) error(32); /* Bypass this if writing null file */
1796 static void
1797 init(void)
1799 long *markp;
1800 mode_t omask;
1802 if (tfile != -1) {
1803 (void) close(tfile);
1804 (void) unlink(tfname);
1807 tline = 2;
1808 for (markp = names; markp < &names[26]; )
1809 *markp++ = 0L;
1810 subnewa = 0L;
1811 anymarks = 0;
1812 iblock = -1;
1813 oblock = -1;
1814 ichanged = 0;
1815 initflg = 1;
1816 omask = umask(0);
1818 if ((tfile = open(tfname, O_CREAT|O_EXCL|O_RDWR,
1819 S_IRUSR|S_IWUSR)) < 0) {
1820 puts(gettext(msgtab[70]));
1821 exit(2);
1824 umask(omask);
1825 if (xflag) {
1826 xtflag = 1;
1827 if (tpermflag)
1828 (void) crypt_close(tperm);
1829 tpermflag = 1;
1830 if (makekey(tperm)) {
1831 xtflag = 0;
1832 puts(gettext(msgtab[65]));
1835 brk((char *)fendcore);
1836 dot = zero = dol = savdot = savdol = fendcore;
1837 flag28 = save28 = 0;
1838 endcore = fendcore - sizeof (struct lin);
1841 static void
1842 global(int k)
1844 char *gp;
1845 wchar_t l;
1846 char multic[MB_LEN_MAX];
1847 wchar_t c;
1848 LINE a1;
1849 char globuf[LBSIZE];
1850 int n;
1851 int len;
1853 if (globp)
1854 (void) error(33);
1855 setall();
1856 nonzero();
1857 if ((n = _mbftowc(multic, &l, getchr, &peekc)) <= 0)
1858 (void) error(67);
1859 if (l == '\n')
1860 (void) error(19);
1861 save();
1862 comple(l);
1863 gp = globuf;
1864 while ((c = get_wchr()) != '\n') {
1865 if (c == EOF)
1866 (void) error(19);
1868 /* '\\' has special meaning only if preceding a '\n' */
1869 if (c == '\\') {
1870 c = get_wchr();
1871 if (c != '\n')
1872 *gp++ = '\\';
1874 if ((gp + (unsigned int)MB_CUR_MAX) >= &globuf[LBSIZE-1])
1875 (void) error(34);
1876 if ((len = wctomb(gp, c)) <= 0) {
1877 *gp = (unsigned char)c;
1878 len = 1;
1880 gp += len;
1882 if (gp == globuf)
1883 *gp++ = 'p';
1884 *gp++ = '\n';
1885 *gp++ = 0;
1886 for (a1 = zero; a1 <= dol; a1++) {
1887 a1->cur &= ~01;
1888 if (a1 >= addr1 && a1 <= addr2 && execute(0, a1) == k)
1889 a1->cur |= 01;
1892 * Special case: g/.../d (avoid n^2 algorithm)
1894 if (globuf[0] == 'd' && globuf[1] == '\n' && globuf[2] == '\0') {
1895 gdelete();
1896 return;
1898 for (a1 = zero; a1 <= dol; a1++) {
1899 if (a1->cur & 01) {
1900 a1->cur &= ~01;
1901 dot = a1;
1902 globp = globuf;
1903 globflg = 1;
1904 commands();
1905 globflg = 0;
1906 a1 = zero;
1911 static void
1912 join(void)
1914 char *gp, *lp;
1915 LINE a1;
1917 if (addr1 == addr2)
1918 return;
1919 gp = genbuf;
1920 for (a1 = addr1; a1 <= addr2; a1++) {
1921 lp = getaline(a1->cur);
1922 while (*gp = *lp++)
1923 if (gp++ > &genbuf[LBSIZE-1])
1924 (void) error(27);
1926 lp = linebuf;
1927 gp = genbuf;
1928 while (*lp++ = *gp++)
1930 addr1->cur = putline();
1931 if (addr1 < addr2)
1932 rdelete(addr1+1, addr2);
1933 dot = addr1;
1936 static void
1937 substitute(int inglob)
1939 int nl;
1940 LINE a1;
1941 long *markp;
1942 int ingsav; /* For saving arg. */
1944 ingsav = inglob;
1945 ocerr2 = 0;
1946 gsubf = compsub();
1947 for (a1 = addr1; a1 <= addr2; a1++) {
1948 if (execute(0, a1) == 0)
1949 continue;
1950 numpass = 0;
1951 ocerr1 = 0;
1952 inglob |= 01;
1953 dosub();
1954 if (gsubf) {
1955 while (*loc2) {
1956 if (execute(1, (LINE)0) == 0)
1957 break;
1958 dosub();
1961 if (ocerr1 == 0)continue; /* Don't put out-not changed. */
1962 subnewa = putline();
1963 a1->cur &= ~01;
1964 if (anymarks) {
1965 for (markp = names; markp < &names[26]; markp++)
1966 if (*markp == a1->cur)
1967 *markp = subnewa;
1969 a1->cur = subnewa;
1970 append(getsub, a1);
1971 nl = nline;
1972 a1 += nl;
1973 addr2 += nl;
1975 if (ingsav)
1976 return; /* Was in global-no error msg allowed. */
1977 if (inglob == 0)
1978 (void) error(35); /* Not in global, but not found. */
1979 if (ocerr2 == 0)
1980 (void) error(35); /* RE found, but occurrence match failed. */
1983 static int
1984 compsub(void)
1986 int c;
1987 wchar_t seof;
1988 char *p;
1989 char multic[MB_LEN_MAX];
1990 int n;
1991 static char remem[RHSIZE];
1992 static int remflg = -1;
1993 int i;
1995 if ((n = _mbftowc(multic, &seof, getchr, &peekc)) <= 0)
1996 (void) error(67);
1997 if (seof == '\n' || seof == ' ')
1998 (void) error(36);
1999 comple(seof);
2000 p = rhsbuf;
2001 for (;;) {
2002 wchar_t cl;
2003 if ((n = _mbftowc(multic, &cl, getchr, &peekc)) <= 0)
2004 (void) error(67);
2005 if (cl == '\\') {
2006 *p++ = '\\';
2007 if (p >= &rhsbuf[RHSIZE])
2008 (void) error(38);
2009 if ((n = _mbftowc(multic, &cl, getchr, &peekc)) <= 0)
2010 (void) error(67);
2011 } else if (cl == '\n') {
2012 if (nodelim == 1) {
2013 nodelim = 0;
2014 (void) error(36);
2016 if (!(globp && globp[0])) {
2017 UNGETC('\n');
2018 pflag++;
2019 break;
2021 } else if (cl == seof)
2022 break;
2023 if (p + n > &rhsbuf[RHSIZE])
2024 (void) error(38);
2025 (void) strncpy(p, multic, n);
2026 p += n;
2028 *p++ = 0;
2029 if (rhsbuf[0] == '%' && rhsbuf[1] == 0)
2031 * If there isn't a remembered string, it is an error;
2032 * otherwise the right hand side is the previous right
2033 * hand side.
2036 if (remflg == -1)
2037 (void) error(55);
2038 else
2039 strcpy(rhsbuf, remem);
2040 else {
2041 strcpy(remem, rhsbuf);
2042 remflg = 0;
2044 c = 0;
2045 peekc = getchr(); /* Gets char after third delimiter. */
2046 if (peekc == 'g') {
2047 c = LBSIZE; peekc = 0;
2049 if (peekc >= '1' && peekc <= '9') {
2050 c = peekc-'0';
2051 peekc = 0; /* Allows getchr() to get next char. */
2052 while (1) {
2053 i = getchr();
2054 if (i < '0' || i > '9')
2055 break;
2056 c = c*10 + i-'0';
2057 if (c > LBSIZE-1)
2058 (void) error(20); /* "Illegal suffix" */
2060 peekc = i; /* Effectively an unget. */
2062 newline();
2063 return (c);
2066 * Returns occurrence value. 0 & 1 both do first occurrence
2067 * only: c = 0 if ordinary substitute; c = 1
2068 * if use 1 in global sub(s/a/b/1). 0 in global form is illegal.
2072 static int
2073 getsub(void)
2075 char *p1, *p2;
2077 p1 = linebuf;
2078 if ((p2 = linebp) == 0)
2079 return (EOF);
2080 while (*p1++ = *p2++)
2082 linebp = 0;
2083 return (0);
2086 static void
2087 dosub(void)
2089 char *lp, *sp, *rp;
2090 int c;
2092 if (gsubf > 0 && gsubf < LBSIZE) {
2093 numpass++;
2094 if (gsubf != numpass)
2095 return;
2097 ocerr1++;
2098 ocerr2++;
2099 lp = linebuf;
2100 sp = genbuf;
2101 rp = rhsbuf;
2102 while (lp < loc1)
2103 *sp++ = *lp++;
2104 while (c = *rp++) {
2105 if (c == '&') {
2106 sp = place(sp, loc1, loc2);
2107 continue;
2108 } else if (c == '\\') {
2109 c = *rp++;
2110 if (c >= '1' && c < nbra + '1') {
2111 sp = place(sp, braslist[c-'1'],
2112 braelist[c-'1']);
2113 continue;
2116 *sp++ = c;
2117 if (sp >= &genbuf[LBSIZE])
2118 (void) error(27);
2120 lp = loc2;
2121 loc2 = sp - genbuf + linebuf;
2122 while (*sp++ = *lp++)
2123 if (sp >= &genbuf[LBSIZE])
2124 (void) error(27);
2125 lp = linebuf;
2126 sp = genbuf;
2127 while (*lp++ = *sp++)
2131 static char *
2132 place(char *sp, char *l1, char *l2)
2135 while (l1 < l2) {
2136 *sp++ = *l1++;
2137 if (sp >= &genbuf[LBSIZE])
2138 (void) error(27);
2140 return (sp);
2143 static void
2144 comple(wchar_t seof)
2146 int cclass = 0;
2147 wchar_t c;
2148 int n;
2149 char *cp = genbuf;
2150 char multic[MB_LEN_MAX];
2152 while (1) {
2153 if ((n = _mbftowc(multic, &c, getchr, &peekc)) < 0)
2154 error1(67);
2155 if (n == 0 || c == '\n') {
2156 if (cclass)
2157 error1(49);
2158 else
2159 break;
2161 if (c == seof && !cclass)
2162 break;
2163 if (cclass && c == ']') {
2164 cclass = 0;
2165 if (cp > &genbuf[LBSIZE-1])
2166 error1(50);
2167 *cp++ = ']';
2168 continue;
2170 if (c == '[' && !cclass) {
2171 cclass = 1;
2172 if (cp > &genbuf[LBSIZE-1])
2173 error1(50);
2174 *cp++ = '[';
2175 if ((n = _mbftowc(multic, &c, getchr, &peekc)) < 0)
2176 error1(67);
2177 if (n == 0 || c == '\n')
2178 error1(49);
2180 if (c == '\\' && !cclass) {
2181 if (cp > &genbuf[LBSIZE-1])
2182 error1(50);
2183 *cp++ = '\\';
2184 if ((n = _mbftowc(multic, &c, getchr, &peekc)) < 0)
2185 error1(67);
2186 if (n == 0 || c == '\n')
2187 error1(36);
2189 if (cp + n > &genbuf[LBSIZE-1])
2190 error1(50);
2191 (void) strncpy(cp, multic, n);
2192 cp += n;
2194 *cp = '\0';
2195 if (n != 0 && c == '\n')
2196 UNGETC('\n');
2197 if (n == 0 || c == '\n')
2198 nodelim = 1;
2201 * NULL RE: do not compile a null regular expression; but process
2202 * input with last regular expression encountered
2205 if (genbuf[0] != '\0') {
2206 free(expbuf);
2207 expbuf = compile(genbuf, (char *)0, (char *)0);
2209 if (regerrno)
2210 error1(regerrno);
2213 static void
2214 move(int cflag)
2216 LINE adt, ad1, ad2;
2218 setdot();
2219 nonzero();
2220 if ((adt = address()) == 0)
2221 (void) error(39);
2222 newline();
2223 if (!globflg) save();
2224 if (cflag) {
2225 ad1 = dol;
2226 append(getcopy, ad1++);
2227 ad2 = dol;
2228 } else {
2229 ad2 = addr2;
2230 for (ad1 = addr1; ad1 <= ad2; )
2231 (ad1++)->cur &= ~01;
2232 ad1 = addr1;
2234 ad2++;
2235 if (adt < ad1) {
2236 dot = adt + (ad2-ad1);
2237 if ((++adt) == ad1)
2238 return;
2239 reverse(adt, ad1);
2240 reverse(ad1, ad2);
2241 reverse(adt, ad2);
2242 } else if (adt >= ad2) {
2243 dot = adt++;
2244 reverse(ad1, ad2);
2245 reverse(ad2, adt);
2246 reverse(ad1, adt);
2247 } else
2248 (void) error(39);
2249 fchange = 1;
2252 static void
2253 reverse(LINE a1, LINE a2)
2255 long t;
2257 for (;;) {
2258 t = (--a2)->cur;
2259 if (a2 <= a1)
2260 return;
2261 a2->cur = a1->cur;
2262 (a1++)->cur = t;
2266 static int
2267 getcopy(void)
2270 if (addr1 > addr2)
2271 return (EOF);
2272 (void) getaline((addr1++)->cur);
2273 return (0);
2278 * Handles error code returned from comple() routine: regular expression
2279 * compile and match routines
2282 static void
2283 error1(int code)
2285 nbra = 0;
2286 (void) error(code);
2290 static int
2291 execute(int gf, LINE addr)
2293 char *p1;
2294 int c;
2296 for (c = 0; c < nbra; c++) {
2297 braslist[c] = 0;
2298 braelist[c] = 0;
2300 if (gf)
2301 locs = p1 = loc2;
2302 else {
2303 if (addr == zero)
2304 return (0);
2305 p1 = getaline(addr->cur);
2306 locs = 0;
2308 return (step(p1, expbuf));
2312 static void
2313 putd()
2315 int r;
2317 r = (int)(count%10);
2318 count /= 10;
2319 if (count)
2320 putd();
2321 putchr(r + '0');
2326 puts(const char *sp)
2328 int n;
2329 wchar_t c;
2330 int sz, i;
2331 if (fss.Ffill && (listf == 0)) {
2333 /* deliberate attempt to remove constness of sp because */
2334 /* it needs to be expanded */
2336 if ((i = expnd((char *)sp, funny, &sz, &fss)) == -1) {
2337 write(1, funny, fss.Flim & 0377);
2338 putchr('\n');
2339 write(1, gettext("too long"),
2340 strlen(gettext("too long")));
2342 else
2343 write(1, funny, sz);
2344 putchr('\n');
2345 if (i == -2)
2346 write(1, gettext("tab count\n"),
2347 strlen(gettext("tab count\n")));
2348 return (0);
2350 col = 0;
2351 while (*sp) {
2352 n = mbtowc(&c, sp, MB_LEN_MAX);
2353 if (listf) {
2354 if (n < 1)
2355 (void) error(28);
2356 else if (n == 1)
2357 putchr((unsigned char)*sp++);
2358 else {
2359 sp += n;
2360 putwchr(c);
2362 } else {
2363 putchr((unsigned char)*sp++);
2366 #ifndef XPG6
2367 if (listf)
2368 putchr('$'); /* end of line is marked with a $ */
2369 #else
2370 if (listf) {
2371 /* xpg6 - ensure that the end of line $ is not preceeded with a "\" */
2372 /* by doing a putchr() with listf=0, thereby avoiding the $ case */
2373 /* statement in putchr() */
2374 listf = 0;
2375 putchr('$'); /* end of line is marked with a $ */
2376 listf++;
2378 #endif
2379 putchr('\n');
2380 return (1);
2384 static void
2385 putwchr(wchar_t ac)
2387 char buf[MB_LEN_MAX], *p;
2388 char *lp;
2389 wchar_t c;
2390 short len;
2392 lp = linp;
2393 c = ac;
2394 if (listf) {
2395 if (!iswprint(c)) {
2396 p = &buf[0];
2397 if ((len = wctomb(p, c)) <= 0) {
2398 *p = (unsigned char)c;
2399 len = 1;
2401 while (len--) {
2402 if (col + 4 >= 72) {
2403 col = 0;
2404 *lp++ = '\\';
2405 *lp++ = '\n';
2407 (void) sprintf(lp, "\\%03o",
2408 *(unsigned char *)p++);
2409 col += 4;
2410 lp += 4;
2412 } else {
2413 if ((len = wcwidth(c)) <= 0)
2414 len = 0;
2415 if (col + len >= 72) {
2416 col = 0;
2417 *lp++ = '\\';
2418 *lp++ = '\n';
2420 col += len;
2421 if ((len = wctomb(lp, c)) <= 0) {
2422 *lp = (unsigned char)c;
2423 len = 1;
2425 lp += len;
2427 } else {
2428 if ((len = wctomb(lp, c)) <= 0) {
2429 *lp = (unsigned char)c;
2430 len = 1;
2432 lp += len;
2434 if (c == '\n' || lp >= &line[64]) {
2435 linp = line;
2436 len = lp - line;
2437 write(1, line, len);
2438 return;
2440 linp = lp;
2444 static void
2445 putchr(unsigned char c)
2447 char *lp;
2448 int len;
2450 lp = linp;
2451 if (listf && c != '\n') {
2452 switch (c) {
2453 case '\\' :
2454 *lp++ = '\\';
2455 *lp++ = '\\';
2456 col += 2;
2457 break;
2458 case '\007' :
2459 *lp++ = '\\';
2460 *lp++ = 'a';
2461 col += 2;
2462 break;
2463 case '\b' :
2464 *lp++ = '\\';
2465 *lp++ = 'b';
2466 col += 2;
2467 break;
2468 case '\f' :
2469 *lp++ = '\\';
2470 *lp++ = 'f';
2471 col += 2;
2472 break;
2473 case '\r' :
2474 *lp++ = '\\';
2475 *lp++ = 'r';
2476 col += 2;
2477 break;
2478 case '\t' :
2479 *lp++ = '\\';
2480 *lp++ = 't';
2481 col += 2;
2482 break;
2483 case '\v' :
2484 *lp++ = '\\';
2485 *lp++ = 'v';
2486 col += 2;
2487 break;
2488 #ifdef XPG6
2489 /* if $ characters are within the line preceed with \ */
2490 case '$' :
2491 *lp++ = '\\';
2492 *lp++ = '$';
2493 col += 2;
2494 break;
2495 #endif
2496 default:
2497 if (isprint(c)) {
2498 *lp++ = c;
2499 col += 1;
2500 } else {
2501 (void) sprintf(lp, "\\%03o", c);
2502 col += 4;
2503 lp += 4;
2505 break;
2509 * long lines are folded w/ pt of folding indicated by writing
2510 * backslash/newline character
2513 if (col + 1 >= 72) {
2514 col = 0;
2515 *lp++ = '\\';
2516 *lp++ = '\n';
2518 } else
2519 *lp++ = c;
2520 if (c == '\n' || lp >= &line[64]) {
2521 linp = line;
2522 len = lp - line;
2523 (void) write(1, line, len);
2524 return;
2526 linp = lp;
2530 static char *
2531 getkey(const char *prompt)
2533 struct termio b;
2534 int save;
2535 void (*sig)();
2536 static char key[KSIZE+1];
2537 char *p;
2538 int c;
2540 sig = signal(SIGINT, SIG_IGN);
2541 ioctl(0, TCGETA, &b);
2542 save = b.c_lflag;
2543 b.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
2544 ioctl(0, TCSETAW, &b);
2545 write(1, gettext(prompt), strlen(gettext(prompt)));
2546 p = key;
2547 while (((c = getchr()) != EOF) && (c != '\n')) {
2548 if (p < &key[KSIZE])
2549 *p++ = c;
2551 *p = 0;
2552 write(1, "\n", 1);
2553 b.c_lflag = save;
2554 ioctl(0, TCSETAW, &b);
2555 signal(SIGINT, sig);
2556 return (key);
2560 static void
2561 globaln(int k)
2563 char *gp;
2564 int c;
2565 int n;
2566 wchar_t cl;
2567 LINE a1;
2568 int nfirst;
2569 char globuf[LBSIZE];
2570 char multic[MB_LEN_MAX];
2571 int len;
2572 int pflag_save = 0;
2573 int listf_save = 0;
2574 int listn_save = 0;
2576 if (globp)
2577 (void) error(33);
2578 setall();
2579 nonzero();
2580 if ((n = _mbftowc(multic, &cl, getchr, &peekc)) <= 0)
2581 (void) error(67);
2582 if (cl == '\n')
2583 (void) error(19);
2584 save();
2585 comple(cl);
2586 for (a1 = zero; a1 <= dol; a1++) {
2587 a1->cur &= ~01;
2588 if (a1 >= addr1 && a1 <= addr2 && execute(0, a1) == k)
2589 a1->cur |= 01;
2591 nfirst = 0;
2592 newline();
2594 * preserve the p, l, and n suffix commands of the G and V
2595 * commands during the interactive section and restore
2596 * on completion of the G and V command.
2598 pflag_save = pflag;
2599 listf_save = listf;
2600 listn_save = listn;
2601 pflag = 0;
2602 listf = 0;
2603 listn = 0;
2604 for (a1 = zero; a1 <= dol; a1++) {
2605 if (a1->cur & 01) {
2606 a1->cur &= ~01;
2607 dot = a1;
2608 puts(getaline(a1->cur));
2609 if ((c = get_wchr()) == EOF)
2610 (void) error(52);
2611 if (c == 'a' || c == 'i' || c == 'c')
2612 (void) error(53);
2613 if (c == '\n') {
2614 a1 = zero;
2615 continue;
2617 if (c != '&') {
2618 gp = globuf;
2619 if ((len = wctomb(gp, c)) <= 0) {
2620 *gp = (unsigned char)c;
2621 len = 1;
2623 gp += len;
2624 while ((c = get_wchr()) != '\n') {
2626 /* '\\' has special meaning only if preceding a '\n' */
2627 if (c == '\\') {
2628 c = get_wchr();
2629 if (c != '\n')
2630 *gp++ = '\\';
2632 if ((gp + (unsigned int)MB_CUR_MAX) >=
2633 &globuf[LBSIZE-1])
2634 (void) error(34);
2636 if ((len = wctomb(gp, c)) <= 0) {
2637 *gp = (unsigned char)c;
2638 len = 1;
2640 gp += len;
2642 *gp++ = '\n';
2643 *gp++ = 0;
2644 nfirst = 1;
2645 } else if ((c = get_wchr()) != '\n')
2646 (void) error(54);
2647 globp = globuf;
2648 if (nfirst) {
2649 globflg = 1;
2650 commands();
2651 globflg = 0;
2652 } else
2653 (void) error(56);
2654 globp = 0;
2655 a1 = zero;
2658 pflag = pflag_save;
2659 listf = listf_save;
2660 listn = listn_save;
2664 static int
2665 eopen(char *string, int rw)
2667 #define w_or_r(a, b) (rw ? a : b)
2668 int pf[2];
2669 pid_t i;
2670 int io;
2671 int chcount; /* # of char read. */
2673 if (rflg) { /* restricted shell */
2674 if (Xqt) {
2675 Xqt = 0;
2676 (void) error(6);
2679 if (!Xqt) {
2680 if ((io = open(string, rw)) >= 0) {
2681 if (fflg) {
2682 chcount = read(io, crbuf, LBSIZE);
2683 if (crflag == -1) {
2684 if (isencrypt(crbuf, chcount))
2685 crflag = 2;
2686 else
2687 crflag = -2;
2689 if (crflag > 0)
2690 if (run_crypt(0L, crbuf, chcount, perm) == -1)
2691 (void) error(63);
2692 if (fspec(crbuf, &fss, 0) < 0) {
2693 fss.Ffill = 0;
2694 fflg = 0;
2695 (void) error(4);
2697 lseek(io, 0L, 0);
2700 fflg = 0;
2701 return (io);
2703 if (pipe(pf) < 0)
2704 xerr: (void) error(0);
2705 if ((i = fork()) == 0) {
2706 signal(SIGHUP, oldhup);
2707 signal(SIGQUIT, oldquit);
2708 signal(SIGPIPE, oldpipe);
2709 signal(SIGINT, (void (*)()) 0);
2710 close(w_or_r(pf[1], pf[0]));
2711 close(w_or_r(0, 1));
2712 dup(w_or_r(pf[0], pf[1]));
2713 close(w_or_r(pf[0], pf[1]));
2714 execlp(_PATH_BSHELL, "sh", "-c", string, (char *)0);
2715 exit(1);
2717 if (i == (pid_t)-1)
2718 goto xerr;
2719 close(w_or_r(pf[0], pf[1]));
2720 return (w_or_r(pf[1], pf[0]));
2724 static void
2725 eclose(int f)
2727 close(f);
2728 if (Xqt)
2729 Xqt = 0, wait((int *)0);
2733 static void
2734 mkfunny(void)
2736 char *p, *p1, *p2;
2738 p2 = p1 = funny;
2739 p = file;
2741 * Go to end of file name
2743 while (*p)
2744 p++;
2745 while (*--p == '/') /* delete trailing slashes */
2746 *p = '\0';
2748 * go back to beginning of file
2750 p = file;
2752 * Copy file name to funny setting p2 at
2753 * basename of file.
2755 while (*p1++ = *p)
2756 if (*p++ == '/')
2757 p2 = p1;
2759 * Set p1 to point to basename of tfname.
2761 p1 = strrchr(tfname, '/');
2762 if (strlen(tfname) > (size_t)6)
2763 p1 = &tfname[strlen(tfname)-6];
2764 p1++;
2765 *p2 = '\007'; /* add unprintable char for funny a unique name */
2767 * Copy tfname to file.
2769 while (*++p2 = *p1++)
2774 static void
2775 getime(void) /* get modified time of file and save */
2777 if (stat(file, &Fl) < 0)
2778 savtime = 0;
2779 else
2780 savtime = Fl.st_mtime;
2784 static void
2785 chktime(void) /* check saved mod time against current mod time */
2787 if (savtime != 0 && Fl.st_mtime != 0) {
2788 if (savtime != Fl.st_mtime)
2789 (void) error(58);
2794 static void
2795 newtime(void) /* get new mod time and save */
2797 stat(file, &Fl);
2798 savtime = Fl.st_mtime;
2803 * restricted - check for '/' in name and delete trailing '/'
2805 static void
2806 red(char *op)
2808 char *p;
2810 p = op;
2811 while (*p)
2812 if (*p++ == '/'&& rflg) {
2813 *op = 0;
2814 (void) error(6);
2816 /* delete trailing '/' */
2817 while (p > op) {
2818 if (*--p == '/')
2819 *p = '\0';
2820 else break;
2826 * Searches thru beginning of file looking for a string of the form
2827 * <: values... :>
2829 * where "values" are
2831 * \b ignored
2832 * s<num> sets the Flim to <num>
2833 * t??? sets tab stop stuff
2834 * d ignored
2835 * m<num> ignored
2836 * e ignored
2839 static int
2840 fspec(char line[], struct Fspec *f, int up)
2842 struct termio arg;
2843 int havespec, n;
2844 int len;
2846 if (!up) clear(f);
2848 havespec = fsprtn = 0;
2849 for (fsp = line; *fsp && *fsp != '\n'; fsp += len) {
2850 if ((len = mblen(fsp, MB_CUR_MAX)) <= 0)
2851 len = 1;
2852 switch (*fsp) {
2854 case '<': if (havespec)
2855 return (-1);
2856 if (*(fsp+1) == ':') {
2857 havespec = 1;
2858 clear(f);
2859 if (!ioctl(1, TCGETA, &arg) &&
2860 ((arg.c_oflag & TAB3) ==
2861 TAB3))
2862 f->Ffill = 1;
2863 fsp++;
2865 continue;
2867 case ' ': continue;
2869 case 's': if (havespec && (n = numb()) >= 0)
2870 f->Flim = n;
2871 continue;
2873 case 't': if (havespec) targ(f);
2874 continue;
2876 case 'd': continue;
2878 case 'm': if (havespec) n = numb();
2879 continue;
2881 case 'e': continue;
2882 case ':': if (!havespec) continue;
2883 if (*(fsp+1) != '>') fsprtn = -1;
2884 return (fsprtn);
2886 default: if (!havespec) continue;
2887 return (-1);
2890 return (1);
2894 static int
2895 numb(void)
2897 int n;
2899 n = 0;
2900 while (*++fsp >= '0' && *fsp <= '9')
2901 n = 10*n + *fsp-'0';
2902 fsp--;
2903 return (n);
2907 static void
2908 targ(struct Fspec *f)
2911 if (*++fsp == '-') {
2912 if (*(fsp + 1) >= '0' && *(fsp+1) <= '9') tincr(numb(), f);
2913 else tstd(f);
2914 return;
2916 if (*fsp >= '0' && *fsp <= '9') {
2917 tlist(f);
2918 return;
2920 fsprtn = -1;
2921 fsp--;
2925 static void
2926 tincr(int n, struct Fspec *f)
2928 int l, i;
2930 l = 1;
2931 for (i = 0; i < 20; i++)
2932 f->Ftabs[i] = l += n;
2933 f->Ftabs[i] = 0;
2937 static void
2938 tstd(struct Fspec *f)
2940 char std[3];
2942 std[0] = *++fsp;
2943 if (*(fsp+1) >= '0' && *(fsp+1) <= '9') {
2944 std[1] = *++fsp;
2945 std[2] = '\0';
2946 } else {
2947 std[1] = '\0';
2949 fsprtn = stdtab(std, f->Ftabs);
2953 static void
2954 tlist(struct Fspec *f)
2956 int n, last, i;
2958 fsp--;
2959 last = i = 0;
2961 do {
2962 if ((n = numb()) <= last || i >= 20) {
2963 fsprtn = -1;
2964 return;
2966 f->Ftabs[i++] = last = n;
2967 } while (*++fsp == ',');
2969 f->Ftabs[i] = 0;
2970 fsp--;
2974 static int
2975 expnd(char line[], char buf[], int *sz, struct Fspec *f)
2977 char *l, *t;
2978 int b;
2980 l = line - 1;
2981 b = 1;
2982 t = f->Ftabs;
2983 fsprtn = 0;
2985 while (*++l && *l != '\n' && b < 511) {
2986 if (*l == '\t') {
2987 while (*t && b >= *t)
2988 t++;
2989 if (*t == 0)
2990 fsprtn = -2;
2991 do {
2992 buf[b-1] = ' ';
2993 } while (++b < *t);
2994 } else {
2995 buf[b++ - 1] = *l;
2999 buf[b] = '\0';
3000 *sz = b;
3001 if (*l != '\0' && *l != '\n') {
3002 buf[b-1] = '\n';
3003 return (-1);
3005 buf[b-1] = *l;
3006 if (f->Flim && (b-1 > (int)f->Flim))
3007 return (-1);
3008 return (fsprtn);
3012 static void
3013 clear(struct Fspec *f)
3015 f->Ftabs[0] = f->Fdel = f->Fmov = f->Ffill = 0;
3016 f->Flim = 0;
3020 static int
3021 lenchk(char line[], struct Fspec *f)
3023 char *l, *t;
3024 int b;
3026 l = line - 1;
3027 b = 1;
3028 t = f->Ftabs;
3030 while (*++l && *l != '\n' && b < 511) {
3031 if (*l == '\t') {
3032 while (*t && b >= *t)
3033 t++;
3034 while (++b < *t)
3036 } else {
3037 b++;
3041 if ((*l != '\0' && *l != '\n') || (f->Flim && (b-1 > (int)f->Flim)))
3042 return (-1);
3043 return (0);
3045 #define NTABS 21
3049 * stdtabs: standard tabs table
3050 * format: option code letter(s), null, tabs, null
3053 static char stdtabs[] = {
3054 'a', 0, 1, 10, 16, 36, 72, 0, /* IBM 370 Assembler */
3055 'a', '2', 0, 1, 10, 16, 40, 72, 0, /* IBM Assembler alternative */
3056 'c', 0, 1, 8, 12, 16, 20, 55, 0, /* COBOL, normal */
3057 'c', '2', 0, 1, 6, 10, 14, 49, 0, /* COBOL, crunched */
3058 'c', '3', 0, 1, 6, 10, 14, 18, 22, 26, 30, 34, 38, 42, 46, 50,
3059 54, 58, 62, 67, 0,
3060 'f', 0, 1, 7, 11, 15, 19, 23, 0, /* FORTRAN */
3061 'p', 0, 1, 5, 9, 13, 17, 21, 25, 29, 33, 37, 41, 45, 49, 53, 57, 61, 0,
3062 /* PL/I */
3063 's', 0, 1, 10, 55, 0, /* SNOBOL */
3064 'u', 0, 1, 12, 20, 44, 0, /* UNIVAC ASM */
3065 0 };
3069 * stdtab: return tab list for any "canned" tab option.
3070 * entry: option points to null-terminated option string
3071 * tabvect points to vector to be filled in
3072 * exit: return (0) if legal, tabvect filled, ending with zero
3073 * return (-1) if unknown option
3077 static int
3078 stdtab(char option[], char tabvect[NTABS])
3080 char *scan;
3081 tabvect[0] = 0;
3082 scan = stdtabs;
3083 while (*scan) {
3084 if (strequal(&scan, option)) {
3085 strcopy(scan, tabvect);
3086 break;
3087 } else
3088 while (*scan++) /* skip over tab specs */
3092 /* later: look up code in /etc/something */
3093 return (tabvect[0] ? 0 : -1);
3098 * strequal: checks strings for equality
3099 * entry: scan1 points to scan pointer, str points to string
3100 * exit: return (1) if equal, return (0) if not
3101 * *scan1 is advanced to next nonzero byte after null
3105 static int
3106 strequal(char **scan1, char *str)
3108 char c, *scan;
3109 scan = *scan1;
3110 while ((c = *scan++) == *str && c)
3111 str++;
3112 *scan1 = scan;
3113 if (c == 0 && *str == 0)
3114 return (1);
3115 if (c)
3116 while (*scan++)
3118 *scan1 = scan;
3119 return (0);
3123 /* strcopy: copy source to destination */
3126 static void
3127 strcopy(char *source, char *dest)
3129 while (*dest++ = *source++)
3134 /* This is called before a buffer modifying command so that the */
3135 /* current array of line ptrs is saved in sav and dot and dol are saved */
3138 static void
3139 save(void)
3141 LINE i;
3142 int j;
3144 savdot = dot;
3145 savdol = dol;
3146 for (j = 0; j <= 25; j++)
3147 savnames[j] = names[j];
3149 for (i = zero + 1; i <= dol; i++)
3150 i->sav = i->cur;
3151 initflg = 0;
3155 /* The undo command calls this to restore the previous ptr array sav */
3156 /* and swap with cur - dot and dol are swapped also. This allows user to */
3157 /* undo an undo */
3160 static void
3161 undo(void)
3163 int j;
3164 long tmp;
3165 LINE i, tmpdot, tmpdol;
3167 tmpdot = dot; dot = savdot; savdot = tmpdot;
3168 tmpdol = dol; dol = savdol; savdol = tmpdol;
3169 /* swap arrays using the greater of dol or savdol as upper limit */
3170 for (i = zero + 1; i <= ((dol > savdol) ? dol : savdol); i++) {
3171 tmp = i->cur;
3172 i->cur = i->sav;
3173 i->sav = tmp;
3176 * If the current text lines are swapped with the
3177 * text lines in the save buffer, then swap the current
3178 * marks with those in the save area.
3181 for (j = 0; j <= 25; j++) {
3182 tmp = names[j];
3183 names[j] = savnames[j];
3184 savnames[j] = tmp;
3188 static wchar_t
3189 get_wchr(void)
3191 wchar_t wc;
3192 char multi[MB_LEN_MAX];
3194 if (_mbftowc(multi, &wc, getchr, &peekc) <= 0)
3195 wc = getchr();
3196 return (wc);