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