8322 nl: misleading-indentation
[unleashed/tickless.git] / usr / src / cmd / ed / ed.c
blobe8a12eea0c444d61466899714af00333cb51be2c
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++);
384 while (--p1 >= *argv)
385 if (*p1 == '/')
386 break;
387 *argv = p1 + 1;
388 /* if SHELL set in environment and is /usr/lib/rsh, set rflg */
389 if ((val = getenv(name)) != NULL)
390 if (strcmp(val, rshell) == 0)
391 rflg++;
392 if (**argv == 'r')
393 rflg++;
394 home = getenv("HOME");
395 while (1) {
396 while ((c = getopt(argc, argv, "sp:qxC")) != EOF) {
397 switch (c) {
399 case 's':
400 vflag = 0;
401 break;
403 case 'p':
404 strncpy(prompt, optarg, sizeof (prompt)-1);
405 shflg = 1;
406 break;
408 case 'q':
409 signal(SIGQUIT, SIG_DFL);
410 vflag = 1;
411 break;
413 case 'x':
414 crflag = -1;
415 xflag = 1;
416 break;
418 case 'C':
419 crflag = 1;
420 xflag = 1;
421 break;
423 case '?':
424 (void) fprintf(stderr, gettext(
425 "Usage: ed [- | -s] [-p string] [-x] [-C] [file]\n"
426 " red [- | -s] [-p string] [-x] [-C] [file]\n"));
427 exit(2);
430 if (argv[optind] && strcmp(argv[optind], "-") == 0 &&
431 strcmp(argv[optind-1], "--") != 0) {
432 vflag = 0;
433 optind++;
434 continue;
436 break;
438 argc = argc - optind;
439 argv = &argv[optind];
441 if (xflag) {
442 if (permflag)
443 crypt_close(perm);
444 permflag = 1;
445 if ((kflag = run_setkey(&perm[0], getkey(msgtab[66])))
446 == -1) {
447 puts(gettext(msgtab[64]));
448 xflag = 0;
449 kflag = 0;
451 if (kflag == 0)
452 crflag = 0;
455 if (argc > 0) {
456 p1 = *argv;
457 if (strlen(p1) >= (size_t)FNSIZE) {
458 puts(gettext("file name too long"));
459 if (kflag)
460 crypt_close(perm);
461 exit(2);
463 p2 = savedfile;
464 while (*p2++ = *p1++);
465 globp = "e";
466 fflg++;
467 } else /* editing with no file so set savtime to 0 */
468 savtime = 0;
469 eflg++;
470 if ((tfname = tempnam("", "ea")) == NULL) {
471 puts(gettext(msgtab[69]));
472 exit(2);
475 fendcore = (LINE)sbrk(0);
476 init();
477 if (oldintr != SIG_IGN)
478 signal(SIGINT, onintr);
479 if (oldhup != SIG_IGN)
480 signal(SIGHUP, onhup);
481 setjmp(savej);
482 commands();
483 quit(sig);
484 return (0);
487 static void
488 commands(void)
490 LINE a1;
491 int c;
492 char *p1, *p2;
493 int fsave, m, n;
495 for (;;) {
496 nodelim = 0;
497 if (pflag) {
498 pflag = 0;
499 addr1 = addr2 = dot;
500 goto print;
502 if (shflg && globp == 0)
503 write(1, gettext(prompt), strlen(gettext(prompt)));
504 addr1 = 0;
505 addr2 = 0;
506 if ((c = getchr()) == ',') {
507 addr1 = zero + 1;
508 addr2 = dol;
509 #ifdef XPG6
510 /* XPG4 - it was an error if the second address was */
511 /* input and the first address was ommitted */
512 /* Parse second address */
513 if ((a1 = address()) != 0) {
514 addr2 = a1;
516 #endif
517 c = getchr();
518 goto swch;
519 } else if (c == ';') {
520 addr1 = dot;
521 addr2 = dol;
522 #ifdef XPG6
523 /* XPG4 - it was an error if the second address was */
524 /* input and the first address was ommitted */
525 /* Parse second address */
526 if ((a1 = address()) != 0) {
527 addr2 = a1;
529 #endif
530 c = getchr();
531 goto swch;
532 } else
533 peekc = c;
534 do {
535 addr1 = addr2;
536 if ((a1 = address()) == 0) {
537 c = getchr();
538 break;
540 addr2 = a1;
541 if ((c = getchr()) == ';') {
542 c = ',';
543 dot = a1;
545 } while (c == ',');
546 if (addr1 == 0)
547 addr1 = addr2;
548 swch:
549 switch (c) {
551 case 'a':
552 setdot();
553 newline();
554 if (!globflg) save();
555 append(gettty, addr2);
556 continue;
558 case 'c':
559 #ifdef XPG6
560 setzeroasone();
561 #endif
562 delete();
563 append(gettty, addr1-1);
565 /* XPG4 - If no new lines are inserted, then the current */
566 /* line becomes the line after the lines deleted. */
568 if (((linebuf[0] != '.') || (dot == (addr1-1))) &&
569 (addr2 <= dol))
570 dot = addr1;
571 continue;
573 case 'd':
574 delete();
575 continue;
577 case 'E':
578 fchange = 0;
579 c = 'e';
580 case 'e':
581 fflg++;
582 setnoaddr();
583 if (vflag && fchange) {
584 fchange = 0;
585 (void) error(1);
587 filename(c);
588 eflg++;
589 init();
590 addr2 = zero;
591 goto caseread;
593 case 'f':
594 setnoaddr();
595 filename(c);
596 if (!ncflg) /* there is a filename */
597 getime();
598 else
599 ncflg--;
600 puts(savedfile);
601 continue;
603 case 'g':
604 global(1);
605 continue;
606 case 'G':
607 globaln(1);
608 continue;
610 case 'h':
611 newline();
612 setnoaddr();
613 PUTM();
614 continue;
616 case 'H':
617 newline();
618 setnoaddr();
619 if (!hflag) {
620 hflag = 1;
621 PUTM();
623 else
624 hflag = 0;
625 continue;
627 case 'i':
628 #ifdef XPG6
629 setzeroasone();
630 #endif
631 setdot();
632 nonzero();
633 newline();
634 if (!globflg) save();
635 append(gettty, addr2-1);
636 if (dot == addr2-1)
637 dot += 1;
638 continue;
640 case 'j':
641 if (addr2 == 0) {
642 addr1 = dot;
643 addr2 = dot+1;
645 setdot();
646 newline();
647 nonzero();
648 if (!globflg) save();
649 join();
650 continue;
652 case 'k':
653 if ((c = getchr()) < 'a' || c > 'z')
654 (void) error(2);
655 newline();
656 setdot();
657 nonzero();
658 names[c-'a'] = addr2->cur & ~01;
659 anymarks |= 01;
660 continue;
662 case 'm':
663 move(0);
664 continue;
666 case '\n':
667 if (addr2 == 0)
668 addr2 = dot+1;
669 addr1 = addr2;
670 goto print;
672 case 'n':
673 listn++;
674 newline();
675 goto print;
677 case 'l':
678 listf++;
679 case 'p':
680 newline();
681 print:
682 setdot();
683 nonzero();
684 a1 = addr1;
685 do {
686 if (listn) {
687 count = a1 - zero;
688 putd();
689 putchr('\t');
691 puts(getaline((a1++)->cur));
693 while (a1 <= addr2);
694 dot = addr2;
695 pflag = 0;
696 listn = 0;
697 listf = 0;
698 continue;
700 case 'Q':
701 fchange = 0;
702 case 'q':
703 setnoaddr();
704 newline();
705 quit(sig);
707 case 'r':
708 filename(c);
709 caseread:
710 readflg = 1;
711 save28 = (dol != fendcore);
712 if (crflag == 2 || crflag == -2)
713 crflag = -1; /* restore crflag for next file */
714 errno = 0;
715 if ((io = eopen(file, O_RDONLY)) < 0) {
716 lastc = '\n';
717 /* if first entering editor and file does not exist */
718 /* set saved access time to 0 */
719 if (eflg) {
720 savtime = 0;
721 eflg = 0;
722 if (c == 'e' && vflag == 0)
723 qflg = 1;
725 if (errno == ENOENT) {
726 (void) error(68);
727 } else {
728 (void) error(3);
731 /* get last mod time of file */
732 /* eflg - entered editor with ed or e */
733 if (eflg) {
734 eflg = 0;
735 getime();
737 setall();
738 ninbuf = 0;
739 n = zero != dol;
740 #ifdef NULLS
741 nulls = 0;
742 #endif
743 if (!globflg && (c == 'r')) save();
744 append(getfile, addr2);
745 exfile();
746 readflg = 0;
747 fchange = n;
748 continue;
750 case 's':
751 setdot();
752 nonzero();
753 if (!globflg) save();
754 substitute(globp != 0);
755 continue;
757 case 't':
758 move(1);
759 continue;
761 case 'u':
762 setdot();
763 newline();
764 if (!initflg)
765 undo();
766 else
767 (void) error(5);
768 fchange = 1;
769 continue;
771 case 'v':
772 global(0);
773 continue;
774 case 'V':
775 globaln(0);
776 continue;
778 case 'W':
779 case 'w':
780 if (flag28) {
781 flag28 = 0;
782 fchange = 0;
783 (void) error(61);
785 setall();
787 /* on NULL-RE condition do not generate error */
789 if ((linebuf[0] != '.') && (zero != dol) &&
790 (addr1 <= zero || addr2 > dol))
791 (void) error(15);
792 filename(c);
793 if (Xqt) {
794 io = eopen(file, O_WRONLY);
795 n = 1; /* set n so newtime will not execute */
796 } else {
797 struct stat lFl;
798 fstat(tfile, &Tf);
799 if (stat(file, &Fl) < 0) {
800 if ((io = creat(file, S_IRUSR|S_IWUSR|S_IRGRP
801 |S_IWGRP|S_IROTH|S_IWOTH)) < 0)
802 (void) error(7);
803 fstat(io, &Fl);
804 Fl.st_mtime = 0;
805 lFl = Fl;
806 close(io);
807 } else {
808 #ifndef RESEARCH
809 oldmask = umask(0);
811 * Must determine if file is
812 * a symbolic link
814 lstat(file, &lFl);
815 #endif
817 #ifndef RESEARCH
819 * Determine if there are enough free blocks on system
821 if (!Short && statvfs(file, &U) == 0 &&
822 U.f_bfree < ((Tf.st_size/U.f_frsize) + 100)) {
823 Short = 1;
824 (void) error(8);
826 Short = 0;
827 #endif
828 p1 = savedfile; /* The current filename */
829 p2 = file;
830 m = strcmp(p1, p2);
831 if (c == 'w' && Fl.st_nlink == 1 && ISREG(lFl)) {
832 if (close(open(file, O_WRONLY)) < 0)
833 (void) error(9);
834 if (!(n = m))
835 chktime();
836 mkfunny();
838 * If funlink equals one it means that
839 * funny points to a valid file which must
840 * be unlinked when interrupted.
843 funlink = 1;
844 if ((io = creat(funny, FMODE(Fl))) >= 0) {
845 chown(funny, Fl.st_uid, Fl.st_gid);
846 chmod(funny, FMODE(Fl));
847 putfile();
848 exfile();
850 if (rename(funny, file))
851 (void) error(10);
852 funlink = 0;
853 /* if filenames are the same */
854 if (!n)
855 newtime();
856 /* check if entire buffer was written */
857 fsave = fchange;
858 fchange = (((addr1 == zero) || (addr1 == (zero + 1))) &&
859 (addr2 == dol)) ? 0 : 1;
860 if (fchange == 1 && m != 0) fchange = fsave;
861 continue;
863 } else
864 n = 1; /* set n so newtime will not execute */
865 if ((io = open(file,
866 (c == 'w') ? O_WRONLY|O_CREAT|O_TRUNC
867 : O_WRONLY|O_CREAT|O_APPEND, S_IRUSR|S_IWUSR
868 |S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)) < 0)
869 (void) error(7);
871 putfile();
872 exfile();
873 if (!n) newtime();
874 fsave = fchange;
875 fchange = (((addr1 == zero) || (addr1 == (zero + 1))) &&
876 (addr2 == dol)) ? 0 : 1;
877 /* Leave fchange alone if partial write was to another file */
878 if (fchange == 1 && m != 0) fchange = fsave;
879 continue;
881 case 'C':
882 crflag = 1;
884 * C is same as X, but always assume input files are
885 * ciphertext
887 goto encrypt;
889 case 'X':
890 crflag = -1;
891 encrypt:
892 setnoaddr();
893 newline();
894 xflag = 1;
895 if (permflag)
896 (void) crypt_close(perm);
897 permflag = 1;
898 if ((kflag = run_setkey(&perm[0], getkey(msgtab[66])))
899 == -1) {
900 xflag = 0;
901 kflag = 0;
902 crflag = 0;
903 (void) error(64);
905 if (kflag == 0)
906 crflag = 0;
907 continue;
909 case '=':
910 setall();
911 newline();
912 count = (addr2-zero)&077777;
913 putd();
914 putchr('\n');
915 continue;
917 case '!':
918 unixcom();
919 continue;
921 case EOF:
922 return;
924 case 'P':
925 setnoaddr();
926 newline();
927 if (shflg)
928 shflg = 0;
929 else
930 shflg++;
931 continue;
933 if (c == 'x')
934 (void) error(60);
935 else
936 (void) error(12);
940 LINE
941 address(void)
943 int minus, c;
944 LINE a1;
945 int n, relerr, retval;
947 minus = 0;
948 a1 = 0;
949 for (;;) {
950 c = getchr();
951 if ('0' <= c && c <= '9') {
952 n = 0;
953 do {
954 n *= 10;
955 n += c - '0';
956 } while ((c = getchr()) >= '0' && c <= '9');
957 peekc = c;
958 if (a1 == 0)
959 a1 = zero;
960 if (minus < 0)
961 n = -n;
962 a1 += n;
963 minus = 0;
964 continue;
966 relerr = 0;
967 if (a1 || minus)
968 relerr++;
969 switch (c) {
970 case ' ':
971 case '\t':
972 continue;
974 case '+':
975 minus++;
976 if (a1 == 0)
977 a1 = dot;
978 continue;
980 case '-':
981 case '^':
982 minus--;
983 if (a1 == 0)
984 a1 = dot;
985 continue;
987 case '?':
988 case '/':
989 comple(c);
990 a1 = dot;
991 for (;;) {
992 if (c == '/') {
993 a1++;
994 if (a1 > dol)
995 a1 = zero;
996 } else {
997 a1--;
998 if (a1 < zero)
999 a1 = dol;
1002 if (execute(0, a1))
1003 break;
1004 if (a1 == dot)
1005 (void) error(13);
1007 break;
1009 case '$':
1010 a1 = dol;
1011 break;
1013 case '.':
1014 a1 = dot;
1015 break;
1017 case '\'':
1018 if ((c = getchr()) < 'a' || c > 'z')
1019 (void) error(2);
1020 for (a1 = zero; a1 <= dol; a1++)
1021 if (names[c-'a'] == (a1->cur & ~01))
1022 break;
1023 break;
1025 default:
1026 peekc = c;
1027 if (a1 == 0)
1028 return (0);
1029 a1 += minus;
1031 /* on NULL-RE condition do not generate error */
1033 if ((linebuf[0] != '.') && (a1 < zero || a1 > dol))
1034 (void) error(15);
1035 return (a1);
1037 if (relerr)
1038 (void) error(16);
1042 static void
1043 setdot(void)
1045 if (addr2 == 0)
1046 addr1 = addr2 = dot;
1047 if (addr1 > addr2)
1048 (void) error(17);
1051 static void
1052 setall(void)
1054 if (addr2 == 0) {
1055 addr1 = zero+1;
1056 addr2 = dol;
1057 if (dol == zero)
1058 addr1 = zero;
1060 setdot();
1063 static void
1064 setnoaddr(void)
1066 if (addr2)
1067 (void) error(18);
1070 static void
1071 nonzero(void)
1073 /* on NULL-RE condition do not generate error */
1075 if ((linebuf[0] != '.') && (addr1 <= zero || addr2 > dol))
1076 (void) error(15);
1079 static void
1080 setzeroasone(void)
1082 /* for the c and i commands 0 equal to 1 address */
1083 if (addr1 == zero) {
1084 addr1 = zero+1;
1086 if (addr2 == zero) {
1087 addr2 = zero+1;
1092 static void
1093 newline(void)
1095 int c;
1097 if ((c = getchr()) == '\n')
1098 return;
1099 if (c == 'p' || c == 'l' || c == 'n') {
1100 pflag++;
1101 if (c == 'l') listf++;
1102 if (c == 'n') listn++;
1103 if ((c = getchr()) == '\n')
1104 return;
1106 (void) error(20);
1109 static void
1110 filename(int comm)
1112 char *p1, *p2;
1113 int c;
1114 int i = 0;
1116 count = 0;
1117 c = getchr();
1118 if (c == '\n' || c == EOF) {
1119 p1 = savedfile;
1120 if (*p1 == 0 && comm != 'f')
1121 (void) error(21);
1122 /* ncflg set means do not get mod time of file */
1123 /* since no filename followed f */
1124 if (comm == 'f')
1125 ncflg++;
1126 p2 = file;
1127 while (*p2++ = *p1++);
1128 red(savedfile);
1129 return;
1131 if (c != ' ')
1132 (void) error(22);
1133 while ((c = getchr()) == ' ');
1134 if (c == '!')
1135 ++Xqt, c = getchr();
1136 if (c == '\n')
1137 (void) error(21);
1138 p1 = file;
1139 do {
1140 if (++i >= FNSIZE)
1141 (void) error(24);
1142 *p1++ = c;
1143 if (c == EOF || (c == ' ' && !Xqt))
1144 (void) error(21);
1145 } while ((c = getchr()) != '\n');
1146 *p1++ = 0;
1147 if (Xqt)
1148 if (comm == 'f') {
1149 --Xqt;
1150 (void) error(57);
1152 else
1153 return;
1154 if (savedfile[0] == 0 || comm == 'e' || comm == 'f') {
1155 p1 = savedfile;
1156 p2 = file;
1157 while (*p1++ = *p2++);
1159 red(file);
1163 static void
1164 exfile(void)
1166 #ifdef NULLS
1167 int c;
1168 #endif
1170 #ifndef RESEARCH
1171 if (oldmask) {
1172 umask(oldmask);
1173 oldmask = 0;
1175 #endif
1176 eclose(io);
1177 io = -1;
1178 if (vflag) {
1179 putd();
1180 putchr('\n');
1181 #ifdef NULLS
1182 if (nulls) {
1183 c = count;
1184 count = nulls;
1185 nulls = 0;
1186 putd();
1187 puts(gettext(" nulls replaced by '\\0'"));
1188 count = c;
1190 #endif
1194 static void
1195 onintr(int sig)
1197 signal(SIGINT, onintr);
1198 putchr('\n');
1199 lastc = '\n';
1200 globflg = 0;
1201 if (funlink) unlink(funny); /* remove tmp file */
1202 /* if interrupted a read, only part of file may be in buffer */
1203 if (readflg) {
1204 sprintf(tstring, "\007read may be incomplete - beware!\007");
1205 puts(gettext(tstring));
1206 fchange = 0;
1208 (void) error(26);
1211 static void
1212 onhup(int sig)
1214 signal(SIGINT, SIG_IGN);
1215 signal(SIGHUP, SIG_IGN);
1217 * if there are lines in file and file was not written
1218 * since last update, save in ed.hup, or $HOME/ed.hup
1220 if (dol > zero && fchange == 1) {
1221 addr1 = zero+1;
1222 addr2 = dol;
1223 io = creat("ed.hup",
1224 S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
1225 if (io < 0 && home) {
1226 char *fn;
1228 fn = (char *)calloc(strlen(home) + 8, sizeof (char));
1229 if (fn) {
1230 strcpy(fn, home);
1231 strcat(fn, "/ed.hup");
1232 io = creat(fn, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP
1233 |S_IROTH|S_IWOTH);
1234 free(fn);
1237 if (io > 0)
1238 putfile();
1240 fchange = 0;
1241 ++errcnt;
1242 quit(sig);
1245 static int
1246 error(int code)
1248 int c;
1250 if (code == 28 && save28 == 0) {
1251 fchange = 0;
1252 flag28++;
1254 readflg = 0;
1255 ++errcnt;
1256 listf = listn = 0;
1257 pflag = 0;
1258 #ifndef RESEARCH
1259 if (oldmask) {
1260 umask(oldmask);
1261 oldmask = 0;
1263 #endif
1264 #ifdef NULLS /* Not really nulls, but close enough */
1265 /* This is a bug because of buffering */
1266 if (code == 28) /* illegal char. */
1267 putd();
1268 #endif
1269 /* Cant open file or file does not exist */
1270 if ((code == 3) || (code == 68)) {
1271 if (qflg == 0) {
1272 putchr('?');
1273 puts(file);
1275 else
1276 qflg = 0;
1278 else
1280 putchr('?');
1281 putchr('\n');
1283 count = 0;
1284 lseek(0, (long)0, 2);
1285 if (globp)
1286 lastc = '\n';
1287 globp = 0;
1288 peekc = lastc;
1289 if (lastc)
1290 while ((c = getchr()) != '\n' && c != EOF);
1291 if (io) {
1292 eclose(io);
1293 io = -1;
1295 xcode = code;
1296 if (hflag)
1297 PUTM();
1298 if (code == 4)
1299 return (0); /* Non-fatal error. */
1300 longjmp(savej, 1);
1301 /* NOTREACHED */
1304 static int
1305 getchr(void)
1307 char c;
1308 if (lastc = peekc) {
1309 peekc = 0;
1310 return (lastc);
1312 if (globp) {
1313 if ((lastc = (unsigned char)*globp++) != 0)
1314 return (lastc);
1315 globp = 0;
1316 return (EOF);
1318 if (read(0, &c, 1) <= 0)
1319 return (lastc = EOF);
1320 lastc = (unsigned char)c;
1321 return (lastc);
1324 static int
1325 gettty(void)
1327 int c;
1328 char *gf;
1329 char *p;
1331 p = linebuf;
1332 gf = globp;
1333 while ((c = getchr()) != '\n') {
1334 if (c == EOF) {
1335 if (gf)
1336 peekc = c;
1337 return (c);
1339 if (c == 0)
1340 continue;
1341 *p++ = c;
1343 if (p > &linebuf[LBSIZE-1])
1344 (void) error(27);
1346 *p++ = 0;
1347 if (linebuf[0] == '.' && linebuf[1] == 0)
1348 return (EOF);
1351 * POSIX.2/XPG4 explicitly says no to this:
1353 * in Solaris backslash followed by special character "." is
1354 * special character "." itself; (so terminating input mode can be
1355 * "\.\n").
1357 * however, POSIX2/XPG4 says, input mode is terminated by
1358 * entering line consisting of only 2 characters: ".\n"
1360 * if (linebuf[0]=='\\' && linebuf[1]=='.' && linebuf[2]==0) {
1361 * linebuf[0] = '.';
1362 * linebuf[1] = 0;
1365 return (0);
1368 static int
1369 getfile(void)
1371 char c;
1372 char *lp, *fp;
1374 lp = linebuf;
1375 fp = nextip;
1376 do {
1377 if (--ninbuf < 0) {
1378 if ((ninbuf = read(io, genbuf, LBSIZE)-1) < 0)
1379 if (lp > linebuf) {
1380 puts(gettext("'\\n' appended"));
1381 *genbuf = '\n';
1383 else
1384 return (EOF);
1385 if (crflag == -1) {
1386 if (isencrypt(genbuf, ninbuf + 1))
1387 crflag = 2;
1388 else
1389 crflag = -2;
1391 fp = genbuf;
1392 if (crflag > 0)
1393 if (run_crypt(count, genbuf, ninbuf+1, perm) == -1)
1394 (void) error(63);
1396 if (lp >= &linebuf[LBSIZE]) {
1397 lastc = '\n';
1398 (void) error(27);
1400 if ((*lp++ = c = *fp++) == 0) {
1401 #ifdef NULLS
1402 lp[-1] = '\\';
1403 *lp++ = '0';
1404 nulls++;
1405 #else
1406 lp--;
1407 continue;
1408 #endif
1410 count++;
1411 } while (c != '\n');
1412 *--lp = 0;
1413 nextip = fp;
1414 if (fss.Ffill && fss.Flim && lenchk(linebuf, &fss) < 0) {
1415 write(1, gettext("line too long: lno = "),
1416 strlen(gettext("line too long: lno = ")));
1417 ccount = count;
1418 count = (++dot-zero)&077777;
1419 dot--;
1420 putd();
1421 count = ccount;
1422 putchr('\n');
1424 return (0);
1427 static void
1428 putfile(void)
1430 int n;
1431 LINE a1;
1432 char *fp, *lp;
1433 int nib;
1435 nib = LBSIZE;
1436 fp = genbuf;
1437 a1 = addr1;
1438 do {
1439 lp = getaline(a1++->cur);
1440 if (fss.Ffill && fss.Flim && lenchk(linebuf, &fss) < 0) {
1441 write(1, gettext("line too long: lno = "),
1442 strlen(gettext("line too long: lno = ")));
1443 ccount = count;
1444 count = (a1-zero-1)&077777;
1445 putd();
1446 count = ccount;
1447 putchr('\n');
1449 for (;;) {
1450 if (--nib < 0) {
1451 n = fp-genbuf;
1452 if (kflag)
1453 if (run_crypt(count-n, genbuf, n, perm) == -1)
1454 (void) error(63);
1455 if (write(io, genbuf, n) != n)
1456 (void) error(29);
1457 nib = LBSIZE - 1;
1458 fp = genbuf;
1460 if (dol->cur == 0L)break; /* Allow write of null file */
1461 count++;
1462 if ((*fp++ = *lp++) == 0) {
1463 fp[-1] = '\n';
1464 break;
1467 } while (a1 <= addr2);
1468 n = fp-genbuf;
1469 if (kflag)
1470 if (run_crypt(count-n, genbuf, n, perm) == -1)
1471 (void) error(63);
1472 if (write(io, genbuf, n) != n)
1473 (void) error(29);
1476 static void
1477 append(int (*f)(void), LINE a)
1479 LINE a1, a2, rdot;
1480 long tl;
1482 nline = 0;
1483 dot = a;
1484 while ((*f)() == 0) {
1485 if (dol >= endcore) {
1486 if ((int)sbrk(512 * sizeof (struct lin)) == -1) {
1487 lastc = '\n';
1488 (void) error(30);
1490 endcore += 512;
1492 tl = putline();
1493 nline++;
1494 a1 = ++dol;
1495 a2 = a1+1;
1496 rdot = ++dot;
1497 while (a1 > rdot)
1498 (--a2)->cur = (--a1)->cur;
1499 rdot->cur = tl;
1503 static void
1504 unixcom(void)
1506 void (*savint)();
1507 pid_t pid, rpid;
1508 int retcode;
1509 static char savcmd[LBSIZE]; /* last command */
1510 char curcmd[LBSIZE]; /* current command */
1511 char *psavcmd, *pcurcmd, *psavedfile;
1512 int endflg = 1, shflg = 0;
1513 wchar_t c;
1514 int len;
1516 setnoaddr();
1517 if (rflg)
1518 (void) error(6);
1519 pcurcmd = curcmd;
1520 /* read command til end */
1523 * a '!' found in beginning of command is replaced with the saved
1524 * command. a '%' found in command is replaced with the current
1525 * filename
1528 c = getchr();
1529 if (c == '!') {
1530 if (savcmd[0] == 0)
1531 (void) error(56);
1532 else {
1533 psavcmd = savcmd;
1534 while (*pcurcmd++ = *psavcmd++);
1535 --pcurcmd;
1536 shflg = 1;
1538 } else
1539 UNGETC(c); /* put c back */
1540 while (endflg == 1) {
1541 while ((c = get_wchr()) != '\n' && c != '%' && c != '\\') {
1542 if ((len = wctomb(pcurcmd, c)) <= 0) {
1543 *pcurcmd = (unsigned char)c;
1544 len = 1;
1546 pcurcmd += len;
1549 if (c == '%') {
1550 if (savedfile[0] == 0)
1551 (void) error(21);
1552 else {
1553 psavedfile = savedfile;
1554 while (pcurcmd < curcmd + LBSIZE &&
1555 (*pcurcmd++ = *psavedfile++));
1556 --pcurcmd;
1557 shflg = 1;
1559 } else if (c == '\\') {
1560 c = get_wchr();
1561 if (c != '%')
1562 *pcurcmd++ = '\\';
1563 if ((len = wctomb(pcurcmd, c)) <= 0) {
1564 *pcurcmd = (unsigned char)c;
1565 len = 1;
1567 pcurcmd += len;
1569 else
1570 /* end of command hit */
1571 endflg = 0;
1573 *pcurcmd++ = 0;
1574 if (shflg == 1)
1575 puts(curcmd);
1576 /* save command */
1577 strcpy(savcmd, curcmd);
1579 if ((pid = fork()) == 0) {
1580 signal(SIGHUP, oldhup);
1581 signal(SIGQUIT, oldquit);
1582 close(tfile);
1583 execlp(_PATH_BSHELL, "sh", "-c", curcmd, (char *)0);
1584 exit(0100);
1586 savint = signal(SIGINT, SIG_IGN);
1587 while ((rpid = wait(&retcode)) != pid && rpid != (pid_t)-1);
1588 signal(SIGINT, savint);
1589 if (vflag) puts("!");
1592 static void
1593 quit(int sig)
1595 if (vflag && fchange) {
1596 fchange = 0;
1597 if (flag28) {
1598 flag28 = 0;
1599 (void) error(62);
1603 * For case where user reads in BOTH a good
1604 * file & a bad file
1606 (void) error(1);
1608 unlink(tfname);
1609 if (kflag)
1610 crypt_close(perm);
1611 if (xtflag)
1612 crypt_close(tperm);
1613 exit(errcnt? 2: 0);
1616 static void
1617 delete(void)
1619 setdot();
1620 newline();
1621 nonzero();
1622 if (!globflg) save();
1623 rdelete(addr1, addr2);
1626 static void
1627 rdelete(LINE ad1, LINE ad2)
1629 LINE a1, a2, a3;
1631 a1 = ad1;
1632 a2 = ad2+1;
1633 a3 = dol;
1634 dol -= a2 - a1;
1636 (a1++)->cur = (a2++)->cur;
1637 while (a2 <= a3);
1638 a1 = ad1;
1639 if (a1 > dol)
1640 a1 = dol;
1641 dot = a1;
1642 fchange = 1;
1645 static void
1646 gdelete(void)
1648 LINE a1, a2, a3;
1650 a3 = dol;
1651 for (a1 = zero+1; (a1->cur&01) == 0; a1++)
1652 if (a1 >= a3)
1653 return;
1654 for (a2 = a1 + 1; a2 <= a3; ) {
1655 if (a2->cur & 01) {
1656 a2++;
1657 dot = a1;
1658 } else
1659 (a1++)->cur = (a2++)->cur;
1661 dol = a1-1;
1662 if (dot > dol)
1663 dot = dol;
1664 fchange = 1;
1667 static char *
1668 getaline(long tl)
1670 char *bp, *lp;
1671 int nl;
1673 lp = linebuf;
1674 bp = getblock(tl, READ);
1675 nl = nleft;
1676 tl &= ~0377;
1677 while (*lp++ = *bp++)
1678 if (--nl == 0) {
1679 bp = getblock(tl += 0400, READ);
1680 nl = nleft;
1682 return (linebuf);
1685 static long
1686 putline(void)
1688 char *bp, *lp;
1689 int nl;
1690 long tl;
1692 fchange = 1;
1693 lp = linebuf;
1694 tl = tline;
1695 bp = getblock(tl, WRITE);
1696 nl = nleft;
1697 tl &= ~0377;
1698 while (*bp = *lp++) {
1699 if (*bp++ == '\n') {
1700 *--bp = 0;
1701 linebp = lp;
1702 break;
1704 if (--nl == 0) {
1705 bp = getblock(tl += 0400, WRITE);
1706 nl = nleft;
1709 nl = tline;
1710 tline += (((lp-linebuf)+03)>>1)&077776;
1711 return (nl);
1714 static char *
1715 getblock(long atl, long iof)
1717 int bno, off;
1718 char *p1, *p2;
1719 int n;
1721 bno = atl >> 8;
1722 off = (atl<<1)&0774;
1724 /* bno is limited to 16 bits */
1725 if (bno >= 65535) {
1726 lastc = '\n';
1727 (void) error(31);
1729 nleft = 512 - off;
1730 if (bno == iblock) {
1731 ichanged |= iof;
1732 return (ibuff+off);
1734 if (bno == oblock)
1735 return (obuff+off);
1736 if (iof == READ) {
1737 if (ichanged) {
1738 if (xtflag)
1739 if (run_crypt(0L, ibuff, 512, tperm) == -1)
1740 (void) error(63);
1741 blkio(iblock, ibuff, write);
1743 ichanged = 0;
1744 iblock = bno;
1745 blkio(bno, ibuff, read);
1746 if (xtflag)
1747 if (run_crypt(0L, ibuff, 512, tperm) == -1)
1748 (void) error(63);
1749 return (ibuff+off);
1751 if (oblock >= 0) {
1752 if (xtflag) {
1753 p1 = obuff;
1754 p2 = crbuf;
1755 n = 512;
1756 while (n--)
1757 *p2++ = *p1++;
1758 if (run_crypt(0L, crbuf, 512, tperm) == -1)
1759 (void) error(63);
1760 blkio(oblock, crbuf, write);
1761 } else
1762 blkio(oblock, obuff, write);
1764 oblock = bno;
1765 return (obuff+off);
1768 static void
1769 blkio(int b, char *buf, ssize_t (*iofcn)())
1771 lseek(tfile, (long)b<<9, 0);
1772 if ((*iofcn)(tfile, buf, 512) != 512) {
1773 if (dol != zero)
1774 (void) error(32); /* Bypass this if writing null file */
1778 static void
1779 init(void)
1781 long *markp;
1782 mode_t omask;
1784 if (tfile != -1) {
1785 (void) close(tfile);
1786 (void) unlink(tfname);
1789 tline = 2;
1790 for (markp = names; markp < &names[26]; )
1791 *markp++ = 0L;
1792 subnewa = 0L;
1793 anymarks = 0;
1794 iblock = -1;
1795 oblock = -1;
1796 ichanged = 0;
1797 initflg = 1;
1798 omask = umask(0);
1800 if ((tfile = open(tfname, O_CREAT|O_EXCL|O_RDWR,
1801 S_IRUSR|S_IWUSR)) < 0) {
1802 puts(gettext(msgtab[70]));
1803 exit(2);
1806 umask(omask);
1807 if (xflag) {
1808 xtflag = 1;
1809 if (tpermflag)
1810 (void) crypt_close(tperm);
1811 tpermflag = 1;
1812 if (makekey(tperm)) {
1813 xtflag = 0;
1814 puts(gettext(msgtab[65]));
1817 brk((char *)fendcore);
1818 dot = zero = dol = savdot = savdol = fendcore;
1819 flag28 = save28 = 0;
1820 endcore = fendcore - sizeof (struct lin);
1823 static void
1824 global(int k)
1826 char *gp;
1827 wchar_t l;
1828 char multic[MB_LEN_MAX];
1829 wchar_t c;
1830 LINE a1;
1831 char globuf[LBSIZE];
1832 int n;
1833 int len;
1835 if (globp)
1836 (void) error(33);
1837 setall();
1838 nonzero();
1839 if ((n = _mbftowc(multic, &l, getchr, &peekc)) <= 0)
1840 (void) error(67);
1841 if (l == '\n')
1842 (void) error(19);
1843 save();
1844 comple(l);
1845 gp = globuf;
1846 while ((c = get_wchr()) != '\n') {
1847 if (c == EOF)
1848 (void) error(19);
1850 /* '\\' has special meaning only if preceding a '\n' */
1851 if (c == '\\') {
1852 c = get_wchr();
1853 if (c != '\n')
1854 *gp++ = '\\';
1856 if ((gp + (unsigned int)MB_CUR_MAX) >= &globuf[LBSIZE-1])
1857 (void) error(34);
1858 if ((len = wctomb(gp, c)) <= 0) {
1859 *gp = (unsigned char)c;
1860 len = 1;
1862 gp += len;
1864 if (gp == globuf)
1865 *gp++ = 'p';
1866 *gp++ = '\n';
1867 *gp++ = 0;
1868 for (a1 = zero; a1 <= dol; a1++) {
1869 a1->cur &= ~01;
1870 if (a1 >= addr1 && a1 <= addr2 && execute(0, a1) == k)
1871 a1->cur |= 01;
1874 * Special case: g/.../d (avoid n^2 algorithm)
1876 if (globuf[0] == 'd' && globuf[1] == '\n' && globuf[2] == '\0') {
1877 gdelete();
1878 return;
1880 for (a1 = zero; a1 <= dol; a1++) {
1881 if (a1->cur & 01) {
1882 a1->cur &= ~01;
1883 dot = a1;
1884 globp = globuf;
1885 globflg = 1;
1886 commands();
1887 globflg = 0;
1888 a1 = zero;
1893 static void
1894 join(void)
1896 char *gp, *lp;
1897 LINE a1;
1899 if (addr1 == addr2)
1900 return;
1901 gp = genbuf;
1902 for (a1 = addr1; a1 <= addr2; a1++) {
1903 lp = getaline(a1->cur);
1904 while (*gp = *lp++)
1905 if (gp++ > &genbuf[LBSIZE-1])
1906 (void) error(27);
1908 lp = linebuf;
1909 gp = genbuf;
1910 while (*lp++ = *gp++);
1911 addr1->cur = putline();
1912 if (addr1 < addr2)
1913 rdelete(addr1+1, addr2);
1914 dot = addr1;
1917 static void
1918 substitute(int inglob)
1920 int nl;
1921 LINE a1;
1922 long *markp;
1923 int ingsav; /* For saving arg. */
1925 ingsav = inglob;
1926 ocerr2 = 0;
1927 gsubf = compsub();
1928 for (a1 = addr1; a1 <= addr2; a1++) {
1929 if (execute(0, a1) == 0)
1930 continue;
1931 numpass = 0;
1932 ocerr1 = 0;
1933 inglob |= 01;
1934 dosub();
1935 if (gsubf) {
1936 while (*loc2) {
1937 if (execute(1, (LINE)0) == 0)
1938 break;
1939 dosub();
1942 if (ocerr1 == 0)continue; /* Don't put out-not changed. */
1943 subnewa = putline();
1944 a1->cur &= ~01;
1945 if (anymarks) {
1946 for (markp = names; markp < &names[26]; markp++)
1947 if (*markp == a1->cur)
1948 *markp = subnewa;
1950 a1->cur = subnewa;
1951 append(getsub, a1);
1952 nl = nline;
1953 a1 += nl;
1954 addr2 += nl;
1956 if (ingsav)
1957 return; /* Was in global-no error msg allowed. */
1958 if (inglob == 0)
1959 (void) error(35); /* Not in global, but not found. */
1960 if (ocerr2 == 0)
1961 (void) error(35); /* RE found, but occurrence match failed. */
1964 static int
1965 compsub(void)
1967 int c;
1968 wchar_t seof;
1969 char *p;
1970 char multic[MB_LEN_MAX];
1971 int n;
1972 static char remem[RHSIZE];
1973 static int remflg = -1;
1974 int i;
1976 if ((n = _mbftowc(multic, &seof, getchr, &peekc)) <= 0)
1977 (void) error(67);
1978 if (seof == '\n' || seof == ' ')
1979 (void) error(36);
1980 comple(seof);
1981 p = rhsbuf;
1982 for (;;) {
1983 wchar_t cl;
1984 if ((n = _mbftowc(multic, &cl, getchr, &peekc)) <= 0)
1985 (void) error(67);
1986 if (cl == '\\') {
1987 *p++ = '\\';
1988 if (p >= &rhsbuf[RHSIZE])
1989 (void) error(38);
1990 if ((n = _mbftowc(multic, &cl, getchr, &peekc)) <= 0)
1991 (void) error(67);
1992 } else if (cl == '\n') {
1993 if (nodelim == 1) {
1994 nodelim = 0;
1995 (void) error(36);
1997 if (!(globp && globp[0])) {
1998 UNGETC('\n');
1999 pflag++;
2000 break;
2002 } else if (cl == seof)
2003 break;
2004 if (p + n > &rhsbuf[RHSIZE])
2005 (void) error(38);
2006 (void) strncpy(p, multic, n);
2007 p += n;
2009 *p++ = 0;
2010 if (rhsbuf[0] == '%' && rhsbuf[1] == 0)
2012 * If there isn't a remembered string, it is an error;
2013 * otherwise the right hand side is the previous right
2014 * hand side.
2017 if (remflg == -1)
2018 (void) error(55);
2019 else
2020 strcpy(rhsbuf, remem);
2021 else {
2022 strcpy(remem, rhsbuf);
2023 remflg = 0;
2025 c = 0;
2026 peekc = getchr(); /* Gets char after third delimiter. */
2027 if (peekc == 'g') {
2028 c = LBSIZE; peekc = 0;
2030 if (peekc >= '1' && peekc <= '9') {
2031 c = peekc-'0';
2032 peekc = 0; /* Allows getchr() to get next char. */
2033 while (1) {
2034 i = getchr();
2035 if (i < '0' || i > '9')
2036 break;
2037 c = c*10 + i-'0';
2038 if (c > LBSIZE-1)
2039 (void) error(20); /* "Illegal suffix" */
2041 peekc = i; /* Effectively an unget. */
2043 newline();
2044 return (c);
2047 * Returns occurrence value. 0 & 1 both do first occurrence
2048 * only: c = 0 if ordinary substitute; c = 1
2049 * if use 1 in global sub(s/a/b/1). 0 in global form is illegal.
2053 static int
2054 getsub(void)
2056 char *p1, *p2;
2058 p1 = linebuf;
2059 if ((p2 = linebp) == 0)
2060 return (EOF);
2061 while (*p1++ = *p2++);
2062 linebp = 0;
2063 return (0);
2066 static void
2067 dosub(void)
2069 char *lp, *sp, *rp;
2070 int c;
2072 if (gsubf > 0 && gsubf < LBSIZE) {
2073 numpass++;
2074 if (gsubf != numpass)
2075 return;
2077 ocerr1++;
2078 ocerr2++;
2079 lp = linebuf;
2080 sp = genbuf;
2081 rp = rhsbuf;
2082 while (lp < loc1)
2083 *sp++ = *lp++;
2084 while (c = *rp++) {
2085 if (c == '&') {
2086 sp = place(sp, loc1, loc2);
2087 continue;
2088 } else if (c == '\\') {
2089 c = *rp++;
2090 if (c >= '1' && c < nbra + '1') {
2091 sp = place(sp, braslist[c-'1'], braelist[c-'1']);
2092 continue;
2095 *sp++ = c;
2096 if (sp >= &genbuf[LBSIZE])
2097 (void) error(27);
2099 lp = loc2;
2100 loc2 = sp - genbuf + linebuf;
2101 while (*sp++ = *lp++)
2102 if (sp >= &genbuf[LBSIZE])
2103 (void) error(27);
2104 lp = linebuf;
2105 sp = genbuf;
2106 while (*lp++ = *sp++);
2109 static char *
2110 place(char *sp, char *l1, char *l2)
2113 while (l1 < l2) {
2114 *sp++ = *l1++;
2115 if (sp >= &genbuf[LBSIZE])
2116 (void) error(27);
2118 return (sp);
2121 static void
2122 comple(wchar_t seof)
2124 int cclass = 0;
2125 wchar_t c;
2126 int n;
2127 char *cp = genbuf;
2128 char multic[MB_LEN_MAX];
2130 while (1) {
2131 if ((n = _mbftowc(multic, &c, getchr, &peekc)) < 0)
2132 error1(67);
2133 if (n == 0 || c == '\n') {
2134 if (cclass)
2135 error1(49);
2136 else
2137 break;
2139 if (c == seof && !cclass)
2140 break;
2141 if (cclass && c == ']') {
2142 cclass = 0;
2143 if (cp > &genbuf[LBSIZE-1])
2144 error1(50);
2145 *cp++ = ']';
2146 continue;
2148 if (c == '[' && !cclass) {
2149 cclass = 1;
2150 if (cp > &genbuf[LBSIZE-1])
2151 error1(50);
2152 *cp++ = '[';
2153 if ((n = _mbftowc(multic, &c, getchr, &peekc)) < 0)
2154 error1(67);
2155 if (n == 0 || c == '\n')
2156 error1(49);
2158 if (c == '\\' && !cclass) {
2159 if (cp > &genbuf[LBSIZE-1])
2160 error1(50);
2161 *cp++ = '\\';
2162 if ((n = _mbftowc(multic, &c, getchr, &peekc)) < 0)
2163 error1(67);
2164 if (n == 0 || c == '\n')
2165 error1(36);
2167 if (cp + n > &genbuf[LBSIZE-1])
2168 error1(50);
2169 (void) strncpy(cp, multic, n);
2170 cp += n;
2172 *cp = '\0';
2173 if (n != 0 && c == '\n')
2174 UNGETC('\n');
2175 if (n == 0 || c == '\n')
2176 nodelim = 1;
2179 * NULL RE: do not compile a null regular expression; but process
2180 * input with last regular expression encountered
2183 if (genbuf[0] != '\0') {
2184 if (expbuf)
2185 free(expbuf);
2186 expbuf = compile(genbuf, (char *)0, (char *)0);
2188 if (regerrno)
2189 error1(regerrno);
2192 static void
2193 move(int cflag)
2195 LINE adt, ad1, ad2;
2197 setdot();
2198 nonzero();
2199 if ((adt = address()) == 0)
2200 (void) error(39);
2201 newline();
2202 if (!globflg) save();
2203 if (cflag) {
2204 ad1 = dol;
2205 append(getcopy, ad1++);
2206 ad2 = dol;
2207 } else {
2208 ad2 = addr2;
2209 for (ad1 = addr1; ad1 <= ad2; )
2210 (ad1++)->cur &= ~01;
2211 ad1 = addr1;
2213 ad2++;
2214 if (adt < ad1) {
2215 dot = adt + (ad2-ad1);
2216 if ((++adt) == ad1)
2217 return;
2218 reverse(adt, ad1);
2219 reverse(ad1, ad2);
2220 reverse(adt, ad2);
2221 } else if (adt >= ad2) {
2222 dot = adt++;
2223 reverse(ad1, ad2);
2224 reverse(ad2, adt);
2225 reverse(ad1, adt);
2226 } else
2227 (void) error(39);
2228 fchange = 1;
2231 static void
2232 reverse(LINE a1, LINE a2)
2234 long t;
2236 for (;;) {
2237 t = (--a2)->cur;
2238 if (a2 <= a1)
2239 return;
2240 a2->cur = a1->cur;
2241 (a1++)->cur = t;
2245 static int
2246 getcopy(void)
2249 if (addr1 > addr2)
2250 return (EOF);
2251 (void) getaline((addr1++)->cur);
2252 return (0);
2257 * Handles error code returned from comple() routine: regular expression
2258 * compile and match routines
2261 static void
2262 error1(int code)
2264 nbra = 0;
2265 (void) error(code);
2269 static int
2270 execute(int gf, LINE addr)
2272 char *p1;
2273 int c;
2275 for (c = 0; c < nbra; c++) {
2276 braslist[c] = 0;
2277 braelist[c] = 0;
2279 if (gf)
2280 locs = p1 = loc2;
2281 else {
2282 if (addr == zero)
2283 return (0);
2284 p1 = getaline(addr->cur);
2285 locs = 0;
2287 return (step(p1, expbuf));
2291 static void
2292 putd()
2294 int r;
2296 r = (int)(count%10);
2297 count /= 10;
2298 if (count)
2299 putd();
2300 putchr(r + '0');
2305 puts(const char *sp)
2307 int n;
2308 wchar_t c;
2309 int sz, i;
2310 if (fss.Ffill && (listf == 0)) {
2312 /* deliberate attempt to remove constness of sp because */
2313 /* it needs to be expanded */
2315 if ((i = expnd((char *)sp, funny, &sz, &fss)) == -1) {
2316 write(1, funny, fss.Flim & 0377);
2317 putchr('\n');
2318 write(1, gettext("too long"),
2319 strlen(gettext("too long")));
2321 else
2322 write(1, funny, sz);
2323 putchr('\n');
2324 if (i == -2)
2325 write(1, gettext("tab count\n"),
2326 strlen(gettext("tab count\n")));
2327 return (0);
2329 col = 0;
2330 while (*sp) {
2331 n = mbtowc(&c, sp, MB_LEN_MAX);
2332 if (listf) {
2333 if (n < 1)
2334 (void) error(28);
2335 else if (n == 1)
2336 putchr((unsigned char)*sp++);
2337 else {
2338 sp += n;
2339 putwchr(c);
2341 } else {
2342 putchr((unsigned char)*sp++);
2345 #ifndef XPG6
2346 if (listf)
2347 putchr('$'); /* end of line is marked with a $ */
2348 #else
2349 if (listf) {
2350 /* xpg6 - ensure that the end of line $ is not preceeded with a "\" */
2351 /* by doing a putchr() with listf=0, thereby avoiding the $ case */
2352 /* statement in putchr() */
2353 listf = 0;
2354 putchr('$'); /* end of line is marked with a $ */
2355 listf++;
2357 #endif
2358 putchr('\n');
2359 return (1);
2363 static void
2364 putwchr(wchar_t ac)
2366 char buf[MB_LEN_MAX], *p;
2367 char *lp;
2368 wchar_t c;
2369 short len;
2371 lp = linp;
2372 c = ac;
2373 if (listf) {
2374 if (!iswprint(c)) {
2375 p = &buf[0];
2376 if ((len = wctomb(p, c)) <= 0) {
2377 *p = (unsigned char)c;
2378 len = 1;
2380 while (len--) {
2381 if (col + 4 >= 72) {
2382 col = 0;
2383 *lp++ = '\\';
2384 *lp++ = '\n';
2386 (void) sprintf(lp, "\\%03o",
2387 *(unsigned char *)p++);
2388 col += 4;
2389 lp += 4;
2391 } else {
2392 if ((len = wcwidth(c)) <= 0)
2393 len = 0;
2394 if (col + len >= 72) {
2395 col = 0;
2396 *lp++ = '\\';
2397 *lp++ = '\n';
2399 col += len;
2400 if ((len = wctomb(lp, c)) <= 0) {
2401 *lp = (unsigned char)c;
2402 len = 1;
2404 lp += len;
2406 } else {
2407 if ((len = wctomb(lp, c)) <= 0) {
2408 *lp = (unsigned char)c;
2409 len = 1;
2411 lp += len;
2413 if (c == '\n' || lp >= &line[64]) {
2414 linp = line;
2415 len = lp - line;
2416 write(1, line, len);
2417 return;
2419 linp = lp;
2423 static void
2424 putchr(unsigned char c)
2426 char *lp;
2427 int len;
2429 lp = linp;
2430 if (listf && c != '\n') {
2431 switch (c) {
2432 case '\\' :
2433 *lp++ = '\\';
2434 *lp++ = '\\';
2435 col += 2;
2436 break;
2437 case '\007' :
2438 *lp++ = '\\';
2439 *lp++ = 'a';
2440 col += 2;
2441 break;
2442 case '\b' :
2443 *lp++ = '\\';
2444 *lp++ = 'b';
2445 col += 2;
2446 break;
2447 case '\f' :
2448 *lp++ = '\\';
2449 *lp++ = 'f';
2450 col += 2;
2451 break;
2452 case '\r' :
2453 *lp++ = '\\';
2454 *lp++ = 'r';
2455 col += 2;
2456 break;
2457 case '\t' :
2458 *lp++ = '\\';
2459 *lp++ = 't';
2460 col += 2;
2461 break;
2462 case '\v' :
2463 *lp++ = '\\';
2464 *lp++ = 'v';
2465 col += 2;
2466 break;
2467 #ifdef XPG6
2468 /* if $ characters are within the line preceed with \ */
2469 case '$' :
2470 *lp++ = '\\';
2471 *lp++ = '$';
2472 col += 2;
2473 break;
2474 #endif
2475 default:
2476 if (isprint(c)) {
2477 *lp++ = c;
2478 col += 1;
2479 } else {
2480 (void) sprintf(lp, "\\%03o", c);
2481 col += 4;
2482 lp += 4;
2484 break;
2488 * long lines are folded w/ pt of folding indicated by writing
2489 * backslash/newline character
2492 if (col + 1 >= 72) {
2493 col = 0;
2494 *lp++ = '\\';
2495 *lp++ = '\n';
2497 } else
2498 *lp++ = c;
2499 if (c == '\n' || lp >= &line[64]) {
2500 linp = line;
2501 len = lp - line;
2502 (void) write(1, line, len);
2503 return;
2505 linp = lp;
2509 static char *
2510 getkey(const char *prompt)
2512 struct termio b;
2513 int save;
2514 void (*sig)();
2515 static char key[KSIZE+1];
2516 char *p;
2517 int c;
2519 sig = signal(SIGINT, SIG_IGN);
2520 ioctl(0, TCGETA, &b);
2521 save = b.c_lflag;
2522 b.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
2523 ioctl(0, TCSETAW, &b);
2524 write(1, gettext(prompt), strlen(gettext(prompt)));
2525 p = key;
2526 while (((c = getchr()) != EOF) && (c != '\n')) {
2527 if (p < &key[KSIZE])
2528 *p++ = c;
2530 *p = 0;
2531 write(1, "\n", 1);
2532 b.c_lflag = save;
2533 ioctl(0, TCSETAW, &b);
2534 signal(SIGINT, sig);
2535 return (key);
2539 static void
2540 globaln(int k)
2542 char *gp;
2543 int c;
2544 int n;
2545 wchar_t cl;
2546 LINE a1;
2547 int nfirst;
2548 char globuf[LBSIZE];
2549 char multic[MB_LEN_MAX];
2550 int len;
2551 int pflag_save = 0;
2552 int listf_save = 0;
2553 int listn_save = 0;
2555 if (globp)
2556 (void) error(33);
2557 setall();
2558 nonzero();
2559 if ((n = _mbftowc(multic, &cl, getchr, &peekc)) <= 0)
2560 (void) error(67);
2561 if (cl == '\n')
2562 (void) error(19);
2563 save();
2564 comple(cl);
2565 for (a1 = zero; a1 <= dol; a1++) {
2566 a1->cur &= ~01;
2567 if (a1 >= addr1 && a1 <= addr2 && execute(0, a1) == k)
2568 a1->cur |= 01;
2570 nfirst = 0;
2571 newline();
2573 * preserve the p, l, and n suffix commands of the G and V
2574 * commands during the interactive section and restore
2575 * on completion of the G and V command.
2577 pflag_save = pflag;
2578 listf_save = listf;
2579 listn_save = listn;
2580 pflag = 0;
2581 listf = 0;
2582 listn = 0;
2583 for (a1 = zero; a1 <= dol; a1++) {
2584 if (a1->cur & 01) {
2585 a1->cur &= ~01;
2586 dot = a1;
2587 puts(getaline(a1->cur));
2588 if ((c = get_wchr()) == EOF)
2589 (void) error(52);
2590 if (c == 'a' || c == 'i' || c == 'c')
2591 (void) error(53);
2592 if (c == '\n') {
2593 a1 = zero;
2594 continue;
2596 if (c != '&') {
2597 gp = globuf;
2598 if ((len = wctomb(gp, c)) <= 0) {
2599 *gp = (unsigned char)c;
2600 len = 1;
2602 gp += len;
2603 while ((c = get_wchr()) != '\n') {
2605 /* '\\' has special meaning only if preceding a '\n' */
2606 if (c == '\\') {
2607 c = get_wchr();
2608 if (c != '\n')
2609 *gp++ = '\\';
2611 if ((gp + (unsigned int)MB_CUR_MAX) >=
2612 &globuf[LBSIZE-1])
2613 (void) error(34);
2615 if ((len = wctomb(gp, c)) <= 0) {
2616 *gp = (unsigned char)c;
2617 len = 1;
2619 gp += len;
2621 *gp++ = '\n';
2622 *gp++ = 0;
2623 nfirst = 1;
2624 } else if ((c = get_wchr()) != '\n')
2625 (void) error(54);
2626 globp = globuf;
2627 if (nfirst) {
2628 globflg = 1;
2629 commands();
2630 globflg = 0;
2631 } else
2632 (void) error(56);
2633 globp = 0;
2634 a1 = zero;
2637 pflag = pflag_save;
2638 listf = listf_save;
2639 listn = listn_save;
2643 static int
2644 eopen(char *string, int rw)
2646 #define w_or_r(a, b) (rw ? a : b)
2647 int pf[2];
2648 pid_t i;
2649 int io;
2650 int chcount; /* # of char read. */
2652 if (rflg) { /* restricted shell */
2653 if (Xqt) {
2654 Xqt = 0;
2655 (void) error(6);
2658 if (!Xqt) {
2659 if ((io = open(string, rw)) >= 0) {
2660 if (fflg) {
2661 chcount = read(io, crbuf, LBSIZE);
2662 if (crflag == -1) {
2663 if (isencrypt(crbuf, chcount))
2664 crflag = 2;
2665 else
2666 crflag = -2;
2668 if (crflag > 0)
2669 if (run_crypt(0L, crbuf, chcount, perm) == -1)
2670 (void) error(63);
2671 if (fspec(crbuf, &fss, 0) < 0) {
2672 fss.Ffill = 0;
2673 fflg = 0;
2674 (void) error(4);
2676 lseek(io, 0L, 0);
2679 fflg = 0;
2680 return (io);
2682 if (pipe(pf) < 0)
2683 xerr: (void) error(0);
2684 if ((i = fork()) == 0) {
2685 signal(SIGHUP, oldhup);
2686 signal(SIGQUIT, oldquit);
2687 signal(SIGPIPE, oldpipe);
2688 signal(SIGINT, (void (*)()) 0);
2689 close(w_or_r(pf[1], pf[0]));
2690 close(w_or_r(0, 1));
2691 dup(w_or_r(pf[0], pf[1]));
2692 close(w_or_r(pf[0], pf[1]));
2693 execlp(_PATH_BSHELL, "sh", "-c", string, (char *)0);
2694 exit(1);
2696 if (i == (pid_t)-1)
2697 goto xerr;
2698 close(w_or_r(pf[0], pf[1]));
2699 return (w_or_r(pf[1], pf[0]));
2703 static void
2704 eclose(int f)
2706 close(f);
2707 if (Xqt)
2708 Xqt = 0, wait((int *)0);
2712 static void
2713 mkfunny(void)
2715 char *p, *p1, *p2;
2717 p2 = p1 = funny;
2718 p = file;
2720 * Go to end of file name
2722 while (*p)
2723 p++;
2724 while (*--p == '/') /* delete trailing slashes */
2725 *p = '\0';
2727 * go back to beginning of file
2729 p = file;
2731 * Copy file name to funny setting p2 at
2732 * basename of file.
2734 while (*p1++ = *p)
2735 if (*p++ == '/') p2 = p1;
2737 * Set p1 to point to basename of tfname.
2739 p1 = strrchr(tfname, '/');
2740 if (strlen(tfname) > (size_t)6)
2741 p1 = &tfname[strlen(tfname)-6];
2742 p1++;
2743 *p2 = '\007'; /* add unprintable char for funny a unique name */
2745 * Copy tfname to file.
2747 while (*++p2 = *p1++);
2751 static void
2752 getime(void) /* get modified time of file and save */
2754 if (stat(file, &Fl) < 0)
2755 savtime = 0;
2756 else
2757 savtime = Fl.st_mtime;
2761 static void
2762 chktime(void) /* check saved mod time against current mod time */
2764 if (savtime != 0 && Fl.st_mtime != 0) {
2765 if (savtime != Fl.st_mtime)
2766 (void) error(58);
2771 static void
2772 newtime(void) /* get new mod time and save */
2774 stat(file, &Fl);
2775 savtime = Fl.st_mtime;
2779 static void
2780 red(char *op) /* restricted - check for '/' in name */
2781 /* and delete trailing '/' */
2783 char *p;
2785 p = op;
2786 while (*p)
2787 if (*p++ == '/'&& rflg) {
2788 *op = 0;
2789 (void) error(6);
2791 /* delete trailing '/' */
2792 while (p > op) {
2793 if (*--p == '/')
2794 *p = '\0';
2795 else break;
2801 * Searches thru beginning of file looking for a string of the form
2802 * <: values... :>
2804 * where "values" are
2806 * \b ignored
2807 * s<num> sets the Flim to <num>
2808 * t??? sets tab stop stuff
2809 * d ignored
2810 * m<num> ignored
2811 * e ignored
2814 static int
2815 fspec(char line[], struct Fspec *f, int up)
2817 struct termio arg;
2818 int havespec, n;
2819 int len;
2821 if (!up) clear(f);
2823 havespec = fsprtn = 0;
2824 for (fsp = line; *fsp && *fsp != '\n'; fsp += len) {
2825 if ((len = mblen(fsp, MB_CUR_MAX)) <= 0)
2826 len = 1;
2827 switch (*fsp) {
2829 case '<': if (havespec)
2830 return (-1);
2831 if (*(fsp+1) == ':') {
2832 havespec = 1;
2833 clear(f);
2834 if (!ioctl(1, TCGETA, &arg) &&
2835 ((arg.c_oflag&TAB3) == TAB3))
2836 f->Ffill = 1;
2837 fsp++;
2838 continue;
2841 case ' ': continue;
2843 case 's': if (havespec && (n = numb()) >= 0)
2844 f->Flim = n;
2845 continue;
2847 case 't': if (havespec) targ(f);
2848 continue;
2850 case 'd': continue;
2852 case 'm': if (havespec) n = numb();
2853 continue;
2855 case 'e': continue;
2856 case ':': if (!havespec) continue;
2857 if (*(fsp+1) != '>') fsprtn = -1;
2858 return (fsprtn);
2860 default: if (!havespec) continue;
2861 return (-1);
2864 return (1);
2868 static int
2869 numb(void)
2871 int n;
2873 n = 0;
2874 while (*++fsp >= '0' && *fsp <= '9')
2875 n = 10*n + *fsp-'0';
2876 fsp--;
2877 return (n);
2881 static void
2882 targ(struct Fspec *f)
2885 if (*++fsp == '-') {
2886 if (*(fsp + 1) >= '0' && *(fsp+1) <= '9') tincr(numb(), f);
2887 else tstd(f);
2888 return;
2890 if (*fsp >= '0' && *fsp <= '9') {
2891 tlist(f);
2892 return;
2894 fsprtn = -1;
2895 fsp--;
2899 static void
2900 tincr(int n, struct Fspec *f)
2902 int l, i;
2904 l = 1;
2905 for (i = 0; i < 20; i++)
2906 f->Ftabs[i] = l += n;
2907 f->Ftabs[i] = 0;
2911 static void
2912 tstd(struct Fspec *f)
2914 char std[3];
2916 std[0] = *++fsp;
2917 if (*(fsp+1) >= '0' && *(fsp+1) <= '9') {
2918 std[1] = *++fsp;
2919 std[2] = '\0';
2920 } else std[1] = '\0';
2921 fsprtn = stdtab(std, f->Ftabs);
2925 static void
2926 tlist(struct Fspec *f)
2928 int n, last, i;
2930 fsp--;
2931 last = i = 0;
2933 do {
2934 if ((n = numb()) <= last || i >= 20) {
2935 fsprtn = -1;
2936 return;
2938 f->Ftabs[i++] = last = n;
2939 } while (*++fsp == ',');
2941 f->Ftabs[i] = 0;
2942 fsp--;
2946 static int
2947 expnd(char line[], char buf[], int *sz, struct Fspec *f)
2949 char *l, *t;
2950 int b;
2952 l = line - 1;
2953 b = 1;
2954 t = f->Ftabs;
2955 fsprtn = 0;
2957 while (*++l && *l != '\n' && b < 511) {
2958 if (*l == '\t') {
2959 while (*t && b >= *t) t++;
2960 if (*t == 0) fsprtn = -2;
2961 do buf[b-1] = ' '; while (++b < *t);
2962 } else buf[b++ - 1] = *l;
2965 buf[b] = '\0';
2966 *sz = b;
2967 if (*l != '\0' && *l != '\n') {
2968 buf[b-1] = '\n';
2969 return (-1);
2971 buf[b-1] = *l;
2972 if (f->Flim && (b-1 > (int)f->Flim))
2973 return (-1);
2974 return (fsprtn);
2978 static void
2979 clear(struct Fspec *f)
2981 f->Ftabs[0] = f->Fdel = f->Fmov = f->Ffill = 0;
2982 f->Flim = 0;
2986 static int
2987 lenchk(char line[], struct Fspec *f)
2989 char *l, *t;
2990 int b;
2992 l = line - 1;
2993 b = 1;
2994 t = f->Ftabs;
2996 while (*++l && *l != '\n' && b < 511) {
2997 if (*l == '\t') {
2998 while (*t && b >= *t) t++;
2999 while (++b < *t);
3000 } else b++;
3003 if ((*l != '\0' && *l != '\n') || (f->Flim && (b-1 > (int)f->Flim)))
3004 return (-1);
3005 return (0);
3007 #define NTABS 21
3011 * stdtabs: standard tabs table
3012 * format: option code letter(s), null, tabs, null
3015 static char stdtabs[] = {
3016 'a', 0, 1, 10, 16, 36, 72, 0, /* IBM 370 Assembler */
3017 'a', '2', 0, 1, 10, 16, 40, 72, 0, /* IBM Assembler alternative */
3018 'c', 0, 1, 8, 12, 16, 20, 55, 0, /* COBOL, normal */
3019 'c', '2', 0, 1, 6, 10, 14, 49, 0, /* COBOL, crunched */
3020 'c', '3', 0, 1, 6, 10, 14, 18, 22, 26, 30, 34, 38, 42, 46, 50,
3021 54, 58, 62, 67, 0,
3022 'f', 0, 1, 7, 11, 15, 19, 23, 0, /* FORTRAN */
3023 'p', 0, 1, 5, 9, 13, 17, 21, 25, 29, 33, 37, 41, 45, 49, 53, 57, 61, 0,
3024 /* PL/I */
3025 's', 0, 1, 10, 55, 0, /* SNOBOL */
3026 'u', 0, 1, 12, 20, 44, 0, /* UNIVAC ASM */
3027 0 };
3031 * stdtab: return tab list for any "canned" tab option.
3032 * entry: option points to null-terminated option string
3033 * tabvect points to vector to be filled in
3034 * exit: return (0) if legal, tabvect filled, ending with zero
3035 * return (-1) if unknown option
3039 static int
3040 stdtab(char option[], char tabvect[NTABS])
3042 char *scan;
3043 tabvect[0] = 0;
3044 scan = stdtabs;
3045 while (*scan) {
3046 if (strequal(&scan, option)) {
3047 strcopy(scan, tabvect);
3048 break;
3049 } else
3050 while (*scan++); /* skip over tab specs */
3053 /* later: look up code in /etc/something */
3054 return (tabvect[0] ? 0 : -1);
3059 * strequal: checks strings for equality
3060 * entry: scan1 points to scan pointer, str points to string
3061 * exit: return (1) if equal, return (0) if not
3062 * *scan1 is advanced to next nonzero byte after null
3066 static int
3067 strequal(char **scan1, char *str)
3069 char c, *scan;
3070 scan = *scan1;
3071 while ((c = *scan++) == *str && c) str++;
3072 *scan1 = scan;
3073 if (c == 0 && *str == 0)
3074 return (1);
3075 if (c)
3076 while (*scan++);
3077 *scan1 = scan;
3078 return (0);
3082 /* strcopy: copy source to destination */
3085 static void
3086 strcopy(char *source, char *dest)
3088 while (*dest++ = *source++);
3092 /* This is called before a buffer modifying command so that the */
3093 /* current array of line ptrs is saved in sav and dot and dol are saved */
3096 static void
3097 save(void) {
3098 LINE i;
3099 int j;
3101 savdot = dot;
3102 savdol = dol;
3103 for (j = 0; j <= 25; j++)
3104 savnames[j] = names[j];
3106 for (i = zero + 1; i <= dol; i++)
3107 i->sav = i->cur;
3108 initflg = 0;
3112 /* The undo command calls this to restore the previous ptr array sav */
3113 /* and swap with cur - dot and dol are swapped also. This allows user to */
3114 /* undo an undo */
3117 static void
3118 undo(void) {
3119 int j;
3120 long tmp;
3121 LINE i, tmpdot, tmpdol;
3123 tmpdot = dot; dot = savdot; savdot = tmpdot;
3124 tmpdol = dol; dol = savdol; savdol = tmpdol;
3125 /* swap arrays using the greater of dol or savdol as upper limit */
3126 for (i = zero + 1; i <= ((dol > savdol) ? dol : savdol); i++) {
3127 tmp = i->cur;
3128 i->cur = i->sav;
3129 i->sav = tmp;
3132 * If the current text lines are swapped with the
3133 * text lines in the save buffer, then swap the current
3134 * marks with those in the save area.
3137 for (j = 0; j <= 25; j++) {
3138 tmp = names[j];
3139 names[j] = savnames[j];
3140 savnames[j] = tmp;
3144 static wchar_t
3145 get_wchr()
3147 wchar_t wc;
3148 char multi[MB_LEN_MAX];
3150 if (_mbftowc(multi, &wc, getchr, &peekc) <= 0)
3151 wc = getchr();
3152 return (wc);