|
|
1.1 root 1: /* Copyright (c) 1979 Regents of the University of California */
2: #include "ex.h"
3: #include "ex_argv.h"
4: #include "ex_temp.h"
5: #include "ex_tty.h"
6: #include "ex_vis.h"
7:
8: /*
9: * File input/output, unix escapes, source, filtering preserve and recover
10: */
11:
12: /*
13: * Following remember where . was in the previous file for return
14: * on file switching.
15: */
16: int altdot;
17: int oldadot;
18: bool wasalt;
19:
20: long cntch; /* Count of characters on unit io */
21: #ifndef VMUNIX
22: short cntln; /* Count of lines " */
23: #else
24: int cntln;
25: #endif
26: long cntnull; /* Count of nulls " */
27: long cntodd; /* Count of non-ascii characters " */
28:
29: /*
30: * Parse file name for command encoded by comm.
31: * If comm is E then command is doomed and we are
32: * parsing just so user won't have to retype the name.
33: */
34: filename(comm)
35: int comm;
36: {
37: register int c = comm, d;
38: register int i;
39:
40: d = getchar();
41: if (endcmd(d)) {
42: if (savedfile[0] == 0 && comm != 'f')
43: error("No file|No current filename");
44: CP(file, savedfile);
45: wasalt = 0;
46: oldadot = altdot;
47: if (d == EOF)
48: ungetchar(d);
49: } else {
50: ungetchar(d);
51: getone();
52: eol();
53: if (savedfile[0] == 0 && c != 'E' && c != 'e') {
54: c = 'e';
55: edited = 0;
56: }
57: wasalt = strcmp(file, altfile) == 0;
58: oldadot = altdot;
59: switch (c) {
60:
61: case 'f':
62: edited = 0;
63: /* fall into ... */
64:
65: case 'e':
66: if (savedfile[0]) {
67: altdot = lineDOT();
68: CP(altfile, savedfile);
69: }
70: CP(savedfile, file);
71: break;
72:
73: default:
74: if (file[0]) {
75: if (c != 'E')
76: altdot = lineDOT();
77: CP(altfile, file);
78: }
79: break;
80: }
81: }
82: if (hush && comm != 'f' || comm == 'E')
83: return;
84: if (file[0] != 0) {
85: lprintf("\"%s\"", file);
86: if (comm == 'f') {
87: if (!edited)
88: printf(" [Not edited]");
89: if (tchng)
90: printf(" [Modified]");
91: }
92: flush();
93: } else
94: printf("No file ");
95: if (comm == 'f') {
96: if (!(i = lineDOL()))
97: i++;
98: printf(" line %d of %d --%ld%%--", lineDOT(), lineDOL(),
99: (long) 100 * lineDOT() / i);
100: }
101: }
102:
103: /*
104: * Get the argument words for a command into genbuf
105: * expanding # and %.
106: */
107: getargs()
108: {
109: register int c;
110: register char *cp, *fp;
111: static char fpatbuf[32]; /* hence limit on :next +/pat */
112:
113: pastwh();
114: if (peekchar() == '+') {
115: for (cp = fpatbuf;;) {
116: c = *cp++ = getchar();
117: if (cp >= &fpatbuf[sizeof(fpatbuf)])
118: error("Pattern too long");
119: if (c == '\\' && isspace(peekchar()))
120: c = getchar();
121: if (c == EOF || isspace(c)) {
122: ungetchar(c);
123: *--cp = 0;
124: firstpat = &fpatbuf[1];
125: break;
126: }
127: }
128: }
129: if (skipend())
130: return (0);
131: CP(genbuf, "echo "); cp = &genbuf[5];
132: for (;;) {
133: c = getchar();
134: if (endcmd(c)) {
135: ungetchar(c);
136: break;
137: }
138: switch (c) {
139:
140: case '\\':
141: if (any(peekchar(), "#%"))
142: c = getchar();
143: /* fall into... */
144:
145: default:
146: if (cp > &genbuf[LBSIZE - 2])
147: flong:
148: error("Argument buffer overflow");
149: *cp++ = c;
150: break;
151:
152: case '#':
153: fp = altfile;
154: if (*fp == 0)
155: error("No alternate filename@to substitute for #");
156: goto filexp;
157:
158: case '%':
159: fp = savedfile;
160: if (*fp == 0)
161: error("No current filename@to substitute for %%");
162: filexp:
163: while (*fp) {
164: if (cp > &genbuf[LBSIZE - 2])
165: goto flong;
166: *cp++ = *fp++;
167: }
168: break;
169: }
170: }
171: *cp = 0;
172: return (1);
173: }
174:
175: /*
176: * Glob the argument words in genbuf, or if no globbing
177: * is implied, just split them up directly.
178: */
179: glob(gp)
180: struct glob *gp;
181: {
182: int pvec[2];
183: register char **argv = gp->argv;
184: register char *cp = gp->argspac;
185: register int c;
186: char ch;
187: int nleft = NCARGS;
188:
189: gp->argc0 = 0;
190: if (gscan() == 0) {
191: register char *v = genbuf + 5; /* strlen("echo ") */
192:
193: for (;;) {
194: while (isspace(*v))
195: v++;
196: if (!*v)
197: break;
198: *argv++ = cp;
199: while (*v && !isspace(*v))
200: *cp++ = *v++;
201: *cp++ = 0;
202: gp->argc0++;
203: }
204: *argv = 0;
205: return;
206: }
207: if (pipe(pvec) < 0)
208: error("Can't make pipe to glob");
209: pid = fork();
210: io = pvec[0];
211: if (pid < 0) {
212: close(pvec[1]);
213: error("Can't fork to do glob");
214: }
215: if (pid == 0) {
216: int oerrno;
217:
218: close(1);
219: dup(pvec[1]);
220: close(pvec[0]);
221: execl(svalue(SHELL), "sh", "-c", genbuf, 0);
222: oerrno = errno; close(1); dup(2); errno = oerrno;
223: filioerr(svalue(SHELL));
224: }
225: close(pvec[1]);
226: do {
227: *argv = cp;
228: for (;;) {
229: if (read(io, &ch, 1) != 1) {
230: close(io);
231: c = -1;
232: } else
233: c = ch & TRIM;
234: if (c <= 0 || isspace(c))
235: break;
236: *cp++ = c;
237: if (--nleft <= 0)
238: error("Arg list too long");
239: }
240: if (cp != *argv) {
241: --nleft;
242: *cp++ = 0;
243: gp->argc0++;
244: if (gp->argc0 >= NARGS)
245: error("Arg list too long");
246: argv++;
247: }
248: } while (c >= 0);
249: waitfor();
250: if (gp->argc0 == 0)
251: error(NOSTR);
252: }
253:
254: /*
255: * Scan genbuf for shell metacharacters.
256: * Set is union of v7 shell and csh metas.
257: */
258: gscan()
259: {
260: register char *cp;
261:
262: for (cp = genbuf; *cp; cp++)
263: if (any(*cp, "~{[*?$`'\"\\"))
264: return (1);
265: return (0);
266: }
267:
268: /*
269: * Parse one filename into file.
270: */
271: getone()
272: {
273: register char *str;
274: struct glob G;
275:
276: if (getargs() == 0)
277: error("Missing filename");
278: glob(&G);
279: if (G.argc0 > 1)
280: error("Ambiguous|Too many file names");
281: str = G.argv[G.argc0 - 1];
282: if (strlen(str) > FNSIZE - 4)
283: error("Filename too long");
284: samef:
285: CP(file, str);
286: }
287:
288: /*
289: * Read a file from the world.
290: * C is command, 'e' if this really an edit (or a recover).
291: */
292: rop(c)
293: int c;
294: {
295: register int i;
296: struct stat stbuf;
297: short magic;
298:
299: io = open(file, 0);
300: if (io < 0) {
301: if (c == 'e' && errno == ENOENT)
302: edited++;
303: syserror();
304: }
305: if (fstat(io, &stbuf))
306: syserror();
307: switch (stbuf.st_mode & S_IFMT) {
308:
309: case S_IFBLK:
310: error(" Block special file");
311:
312: case S_IFCHR:
313: if (isatty(io))
314: error(" Teletype");
315: if (samei(&stbuf, "/dev/null"))
316: break;
317: error(" Character special file");
318:
319: case S_IFDIR:
320: error(" Directory");
321:
322: case S_IFREG:
323: i = read(io, (char *) &magic, sizeof(magic));
324: lseek(io, 0l, 0);
325: if (i != sizeof(magic))
326: break;
327: switch (magic) {
328:
329: case 0405:
330: case 0407:
331: case 0410:
332: case 0411:
333: error(" Executable");
334:
335: case 0177545:
336: case 0177555:
337: error(" Archive");
338:
339: default:
340: if (magic & 0100200)
341: error(" Non-ascii file");
342: break;
343: }
344: }
345: if (c == 'r')
346: setdot();
347: else
348: setall();
349: if (inopen && c == 'r')
350: undap1 = undap2 = dot + 1;
351: rop2();
352: rop3(c);
353: }
354:
355: rop2()
356: {
357:
358: deletenone();
359: clrstats();
360: ignore(append(getfile, addr2));
361: }
362:
363: rop3(c)
364: int c;
365: {
366:
367: if (iostats() == 0 && c == 'e')
368: edited++;
369: if (c == 'e') {
370: if (wasalt || firstpat) {
371: register line *addr = zero + oldadot;
372:
373: if (addr > dol)
374: addr = dol;
375: if (firstpat) {
376: globp = (*firstpat) ? firstpat : "$";
377: commands(1,1);
378: firstpat = 0;
379: } else if (addr >= one) {
380: if (inopen)
381: dot = addr;
382: markpr(addr);
383: } else
384: goto other;
385: } else
386: other:
387: if (dol > zero) {
388: if (inopen)
389: dot = one;
390: markpr(one);
391: }
392: undkind = UNDNONE;
393: if (inopen) {
394: vcline = 0;
395: vreplace(0, LINES, lineDOL());
396: }
397: }
398: if (laste) {
399: laste = 0;
400: sync();
401: }
402: }
403:
404: /*
405: * Are these two really the same inode?
406: */
407: samei(sp, cp)
408: struct stat *sp;
409: char *cp;
410: {
411: struct stat stb;
412:
413: if (stat(cp, &stb) < 0 || sp->st_dev != stb.st_dev)
414: return (0);
415: return (sp->st_ino == stb.st_ino);
416: }
417:
418: /* Returns from edited() */
419: #define EDF 0 /* Edited file */
420: #define NOTEDF -1 /* Not edited file */
421: #define PARTBUF 1 /* Write of partial buffer to Edited file */
422:
423: /*
424: * Write a file.
425: */
426: wop(dofname)
427: bool dofname; /* if 1 call filename, else use savedfile */
428: {
429: register int c, exclam, nonexist;
430: line *saddr1, *saddr2;
431: struct stat stbuf;
432:
433: c = 0;
434: exclam = 0;
435: if (dofname) {
436: if (peekchar() == '!')
437: exclam++, ignchar();
438: ignore(skipwh());
439: while (peekchar() == '>')
440: ignchar(), c++, ignore(skipwh());
441: if (c != 0 && c != 2)
442: error("Write forms are 'w' and 'w>>'");
443: filename('w');
444: } else {
445: saddr1=addr1;
446: saddr2=addr2;
447: addr1=one;
448: addr2=dol;
449: CP(file, savedfile);
450: if (inopen) {
451: vclrech(0);
452: splitw++;
453: }
454: lprintf("\"%s\"", file);
455: }
456: nonexist = stat(file, &stbuf);
457: switch (c) {
458:
459: case 0:
460: if (!exclam && !value(WRITEANY)) switch (edfile()) {
461:
462: case NOTEDF:
463: if (nonexist)
464: break;
465: if ((stbuf.st_mode & S_IFMT) == S_IFCHR) {
466: if (samei(&stbuf, "/dev/null"))
467: break;
468: if (samei(&stbuf, "/dev/tty"))
469: break;
470: }
471: io = open(file, 1);
472: if (io < 0)
473: syserror();
474: if (!isatty(io))
475: serror(" File exists| File exists - use \"w! %s\" to overwrite", file);
476: close(io);
477: break;
478:
479: case PARTBUF:
480: error(" Use \"w!\" to write partial buffer");
481: }
482: cre:
483: /*
484: synctmp();
485: */
486: #ifdef V6
487: io = creat(file, 0644);
488: #else
489: io = creat(file, 0666);
490: #endif
491: if (io < 0)
492: syserror();
493: if (hush == 0)
494: if (nonexist)
495: printf(" [New file]");
496: else if (value(WRITEANY) && edfile() != EDF)
497: printf(" [Existing file]");
498: break;
499:
500: case 2:
501: io = open(file, 1);
502: if (io < 0) {
503: if (exclam || value(WRITEANY))
504: goto cre;
505: syserror();
506: }
507: lseek(io, 0l, 2);
508: break;
509: }
510: putfile();
511: ignore(iostats());
512: if (c != 2 && addr1 == one && addr2 == dol) {
513: if (eq(file, savedfile))
514: edited = 1;
515: sync();
516: }
517: if (!dofname) {
518: addr1 = saddr1;
519: addr2 = saddr2;
520: }
521: }
522:
523: /*
524: * Is file the edited file?
525: * Work here is that it is not considered edited
526: * if this is a partial buffer, and distinguish
527: * all cases.
528: */
529: edfile()
530: {
531:
532: if (!edited || !eq(file, savedfile))
533: return (NOTEDF);
534: return (addr1 == one && addr2 == dol ? EDF : PARTBUF);
535: }
536:
537: /*
538: * First part of a shell escape,
539: * parse the line, expanding # and % and ! and printing if implied.
540: */
541: unix0(warn)
542: bool warn;
543: {
544: register char *up, *fp;
545: register short c;
546: char printub, puxb[UXBSIZE + sizeof (int)];
547:
548: printub = 0;
549: CP(puxb, uxb);
550: c = getchar();
551: if (c == '\n' || c == EOF)
552: error("Incomplete shell escape command@- use 'shell' to get a shell");
553: up = uxb;
554: do {
555: switch (c) {
556:
557: case '\\':
558: if (any(peekchar(), "%#!"))
559: c = getchar();
560: default:
561: if (up >= &uxb[UXBSIZE]) {
562: tunix:
563: uxb[0] = 0;
564: error("Command too long");
565: }
566: *up++ = c;
567: break;
568:
569: case '!':
570: fp = puxb;
571: if (*fp == 0) {
572: uxb[0] = 0;
573: error("No previous command@to substitute for !");
574: }
575: printub++;
576: while (*fp) {
577: if (up >= &uxb[UXBSIZE])
578: goto tunix;
579: *up++ = *fp++;
580: }
581: break;
582:
583: case '#':
584: fp = altfile;
585: if (*fp == 0) {
586: uxb[0] = 0;
587: error("No alternate filename@to substitute for #");
588: }
589: goto uexp;
590:
591: case '%':
592: fp = savedfile;
593: if (*fp == 0) {
594: uxb[0] = 0;
595: error("No filename@to substitute for %%");
596: }
597: uexp:
598: printub++;
599: while (*fp) {
600: if (up >= &uxb[UXBSIZE])
601: goto tunix;
602: *up++ = *fp++ | QUOTE;
603: }
604: break;
605: }
606: c = getchar();
607: } while (c == '|' || !endcmd(c));
608: if (c == EOF)
609: ungetchar(c);
610: *up = 0;
611: if (!inopen)
612: resetflav();
613: if (warn)
614: ckaw();
615: if (warn && hush == 0 && chng && xchng != chng && value(WARN) && dol > zero) {
616: xchng = chng;
617: vnfl();
618: printf(mesg("[No write]|[No write since last change]"));
619: noonl();
620: flush();
621: } else
622: warn = 0;
623: if (printub) {
624: if (uxb[0] == 0)
625: error("No previous command@to repeat");
626: if (inopen) {
627: splitw++;
628: vclean();
629: vgoto(WECHO, 0);
630: }
631: if (warn)
632: vnfl();
633: if (hush == 0)
634: lprintf("!%s", uxb);
635: if (inopen) {
636: vclreol();
637: vgoto(WECHO, 0);
638: } else
639: putnl();
640: flush();
641: }
642: }
643:
644: /*
645: * Do the real work for execution of a shell escape.
646: * Mode is like the number passed to open system calls
647: * and indicates filtering. If input is implied, newstdin
648: * must have been setup already.
649: */
650: unixex(opt, up, newstdin, mode)
651: char *opt, *up;
652: int newstdin, mode;
653: {
654: int pvec[2], f;
655:
656: signal(SIGINT, SIG_IGN);
657: if (inopen)
658: f = setty(normf);
659: if ((mode & 1) && pipe(pvec) < 0) {
660: /* Newstdin should be io so it will be closed */
661: if (inopen)
662: setty(f);
663: error("Can't make pipe for filter");
664: }
665: #ifndef VFORK
666: pid = fork();
667: #else
668: pid = vfork();
669: #endif
670: if (pid < 0) {
671: if (mode & 1) {
672: close(pvec[0]);
673: close(pvec[1]);
674: }
675: setrupt();
676: error("No more processes");
677: }
678: if (pid == 0) {
679: if (mode & 2) {
680: close(0);
681: dup(newstdin);
682: close(newstdin);
683: }
684: if (mode & 1) {
685: close(pvec[0]);
686: close(1);
687: dup(pvec[1]);
688: if (inopen) {
689: close(2);
690: dup(1);
691: }
692: close(pvec[1]);
693: }
694: if (io)
695: close(io);
696: if (tfile)
697: close(tfile);
698: #ifndef VMUNIX
699: close(erfile);
700: #endif
701: signal(SIGHUP, oldhup);
702: signal(SIGQUIT, oldquit);
703: if (ruptible)
704: signal(SIGINT, SIG_DFL);
705: execl(svalue(SHELL), "sh", opt, up, (char *) 0);
706: printf("No %s!\n", svalue(SHELL));
707: error(NOSTR);
708: }
709: if (mode & 1) {
710: io = pvec[0];
711: close(pvec[1]);
712: }
713: if (newstdin)
714: close(newstdin);
715: return (f);
716: }
717:
718: /*
719: * Wait for the command to complete.
720: * F is for restoration of tty mode if from open/visual.
721: * C flags suppression of printing.
722: */
723: unixwt(c, f)
724: bool c;
725: int f;
726: {
727:
728: waitfor();
729: if (inopen)
730: setty(f);
731: setrupt();
732: if (!inopen && c && hush == 0) {
733: printf("!\n");
734: flush();
735: termreset();
736: gettmode();
737: }
738: }
739:
740: /*
741: * Setup a pipeline for the filtration implied by mode
742: * which is like a open number. If input is required to
743: * the filter, then a child editor is created to write it.
744: * If output is catch it from io which is created by unixex.
745: */
746: filter(mode)
747: register int mode;
748: {
749: static int pvec[2];
750: register int f;
751: register int lines = lineDOL();
752:
753: mode++;
754: if (mode & 2) {
755: signal(SIGINT, SIG_IGN);
756: if (pipe(pvec) < 0)
757: error("Can't make pipe");
758: pid = fork();
759: io = pvec[0];
760: if (pid < 0) {
761: setrupt();
762: close(pvec[1]);
763: error("No more processes");
764: }
765: if (pid == 0) {
766: setrupt();
767: io = pvec[1];
768: close(pvec[0]);
769: putfile();
770: exit(0);
771: }
772: close(pvec[1]);
773: io = pvec[0];
774: setrupt();
775: }
776: f = unixex("-c", uxb, (mode & 2) ? pvec[0] : 0, mode);
777: if (mode == 3) {
778: delete(0);
779: addr2 = addr1 - 1;
780: }
781: if (mode & 1) {
782: undap1 = undap2 = addr2+1;
783: ignore(append(getfile, addr2));
784: }
785: close(io);
786: io = -1;
787: unixwt(!inopen, f);
788: netchHAD(lines);
789: }
790:
791: /*
792: * Set up to do a recover, getting io to be a pipe from
793: * the recover process.
794: */
795: recover()
796: {
797: static int pvec[2];
798:
799: if (pipe(pvec) < 0)
800: error(" Can't make pipe for recovery");
801: pid = fork();
802: io = pvec[0];
803: if (pid < 0) {
804: close(pvec[1]);
805: error(" Can't fork to execute recovery");
806: }
807: if (pid == 0) {
808: close(2);
809: dup(1);
810: close(1);
811: dup(pvec[1]);
812: close(pvec[1]);
813: execl(EXRECOVER, "exrecover", svalue(DIRECTORY), file, (char *) 0);
814: close(1);
815: dup(2);
816: error(" No recovery routine");
817: }
818: close(pvec[1]);
819: }
820:
821: /*
822: * Wait for the process (pid an external) to complete.
823: */
824: waitfor()
825: {
826:
827: do
828: rpid = wait(&status);
829: while (rpid != pid && rpid != -1);
830: status = (status >> 8) & 0377;
831: }
832:
833: /*
834: * The end of a recover operation. If the process
835: * exits non-zero, force not edited; otherwise force
836: * a write.
837: */
838: revocer()
839: {
840:
841: waitfor();
842: if (pid == rpid && status != 0)
843: edited = 0;
844: else
845: change();
846: }
847:
848: /*
849: * Extract the next line from the io stream.
850: */
851: static char *nextip;
852:
853: getfile()
854: {
855: register short c;
856: register char *lp, *fp;
857:
858: lp = linebuf;
859: fp = nextip;
860: do {
861: if (--ninbuf < 0) {
862: ninbuf = read(io, genbuf, LBSIZE) - 1;
863: if (ninbuf < 0) {
864: if (lp != linebuf) {
865: printf(" [Incomplete last line]");
866: break;
867: }
868: return (EOF);
869: }
870: fp = genbuf;
871: }
872: if (lp >= &linebuf[LBSIZE]) {
873: error(" Line too long");
874: }
875: c = *fp++;
876: if (c == 0) {
877: cntnull++;
878: continue;
879: }
880: if (c & QUOTE) {
881: cntodd++;
882: c &= TRIM;
883: if (c == 0)
884: continue;
885: }
886: *lp++ = c;
887: } while (c != '\n');
888: cntch += lp - linebuf;
889: *--lp = 0;
890: nextip = fp;
891: cntln++;
892: return (0);
893: }
894:
895: /*
896: * Write a range onto the io stream.
897: */
898: putfile()
899: {
900: line *a1;
901: register char *fp, *lp;
902: register int nib;
903:
904: a1 = addr1;
905: clrstats();
906: cntln = addr2 - a1 + 1;
907: if (cntln == 0)
908: return;
909: nib = BUFSIZ;
910: fp = genbuf;
911: do {
912: getline(*a1++);
913: lp = linebuf;
914: for (;;) {
915: if (--nib < 0) {
916: nib = fp - genbuf;
917: if (write(io, genbuf, nib) != nib) {
918: wrerror();
919: }
920: cntch += nib;
921: nib = BUFSIZ - 1;
922: fp = genbuf;
923: }
924: if ((*fp++ = *lp++) == 0) {
925: fp[-1] = '\n';
926: break;
927: }
928: }
929: } while (a1 <= addr2);
930: nib = fp - genbuf;
931: if (write(io, genbuf, nib) != nib) {
932: wrerror();
933: }
934: cntch += nib;
935: }
936:
937: /*
938: * A write error has occurred; if the file being written was
939: * the edited file then we consider it to have changed since it is
940: * now likely scrambled.
941: */
942: wrerror()
943: {
944:
945: if (eq(file, savedfile) && edited)
946: change();
947: syserror();
948: }
949:
950: /*
951: * Source command, handles nested sources.
952: * Traps errors since it mungs unit 0 during the source.
953: */
954: static short slevel;
955:
956: source(fil, okfail)
957: char *fil;
958: bool okfail;
959: {
960: jmp_buf osetexit;
961: register int saveinp, ointty, oerrno;
962: int oprompt;
963:
964: signal(SIGINT, SIG_IGN);
965: saveinp = dup(0);
966: if (saveinp < 0)
967: error("Too many nested sources");
968: close(0);
969: if (open(fil, 0) < 0) {
970: oerrno = errno;
971: setrupt();
972: dup(saveinp);
973: close(saveinp);
974: errno = oerrno;
975: if (!okfail)
976: filioerr(fil);
977: return;
978: }
979: slevel++;
980: ointty = intty;
981: intty = isatty(0);
982: oprompt = value(PROMPT);
983: value(PROMPT) &= intty;
984: getexit(osetexit);
985: setrupt();
986: if (setexit() == 0)
987: commands(1, 1);
988: else if (slevel > 1) {
989: close(0);
990: dup(saveinp);
991: close(saveinp);
992: slevel--;
993: resexit(osetexit);
994: reset();
995: }
996: intty = ointty;
997: value(PROMPT) = oprompt;
998: close(0);
999: dup(saveinp);
1000: close(saveinp);
1001: slevel--;
1002: resexit(osetexit);
1003: }
1004:
1005: /*
1006: * Clear io statistics before a read or write.
1007: */
1008: clrstats()
1009: {
1010:
1011: ninbuf = 0;
1012: cntch = 0;
1013: cntln = 0;
1014: cntnull = 0;
1015: cntodd = 0;
1016: }
1017:
1018: /*
1019: * Io is finished, close the unit and print statistics.
1020: */
1021: iostats()
1022: {
1023:
1024: close(io);
1025: io = -1;
1026: if (hush == 0) {
1027: if (value(TERSE))
1028: printf(" %d/%D", cntln, cntch);
1029: else
1030: printf(" %d line%s, %D character%s", cntln, plural((long) cntln),
1031: cntch, plural(cntch));
1032: if (cntnull || cntodd) {
1033: printf(" (");
1034: if (cntnull) {
1035: printf("%D null", cntnull);
1036: if (cntodd)
1037: printf(", ");
1038: }
1039: if (cntodd)
1040: printf("%D non-ASCII", cntodd);
1041: putchar(')');
1042: }
1043: noonl();
1044: flush();
1045: }
1046: return (cntnull != 0 || cntodd != 0);
1047: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.