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