date and time created 83/02/11 15:44:54 by rrh
[unix-history] / usr / src / usr.bin / ex / ex_io.c
CommitLineData
299f2784 1/* Copyright (c) 1981 Regents of the University of California */
b8c3ed58 2static char *sccsid = "@(#)ex_io.c 7.4 %G%";
544cd46c
MH
3#include "ex.h"
4#include "ex_argv.h"
5#include "ex_temp.h"
6#include "ex_tty.h"
7#include "ex_vis.h"
8
9/*
d266c416 10 * File input/output, source, preserve and recover
544cd46c
MH
11 */
12
13/*
14 * Following remember where . was in the previous file for return
15 * on file switching.
16 */
44232d5b
MH
17int altdot;
18int oldadot;
544cd46c 19bool wasalt;
d266c416 20short isalt;
544cd46c
MH
21
22long cntch; /* Count of characters on unit io */
44232d5b 23#ifndef VMUNIX
544cd46c 24short cntln; /* Count of lines " */
44232d5b
MH
25#else
26int cntln;
27#endif
544cd46c
MH
28long cntnull; /* Count of nulls " */
29long cntodd; /* Count of non-ascii characters " */
30
31/*
32 * Parse file name for command encoded by comm.
33 * If comm is E then command is doomed and we are
34 * parsing just so user won't have to retype the name.
35 */
36filename(comm)
37 int comm;
38{
39 register int c = comm, d;
40 register int i;
41
42 d = getchar();
43 if (endcmd(d)) {
44 if (savedfile[0] == 0 && comm != 'f')
45 error("No file|No current filename");
46 CP(file, savedfile);
d266c416
MH
47 wasalt = (isalt > 0) ? isalt-1 : 0;
48 isalt = 0;
544cd46c 49 oldadot = altdot;
d266c416
MH
50 if (c == 'e' || c == 'E')
51 altdot = lineDOT();
544cd46c
MH
52 if (d == EOF)
53 ungetchar(d);
54 } else {
55 ungetchar(d);
56 getone();
57 eol();
58 if (savedfile[0] == 0 && c != 'E' && c != 'e') {
59 c = 'e';
60 edited = 0;
61 }
62 wasalt = strcmp(file, altfile) == 0;
63 oldadot = altdot;
64 switch (c) {
65
66 case 'f':
67 edited = 0;
68 /* fall into ... */
69
70 case 'e':
71 if (savedfile[0]) {
72 altdot = lineDOT();
73 CP(altfile, savedfile);
74 }
75 CP(savedfile, file);
76 break;
77
78 default:
79 if (file[0]) {
80 if (c != 'E')
81 altdot = lineDOT();
82 CP(altfile, file);
83 }
84 break;
85 }
86 }
87 if (hush && comm != 'f' || comm == 'E')
88 return;
89 if (file[0] != 0) {
90 lprintf("\"%s\"", file);
91 if (comm == 'f') {
d266c416
MH
92 if (value(READONLY))
93 printf(" [Read only]");
544cd46c
MH
94 if (!edited)
95 printf(" [Not edited]");
96 if (tchng)
97 printf(" [Modified]");
98 }
99 flush();
100 } else
101 printf("No file ");
102 if (comm == 'f') {
103 if (!(i = lineDOL()))
104 i++;
105 printf(" line %d of %d --%ld%%--", lineDOT(), lineDOL(),
106 (long) 100 * lineDOT() / i);
107 }
108}
109
110/*
111 * Get the argument words for a command into genbuf
112 * expanding # and %.
113 */
114getargs()
115{
116 register int c;
117 register char *cp, *fp;
118 static char fpatbuf[32]; /* hence limit on :next +/pat */
119
120 pastwh();
121 if (peekchar() == '+') {
122 for (cp = fpatbuf;;) {
123 c = *cp++ = getchar();
124 if (cp >= &fpatbuf[sizeof(fpatbuf)])
125 error("Pattern too long");
126 if (c == '\\' && isspace(peekchar()))
127 c = getchar();
128 if (c == EOF || isspace(c)) {
129 ungetchar(c);
130 *--cp = 0;
131 firstpat = &fpatbuf[1];
132 break;
133 }
134 }
135 }
136 if (skipend())
137 return (0);
138 CP(genbuf, "echo "); cp = &genbuf[5];
139 for (;;) {
140 c = getchar();
141 if (endcmd(c)) {
142 ungetchar(c);
143 break;
144 }
145 switch (c) {
146
147 case '\\':
d266c416 148 if (any(peekchar(), "#%|"))
544cd46c
MH
149 c = getchar();
150 /* fall into... */
151
152 default:
153 if (cp > &genbuf[LBSIZE - 2])
154flong:
155 error("Argument buffer overflow");
156 *cp++ = c;
157 break;
158
159 case '#':
160 fp = altfile;
161 if (*fp == 0)
162 error("No alternate filename@to substitute for #");
163 goto filexp;
164
165 case '%':
166 fp = savedfile;
167 if (*fp == 0)
168 error("No current filename@to substitute for %%");
169filexp:
170 while (*fp) {
171 if (cp > &genbuf[LBSIZE - 2])
172 goto flong;
173 *cp++ = *fp++;
174 }
175 break;
176 }
177 }
178 *cp = 0;
179 return (1);
180}
181
182/*
183 * Glob the argument words in genbuf, or if no globbing
184 * is implied, just split them up directly.
185 */
186glob(gp)
187 struct glob *gp;
188{
189 int pvec[2];
190 register char **argv = gp->argv;
191 register char *cp = gp->argspac;
192 register int c;
193 char ch;
194 int nleft = NCARGS;
195
196 gp->argc0 = 0;
197 if (gscan() == 0) {
198 register char *v = genbuf + 5; /* strlen("echo ") */
199
200 for (;;) {
201 while (isspace(*v))
202 v++;
203 if (!*v)
204 break;
205 *argv++ = cp;
206 while (*v && !isspace(*v))
207 *cp++ = *v++;
208 *cp++ = 0;
209 gp->argc0++;
210 }
211 *argv = 0;
212 return;
213 }
214 if (pipe(pvec) < 0)
215 error("Can't make pipe to glob");
216 pid = fork();
217 io = pvec[0];
218 if (pid < 0) {
219 close(pvec[1]);
220 error("Can't fork to do glob");
221 }
222 if (pid == 0) {
223 int oerrno;
224
225 close(1);
226 dup(pvec[1]);
227 close(pvec[0]);
887e3e0d
MH
228 close(2); /* so errors don't mess up the screen */
229 open("/dev/null", 1);
544cd46c
MH
230 execl(svalue(SHELL), "sh", "-c", genbuf, 0);
231 oerrno = errno; close(1); dup(2); errno = oerrno;
232 filioerr(svalue(SHELL));
233 }
234 close(pvec[1]);
235 do {
236 *argv = cp;
237 for (;;) {
238 if (read(io, &ch, 1) != 1) {
239 close(io);
240 c = -1;
241 } else
242 c = ch & TRIM;
243 if (c <= 0 || isspace(c))
244 break;
245 *cp++ = c;
246 if (--nleft <= 0)
247 error("Arg list too long");
248 }
249 if (cp != *argv) {
250 --nleft;
251 *cp++ = 0;
252 gp->argc0++;
253 if (gp->argc0 >= NARGS)
254 error("Arg list too long");
255 argv++;
256 }
257 } while (c >= 0);
258 waitfor();
259 if (gp->argc0 == 0)
887e3e0d 260 error("No match");
544cd46c
MH
261}
262
263/*
264 * Scan genbuf for shell metacharacters.
265 * Set is union of v7 shell and csh metas.
266 */
267gscan()
268{
269 register char *cp;
270
271 for (cp = genbuf; *cp; cp++)
272 if (any(*cp, "~{[*?$`'\"\\"))
273 return (1);
274 return (0);
275}
276
277/*
278 * Parse one filename into file.
279 */
3c7b865a 280struct glob G;
544cd46c
MH
281getone()
282{
283 register char *str;
544cd46c
MH
284
285 if (getargs() == 0)
286 error("Missing filename");
287 glob(&G);
288 if (G.argc0 > 1)
289 error("Ambiguous|Too many file names");
290 str = G.argv[G.argc0 - 1];
291 if (strlen(str) > FNSIZE - 4)
292 error("Filename too long");
293samef:
294 CP(file, str);
295}
296
297/*
298 * Read a file from the world.
299 * C is command, 'e' if this really an edit (or a recover).
300 */
301rop(c)
302 int c;
303{
304 register int i;
305 struct stat stbuf;
306 short magic;
dcbc1067
MH
307 static int ovro; /* old value(READONLY) */
308 static int denied; /* 1 if READONLY was set due to file permissions */
544cd46c
MH
309
310 io = open(file, 0);
311 if (io < 0) {
04379bab 312 if (c == 'e' && errno == ENOENT) {
544cd46c 313 edited++;
04379bab
MH
314 /*
315 * If the user just did "ex foo" he is probably
316 * creating a new file. Don't be an error, since
317 * this is ugly, and it screws up the + option.
318 */
319 if (!seenprompt) {
320 printf(" [New file]");
321 noonl();
322 return;
323 }
324 }
544cd46c
MH
325 syserror();
326 }
327 if (fstat(io, &stbuf))
328 syserror();
329 switch (stbuf.st_mode & S_IFMT) {
330
331 case S_IFBLK:
332 error(" Block special file");
333
334 case S_IFCHR:
335 if (isatty(io))
336 error(" Teletype");
337 if (samei(&stbuf, "/dev/null"))
338 break;
339 error(" Character special file");
340
341 case S_IFDIR:
342 error(" Directory");
343
344 case S_IFREG:
1807bf33 345#ifdef CRYPT
d266c416
MH
346 if (xflag)
347 break;
1807bf33 348#endif
544cd46c
MH
349 i = read(io, (char *) &magic, sizeof(magic));
350 lseek(io, 0l, 0);
351 if (i != sizeof(magic))
352 break;
353 switch (magic) {
354
299f2784 355 case 0405: /* data overlay on exec */
f0f2d980
MH
356 case 0407: /* unshared */
357 case 0410: /* shared text */
358 case 0411: /* separate I/D */
359 case 0413: /* VM/Unix demand paged */
360 case 0430: /* PDP-11 Overlay shared */
361 case 0431: /* PDP-11 Overlay sep I/D */
544cd46c
MH
362 error(" Executable");
363
f0f2d980
MH
364 /*
365 * We do not forbid the editing of portable archives
366 * because it is reasonable to edit them, especially
367 * if they are archives of text files. This is
368 * especially useful if you archive source files together
369 * and copy them to another system with ~%take, since
370 * the files sometimes show up munged and must be fixed.
371 */
544cd46c
MH
372 case 0177545:
373 case 0177555:
374 error(" Archive");
375
376 default:
52fd6c9c
MH
377#ifdef mbb
378 /* C/70 has a 10 bit byte */
379 if (magic & 03401600)
380#else
381 /* Everybody else has an 8 bit byte */
544cd46c 382 if (magic & 0100200)
52fd6c9c 383#endif
544cd46c
MH
384 error(" Non-ascii file");
385 break;
386 }
387 }
04379bab
MH
388 if (c != 'r') {
389 if (value(READONLY) && denied) {
390 value(READONLY) = ovro;
391 denied = 0;
392 }
393 if ((stbuf.st_mode & 0222) == 0 || access(file, 2) < 0) {
394 ovro = value(READONLY);
395 denied = 1;
396 value(READONLY) = 1;
397 }
dcbc1067 398 }
fdc664ec 399 if (value(READONLY)) {
718337ed 400 printf(" [Read only]");
fdc664ec
MH
401 flush();
402 }
544cd46c
MH
403 if (c == 'r')
404 setdot();
405 else
406 setall();
887e3e0d 407 if (FIXUNDO && inopen && c == 'r')
544cd46c
MH
408 undap1 = undap2 = dot + 1;
409 rop2();
410 rop3(c);
411}
412
413rop2()
414{
299f2784 415 line *first, *last, *a;
544cd46c
MH
416
417 deletenone();
418 clrstats();
299f2784 419 first = addr2 + 1;
544cd46c 420 ignore(append(getfile, addr2));
299f2784
MH
421 last = dot;
422 for (a=first; a<=last; a++) {
423 if (a==first+5 && last-first > 10)
424 a = last - 4;
425 getline(*a);
426 checkmodeline(linebuf);
427 }
544cd46c
MH
428}
429
430rop3(c)
431 int c;
432{
433
434 if (iostats() == 0 && c == 'e')
435 edited++;
436 if (c == 'e') {
437 if (wasalt || firstpat) {
438 register line *addr = zero + oldadot;
439
440 if (addr > dol)
441 addr = dol;
442 if (firstpat) {
443 globp = (*firstpat) ? firstpat : "$";
444 commands(1,1);
445 firstpat = 0;
446 } else if (addr >= one) {
447 if (inopen)
448 dot = addr;
449 markpr(addr);
450 } else
451 goto other;
452 } else
453other:
454 if (dol > zero) {
455 if (inopen)
456 dot = one;
457 markpr(one);
458 }
887e3e0d
MH
459 if(FIXUNDO)
460 undkind = UNDNONE;
544cd46c
MH
461 if (inopen) {
462 vcline = 0;
463 vreplace(0, LINES, lineDOL());
464 }
465 }
466 if (laste) {
7c4625ef
MH
467#ifdef VMUNIX
468 tlaste();
469#endif
544cd46c
MH
470 laste = 0;
471 sync();
472 }
473}
474
475/*
476 * Are these two really the same inode?
477 */
478samei(sp, cp)
479 struct stat *sp;
480 char *cp;
481{
482 struct stat stb;
483
484 if (stat(cp, &stb) < 0 || sp->st_dev != stb.st_dev)
485 return (0);
486 return (sp->st_ino == stb.st_ino);
487}
488
489/* Returns from edited() */
490#define EDF 0 /* Edited file */
491#define NOTEDF -1 /* Not edited file */
492#define PARTBUF 1 /* Write of partial buffer to Edited file */
493
494/*
495 * Write a file.
496 */
497wop(dofname)
498bool dofname; /* if 1 call filename, else use savedfile */
499{
500 register int c, exclam, nonexist;
501 line *saddr1, *saddr2;
502 struct stat stbuf;
503
504 c = 0;
505 exclam = 0;
506 if (dofname) {
507 if (peekchar() == '!')
508 exclam++, ignchar();
509 ignore(skipwh());
510 while (peekchar() == '>')
511 ignchar(), c++, ignore(skipwh());
512 if (c != 0 && c != 2)
513 error("Write forms are 'w' and 'w>>'");
514 filename('w');
515 } else {
887e3e0d
MH
516 if (savedfile[0] == 0)
517 error("No file|No current filename");
544cd46c
MH
518 saddr1=addr1;
519 saddr2=addr2;
520 addr1=one;
521 addr2=dol;
522 CP(file, savedfile);
523 if (inopen) {
524 vclrech(0);
525 splitw++;
526 }
527 lprintf("\"%s\"", file);
528 }
529 nonexist = stat(file, &stbuf);
530 switch (c) {
531
532 case 0:
d266c416
MH
533 if (!exclam && (!value(WRITEANY) || value(READONLY)))
534 switch (edfile()) {
544cd46c
MH
535
536 case NOTEDF:
537 if (nonexist)
538 break;
539 if ((stbuf.st_mode & S_IFMT) == S_IFCHR) {
540 if (samei(&stbuf, "/dev/null"))
541 break;
542 if (samei(&stbuf, "/dev/tty"))
543 break;
544 }
545 io = open(file, 1);
546 if (io < 0)
547 syserror();
548 if (!isatty(io))
549 serror(" File exists| File exists - use \"w! %s\" to overwrite", file);
550 close(io);
551 break;
552
d266c416
MH
553 case EDF:
554 if (value(READONLY))
555 error(" File is read only");
556 break;
557
544cd46c 558 case PARTBUF:
d266c416
MH
559 if (value(READONLY))
560 error(" File is read only");
544cd46c
MH
561 error(" Use \"w!\" to write partial buffer");
562 }
563cre:
44232d5b 564/*
544cd46c 565 synctmp();
44232d5b 566*/
544cd46c
MH
567#ifdef V6
568 io = creat(file, 0644);
569#else
570 io = creat(file, 0666);
571#endif
572 if (io < 0)
573 syserror();
04379bab 574 writing = 1;
544cd46c
MH
575 if (hush == 0)
576 if (nonexist)
577 printf(" [New file]");
578 else if (value(WRITEANY) && edfile() != EDF)
579 printf(" [Existing file]");
580 break;
581
582 case 2:
583 io = open(file, 1);
584 if (io < 0) {
585 if (exclam || value(WRITEANY))
586 goto cre;
587 syserror();
588 }
589 lseek(io, 0l, 2);
590 break;
591 }
299f2784 592 putfile(0);
544cd46c
MH
593 ignore(iostats());
594 if (c != 2 && addr1 == one && addr2 == dol) {
595 if (eq(file, savedfile))
596 edited = 1;
597 sync();
598 }
599 if (!dofname) {
600 addr1 = saddr1;
601 addr2 = saddr2;
602 }
04379bab 603 writing = 0;
544cd46c
MH
604}
605
606/*
607 * Is file the edited file?
608 * Work here is that it is not considered edited
609 * if this is a partial buffer, and distinguish
610 * all cases.
611 */
612edfile()
613{
614
615 if (!edited || !eq(file, savedfile))
616 return (NOTEDF);
617 return (addr1 == one && addr2 == dol ? EDF : PARTBUF);
618}
619
544cd46c
MH
620/*
621 * Extract the next line from the io stream.
622 */
3c7b865a 623char *nextip;
544cd46c
MH
624
625getfile()
626{
627 register short c;
628 register char *lp, *fp;
629
630 lp = linebuf;
631 fp = nextip;
632 do {
633 if (--ninbuf < 0) {
634 ninbuf = read(io, genbuf, LBSIZE) - 1;
635 if (ninbuf < 0) {
636 if (lp != linebuf) {
d266c416 637 lp++;
544cd46c
MH
638 printf(" [Incomplete last line]");
639 break;
640 }
641 return (EOF);
642 }
1807bf33 643#ifdef CRYPT
544cd46c 644 fp = genbuf;
d266c416
MH
645 while(fp < &genbuf[ninbuf]) {
646 if (*fp++ & 0200) {
647 if (kflag)
648 crblock(perm, genbuf, ninbuf+1,
649cntch);
650 break;
651 }
652 }
1807bf33 653#endif
d266c416
MH
654 fp = genbuf;
655 cntch += ninbuf+1;
544cd46c
MH
656 }
657 if (lp >= &linebuf[LBSIZE]) {
658 error(" Line too long");
659 }
660 c = *fp++;
661 if (c == 0) {
662 cntnull++;
663 continue;
664 }
665 if (c & QUOTE) {
666 cntodd++;
667 c &= TRIM;
668 if (c == 0)
669 continue;
670 }
671 *lp++ = c;
672 } while (c != '\n');
544cd46c
MH
673 *--lp = 0;
674 nextip = fp;
675 cntln++;
676 return (0);
677}
678
679/*
680 * Write a range onto the io stream.
681 */
299f2784
MH
682putfile(isfilter)
683int isfilter;
544cd46c
MH
684{
685 line *a1;
686 register char *fp, *lp;
687 register int nib;
688
689 a1 = addr1;
690 clrstats();
691 cntln = addr2 - a1 + 1;
692 if (cntln == 0)
693 return;
694 nib = BUFSIZ;
695 fp = genbuf;
696 do {
697 getline(*a1++);
698 lp = linebuf;
699 for (;;) {
700 if (--nib < 0) {
701 nib = fp - genbuf;
1807bf33 702#ifdef CRYPT
299f2784 703 if(kflag && !isfilter)
d266c416 704 crblock(perm, genbuf, nib, cntch);
1807bf33 705#endif
544cd46c
MH
706 if (write(io, genbuf, nib) != nib) {
707 wrerror();
708 }
709 cntch += nib;
710 nib = BUFSIZ - 1;
711 fp = genbuf;
712 }
713 if ((*fp++ = *lp++) == 0) {
714 fp[-1] = '\n';
715 break;
716 }
717 }
718 } while (a1 <= addr2);
719 nib = fp - genbuf;
1807bf33 720#ifdef CRYPT
299f2784 721 if(kflag && !isfilter)
d266c416 722 crblock(perm, genbuf, nib, cntch);
1807bf33 723#endif
544cd46c
MH
724 if (write(io, genbuf, nib) != nib) {
725 wrerror();
726 }
727 cntch += nib;
728}
729
730/*
731 * A write error has occurred; if the file being written was
732 * the edited file then we consider it to have changed since it is
733 * now likely scrambled.
734 */
735wrerror()
736{
737
738 if (eq(file, savedfile) && edited)
739 change();
740 syserror();
741}
742
743/*
744 * Source command, handles nested sources.
745 * Traps errors since it mungs unit 0 during the source.
746 */
d266c416
MH
747short slevel;
748short ttyindes;
544cd46c
MH
749
750source(fil, okfail)
751 char *fil;
752 bool okfail;
753{
754 jmp_buf osetexit;
755 register int saveinp, ointty, oerrno;
b8c3ed58
MH
756 char *saveglobp;
757 short savepeekc;
544cd46c
MH
758
759 signal(SIGINT, SIG_IGN);
760 saveinp = dup(0);
04379bab
MH
761 savepeekc = peekc;
762 saveglobp = globp;
763 peekc = 0; globp = 0;
544cd46c
MH
764 if (saveinp < 0)
765 error("Too many nested sources");
d266c416
MH
766 if (slevel <= 0)
767 ttyindes = saveinp;
544cd46c
MH
768 close(0);
769 if (open(fil, 0) < 0) {
770 oerrno = errno;
771 setrupt();
772 dup(saveinp);
773 close(saveinp);
774 errno = oerrno;
775 if (!okfail)
776 filioerr(fil);
777 return;
778 }
779 slevel++;
780 ointty = intty;
781 intty = isatty(0);
782 oprompt = value(PROMPT);
783 value(PROMPT) &= intty;
784 getexit(osetexit);
785 setrupt();
786 if (setexit() == 0)
787 commands(1, 1);
788 else if (slevel > 1) {
789 close(0);
790 dup(saveinp);
791 close(saveinp);
792 slevel--;
793 resexit(osetexit);
794 reset();
795 }
796 intty = ointty;
797 value(PROMPT) = oprompt;
798 close(0);
799 dup(saveinp);
800 close(saveinp);
04379bab
MH
801 globp = saveglobp;
802 peekc = savepeekc;
544cd46c
MH
803 slevel--;
804 resexit(osetexit);
805}
806
807/*
808 * Clear io statistics before a read or write.
809 */
810clrstats()
811{
812
813 ninbuf = 0;
814 cntch = 0;
815 cntln = 0;
816 cntnull = 0;
817 cntodd = 0;
818}
819
820/*
821 * Io is finished, close the unit and print statistics.
822 */
823iostats()
824{
825
826 close(io);
827 io = -1;
828 if (hush == 0) {
829 if (value(TERSE))
830 printf(" %d/%D", cntln, cntch);
831 else
832 printf(" %d line%s, %D character%s", cntln, plural((long) cntln),
833 cntch, plural(cntch));
834 if (cntnull || cntodd) {
835 printf(" (");
836 if (cntnull) {
837 printf("%D null", cntnull);
838 if (cntodd)
839 printf(", ");
840 }
841 if (cntodd)
842 printf("%D non-ASCII", cntodd);
843 putchar(')');
844 }
845 noonl();
846 flush();
847 }
848 return (cntnull != 0 || cntodd != 0);
849}
299f2784
MH
850
851#if USG | USG3TTY
852/* It's so wonderful how we all speak the same language... */
853# define index strchr
854# define rindex strrchr
855#endif
856
857checkmodeline(line)
858char *line;
859{
860 char *beg, *end;
861 char cmdbuf[1024];
862 char *index(), *rindex();
863
864 beg = index(line, ':');
865 if (beg == NULL)
866 return;
867 if (beg[-2] != 'e' && beg[-2] != 'v') return;
868 if (beg[-1] != 'x' && beg[-1] != 'i') return;
869
870 strncpy(cmdbuf, beg+1, sizeof cmdbuf);
871 end = rindex(cmdbuf, ':');
872 if (end == NULL)
873 return;
874 *end = 0;
875 globp = cmdbuf;
876 commands(1, 1);
877}