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