|
|
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: #ifdef SUICIDE /* and it wasnt even an option! */
423: for (a=first; a<=last; a++) {
424: if (a==first+5 && last-first > 10)
425: a = last - 4;
426: getline(*a);
427: checkmodeline(linebuf);
428: }
429: #endif
430: }
431:
432: rop3(c)
433: int c;
434: {
435:
436: if (iostats() == 0 && c == 'e')
437: edited++;
438: if (c == 'e') {
439: if (wasalt || firstpat) {
440: register line *addr = zero + oldadot;
441:
442: if (addr > dol)
443: addr = dol;
444: if (firstpat) {
445: globp = (*firstpat) ? firstpat : "$";
446: commands(1,1);
447: firstpat = 0;
448: } else if (addr >= one) {
449: if (inopen)
450: dot = addr;
451: markpr(addr);
452: } else
453: goto other;
454: } else
455: other:
456: if (dol > zero) {
457: if (inopen)
458: dot = one;
459: markpr(one);
460: }
461: if(FIXUNDO)
462: undkind = UNDNONE;
463: if (inopen) {
464: vcline = 0;
465: vreplace(0, LINES, lineDOL());
466: }
467: }
468: }
469:
470: /*
471: * Are these two really the same inode?
472: */
473: samei(sp, cp)
474: struct stat *sp;
475: char *cp;
476: {
477: struct stat stb;
478:
479: if (stat(cp, &stb) < 0 || sp->st_dev != stb.st_dev)
480: return (0);
481: return (sp->st_ino == stb.st_ino);
482: }
483:
484: /* Returns from edited() */
485: #define EDF 0 /* Edited file */
486: #define NOTEDF -1 /* Not edited file */
487: #define PARTBUF 1 /* Write of partial buffer to Edited file */
488:
489: /*
490: * Write a file.
491: */
492: wop(dofname)
493: bool dofname; /* if 1 call filename, else use savedfile */
494: {
495: register int c, exclam, nonexist;
496: line *saddr1, *saddr2;
497: struct stat stbuf;
498:
499: c = 0;
500: exclam = 0;
501: if (dofname) {
502: if (peekchar() == '!')
503: exclam++, ignchar();
504: ignore(skipwh());
505: while (peekchar() == '>')
506: ignchar(), c++, ignore(skipwh());
507: if (c != 0 && c != 2)
508: error("Write forms are 'w' and 'w>>'");
509: filename('w');
510: } else {
511: if (savedfile[0] == 0)
512: error("No file|No current filename");
513: saddr1=addr1;
514: saddr2=addr2;
515: addr1=one;
516: addr2=dol;
517: CP(file, savedfile);
518: if (inopen) {
519: vclrech(0);
520: splitw++;
521: }
522: lprintf("\"%s\"", file);
523: }
524: nonexist = stat(file, &stbuf);
525: switch (c) {
526:
527: case 0:
528: if (!exclam && (!value(WRITEANY) || value(READONLY)))
529: switch (edfile()) {
530:
531: case NOTEDF:
532: if (nonexist)
533: break;
534: if ((stbuf.st_mode & S_IFMT) == S_IFCHR) {
535: if (samei(&stbuf, "/dev/null"))
536: break;
537: if (samei(&stbuf, "/dev/tty"))
538: break;
539: }
540: io = open(file, 1);
541: if (io < 0)
542: syserror();
543: if (!isatty(io))
544: serror(" File exists| File exists - use \"w! %s\" to overwrite", file);
545: close(io);
546: break;
547:
548: case EDF:
549: if (value(READONLY))
550: error(" File is read only");
551: break;
552:
553: case PARTBUF:
554: if (value(READONLY))
555: error(" File is read only");
556: error(" Use \"w!\" to write partial buffer");
557: }
558: cre:
559: /*
560: synctmp();
561: */
562: #ifdef V6
563: io = creat(file, 0644);
564: #else
565: io = creat(file, 0666);
566: #endif
567: if (io < 0)
568: syserror();
569: writing = 1;
570: if (hush == 0)
571: if (nonexist)
572: printf(" [New file]");
573: else if (value(WRITEANY) && edfile() != EDF)
574: printf(" [Existing file]");
575: break;
576:
577: case 2:
578: io = open(file, 1);
579: if (io < 0) {
580: if (exclam || value(WRITEANY))
581: goto cre;
582: syserror();
583: }
584: lseek(io, 0l, 2);
585: break;
586: }
587: putfile(0);
588: ignore(iostats());
589: if (c != 2 && addr1 == one && addr2 == dol) {
590: if (eq(file, savedfile))
591: edited = 1;
592: sync();
593: }
594: if (!dofname) {
595: addr1 = saddr1;
596: addr2 = saddr2;
597: }
598: writing = 0;
599: }
600:
601: /*
602: * Is file the edited file?
603: * Work here is that it is not considered edited
604: * if this is a partial buffer, and distinguish
605: * all cases.
606: */
607: edfile()
608: {
609:
610: if (!edited || !eq(file, savedfile))
611: return (NOTEDF);
612: return (addr1 == one && addr2 == dol ? EDF : PARTBUF);
613: }
614:
615: /*
616: * Extract the next line from the io stream.
617: */
618: char *nextip;
619:
620: getfile()
621: {
622: register short c;
623: register char *lp, *fp;
624:
625: lp = linebuf;
626: fp = nextip;
627: do {
628: if (--ninbuf < 0) {
629: ninbuf = read(io, genbuf, LBSIZE) - 1;
630: if (ninbuf < 0) {
631: if (lp != linebuf) {
632: lp++;
633: printf(" [Incomplete last line]");
634: break;
635: }
636: return (EOF);
637: }
638: #ifdef CRYPT
639: fp = genbuf;
640: while(fp < &genbuf[ninbuf]) {
641: if (*fp++ & 0200) {
642: if (kflag)
643: crblock(perm, genbuf, ninbuf+1,
644: cntch);
645: break;
646: }
647: }
648: #endif
649: fp = genbuf;
650: cntch += ninbuf+1;
651: }
652: if (lp >= &linebuf[LBSIZE]) {
653: error(" Line too long");
654: }
655: c = *fp++;
656: if (c == 0) {
657: cntnull++;
658: continue;
659: }
660: if (c & QUOTE) {
661: cntodd++;
662: c &= TRIM;
663: if (c == 0)
664: continue;
665: }
666: *lp++ = c;
667: } while (c != '\n');
668: *--lp = 0;
669: nextip = fp;
670: cntln++;
671: return (0);
672: }
673:
674: /*
675: * Write a range onto the io stream.
676: */
677: putfile(isfilter)
678: int isfilter;
679: {
680: line *a1;
681: register char *fp, *lp;
682: register int nib;
683:
684: a1 = addr1;
685: clrstats();
686: cntln = addr2 - a1 + 1;
687: if (cntln == 0)
688: return;
689: nib = BUFSIZ;
690: fp = genbuf;
691: do {
692: getline(*a1++);
693: lp = linebuf;
694: for (;;) {
695: if (--nib < 0) {
696: nib = fp - genbuf;
697: #ifdef CRYPT
698: if(kflag && !isfilter)
699: crblock(perm, genbuf, nib, cntch);
700: #endif
701: if (write(io, genbuf, nib) != nib) {
702: wrerror();
703: }
704: cntch += nib;
705: nib = BUFSIZ - 1;
706: fp = genbuf;
707: }
708: if ((*fp++ = *lp++) == 0) {
709: fp[-1] = '\n';
710: break;
711: }
712: }
713: } while (a1 <= addr2);
714: nib = fp - genbuf;
715: #ifdef CRYPT
716: if(kflag && !isfilter)
717: crblock(perm, genbuf, nib, cntch);
718: #endif
719: if (write(io, genbuf, nib) != nib) {
720: wrerror();
721: }
722: cntch += nib;
723: }
724:
725: /*
726: * A write error has occurred; if the file being written was
727: * the edited file then we consider it to have changed since it is
728: * now likely scrambled.
729: */
730: wrerror()
731: {
732:
733: if (eq(file, savedfile) && edited)
734: change();
735: syserror();
736: }
737:
738: /*
739: * Source command, handles nested sources.
740: * Traps errors since it mungs unit 0 during the source.
741: */
742: short slevel;
743: short ttyindes;
744:
745: source(fil, okfail)
746: char *fil;
747: bool okfail;
748: {
749: jmp_buf osetexit;
750: register int saveinp, ointty, oerrno;
751: char *saveglobp;
752: short savepeekc;
753:
754: signal(SIGINT, SIG_IGN);
755: saveinp = dup(0);
756: savepeekc = peekc;
757: saveglobp = globp;
758: peekc = 0; globp = 0;
759: if (saveinp < 0)
760: error("Too many nested sources");
761: if (slevel <= 0)
762: ttyindes = saveinp;
763: close(0);
764: if (open(fil, 0) < 0) {
765: oerrno = errno;
766: setrupt();
767: dup(saveinp);
768: close(saveinp);
769: errno = oerrno;
770: if (!okfail)
771: filioerr(fil);
772: return;
773: }
774: slevel++;
775: ointty = intty;
776: intty = isatty(0);
777: oprompt = value(PROMPT);
778: value(PROMPT) &= intty;
779: getexit(osetexit);
780: setrupt();
781: if (setexit() == 0)
782: commands(1, 1);
783: else if (slevel > 1) {
784: close(0);
785: dup(saveinp);
786: close(saveinp);
787: slevel--;
788: resexit(osetexit);
789: reset();
790: }
791: intty = ointty;
792: value(PROMPT) = oprompt;
793: close(0);
794: dup(saveinp);
795: close(saveinp);
796: globp = saveglobp;
797: peekc = savepeekc;
798: slevel--;
799: resexit(osetexit);
800: }
801:
802: /*
803: * Clear io statistics before a read or write.
804: */
805: clrstats()
806: {
807:
808: ninbuf = 0;
809: cntch = 0;
810: cntln = 0;
811: cntnull = 0;
812: cntodd = 0;
813: }
814:
815: /*
816: * Io is finished, close the unit and print statistics.
817: */
818: iostats()
819: {
820:
821: (void) fsync(io);
822: close(io);
823: io = -1;
824: if (hush == 0) {
825: if (value(TERSE))
826: printf(" %d/%D", cntln, cntch);
827: else
828: printf(" %d line%s, %D character%s", cntln, plural((long) cntln),
829: cntch, plural(cntch));
830: if (cntnull || cntodd) {
831: printf(" (");
832: if (cntnull) {
833: printf("%D null", cntnull);
834: if (cntodd)
835: printf(", ");
836: }
837: if (cntodd)
838: printf("%D non-ASCII", cntodd);
839: putchar(')');
840: }
841: noonl();
842: flush();
843: }
844: return (cntnull != 0 || cntodd != 0);
845: }
846:
847: #if USG | USG3TTY
848: /* It's so wonderful how we all speak the same language... */
849: # define index strchr
850: # define rindex strrchr
851: #endif
852:
853: #ifdef SUICIDE /* and it wasnt even an option! */
854: checkmodeline(line)
855: char *line;
856: {
857: char *beg, *end;
858: char cmdbuf[1024];
859: char *index(), *rindex();
860:
861: beg = index(line, ':');
862: if (beg == NULL)
863: return;
864: if (beg[-2] != 'e' && beg[-2] != 'v') return;
865: if (beg[-1] != 'x' && beg[-1] != 'i') return;
866:
867: strncpy(cmdbuf, beg+1, sizeof cmdbuf);
868: end = rindex(cmdbuf, ':');
869: if (end == NULL)
870: return;
871: *end = 0;
872: globp = cmdbuf;
873: commands(1, 1);
874: }
875: #endif
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.