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