dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / cmd / ed / ed.c
blob6837818320f28b20de5e0d4382328941d9b8777e
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);
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 case 'e':
583 fflg++;
584 setnoaddr();
585 if (vflag && fchange) {
586 fchange = 0;
587 (void) error(1);
589 filename(c);
590 eflg++;
591 init();
592 addr2 = zero;
593 goto caseread;
595 case 'f':
596 setnoaddr();
597 filename(c);
598 if (!ncflg) /* there is a filename */
599 getime();
600 else
601 ncflg--;
602 puts(savedfile);
603 continue;
605 case 'g':
606 global(1);
607 continue;
608 case 'G':
609 globaln(1);
610 continue;
612 case 'h':
613 newline();
614 setnoaddr();
615 PUTM();
616 continue;
618 case 'H':
619 newline();
620 setnoaddr();
621 if (!hflag) {
622 hflag = 1;
623 PUTM();
625 else
626 hflag = 0;
627 continue;
629 case 'i':
630 #ifdef XPG6
631 setzeroasone();
632 #endif
633 setdot();
634 nonzero();
635 newline();
636 if (!globflg) save();
637 append(gettty, addr2-1);
638 if (dot == addr2-1)
639 dot += 1;
640 continue;
642 case 'j':
643 if (addr2 == 0) {
644 addr1 = dot;
645 addr2 = dot+1;
647 setdot();
648 newline();
649 nonzero();
650 if (!globflg) save();
651 join();
652 continue;
654 case 'k':
655 if ((c = getchr()) < 'a' || c > 'z')
656 (void) error(2);
657 newline();
658 setdot();
659 nonzero();
660 names[c-'a'] = addr2->cur & ~01;
661 anymarks |= 01;
662 continue;
664 case 'm':
665 move(0);
666 continue;
668 case '\n':
669 if (addr2 == 0)
670 addr2 = dot+1;
671 addr1 = addr2;
672 goto print;
674 case 'n':
675 listn++;
676 newline();
677 goto print;
679 case 'l':
680 listf++;
681 case 'p':
682 newline();
683 print:
684 setdot();
685 nonzero();
686 a1 = addr1;
687 do {
688 if (listn) {
689 count = a1 - zero;
690 putd();
691 putchr('\t');
693 puts(getaline((a1++)->cur));
694 } while (a1 <= addr2);
695 dot = addr2;
696 pflag = 0;
697 listn = 0;
698 listf = 0;
699 continue;
701 case 'Q':
702 fchange = 0;
703 case 'q':
704 setnoaddr();
705 newline();
706 quit(sig);
708 case 'r':
709 filename(c);
710 caseread:
711 readflg = 1;
712 save28 = (dol != fendcore);
713 if (crflag == 2 || crflag == -2)
714 crflag = -1; /* restore crflag for next file */
715 errno = 0;
716 if ((io = eopen(file, O_RDONLY)) < 0) {
717 lastc = '\n';
718 /* if first entering editor and file does not exist */
719 /* set saved access time to 0 */
720 if (eflg) {
721 savtime = 0;
722 eflg = 0;
723 if (c == 'e' && vflag == 0)
724 qflg = 1;
726 if (errno == ENOENT) {
727 (void) error(68);
728 } else {
729 (void) error(3);
732 /* get last mod time of file */
733 /* eflg - entered editor with ed or e */
734 if (eflg) {
735 eflg = 0;
736 getime();
738 setall();
739 ninbuf = 0;
740 n = zero != dol;
741 #ifdef NULLS
742 nulls = 0;
743 #endif
744 if (!globflg && (c == 'r')) save();
745 append(getfile, addr2);
746 exfile();
747 readflg = 0;
748 fchange = n;
749 continue;
751 case 's':
752 setdot();
753 nonzero();
754 if (!globflg) save();
755 substitute(globp != 0);
756 continue;
758 case 't':
759 move(1);
760 continue;
762 case 'u':
763 setdot();
764 newline();
765 if (!initflg)
766 undo();
767 else
768 (void) error(5);
769 fchange = 1;
770 continue;
772 case 'v':
773 global(0);
774 continue;
775 case 'V':
776 globaln(0);
777 continue;
779 case 'W':
780 case 'w':
781 if (flag28) {
782 flag28 = 0;
783 fchange = 0;
784 (void) error(61);
786 setall();
788 /* on NULL-RE condition do not generate error */
790 if ((linebuf[0] != '.') && (zero != dol) &&
791 (addr1 <= zero || addr2 > dol))
792 (void) error(15);
793 filename(c);
794 if (Xqt) {
795 io = eopen(file, O_WRONLY);
796 n = 1; /* set n so newtime will not execute */
797 } else {
798 struct stat lFl;
799 fstat(tfile, &Tf);
800 if (stat(file, &Fl) < 0) {
801 if ((io = creat(file, S_IRUSR|S_IWUSR|S_IRGRP
802 |S_IWGRP|S_IROTH|S_IWOTH)) < 0)
803 (void) error(7);
804 fstat(io, &Fl);
805 Fl.st_mtime = 0;
806 lFl = Fl;
807 close(io);
808 } else {
809 #ifndef RESEARCH
810 oldmask = umask(0);
812 * Must determine if file is
813 * a symbolic link
815 lstat(file, &lFl);
816 #endif
818 #ifndef RESEARCH
820 * Determine if there are enough free blocks on system
822 if (!Short && statvfs(file, &U) == 0 &&
823 U.f_bfree < ((Tf.st_size/U.f_frsize) + 100)) {
824 Short = 1;
825 (void) error(8);
827 Short = 0;
828 #endif
829 p1 = savedfile; /* The current filename */
830 p2 = file;
831 m = strcmp(p1, p2);
832 if (c == 'w' && Fl.st_nlink == 1 && ISREG(lFl)) {
833 if (close(open(file, O_WRONLY)) < 0)
834 (void) error(9);
835 if (!(n = m))
836 chktime();
837 mkfunny();
839 * If funlink equals one it means that
840 * funny points to a valid file which must
841 * be unlinked when interrupted.
844 funlink = 1;
845 if ((io = creat(funny, FMODE(Fl))) >= 0) {
846 chown(funny, Fl.st_uid, Fl.st_gid);
847 chmod(funny, FMODE(Fl));
848 putfile();
849 exfile();
851 if (rename(funny, file))
852 (void) error(10);
853 funlink = 0;
854 /* if filenames are the same */
855 if (!n)
856 newtime();
857 /* check if entire buffer was written */
858 fsave = fchange;
859 if (((addr1 == zero) ||
860 (addr1 == (zero + 1))) &&
861 (addr2 == dol))
862 fchange = 0;
863 else
864 fchange = 1;
865 if (fchange == 1 && m != 0)
866 fchange = fsave;
867 continue;
869 } else {
870 n = 1; /* set n so newtime will not execute */
872 if ((io = open(file,
873 (c == 'w') ? O_WRONLY|O_CREAT|O_TRUNC
874 : O_WRONLY|O_CREAT|O_APPEND, S_IRUSR|S_IWUSR
875 |S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)) < 0)
876 (void) error(7);
878 putfile();
879 exfile();
880 if (!n)
881 newtime();
882 fsave = fchange;
883 fchange = (((addr1 == zero) || (addr1 == (zero + 1))) &&
884 (addr2 == dol)) ? 0 : 1;
885 /* Leave fchange alone if partial write was to another file */
886 if (fchange == 1 && m != 0) fchange = fsave;
887 continue;
889 case 'C':
890 crflag = 1;
892 * C is same as X, but always assume input files are
893 * ciphertext
895 goto encrypt;
897 case 'X':
898 crflag = -1;
899 encrypt:
900 setnoaddr();
901 newline();
902 xflag = 1;
903 if (permflag)
904 (void) crypt_close(perm);
905 permflag = 1;
906 if ((kflag = run_setkey(&perm[0], getkey(msgtab[66]))) == -1) {
907 xflag = 0;
908 kflag = 0;
909 crflag = 0;
910 (void) error(64);
912 if (kflag == 0)
913 crflag = 0;
914 continue;
916 case '=':
917 setall();
918 newline();
919 count = (addr2-zero)&077777;
920 putd();
921 putchr('\n');
922 continue;
924 case '!':
925 unixcom();
926 continue;
928 case EOF:
929 return;
931 case 'P':
932 setnoaddr();
933 newline();
934 if (shflg)
935 shflg = 0;
936 else
937 shflg++;
938 continue;
940 if (c == 'x')
941 (void) error(60);
942 else
943 (void) error(12);
947 LINE
948 address(void)
950 int minus, c;
951 LINE a1;
952 int n, relerr, retval;
954 minus = 0;
955 a1 = 0;
956 for (;;) {
957 c = getchr();
958 if ('0' <= c && c <= '9') {
959 n = 0;
960 do {
961 n *= 10;
962 n += c - '0';
963 } while ((c = getchr()) >= '0' && c <= '9');
964 peekc = c;
965 if (a1 == 0)
966 a1 = zero;
967 if (minus < 0)
968 n = -n;
969 a1 += n;
970 minus = 0;
971 continue;
973 relerr = 0;
974 if (a1 || minus)
975 relerr++;
976 switch (c) {
977 case ' ':
978 case '\t':
979 continue;
981 case '+':
982 minus++;
983 if (a1 == 0)
984 a1 = dot;
985 continue;
987 case '-':
988 case '^':
989 minus--;
990 if (a1 == 0)
991 a1 = dot;
992 continue;
994 case '?':
995 case '/':
996 comple(c);
997 a1 = dot;
998 for (;;) {
999 if (c == '/') {
1000 a1++;
1001 if (a1 > dol)
1002 a1 = zero;
1003 } else {
1004 a1--;
1005 if (a1 < zero)
1006 a1 = dol;
1009 if (execute(0, a1))
1010 break;
1011 if (a1 == dot)
1012 (void) error(13);
1014 break;
1016 case '$':
1017 a1 = dol;
1018 break;
1020 case '.':
1021 a1 = dot;
1022 break;
1024 case '\'':
1025 if ((c = getchr()) < 'a' || c > 'z')
1026 (void) error(2);
1027 for (a1 = zero; a1 <= dol; a1++)
1028 if (names[c-'a'] == (a1->cur & ~01))
1029 break;
1030 break;
1032 default:
1033 peekc = c;
1034 if (a1 == 0)
1035 return (0);
1036 a1 += minus;
1038 /* on NULL-RE condition do not generate error */
1040 if ((linebuf[0] != '.') && (a1 < zero || a1 > dol))
1041 (void) error(15);
1042 return (a1);
1044 if (relerr)
1045 (void) error(16);
1049 static void
1050 setdot(void)
1052 if (addr2 == 0)
1053 addr1 = addr2 = dot;
1054 if (addr1 > addr2)
1055 (void) error(17);
1058 static void
1059 setall(void)
1061 if (addr2 == 0) {
1062 addr1 = zero+1;
1063 addr2 = dol;
1064 if (dol == zero)
1065 addr1 = zero;
1067 setdot();
1070 static void
1071 setnoaddr(void)
1073 if (addr2)
1074 (void) error(18);
1077 static void
1078 nonzero(void)
1080 /* on NULL-RE condition do not generate error */
1082 if ((linebuf[0] != '.') && (addr1 <= zero || addr2 > dol))
1083 (void) error(15);
1086 static void
1087 setzeroasone(void)
1089 /* for the c and i commands 0 equal to 1 address */
1090 if (addr1 == zero) {
1091 addr1 = zero+1;
1093 if (addr2 == zero) {
1094 addr2 = zero+1;
1099 static void
1100 newline(void)
1102 int c;
1104 if ((c = getchr()) == '\n')
1105 return;
1106 if (c == 'p' || c == 'l' || c == 'n') {
1107 pflag++;
1108 if (c == 'l') listf++;
1109 if (c == 'n') listn++;
1110 if ((c = getchr()) == '\n')
1111 return;
1113 (void) error(20);
1116 static void
1117 filename(int comm)
1119 char *p1, *p2;
1120 int c;
1121 int i = 0;
1123 count = 0;
1124 c = getchr();
1125 if (c == '\n' || c == EOF) {
1126 p1 = savedfile;
1127 if (*p1 == 0 && comm != 'f')
1128 (void) error(21);
1129 /* ncflg set means do not get mod time of file */
1130 /* since no filename followed f */
1131 if (comm == 'f')
1132 ncflg++;
1133 p2 = file;
1134 while (*p2++ = *p1++)
1136 red(savedfile);
1137 return;
1139 if (c != ' ')
1140 (void) error(22);
1141 while ((c = getchr()) == ' ')
1143 if (c == '!')
1144 ++Xqt, c = getchr();
1145 if (c == '\n')
1146 (void) error(21);
1147 p1 = file;
1148 do {
1149 if (++i >= FNSIZE)
1150 (void) error(24);
1151 *p1++ = c;
1152 if (c == EOF || (c == ' ' && !Xqt))
1153 (void) error(21);
1154 } while ((c = getchr()) != '\n');
1155 *p1++ = 0;
1156 if (Xqt)
1157 if (comm == 'f') {
1158 --Xqt;
1159 (void) error(57);
1161 else
1162 return;
1163 if (savedfile[0] == 0 || comm == 'e' || comm == 'f') {
1164 p1 = savedfile;
1165 p2 = file;
1166 while (*p1++ = *p2++)
1169 red(file);
1173 static void
1174 exfile(void)
1176 #ifdef NULLS
1177 int c;
1178 #endif
1180 #ifndef RESEARCH
1181 if (oldmask) {
1182 umask(oldmask);
1183 oldmask = 0;
1185 #endif
1186 eclose(io);
1187 io = -1;
1188 if (vflag) {
1189 putd();
1190 putchr('\n');
1191 #ifdef NULLS
1192 if (nulls) {
1193 c = count;
1194 count = nulls;
1195 nulls = 0;
1196 putd();
1197 puts(gettext(" nulls replaced by '\\0'"));
1198 count = c;
1200 #endif
1204 static void
1205 onintr(int sig)
1207 signal(SIGINT, onintr);
1208 putchr('\n');
1209 lastc = '\n';
1210 globflg = 0;
1211 if (funlink) unlink(funny); /* remove tmp file */
1212 /* if interrupted a read, only part of file may be in buffer */
1213 if (readflg) {
1214 sprintf(tstring, "\007read may be incomplete - beware!\007");
1215 puts(gettext(tstring));
1216 fchange = 0;
1218 (void) error(26);
1221 static void
1222 onhup(int sig)
1224 signal(SIGINT, SIG_IGN);
1225 signal(SIGHUP, SIG_IGN);
1227 * if there are lines in file and file was not written
1228 * since last update, save in ed.hup, or $HOME/ed.hup
1230 if (dol > zero && fchange == 1) {
1231 addr1 = zero+1;
1232 addr2 = dol;
1233 io = creat("ed.hup",
1234 S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
1235 if (io < 0 && home) {
1236 char *fn;
1238 fn = (char *)calloc(strlen(home) + 8, sizeof (char));
1239 if (fn) {
1240 strcpy(fn, home);
1241 strcat(fn, "/ed.hup");
1242 io = creat(fn, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP
1243 |S_IROTH|S_IWOTH);
1244 free(fn);
1247 if (io > 0)
1248 putfile();
1250 fchange = 0;
1251 ++errcnt;
1252 quit(sig);
1255 static int
1256 error(int code)
1258 int c;
1260 if (code == 28 && save28 == 0) {
1261 fchange = 0;
1262 flag28++;
1264 readflg = 0;
1265 ++errcnt;
1266 listf = listn = 0;
1267 pflag = 0;
1268 #ifndef RESEARCH
1269 if (oldmask) {
1270 umask(oldmask);
1271 oldmask = 0;
1273 #endif
1274 #ifdef NULLS /* Not really nulls, but close enough */
1275 /* This is a bug because of buffering */
1276 if (code == 28) /* illegal char. */
1277 putd();
1278 #endif
1279 /* Cant open file or file does not exist */
1280 if ((code == 3) || (code == 68)) {
1281 if (qflg == 0) {
1282 putchr('?');
1283 puts(file);
1285 else
1286 qflg = 0;
1288 else
1290 putchr('?');
1291 putchr('\n');
1293 count = 0;
1294 lseek(0, (long)0, 2);
1295 if (globp)
1296 lastc = '\n';
1297 globp = 0;
1298 peekc = lastc;
1299 if (lastc)
1300 while ((c = getchr()) != '\n' && c != EOF)
1302 if (io) {
1303 eclose(io);
1304 io = -1;
1306 xcode = code;
1307 if (hflag)
1308 PUTM();
1309 if (code == 4)
1310 return (0); /* Non-fatal error. */
1311 longjmp(savej, 1);
1312 /* NOTREACHED */
1315 static int
1316 getchr(void)
1318 char c;
1319 if (lastc = peekc) {
1320 peekc = 0;
1321 return (lastc);
1323 if (globp) {
1324 if ((lastc = (unsigned char)*globp++) != 0)
1325 return (lastc);
1326 globp = 0;
1327 return (EOF);
1329 if (read(0, &c, 1) <= 0)
1330 return (lastc = EOF);
1331 lastc = (unsigned char)c;
1332 return (lastc);
1335 static int
1336 gettty(void)
1338 int c;
1339 char *gf;
1340 char *p;
1342 p = linebuf;
1343 gf = globp;
1344 while ((c = getchr()) != '\n') {
1345 if (c == EOF) {
1346 if (gf)
1347 peekc = c;
1348 return (c);
1350 if (c == 0)
1351 continue;
1352 *p++ = c;
1354 if (p > &linebuf[LBSIZE-1])
1355 (void) error(27);
1357 *p++ = 0;
1358 if (linebuf[0] == '.' && linebuf[1] == 0)
1359 return (EOF);
1362 * POSIX.2/XPG4 explicitly says no to this:
1364 * in Solaris backslash followed by special character "." is
1365 * special character "." itself; (so terminating input mode can be
1366 * "\.\n").
1368 * however, POSIX2/XPG4 says, input mode is terminated by
1369 * entering line consisting of only 2 characters: ".\n"
1371 * if (linebuf[0]=='\\' && linebuf[1]=='.' && linebuf[2]==0) {
1372 * linebuf[0] = '.';
1373 * linebuf[1] = 0;
1376 return (0);
1379 static int
1380 getfile(void)
1382 char c;
1383 char *lp, *fp;
1385 lp = linebuf;
1386 fp = nextip;
1387 do {
1388 if (--ninbuf < 0) {
1389 if ((ninbuf = read(io, genbuf, LBSIZE)-1) < 0)
1390 if (lp > linebuf) {
1391 puts(gettext("'\\n' appended"));
1392 *genbuf = '\n';
1394 else
1395 return (EOF);
1396 if (crflag == -1) {
1397 if (isencrypt(genbuf, ninbuf + 1))
1398 crflag = 2;
1399 else
1400 crflag = -2;
1402 fp = genbuf;
1403 if (crflag > 0)
1404 if (run_crypt(count, genbuf, ninbuf+1, perm) == -1)
1405 (void) error(63);
1407 if (lp >= &linebuf[LBSIZE]) {
1408 lastc = '\n';
1409 (void) error(27);
1411 if ((*lp++ = c = *fp++) == 0) {
1412 #ifdef NULLS
1413 lp[-1] = '\\';
1414 *lp++ = '0';
1415 nulls++;
1416 #else
1417 lp--;
1418 continue;
1419 #endif
1421 count++;
1422 } while (c != '\n');
1423 *--lp = 0;
1424 nextip = fp;
1425 if (fss.Ffill && fss.Flim && lenchk(linebuf, &fss) < 0) {
1426 write(1, gettext("line too long: lno = "),
1427 strlen(gettext("line too long: lno = ")));
1428 ccount = count;
1429 count = (++dot-zero)&077777;
1430 dot--;
1431 putd();
1432 count = ccount;
1433 putchr('\n');
1435 return (0);
1438 static void
1439 putfile(void)
1441 int n;
1442 LINE a1;
1443 char *fp, *lp;
1444 int nib;
1446 nib = LBSIZE;
1447 fp = genbuf;
1448 a1 = addr1;
1449 do {
1450 lp = getaline(a1++->cur);
1451 if (fss.Ffill && fss.Flim && lenchk(linebuf, &fss) < 0) {
1452 write(1, gettext("line too long: lno = "),
1453 strlen(gettext("line too long: lno = ")));
1454 ccount = count;
1455 count = (a1-zero-1)&077777;
1456 putd();
1457 count = ccount;
1458 putchr('\n');
1460 for (;;) {
1461 if (--nib < 0) {
1462 n = fp-genbuf;
1463 if (kflag)
1464 if (run_crypt(count-n, genbuf, n, perm) == -1)
1465 (void) error(63);
1466 if (write(io, genbuf, n) != n)
1467 (void) error(29);
1468 nib = LBSIZE - 1;
1469 fp = genbuf;
1471 if (dol->cur == 0L)break; /* Allow write of null file */
1472 count++;
1473 if ((*fp++ = *lp++) == 0) {
1474 fp[-1] = '\n';
1475 break;
1478 } while (a1 <= addr2);
1479 n = fp-genbuf;
1480 if (kflag)
1481 if (run_crypt(count-n, genbuf, n, perm) == -1)
1482 (void) error(63);
1483 if (write(io, genbuf, n) != n)
1484 (void) error(29);
1487 static void
1488 append(int (*f)(void), LINE a)
1490 LINE a1, a2, rdot;
1491 long tl;
1493 nline = 0;
1494 dot = a;
1495 while ((*f)() == 0) {
1496 if (dol >= endcore) {
1497 if ((int)sbrk(512 * sizeof (struct lin)) == -1) {
1498 lastc = '\n';
1499 (void) error(30);
1501 endcore += 512;
1503 tl = putline();
1504 nline++;
1505 a1 = ++dol;
1506 a2 = a1+1;
1507 rdot = ++dot;
1508 while (a1 > rdot)
1509 (--a2)->cur = (--a1)->cur;
1510 rdot->cur = tl;
1514 static void
1515 unixcom(void)
1517 void (*savint)();
1518 pid_t pid, rpid;
1519 int retcode;
1520 static char savcmd[LBSIZE]; /* last command */
1521 char curcmd[LBSIZE]; /* current command */
1522 char *psavcmd, *pcurcmd, *psavedfile;
1523 int endflg = 1, shflg = 0;
1524 wchar_t c;
1525 int len;
1527 setnoaddr();
1528 if (rflg)
1529 (void) error(6);
1530 pcurcmd = curcmd;
1531 /* read command til end */
1534 * a '!' found in beginning of command is replaced with the saved
1535 * command. a '%' found in command is replaced with the current
1536 * filename
1539 c = getchr();
1540 if (c == '!') {
1541 if (savcmd[0] == 0)
1542 (void) error(56);
1543 else {
1544 psavcmd = savcmd;
1545 while (*pcurcmd++ = *psavcmd++)
1547 --pcurcmd;
1548 shflg = 1;
1550 } else
1551 UNGETC(c); /* put c back */
1552 while (endflg == 1) {
1553 while ((c = get_wchr()) != '\n' && c != '%' && c != '\\') {
1554 if ((len = wctomb(pcurcmd, c)) <= 0) {
1555 *pcurcmd = (unsigned char)c;
1556 len = 1;
1558 pcurcmd += len;
1561 if (c == '%') {
1562 if (savedfile[0] == 0)
1563 (void) error(21);
1564 else {
1565 psavedfile = savedfile;
1566 while (pcurcmd < curcmd + LBSIZE &&
1567 (*pcurcmd++ = *psavedfile++))
1569 --pcurcmd;
1570 shflg = 1;
1572 } else if (c == '\\') {
1573 c = get_wchr();
1574 if (c != '%')
1575 *pcurcmd++ = '\\';
1576 if ((len = wctomb(pcurcmd, c)) <= 0) {
1577 *pcurcmd = (unsigned char)c;
1578 len = 1;
1580 pcurcmd += len;
1582 else
1583 /* end of command hit */
1584 endflg = 0;
1586 *pcurcmd++ = 0;
1587 if (shflg == 1)
1588 puts(curcmd);
1589 /* save command */
1590 strcpy(savcmd, curcmd);
1592 if ((pid = fork()) == 0) {
1593 signal(SIGHUP, oldhup);
1594 signal(SIGQUIT, oldquit);
1595 close(tfile);
1596 execlp(_PATH_BSHELL, "sh", "-c", curcmd, NULL);
1597 exit(0100);
1599 savint = signal(SIGINT, SIG_IGN);
1600 while ((rpid = wait(&retcode)) != pid && rpid != (pid_t)-1)
1602 signal(SIGINT, savint);
1603 if (vflag) puts("!");
1606 static void
1607 quit(int sig)
1609 if (vflag && fchange) {
1610 fchange = 0;
1611 if (flag28) {
1612 flag28 = 0;
1613 (void) error(62);
1617 * For case where user reads in BOTH a good
1618 * file & a bad file
1620 (void) error(1);
1622 unlink(tfname);
1623 if (kflag)
1624 crypt_close(perm);
1625 if (xtflag)
1626 crypt_close(tperm);
1627 exit(errcnt? 2: 0);
1630 static void
1631 delete(void)
1633 setdot();
1634 newline();
1635 nonzero();
1636 if (!globflg)
1637 save();
1638 rdelete(addr1, addr2);
1641 static void
1642 rdelete(LINE ad1, LINE ad2)
1644 LINE a1, a2, a3;
1646 a1 = ad1;
1647 a2 = ad2+1;
1648 a3 = dol;
1649 dol -= a2 - a1;
1650 do {
1651 (a1++)->cur = (a2++)->cur;
1652 } while (a2 <= a3);
1653 a1 = ad1;
1654 if (a1 > dol)
1655 a1 = dol;
1656 dot = a1;
1657 fchange = 1;
1660 static void
1661 gdelete(void)
1663 LINE a1, a2, a3;
1665 a3 = dol;
1666 for (a1 = zero+1; (a1->cur&01) == 0; a1++)
1667 if (a1 >= a3)
1668 return;
1669 for (a2 = a1 + 1; a2 <= a3; ) {
1670 if (a2->cur & 01) {
1671 a2++;
1672 dot = a1;
1673 } else
1674 (a1++)->cur = (a2++)->cur;
1676 dol = a1-1;
1677 if (dot > dol)
1678 dot = dol;
1679 fchange = 1;
1682 static char *
1683 getaline(long tl)
1685 char *bp, *lp;
1686 int nl;
1688 lp = linebuf;
1689 bp = getblock(tl, READ);
1690 nl = nleft;
1691 tl &= ~0377;
1692 while (*lp++ = *bp++)
1693 if (--nl == 0) {
1694 bp = getblock(tl += 0400, READ);
1695 nl = nleft;
1697 return (linebuf);
1700 static long
1701 putline(void)
1703 char *bp, *lp;
1704 int nl;
1705 long tl;
1707 fchange = 1;
1708 lp = linebuf;
1709 tl = tline;
1710 bp = getblock(tl, WRITE);
1711 nl = nleft;
1712 tl &= ~0377;
1713 while (*bp = *lp++) {
1714 if (*bp++ == '\n') {
1715 *--bp = 0;
1716 linebp = lp;
1717 break;
1719 if (--nl == 0) {
1720 bp = getblock(tl += 0400, WRITE);
1721 nl = nleft;
1724 nl = tline;
1725 tline += (((lp-linebuf)+03)>>1)&077776;
1726 return (nl);
1729 static char *
1730 getblock(long atl, long iof)
1732 int bno, off;
1733 char *p1, *p2;
1734 int n;
1736 bno = atl >> 8;
1737 off = (atl<<1)&0774;
1739 /* bno is limited to 16 bits */
1740 if (bno >= 65535) {
1741 lastc = '\n';
1742 (void) error(31);
1744 nleft = 512 - off;
1745 if (bno == iblock) {
1746 ichanged |= iof;
1747 return (ibuff+off);
1749 if (bno == oblock)
1750 return (obuff+off);
1751 if (iof == READ) {
1752 if (ichanged) {
1753 if (xtflag)
1754 if (run_crypt(0L, ibuff, 512, tperm) == -1)
1755 (void) error(63);
1756 blkio(iblock, ibuff, write);
1758 ichanged = 0;
1759 iblock = bno;
1760 blkio(bno, ibuff, read);
1761 if (xtflag)
1762 if (run_crypt(0L, ibuff, 512, tperm) == -1)
1763 (void) error(63);
1764 return (ibuff+off);
1766 if (oblock >= 0) {
1767 if (xtflag) {
1768 p1 = obuff;
1769 p2 = crbuf;
1770 n = 512;
1771 while (n--)
1772 *p2++ = *p1++;
1773 if (run_crypt(0L, crbuf, 512, tperm) == -1)
1774 (void) error(63);
1775 blkio(oblock, crbuf, write);
1776 } else
1777 blkio(oblock, obuff, write);
1779 oblock = bno;
1780 return (obuff+off);
1783 static void
1784 blkio(int b, char *buf, ssize_t (*iofcn)())
1786 lseek(tfile, (long)b<<9, 0);
1787 if ((*iofcn)(tfile, buf, 512) != 512) {
1788 if (dol != zero)
1789 (void) error(32); /* Bypass this if writing null file */
1793 static void
1794 init(void)
1796 long *markp;
1797 mode_t omask;
1799 if (tfile != -1) {
1800 (void) close(tfile);
1801 (void) unlink(tfname);
1804 tline = 2;
1805 for (markp = names; markp < &names[26]; )
1806 *markp++ = 0L;
1807 subnewa = 0L;
1808 anymarks = 0;
1809 iblock = -1;
1810 oblock = -1;
1811 ichanged = 0;
1812 initflg = 1;
1813 omask = umask(0);
1815 if ((tfile = open(tfname, O_CREAT|O_EXCL|O_RDWR,
1816 S_IRUSR|S_IWUSR)) < 0) {
1817 puts(gettext(msgtab[70]));
1818 exit(2);
1821 umask(omask);
1822 if (xflag) {
1823 xtflag = 1;
1824 if (tpermflag)
1825 (void) crypt_close(tperm);
1826 tpermflag = 1;
1827 if (makekey(tperm)) {
1828 xtflag = 0;
1829 puts(gettext(msgtab[65]));
1832 brk((char *)fendcore);
1833 dot = zero = dol = savdot = savdol = fendcore;
1834 flag28 = save28 = 0;
1835 endcore = fendcore - sizeof (struct lin);
1838 static void
1839 global(int k)
1841 char *gp;
1842 wchar_t l;
1843 char multic[MB_LEN_MAX];
1844 wchar_t c;
1845 LINE a1;
1846 char globuf[LBSIZE];
1847 int n;
1848 int len;
1850 if (globp)
1851 (void) error(33);
1852 setall();
1853 nonzero();
1854 if ((n = _mbftowc(multic, &l, getchr, &peekc)) <= 0)
1855 (void) error(67);
1856 if (l == '\n')
1857 (void) error(19);
1858 save();
1859 comple(l);
1860 gp = globuf;
1861 while ((c = get_wchr()) != '\n') {
1862 if (c == EOF)
1863 (void) error(19);
1865 /* '\\' has special meaning only if preceding a '\n' */
1866 if (c == '\\') {
1867 c = get_wchr();
1868 if (c != '\n')
1869 *gp++ = '\\';
1871 if ((gp + (unsigned int)MB_CUR_MAX) >= &globuf[LBSIZE-1])
1872 (void) error(34);
1873 if ((len = wctomb(gp, c)) <= 0) {
1874 *gp = (unsigned char)c;
1875 len = 1;
1877 gp += len;
1879 if (gp == globuf)
1880 *gp++ = 'p';
1881 *gp++ = '\n';
1882 *gp++ = 0;
1883 for (a1 = zero; a1 <= dol; a1++) {
1884 a1->cur &= ~01;
1885 if (a1 >= addr1 && a1 <= addr2 && execute(0, a1) == k)
1886 a1->cur |= 01;
1889 * Special case: g/.../d (avoid n^2 algorithm)
1891 if (globuf[0] == 'd' && globuf[1] == '\n' && globuf[2] == '\0') {
1892 gdelete();
1893 return;
1895 for (a1 = zero; a1 <= dol; a1++) {
1896 if (a1->cur & 01) {
1897 a1->cur &= ~01;
1898 dot = a1;
1899 globp = globuf;
1900 globflg = 1;
1901 commands();
1902 globflg = 0;
1903 a1 = zero;
1908 static void
1909 join(void)
1911 char *gp, *lp;
1912 LINE a1;
1914 if (addr1 == addr2)
1915 return;
1916 gp = genbuf;
1917 for (a1 = addr1; a1 <= addr2; a1++) {
1918 lp = getaline(a1->cur);
1919 while (*gp = *lp++)
1920 if (gp++ > &genbuf[LBSIZE-1])
1921 (void) error(27);
1923 lp = linebuf;
1924 gp = genbuf;
1925 while (*lp++ = *gp++)
1927 addr1->cur = putline();
1928 if (addr1 < addr2)
1929 rdelete(addr1+1, addr2);
1930 dot = addr1;
1933 static void
1934 substitute(int inglob)
1936 int nl;
1937 LINE a1;
1938 long *markp;
1939 int ingsav; /* For saving arg. */
1941 ingsav = inglob;
1942 ocerr2 = 0;
1943 gsubf = compsub();
1944 for (a1 = addr1; a1 <= addr2; a1++) {
1945 if (execute(0, a1) == 0)
1946 continue;
1947 numpass = 0;
1948 ocerr1 = 0;
1949 inglob |= 01;
1950 dosub();
1951 if (gsubf) {
1952 while (*loc2) {
1953 if (execute(1, (LINE)0) == 0)
1954 break;
1955 dosub();
1958 if (ocerr1 == 0)continue; /* Don't put out-not changed. */
1959 subnewa = putline();
1960 a1->cur &= ~01;
1961 if (anymarks) {
1962 for (markp = names; markp < &names[26]; markp++)
1963 if (*markp == a1->cur)
1964 *markp = subnewa;
1966 a1->cur = subnewa;
1967 append(getsub, a1);
1968 nl = nline;
1969 a1 += nl;
1970 addr2 += nl;
1972 if (ingsav)
1973 return; /* Was in global-no error msg allowed. */
1974 if (inglob == 0)
1975 (void) error(35); /* Not in global, but not found. */
1976 if (ocerr2 == 0)
1977 (void) error(35); /* RE found, but occurrence match failed. */
1980 static int
1981 compsub(void)
1983 int c;
1984 wchar_t seof;
1985 char *p;
1986 char multic[MB_LEN_MAX];
1987 int n;
1988 static char remem[RHSIZE];
1989 static int remflg = -1;
1990 int i;
1992 if ((n = _mbftowc(multic, &seof, getchr, &peekc)) <= 0)
1993 (void) error(67);
1994 if (seof == '\n' || seof == ' ')
1995 (void) error(36);
1996 comple(seof);
1997 p = rhsbuf;
1998 for (;;) {
1999 wchar_t cl;
2000 if ((n = _mbftowc(multic, &cl, getchr, &peekc)) <= 0)
2001 (void) error(67);
2002 if (cl == '\\') {
2003 *p++ = '\\';
2004 if (p >= &rhsbuf[RHSIZE])
2005 (void) error(38);
2006 if ((n = _mbftowc(multic, &cl, getchr, &peekc)) <= 0)
2007 (void) error(67);
2008 } else if (cl == '\n') {
2009 if (nodelim == 1) {
2010 nodelim = 0;
2011 (void) error(36);
2013 if (!(globp && globp[0])) {
2014 UNGETC('\n');
2015 pflag++;
2016 break;
2018 } else if (cl == seof)
2019 break;
2020 if (p + n > &rhsbuf[RHSIZE])
2021 (void) error(38);
2022 (void) strncpy(p, multic, n);
2023 p += n;
2025 *p++ = 0;
2026 if (rhsbuf[0] == '%' && rhsbuf[1] == 0)
2028 * If there isn't a remembered string, it is an error;
2029 * otherwise the right hand side is the previous right
2030 * hand side.
2033 if (remflg == -1)
2034 (void) error(55);
2035 else
2036 strcpy(rhsbuf, remem);
2037 else {
2038 strcpy(remem, rhsbuf);
2039 remflg = 0;
2041 c = 0;
2042 peekc = getchr(); /* Gets char after third delimiter. */
2043 if (peekc == 'g') {
2044 c = LBSIZE; peekc = 0;
2046 if (peekc >= '1' && peekc <= '9') {
2047 c = peekc-'0';
2048 peekc = 0; /* Allows getchr() to get next char. */
2049 while (1) {
2050 i = getchr();
2051 if (i < '0' || i > '9')
2052 break;
2053 c = c*10 + i-'0';
2054 if (c > LBSIZE-1)
2055 (void) error(20); /* "Illegal suffix" */
2057 peekc = i; /* Effectively an unget. */
2059 newline();
2060 return (c);
2063 * Returns occurrence value. 0 & 1 both do first occurrence
2064 * only: c = 0 if ordinary substitute; c = 1
2065 * if use 1 in global sub(s/a/b/1). 0 in global form is illegal.
2069 static int
2070 getsub(void)
2072 char *p1, *p2;
2074 p1 = linebuf;
2075 if ((p2 = linebp) == 0)
2076 return (EOF);
2077 while (*p1++ = *p2++)
2079 linebp = 0;
2080 return (0);
2083 static void
2084 dosub(void)
2086 char *lp, *sp, *rp;
2087 int c;
2089 if (gsubf > 0 && gsubf < LBSIZE) {
2090 numpass++;
2091 if (gsubf != numpass)
2092 return;
2094 ocerr1++;
2095 ocerr2++;
2096 lp = linebuf;
2097 sp = genbuf;
2098 rp = rhsbuf;
2099 while (lp < loc1)
2100 *sp++ = *lp++;
2101 while (c = *rp++) {
2102 if (c == '&') {
2103 sp = place(sp, loc1, loc2);
2104 continue;
2105 } else if (c == '\\') {
2106 c = *rp++;
2107 if (c >= '1' && c < nbra + '1') {
2108 sp = place(sp, braslist[c-'1'],
2109 braelist[c-'1']);
2110 continue;
2113 *sp++ = c;
2114 if (sp >= &genbuf[LBSIZE])
2115 (void) error(27);
2117 lp = loc2;
2118 loc2 = sp - genbuf + linebuf;
2119 while (*sp++ = *lp++)
2120 if (sp >= &genbuf[LBSIZE])
2121 (void) error(27);
2122 lp = linebuf;
2123 sp = genbuf;
2124 while (*lp++ = *sp++)
2128 static char *
2129 place(char *sp, char *l1, char *l2)
2132 while (l1 < l2) {
2133 *sp++ = *l1++;
2134 if (sp >= &genbuf[LBSIZE])
2135 (void) error(27);
2137 return (sp);
2140 static void
2141 comple(wchar_t seof)
2143 int cclass = 0;
2144 wchar_t c;
2145 int n;
2146 char *cp = genbuf;
2147 char multic[MB_LEN_MAX];
2149 while (1) {
2150 if ((n = _mbftowc(multic, &c, getchr, &peekc)) < 0)
2151 error1(67);
2152 if (n == 0 || c == '\n') {
2153 if (cclass)
2154 error1(49);
2155 else
2156 break;
2158 if (c == seof && !cclass)
2159 break;
2160 if (cclass && c == ']') {
2161 cclass = 0;
2162 if (cp > &genbuf[LBSIZE-1])
2163 error1(50);
2164 *cp++ = ']';
2165 continue;
2167 if (c == '[' && !cclass) {
2168 cclass = 1;
2169 if (cp > &genbuf[LBSIZE-1])
2170 error1(50);
2171 *cp++ = '[';
2172 if ((n = _mbftowc(multic, &c, getchr, &peekc)) < 0)
2173 error1(67);
2174 if (n == 0 || c == '\n')
2175 error1(49);
2177 if (c == '\\' && !cclass) {
2178 if (cp > &genbuf[LBSIZE-1])
2179 error1(50);
2180 *cp++ = '\\';
2181 if ((n = _mbftowc(multic, &c, getchr, &peekc)) < 0)
2182 error1(67);
2183 if (n == 0 || c == '\n')
2184 error1(36);
2186 if (cp + n > &genbuf[LBSIZE-1])
2187 error1(50);
2188 (void) strncpy(cp, multic, n);
2189 cp += n;
2191 *cp = '\0';
2192 if (n != 0 && c == '\n')
2193 UNGETC('\n');
2194 if (n == 0 || c == '\n')
2195 nodelim = 1;
2198 * NULL RE: do not compile a null regular expression; but process
2199 * input with last regular expression encountered
2202 if (genbuf[0] != '\0') {
2203 free(expbuf);
2204 expbuf = compile(genbuf, (char *)0, (char *)0);
2206 if (regerrno)
2207 error1(regerrno);
2210 static void
2211 move(int cflag)
2213 LINE adt, ad1, ad2;
2215 setdot();
2216 nonzero();
2217 if ((adt = address()) == 0)
2218 (void) error(39);
2219 newline();
2220 if (!globflg) save();
2221 if (cflag) {
2222 ad1 = dol;
2223 append(getcopy, ad1++);
2224 ad2 = dol;
2225 } else {
2226 ad2 = addr2;
2227 for (ad1 = addr1; ad1 <= ad2; )
2228 (ad1++)->cur &= ~01;
2229 ad1 = addr1;
2231 ad2++;
2232 if (adt < ad1) {
2233 dot = adt + (ad2-ad1);
2234 if ((++adt) == ad1)
2235 return;
2236 reverse(adt, ad1);
2237 reverse(ad1, ad2);
2238 reverse(adt, ad2);
2239 } else if (adt >= ad2) {
2240 dot = adt++;
2241 reverse(ad1, ad2);
2242 reverse(ad2, adt);
2243 reverse(ad1, adt);
2244 } else
2245 (void) error(39);
2246 fchange = 1;
2249 static void
2250 reverse(LINE a1, LINE a2)
2252 long t;
2254 for (;;) {
2255 t = (--a2)->cur;
2256 if (a2 <= a1)
2257 return;
2258 a2->cur = a1->cur;
2259 (a1++)->cur = t;
2263 static int
2264 getcopy(void)
2267 if (addr1 > addr2)
2268 return (EOF);
2269 (void) getaline((addr1++)->cur);
2270 return (0);
2275 * Handles error code returned from comple() routine: regular expression
2276 * compile and match routines
2279 static void
2280 error1(int code)
2282 nbra = 0;
2283 (void) error(code);
2287 static int
2288 execute(int gf, LINE addr)
2290 char *p1;
2291 int c;
2293 for (c = 0; c < nbra; c++) {
2294 braslist[c] = 0;
2295 braelist[c] = 0;
2297 if (gf)
2298 locs = p1 = loc2;
2299 else {
2300 if (addr == zero)
2301 return (0);
2302 p1 = getaline(addr->cur);
2303 locs = 0;
2305 return (step(p1, expbuf));
2309 static void
2310 putd()
2312 int r;
2314 r = (int)(count%10);
2315 count /= 10;
2316 if (count)
2317 putd();
2318 putchr(r + '0');
2323 puts(const char *sp)
2325 int n;
2326 wchar_t c;
2327 int sz, i;
2328 if (fss.Ffill && (listf == 0)) {
2330 /* deliberate attempt to remove constness of sp because */
2331 /* it needs to be expanded */
2333 if ((i = expnd((char *)sp, funny, &sz, &fss)) == -1) {
2334 write(1, funny, fss.Flim & 0377);
2335 putchr('\n');
2336 write(1, gettext("too long"),
2337 strlen(gettext("too long")));
2339 else
2340 write(1, funny, sz);
2341 putchr('\n');
2342 if (i == -2)
2343 write(1, gettext("tab count\n"),
2344 strlen(gettext("tab count\n")));
2345 return (0);
2347 col = 0;
2348 while (*sp) {
2349 n = mbtowc(&c, sp, MB_LEN_MAX);
2350 if (listf) {
2351 if (n < 1)
2352 (void) error(28);
2353 else if (n == 1)
2354 putchr((unsigned char)*sp++);
2355 else {
2356 sp += n;
2357 putwchr(c);
2359 } else {
2360 putchr((unsigned char)*sp++);
2363 #ifndef XPG6
2364 if (listf)
2365 putchr('$'); /* end of line is marked with a $ */
2366 #else
2367 if (listf) {
2368 /* xpg6 - ensure that the end of line $ is not preceeded with a "\" */
2369 /* by doing a putchr() with listf=0, thereby avoiding the $ case */
2370 /* statement in putchr() */
2371 listf = 0;
2372 putchr('$'); /* end of line is marked with a $ */
2373 listf++;
2375 #endif
2376 putchr('\n');
2377 return (1);
2381 static void
2382 putwchr(wchar_t ac)
2384 char buf[MB_LEN_MAX], *p;
2385 char *lp;
2386 wchar_t c;
2387 short len;
2389 lp = linp;
2390 c = ac;
2391 if (listf) {
2392 if (!iswprint(c)) {
2393 p = &buf[0];
2394 if ((len = wctomb(p, c)) <= 0) {
2395 *p = (unsigned char)c;
2396 len = 1;
2398 while (len--) {
2399 if (col + 4 >= 72) {
2400 col = 0;
2401 *lp++ = '\\';
2402 *lp++ = '\n';
2404 (void) sprintf(lp, "\\%03o",
2405 *(unsigned char *)p++);
2406 col += 4;
2407 lp += 4;
2409 } else {
2410 if ((len = wcwidth(c)) <= 0)
2411 len = 0;
2412 if (col + len >= 72) {
2413 col = 0;
2414 *lp++ = '\\';
2415 *lp++ = '\n';
2417 col += len;
2418 if ((len = wctomb(lp, c)) <= 0) {
2419 *lp = (unsigned char)c;
2420 len = 1;
2422 lp += len;
2424 } else {
2425 if ((len = wctomb(lp, c)) <= 0) {
2426 *lp = (unsigned char)c;
2427 len = 1;
2429 lp += len;
2431 if (c == '\n' || lp >= &line[64]) {
2432 linp = line;
2433 len = lp - line;
2434 write(1, line, len);
2435 return;
2437 linp = lp;
2441 static void
2442 putchr(unsigned char c)
2444 char *lp;
2445 int len;
2447 lp = linp;
2448 if (listf && c != '\n') {
2449 switch (c) {
2450 case '\\' :
2451 *lp++ = '\\';
2452 *lp++ = '\\';
2453 col += 2;
2454 break;
2455 case '\007' :
2456 *lp++ = '\\';
2457 *lp++ = 'a';
2458 col += 2;
2459 break;
2460 case '\b' :
2461 *lp++ = '\\';
2462 *lp++ = 'b';
2463 col += 2;
2464 break;
2465 case '\f' :
2466 *lp++ = '\\';
2467 *lp++ = 'f';
2468 col += 2;
2469 break;
2470 case '\r' :
2471 *lp++ = '\\';
2472 *lp++ = 'r';
2473 col += 2;
2474 break;
2475 case '\t' :
2476 *lp++ = '\\';
2477 *lp++ = 't';
2478 col += 2;
2479 break;
2480 case '\v' :
2481 *lp++ = '\\';
2482 *lp++ = 'v';
2483 col += 2;
2484 break;
2485 #ifdef XPG6
2486 /* if $ characters are within the line preceed with \ */
2487 case '$' :
2488 *lp++ = '\\';
2489 *lp++ = '$';
2490 col += 2;
2491 break;
2492 #endif
2493 default:
2494 if (isprint(c)) {
2495 *lp++ = c;
2496 col += 1;
2497 } else {
2498 (void) sprintf(lp, "\\%03o", c);
2499 col += 4;
2500 lp += 4;
2502 break;
2506 * long lines are folded w/ pt of folding indicated by writing
2507 * backslash/newline character
2510 if (col + 1 >= 72) {
2511 col = 0;
2512 *lp++ = '\\';
2513 *lp++ = '\n';
2515 } else
2516 *lp++ = c;
2517 if (c == '\n' || lp >= &line[64]) {
2518 linp = line;
2519 len = lp - line;
2520 (void) write(1, line, len);
2521 return;
2523 linp = lp;
2527 static char *
2528 getkey(const char *prompt)
2530 struct termio b;
2531 int save;
2532 void (*sig)();
2533 static char key[KSIZE+1];
2534 char *p;
2535 int c;
2537 sig = signal(SIGINT, SIG_IGN);
2538 ioctl(0, TCGETA, &b);
2539 save = b.c_lflag;
2540 b.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
2541 ioctl(0, TCSETAW, &b);
2542 write(1, gettext(prompt), strlen(gettext(prompt)));
2543 p = key;
2544 while (((c = getchr()) != EOF) && (c != '\n')) {
2545 if (p < &key[KSIZE])
2546 *p++ = c;
2548 *p = 0;
2549 write(1, "\n", 1);
2550 b.c_lflag = save;
2551 ioctl(0, TCSETAW, &b);
2552 signal(SIGINT, sig);
2553 return (key);
2557 static void
2558 globaln(int k)
2560 char *gp;
2561 int c;
2562 int n;
2563 wchar_t cl;
2564 LINE a1;
2565 int nfirst;
2566 char globuf[LBSIZE];
2567 char multic[MB_LEN_MAX];
2568 int len;
2569 int pflag_save = 0;
2570 int listf_save = 0;
2571 int listn_save = 0;
2573 if (globp)
2574 (void) error(33);
2575 setall();
2576 nonzero();
2577 if ((n = _mbftowc(multic, &cl, getchr, &peekc)) <= 0)
2578 (void) error(67);
2579 if (cl == '\n')
2580 (void) error(19);
2581 save();
2582 comple(cl);
2583 for (a1 = zero; a1 <= dol; a1++) {
2584 a1->cur &= ~01;
2585 if (a1 >= addr1 && a1 <= addr2 && execute(0, a1) == k)
2586 a1->cur |= 01;
2588 nfirst = 0;
2589 newline();
2591 * preserve the p, l, and n suffix commands of the G and V
2592 * commands during the interactive section and restore
2593 * on completion of the G and V command.
2595 pflag_save = pflag;
2596 listf_save = listf;
2597 listn_save = listn;
2598 pflag = 0;
2599 listf = 0;
2600 listn = 0;
2601 for (a1 = zero; a1 <= dol; a1++) {
2602 if (a1->cur & 01) {
2603 a1->cur &= ~01;
2604 dot = a1;
2605 puts(getaline(a1->cur));
2606 if ((c = get_wchr()) == EOF)
2607 (void) error(52);
2608 if (c == 'a' || c == 'i' || c == 'c')
2609 (void) error(53);
2610 if (c == '\n') {
2611 a1 = zero;
2612 continue;
2614 if (c != '&') {
2615 gp = globuf;
2616 if ((len = wctomb(gp, c)) <= 0) {
2617 *gp = (unsigned char)c;
2618 len = 1;
2620 gp += len;
2621 while ((c = get_wchr()) != '\n') {
2623 /* '\\' has special meaning only if preceding a '\n' */
2624 if (c == '\\') {
2625 c = get_wchr();
2626 if (c != '\n')
2627 *gp++ = '\\';
2629 if ((gp + (unsigned int)MB_CUR_MAX) >=
2630 &globuf[LBSIZE-1])
2631 (void) error(34);
2633 if ((len = wctomb(gp, c)) <= 0) {
2634 *gp = (unsigned char)c;
2635 len = 1;
2637 gp += len;
2639 *gp++ = '\n';
2640 *gp++ = 0;
2641 nfirst = 1;
2642 } else if ((c = get_wchr()) != '\n')
2643 (void) error(54);
2644 globp = globuf;
2645 if (nfirst) {
2646 globflg = 1;
2647 commands();
2648 globflg = 0;
2649 } else
2650 (void) error(56);
2651 globp = 0;
2652 a1 = zero;
2655 pflag = pflag_save;
2656 listf = listf_save;
2657 listn = listn_save;
2661 static int
2662 eopen(char *string, int rw)
2664 #define w_or_r(a, b) (rw ? a : b)
2665 int pf[2];
2666 pid_t i;
2667 int io;
2668 int chcount; /* # of char read. */
2670 if (rflg) { /* restricted shell */
2671 if (Xqt) {
2672 Xqt = 0;
2673 (void) error(6);
2676 if (!Xqt) {
2677 if ((io = open(string, rw)) >= 0) {
2678 if (fflg) {
2679 chcount = read(io, crbuf, LBSIZE);
2680 if (crflag == -1) {
2681 if (isencrypt(crbuf, chcount))
2682 crflag = 2;
2683 else
2684 crflag = -2;
2686 if (crflag > 0)
2687 if (run_crypt(0L, crbuf, chcount, perm) == -1)
2688 (void) error(63);
2689 if (fspec(crbuf, &fss, 0) < 0) {
2690 fss.Ffill = 0;
2691 fflg = 0;
2692 (void) error(4);
2694 lseek(io, 0L, 0);
2697 fflg = 0;
2698 return (io);
2700 if (pipe(pf) < 0)
2701 xerr: (void) error(0);
2702 if ((i = fork()) == 0) {
2703 signal(SIGHUP, oldhup);
2704 signal(SIGQUIT, oldquit);
2705 signal(SIGPIPE, oldpipe);
2706 signal(SIGINT, (void (*)()) 0);
2707 close(w_or_r(pf[1], pf[0]));
2708 close(w_or_r(0, 1));
2709 dup(w_or_r(pf[0], pf[1]));
2710 close(w_or_r(pf[0], pf[1]));
2711 execlp(_PATH_BSHELL, "sh", "-c", string, (char *)0);
2712 exit(1);
2714 if (i == (pid_t)-1)
2715 goto xerr;
2716 close(w_or_r(pf[0], pf[1]));
2717 return (w_or_r(pf[1], pf[0]));
2721 static void
2722 eclose(int f)
2724 close(f);
2725 if (Xqt)
2726 Xqt = 0, wait((int *)0);
2730 static void
2731 mkfunny(void)
2733 char *p, *p1, *p2;
2735 p2 = p1 = funny;
2736 p = file;
2738 * Go to end of file name
2740 while (*p)
2741 p++;
2742 while (*--p == '/') /* delete trailing slashes */
2743 *p = '\0';
2745 * go back to beginning of file
2747 p = file;
2749 * Copy file name to funny setting p2 at
2750 * basename of file.
2752 while (*p1++ = *p)
2753 if (*p++ == '/')
2754 p2 = p1;
2756 * Set p1 to point to basename of tfname.
2758 p1 = strrchr(tfname, '/');
2759 if (strlen(tfname) > (size_t)6)
2760 p1 = &tfname[strlen(tfname)-6];
2761 p1++;
2762 *p2 = '\007'; /* add unprintable char for funny a unique name */
2764 * Copy tfname to file.
2766 while (*++p2 = *p1++)
2771 static void
2772 getime(void) /* get modified time of file and save */
2774 if (stat(file, &Fl) < 0)
2775 savtime = 0;
2776 else
2777 savtime = Fl.st_mtime;
2781 static void
2782 chktime(void) /* check saved mod time against current mod time */
2784 if (savtime != 0 && Fl.st_mtime != 0) {
2785 if (savtime != Fl.st_mtime)
2786 (void) error(58);
2791 static void
2792 newtime(void) /* get new mod time and save */
2794 stat(file, &Fl);
2795 savtime = Fl.st_mtime;
2800 * restricted - check for '/' in name and delete trailing '/'
2802 static void
2803 red(char *op)
2805 char *p;
2807 p = op;
2808 while (*p)
2809 if (*p++ == '/'&& rflg) {
2810 *op = 0;
2811 (void) error(6);
2813 /* delete trailing '/' */
2814 while (p > op) {
2815 if (*--p == '/')
2816 *p = '\0';
2817 else break;
2823 * Searches thru beginning of file looking for a string of the form
2824 * <: values... :>
2826 * where "values" are
2828 * \b ignored
2829 * s<num> sets the Flim to <num>
2830 * t??? sets tab stop stuff
2831 * d ignored
2832 * m<num> ignored
2833 * e ignored
2836 static int
2837 fspec(char line[], struct Fspec *f, int up)
2839 struct termio arg;
2840 int havespec, n;
2841 int len;
2843 if (!up) clear(f);
2845 havespec = fsprtn = 0;
2846 for (fsp = line; *fsp && *fsp != '\n'; fsp += len) {
2847 if ((len = mblen(fsp, MB_CUR_MAX)) <= 0)
2848 len = 1;
2849 switch (*fsp) {
2851 case '<': if (havespec)
2852 return (-1);
2853 if (*(fsp+1) == ':') {
2854 havespec = 1;
2855 clear(f);
2856 if (!ioctl(1, TCGETA, &arg) &&
2857 ((arg.c_oflag & TAB3) ==
2858 TAB3))
2859 f->Ffill = 1;
2860 fsp++;
2861 continue;
2864 case ' ': continue;
2866 case 's': if (havespec && (n = numb()) >= 0)
2867 f->Flim = n;
2868 continue;
2870 case 't': if (havespec) targ(f);
2871 continue;
2873 case 'd': continue;
2875 case 'm': if (havespec) n = numb();
2876 continue;
2878 case 'e': continue;
2879 case ':': if (!havespec) continue;
2880 if (*(fsp+1) != '>') fsprtn = -1;
2881 return (fsprtn);
2883 default: if (!havespec) continue;
2884 return (-1);
2887 return (1);
2891 static int
2892 numb(void)
2894 int n;
2896 n = 0;
2897 while (*++fsp >= '0' && *fsp <= '9')
2898 n = 10*n + *fsp-'0';
2899 fsp--;
2900 return (n);
2904 static void
2905 targ(struct Fspec *f)
2908 if (*++fsp == '-') {
2909 if (*(fsp + 1) >= '0' && *(fsp+1) <= '9') tincr(numb(), f);
2910 else tstd(f);
2911 return;
2913 if (*fsp >= '0' && *fsp <= '9') {
2914 tlist(f);
2915 return;
2917 fsprtn = -1;
2918 fsp--;
2922 static void
2923 tincr(int n, struct Fspec *f)
2925 int l, i;
2927 l = 1;
2928 for (i = 0; i < 20; i++)
2929 f->Ftabs[i] = l += n;
2930 f->Ftabs[i] = 0;
2934 static void
2935 tstd(struct Fspec *f)
2937 char std[3];
2939 std[0] = *++fsp;
2940 if (*(fsp+1) >= '0' && *(fsp+1) <= '9') {
2941 std[1] = *++fsp;
2942 std[2] = '\0';
2943 } else {
2944 std[1] = '\0';
2946 fsprtn = stdtab(std, f->Ftabs);
2950 static void
2951 tlist(struct Fspec *f)
2953 int n, last, i;
2955 fsp--;
2956 last = i = 0;
2958 do {
2959 if ((n = numb()) <= last || i >= 20) {
2960 fsprtn = -1;
2961 return;
2963 f->Ftabs[i++] = last = n;
2964 } while (*++fsp == ',');
2966 f->Ftabs[i] = 0;
2967 fsp--;
2971 static int
2972 expnd(char line[], char buf[], int *sz, struct Fspec *f)
2974 char *l, *t;
2975 int b;
2977 l = line - 1;
2978 b = 1;
2979 t = f->Ftabs;
2980 fsprtn = 0;
2982 while (*++l && *l != '\n' && b < 511) {
2983 if (*l == '\t') {
2984 while (*t && b >= *t)
2985 t++;
2986 if (*t == 0)
2987 fsprtn = -2;
2988 do {
2989 buf[b-1] = ' ';
2990 } while (++b < *t);
2991 } else {
2992 buf[b++ - 1] = *l;
2996 buf[b] = '\0';
2997 *sz = b;
2998 if (*l != '\0' && *l != '\n') {
2999 buf[b-1] = '\n';
3000 return (-1);
3002 buf[b-1] = *l;
3003 if (f->Flim && (b-1 > (int)f->Flim))
3004 return (-1);
3005 return (fsprtn);
3009 static void
3010 clear(struct Fspec *f)
3012 f->Ftabs[0] = f->Fdel = f->Fmov = f->Ffill = 0;
3013 f->Flim = 0;
3017 static int
3018 lenchk(char line[], struct Fspec *f)
3020 char *l, *t;
3021 int b;
3023 l = line - 1;
3024 b = 1;
3025 t = f->Ftabs;
3027 while (*++l && *l != '\n' && b < 511) {
3028 if (*l == '\t') {
3029 while (*t && b >= *t)
3030 t++;
3031 while (++b < *t)
3033 } else {
3034 b++;
3038 if ((*l != '\0' && *l != '\n') || (f->Flim && (b-1 > (int)f->Flim)))
3039 return (-1);
3040 return (0);
3042 #define NTABS 21
3046 * stdtabs: standard tabs table
3047 * format: option code letter(s), null, tabs, null
3050 static char stdtabs[] = {
3051 'a', 0, 1, 10, 16, 36, 72, 0, /* IBM 370 Assembler */
3052 'a', '2', 0, 1, 10, 16, 40, 72, 0, /* IBM Assembler alternative */
3053 'c', 0, 1, 8, 12, 16, 20, 55, 0, /* COBOL, normal */
3054 'c', '2', 0, 1, 6, 10, 14, 49, 0, /* COBOL, crunched */
3055 'c', '3', 0, 1, 6, 10, 14, 18, 22, 26, 30, 34, 38, 42, 46, 50,
3056 54, 58, 62, 67, 0,
3057 'f', 0, 1, 7, 11, 15, 19, 23, 0, /* FORTRAN */
3058 'p', 0, 1, 5, 9, 13, 17, 21, 25, 29, 33, 37, 41, 45, 49, 53, 57, 61, 0,
3059 /* PL/I */
3060 's', 0, 1, 10, 55, 0, /* SNOBOL */
3061 'u', 0, 1, 12, 20, 44, 0, /* UNIVAC ASM */
3062 0 };
3066 * stdtab: return tab list for any "canned" tab option.
3067 * entry: option points to null-terminated option string
3068 * tabvect points to vector to be filled in
3069 * exit: return (0) if legal, tabvect filled, ending with zero
3070 * return (-1) if unknown option
3074 static int
3075 stdtab(char option[], char tabvect[NTABS])
3077 char *scan;
3078 tabvect[0] = 0;
3079 scan = stdtabs;
3080 while (*scan) {
3081 if (strequal(&scan, option)) {
3082 strcopy(scan, tabvect);
3083 break;
3084 } else
3085 while (*scan++) /* skip over tab specs */
3089 /* later: look up code in /etc/something */
3090 return (tabvect[0] ? 0 : -1);
3095 * strequal: checks strings for equality
3096 * entry: scan1 points to scan pointer, str points to string
3097 * exit: return (1) if equal, return (0) if not
3098 * *scan1 is advanced to next nonzero byte after null
3102 static int
3103 strequal(char **scan1, char *str)
3105 char c, *scan;
3106 scan = *scan1;
3107 while ((c = *scan++) == *str && c)
3108 str++;
3109 *scan1 = scan;
3110 if (c == 0 && *str == 0)
3111 return (1);
3112 if (c)
3113 while (*scan++)
3115 *scan1 = scan;
3116 return (0);
3120 /* strcopy: copy source to destination */
3123 static void
3124 strcopy(char *source, char *dest)
3126 while (*dest++ = *source++)
3131 /* This is called before a buffer modifying command so that the */
3132 /* current array of line ptrs is saved in sav and dot and dol are saved */
3135 static void
3136 save(void)
3138 LINE i;
3139 int j;
3141 savdot = dot;
3142 savdol = dol;
3143 for (j = 0; j <= 25; j++)
3144 savnames[j] = names[j];
3146 for (i = zero + 1; i <= dol; i++)
3147 i->sav = i->cur;
3148 initflg = 0;
3152 /* The undo command calls this to restore the previous ptr array sav */
3153 /* and swap with cur - dot and dol are swapped also. This allows user to */
3154 /* undo an undo */
3157 static void
3158 undo(void)
3160 int j;
3161 long tmp;
3162 LINE i, tmpdot, tmpdol;
3164 tmpdot = dot; dot = savdot; savdot = tmpdot;
3165 tmpdol = dol; dol = savdol; savdol = tmpdol;
3166 /* swap arrays using the greater of dol or savdol as upper limit */
3167 for (i = zero + 1; i <= ((dol > savdol) ? dol : savdol); i++) {
3168 tmp = i->cur;
3169 i->cur = i->sav;
3170 i->sav = tmp;
3173 * If the current text lines are swapped with the
3174 * text lines in the save buffer, then swap the current
3175 * marks with those in the save area.
3178 for (j = 0; j <= 25; j++) {
3179 tmp = names[j];
3180 names[j] = savnames[j];
3181 savnames[j] = tmp;
3185 static wchar_t
3186 get_wchr(void)
3188 wchar_t wc;
3189 char multi[MB_LEN_MAX];
3191 if (_mbftowc(multi, &wc, getchr, &peekc) <= 0)
3192 wc = getchr();
3193 return (wc);