home *** CD-ROM | disk | FTP | other *** search
/ minnie.tuhs.org / unixen.tar / unixen / PDP-11 / Distributions / ucb / spencer_2bsd.tar.gz / 2bsd.tar / src / ex / ex_io.c < prev    next >
C/C++ Source or Header  |  1980-02-17  |  17KB  |  1,002 lines

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