|
|
1.1 root 1: /* Copyright (c) 1981 Regents of the University of California */
2: static char *sccsid = "@(#)ex_io.c 7.6 7/30/83";
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: /*
10: * File input/output, source, preserve and recover
11: */
12:
13: /*
14: * Following remember where . was in the previous file for return
15: * on file switching.
16: */
17: int altdot;
18: int oldadot;
19: bool wasalt;
20: short isalt;
21:
22: long cntch; /* Count of characters on unit io */
23: #ifndef VMUNIX
24: short cntln; /* Count of lines " */
25: #else
26: int cntln;
27: #endif
28: long cntnull; /* Count of nulls " */
29: long 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: */
36: filename(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);
47: wasalt = (isalt > 0) ? isalt-1 : 0;
48: isalt = 0;
49: oldadot = altdot;
50: if (c == 'e' || c == 'E')
51: altdot = lineDOT();
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') {
92: if (value(READONLY))
93: printf(" [Read only]");
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: */
114: getargs()
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 '\\':
148: if (any(peekchar(), "#%|"))
149: c = getchar();
150: /* fall into... */
151:
152: default:
153: if (cp > &genbuf[LBSIZE - 2])
154: flong:
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 %%");
169: filexp:
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: */
186: glob(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]);
228: close(2); /* so errors don't mess up the screen */
229: open("/dev/null", 1);
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)
260: error("No match");
261: }
262:
263: /*
264: * Scan genbuf for shell metacharacters.
265: * Set is union of v7 shell and csh metas.
266: */
267: gscan()
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: */
280: struct glob G;
281: getone()
282: {
283: register char *str;
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");
293: samef:
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: */
301: rop(c)
302: int c;
303: {
304: register int i;
305: struct stat stbuf;
306: short magic;
307: static int ovro; /* old value(READONLY) */
308: static int denied; /* 1 if READONLY was set due to file permissions */
309:
310: io = open(file, 0);
311: if (io < 0) {
312: if (c == 'e' && errno == ENOENT) {
313: edited++;
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: }
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:
345: #ifdef CRYPT
346: if (xflag)
347: break;
348: #endif
349: i = read(io, (char *) &magic, sizeof(magic));
350: lseek(io, 0l, 0);
351: if (i != sizeof(magic))
352: break;
353: switch (magic) {
354:
355: case 0405: /* data overlay on exec */
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 */
362: error(" Executable");
363:
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: */
372: case 0177545:
373: case 0177555:
374: error(" Archive");
375:
376: default:
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 */
382: if (magic & 0100200)
383: #endif
384: error(" Non-ascii file");
385: break;
386: }
387: }
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: }
398: }
399: if (value(READONLY)) {
400: printf(" [Read only]");
401: flush();
402: }
403: if (c == 'r')
404: setdot();
405: else
406: setall();
407: if (FIXUNDO && inopen && c == 'r')
408: undap1 = undap2 = dot + 1;
409: rop2();
410: rop3(c);
411: }
412:
413: rop2()
414: {
415: line *first, *last, *a;
416:
417: deletenone();
418: clrstats();
419: first = addr2 + 1;
420: ignore(append(getfile, addr2));
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: }
428: }
429:
430: rop3(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
453: other:
454: if (dol > zero) {
455: if (inopen)
456: dot = one;
457: markpr(one);
458: }
459: if(FIXUNDO)
460: undkind = UNDNONE;
461: if (inopen) {
462: vcline = 0;
463: vreplace(0, LINES, lineDOL());
464: }
465: }
466: }
467:
468: /*
469: * Are these two really the same inode?
470: */
471: samei(sp, cp)
472: struct stat *sp;
473: char *cp;
474: {
475: struct stat stb;
476:
477: if (stat(cp, &stb) < 0 || sp->st_dev != stb.st_dev)
478: return (0);
479: return (sp->st_ino == stb.st_ino);
480: }
481:
482: /* Returns from edited() */
483: #define EDF 0 /* Edited file */
484: #define NOTEDF -1 /* Not edited file */
485: #define PARTBUF 1 /* Write of partial buffer to Edited file */
486:
487: /*
488: * Write a file.
489: */
490: wop(dofname)
491: bool dofname; /* if 1 call filename, else use savedfile */
492: {
493: register int c, exclam, nonexist;
494: line *saddr1, *saddr2;
495: struct stat stbuf;
496:
497: c = 0;
498: exclam = 0;
499: if (dofname) {
500: if (peekchar() == '!')
501: exclam++, ignchar();
502: ignore(skipwh());
503: while (peekchar() == '>')
504: ignchar(), c++, ignore(skipwh());
505: if (c != 0 && c != 2)
506: error("Write forms are 'w' and 'w>>'");
507: filename('w');
508: } else {
509: if (savedfile[0] == 0)
510: error("No file|No current filename");
511: saddr1=addr1;
512: saddr2=addr2;
513: addr1=one;
514: addr2=dol;
515: CP(file, savedfile);
516: if (inopen) {
517: vclrech(0);
518: splitw++;
519: }
520: lprintf("\"%s\"", file);
521: }
522: nonexist = stat(file, &stbuf);
523: switch (c) {
524:
525: case 0:
526: if (!exclam && (!value(WRITEANY) || value(READONLY)))
527: switch (edfile()) {
528:
529: case NOTEDF:
530: if (nonexist)
531: break;
532: if ((stbuf.st_mode & S_IFMT) == S_IFCHR) {
533: if (samei(&stbuf, "/dev/null"))
534: break;
535: if (samei(&stbuf, "/dev/tty"))
536: break;
537: }
538: io = open(file, 1);
539: if (io < 0)
540: syserror();
541: if (!isatty(io))
542: serror(" File exists| File exists - use \"w! %s\" to overwrite", file);
543: close(io);
544: break;
545:
546: case EDF:
547: if (value(READONLY))
548: error(" File is read only");
549: break;
550:
551: case PARTBUF:
552: if (value(READONLY))
553: error(" File is read only");
554: error(" Use \"w!\" to write partial buffer");
555: }
556: cre:
557: /*
558: synctmp();
559: */
560: #ifdef V6
561: io = creat(file, 0644);
562: #else
563: io = creat(file, 0666);
564: #endif
565: if (io < 0)
566: syserror();
567: writing = 1;
568: if (hush == 0)
569: if (nonexist)
570: printf(" [New file]");
571: else if (value(WRITEANY) && edfile() != EDF)
572: printf(" [Existing file]");
573: break;
574:
575: case 2:
576: io = open(file, 1);
577: if (io < 0) {
578: if (exclam || value(WRITEANY))
579: goto cre;
580: syserror();
581: }
582: lseek(io, 0l, 2);
583: break;
584: }
585: putfile(0);
586: ignore(iostats());
587: if (c != 2 && addr1 == one && addr2 == dol) {
588: if (eq(file, savedfile))
589: edited = 1;
590: sync();
591: }
592: if (!dofname) {
593: addr1 = saddr1;
594: addr2 = saddr2;
595: }
596: writing = 0;
597: }
598:
599: /*
600: * Is file the edited file?
601: * Work here is that it is not considered edited
602: * if this is a partial buffer, and distinguish
603: * all cases.
604: */
605: edfile()
606: {
607:
608: if (!edited || !eq(file, savedfile))
609: return (NOTEDF);
610: return (addr1 == one && addr2 == dol ? EDF : PARTBUF);
611: }
612:
613: /*
614: * Extract the next line from the io stream.
615: */
616: char *nextip;
617:
618: getfile()
619: {
620: register short c;
621: register char *lp, *fp;
622:
623: lp = linebuf;
624: fp = nextip;
625: do {
626: if (--ninbuf < 0) {
627: ninbuf = read(io, genbuf, LBSIZE) - 1;
628: if (ninbuf < 0) {
629: if (lp != linebuf) {
630: lp++;
631: printf(" [Incomplete last line]");
632: break;
633: }
634: return (EOF);
635: }
636: #ifdef CRYPT
637: fp = genbuf;
638: while(fp < &genbuf[ninbuf]) {
639: if (*fp++ & 0200) {
640: if (kflag)
641: crblock(perm, genbuf, ninbuf+1,
642: cntch);
643: break;
644: }
645: }
646: #endif
647: fp = genbuf;
648: cntch += ninbuf+1;
649: }
650: if (lp >= &linebuf[LBSIZE]) {
651: error(" Line too long");
652: }
653: c = *fp++;
654: if (c == 0) {
655: cntnull++;
656: continue;
657: }
658: if (c & QUOTE) {
659: cntodd++;
660: c &= TRIM;
661: if (c == 0)
662: continue;
663: }
664: *lp++ = c;
665: } while (c != '\n');
666: *--lp = 0;
667: nextip = fp;
668: cntln++;
669: return (0);
670: }
671:
672: /*
673: * Write a range onto the io stream.
674: */
675: putfile(isfilter)
676: int isfilter;
677: {
678: line *a1;
679: register char *fp, *lp;
680: register int nib;
681:
682: a1 = addr1;
683: clrstats();
684: cntln = addr2 - a1 + 1;
685: if (cntln == 0)
686: return;
687: nib = BUFSIZ;
688: fp = genbuf;
689: do {
690: getline(*a1++);
691: lp = linebuf;
692: for (;;) {
693: if (--nib < 0) {
694: nib = fp - genbuf;
695: #ifdef CRYPT
696: if(kflag && !isfilter)
697: crblock(perm, genbuf, nib, cntch);
698: #endif
699: if (write(io, genbuf, nib) != nib) {
700: wrerror();
701: }
702: cntch += nib;
703: nib = BUFSIZ - 1;
704: fp = genbuf;
705: }
706: if ((*fp++ = *lp++) == 0) {
707: fp[-1] = '\n';
708: break;
709: }
710: }
711: } while (a1 <= addr2);
712: nib = fp - genbuf;
713: #ifdef CRYPT
714: if(kflag && !isfilter)
715: crblock(perm, genbuf, nib, cntch);
716: #endif
717: if (write(io, genbuf, nib) != nib) {
718: wrerror();
719: }
720: cntch += nib;
721: }
722:
723: /*
724: * A write error has occurred; if the file being written was
725: * the edited file then we consider it to have changed since it is
726: * now likely scrambled.
727: */
728: wrerror()
729: {
730:
731: if (eq(file, savedfile) && edited)
732: change();
733: syserror();
734: }
735:
736: /*
737: * Source command, handles nested sources.
738: * Traps errors since it mungs unit 0 during the source.
739: */
740: short slevel;
741: short ttyindes;
742:
743: source(fil, okfail)
744: char *fil;
745: bool okfail;
746: {
747: jmp_buf osetexit;
748: register int saveinp, ointty, oerrno;
749: char *saveglobp;
750: short savepeekc;
751:
752: signal(SIGINT, SIG_IGN);
753: saveinp = dup(0);
754: savepeekc = peekc;
755: saveglobp = globp;
756: peekc = 0; globp = 0;
757: if (saveinp < 0)
758: error("Too many nested sources");
759: if (slevel <= 0)
760: ttyindes = saveinp;
761: close(0);
762: if (open(fil, 0) < 0) {
763: oerrno = errno;
764: setrupt();
765: dup(saveinp);
766: close(saveinp);
767: errno = oerrno;
768: if (!okfail)
769: filioerr(fil);
770: return;
771: }
772: slevel++;
773: ointty = intty;
774: intty = isatty(0);
775: oprompt = value(PROMPT);
776: value(PROMPT) &= intty;
777: getexit(osetexit);
778: setrupt();
779: if (setexit() == 0)
780: commands(1, 1);
781: else if (slevel > 1) {
782: close(0);
783: dup(saveinp);
784: close(saveinp);
785: slevel--;
786: resexit(osetexit);
787: reset();
788: }
789: intty = ointty;
790: value(PROMPT) = oprompt;
791: close(0);
792: dup(saveinp);
793: close(saveinp);
794: globp = saveglobp;
795: peekc = savepeekc;
796: slevel--;
797: resexit(osetexit);
798: }
799:
800: /*
801: * Clear io statistics before a read or write.
802: */
803: clrstats()
804: {
805:
806: ninbuf = 0;
807: cntch = 0;
808: cntln = 0;
809: cntnull = 0;
810: cntodd = 0;
811: }
812:
813: /*
814: * Io is finished, close the unit and print statistics.
815: */
816: iostats()
817: {
818:
819: (void) fsync(io);
820: close(io);
821: io = -1;
822: if (hush == 0) {
823: if (value(TERSE))
824: printf(" %d/%D", cntln, cntch);
825: else
826: printf(" %d line%s, %D character%s", cntln, plural((long) cntln),
827: cntch, plural(cntch));
828: if (cntnull || cntodd) {
829: printf(" (");
830: if (cntnull) {
831: printf("%D null", cntnull);
832: if (cntodd)
833: printf(", ");
834: }
835: if (cntodd)
836: printf("%D non-ASCII", cntodd);
837: putchar(')');
838: }
839: noonl();
840: flush();
841: }
842: return (cntnull != 0 || cntodd != 0);
843: }
844:
845: #if USG | USG3TTY
846: /* It's so wonderful how we all speak the same language... */
847: # define index strchr
848: # define rindex strrchr
849: #endif
850:
851: checkmodeline(line)
852: char *line;
853: {
854: char *beg, *end;
855: char cmdbuf[1024];
856: char *index(), *rindex();
857:
858: beg = index(line, ':');
859: if (beg == NULL)
860: return;
861: if (beg[-2] != 'e' && beg[-2] != 'v') return;
862: if (beg[-1] != 'x' && beg[-1] != 'i') return;
863:
864: strncpy(cmdbuf, beg+1, sizeof cmdbuf);
865: end = rindex(cmdbuf, ':');
866: if (end == NULL)
867: return;
868: *end = 0;
869: globp = cmdbuf;
870: commands(1, 1);
871: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.