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: #if !defined(lint) && defined(DOSCCS)
   8: static char *sccsid = "@(#)ex_io.c	7.11.1.1 (Berkeley) 8/12/86";
   9: #endif
  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:         i = read(io, (char *) &magic, sizeof(magic));
 354:         lseek(io, 0l, 0);
 355:         if (i != sizeof(magic))
 356:             break;
 357:         switch (magic) {
 358: 
 359:         case 0405:  /* data overlay on exec */
 360:         case 0407:  /* unshared */
 361:         case 0410:  /* shared text */
 362:         case 0411:  /* separate I/D */
 363:         case 0413:  /* VM/Unix demand paged */
 364:         case 0430:  /* PDP-11 Overlay shared */
 365:         case 0431:  /* PDP-11 Overlay sep I/D */
 366:             error(" Executable");
 367: 
 368:         /*
 369: 		 * We do not forbid the editing of portable archives
 370: 		 * because it is reasonable to edit them, especially
 371: 		 * if they are archives of text files.  This is
 372: 		 * especially useful if you archive source files together
 373: 		 * and copy them to another system with ~%take, since
 374: 		 * the files sometimes show up munged and must be fixed.
 375: 		 */
 376:         case 0177545:
 377:         case 0177555:
 378:             error(" Archive");
 379: 
 380:         default:
 381: #ifdef mbb
 382:             /* C/70 has a 10 bit byte */
 383:             if (magic & 03401600)
 384: #else
 385:             /* Everybody else has an 8 bit byte */
 386:             if (magic & 0100200)
 387: #endif
 388:                 error(" Non-ascii file");
 389:             break;
 390:         }
 391:     }
 392:     if (c != 'r') {
 393:         if (value(READONLY) && denied) {
 394:             value(READONLY) = ovro;
 395:             denied = 0;
 396:         }
 397:         if ((stbuf.st_mode & 0222) == 0 || access(file, 2) < 0) {
 398:             ovro = value(READONLY);
 399:             denied = 1;
 400:             value(READONLY) = 1;
 401:         }
 402:     }
 403:     if (value(READONLY)) {
 404:         printf(" [Read only]");
 405:         flush();
 406:     }
 407:     if (c == 'r')
 408:         setdot();
 409:     else
 410:         setall();
 411:     if (FIXUNDO && inopen && c == 'r')
 412:         undap1 = undap2 = dot + 1;
 413:     rop2();
 414:     rop3(c);
 415: }
 416: 
 417: rop2()
 418: {
 419:     line *first, *last, *a;
 420:     struct stat statb;
 421: 
 422:     deletenone();
 423:     clrstats();
 424:     first = addr2 + 1;
 425:     if (fstat(io, &statb) < 0)
 426:         bsize = LBSIZE;
 427:     else {
 428:         bsize = statb.st_blksize;
 429:         if (bsize <= 0)
 430:             bsize = LBSIZE;
 431:     }
 432:     ignore(append(getfile, addr2));
 433:     last = dot;
 434:     /*
 435: 	 *	if the modeline variable is set,
 436: 	 *	check the first and last five lines of the file
 437: 	 *	for a mode line.
 438: 	 */
 439:     if (value(MODELINE)) {
 440:         for (a=first; a<=last; a++) {
 441:             if (a==first+5 && last-first > 10)
 442:                 a = last - 4;
 443:             getline(*a);
 444:             checkmodeline(linebuf);
 445:         }
 446:     }
 447: }
 448: 
 449: rop3(c)
 450:     int c;
 451: {
 452: 
 453:     if (iostats() == 0 && c == 'e')
 454:         edited++;
 455:     if (c == 'e') {
 456:         if (wasalt || firstpat) {
 457:             register line *addr = zero + oldadot;
 458: 
 459:             if (addr > dol)
 460:                 addr = dol;
 461:             if (firstpat) {
 462:                 globp = (*firstpat) ? firstpat : "$";
 463:                 commands(1,1);
 464:                 firstpat = 0;
 465:             } else if (addr >= one) {
 466:                 if (inopen)
 467:                     dot = addr;
 468:                 markpr(addr);
 469:             } else
 470:                 goto other;
 471:         } else
 472: other:
 473:             if (dol > zero) {
 474:                 if (inopen)
 475:                     dot = one;
 476:                 markpr(one);
 477:             }
 478:         if(FIXUNDO)
 479:             undkind = UNDNONE;
 480:         if (inopen) {
 481:             vcline = 0;
 482:             vreplace(0, LINES, lineDOL());
 483:         }
 484:     }
 485: }
 486: 
 487: /*
 488:  * Are these two really the same inode?
 489:  */
 490: samei(sp, cp)
 491:     struct stat *sp;
 492:     char *cp;
 493: {
 494:     struct stat stb;
 495: 
 496:     if (stat(cp, &stb) < 0 || sp->st_dev != stb.st_dev)
 497:         return (0);
 498:     return (sp->st_ino == stb.st_ino);
 499: }
 500: 
 501: /* Returns from edited() */
 502: #define EDF 0       /* Edited file */
 503: #define NOTEDF  -1      /* Not edited file */
 504: #define PARTBUF 1       /* Write of partial buffer to Edited file */
 505: 
 506: /*
 507:  * Write a file.
 508:  */
 509: wop(dofname)
 510: bool dofname;   /* if 1 call filename, else use savedfile */
 511: {
 512:     register int c, exclam, nonexist;
 513:     line *saddr1, *saddr2;
 514:     struct stat stbuf;
 515: 
 516:     c = 0;
 517:     exclam = 0;
 518:     if (dofname) {
 519:         if (peekchar() == '!')
 520:             exclam++, ignchar();
 521:         ignore(skipwh());
 522:         while (peekchar() == '>')
 523:             ignchar(), c++, ignore(skipwh());
 524:         if (c != 0 && c != 2)
 525:             error("Write forms are 'w' and 'w>>'");
 526:         filename('w');
 527:     } else {
 528:         if (savedfile[0] == 0)
 529:             error("No file|No current filename");
 530:         saddr1=addr1;
 531:         saddr2=addr2;
 532:         addr1=one;
 533:         addr2=dol;
 534:         CP(file, savedfile);
 535:         if (inopen) {
 536:             vclrech(0);
 537:             splitw++;
 538:         }
 539:         lprintf("\"%s\"", file);
 540:     }
 541:     nonexist = stat(file, &stbuf);
 542:     switch (c) {
 543: 
 544:     case 0:
 545:         if (!exclam && (!value(WRITEANY) || value(READONLY)))
 546:         switch (edfile()) {
 547: 
 548:         case NOTEDF:
 549:             if (nonexist)
 550:                 break;
 551:             if ((stbuf.st_mode & S_IFMT) == S_IFCHR) {
 552:                 if (samei(&stbuf, "/dev/null"))
 553:                     break;
 554:                 if (samei(&stbuf, "/dev/tty"))
 555:                     break;
 556:             }
 557:             io = open(file, 1);
 558:             if (io < 0)
 559:                 syserror();
 560:             if (!isatty(io))
 561:                 serror(" File exists| File exists - use \"w! %s\" to overwrite", file);
 562:             close(io);
 563:             break;
 564: 
 565:         case EDF:
 566:             if (value(READONLY))
 567:                 error(" File is read only");
 568:             break;
 569: 
 570:         case PARTBUF:
 571:             if (value(READONLY))
 572:                 error(" File is read only");
 573:             error(" Use \"w!\" to write partial buffer");
 574:         }
 575: cre:
 576: /*
 577: 		synctmp();
 578: */
 579: #ifdef V6
 580:         io = creat(file, 0644);
 581: #else
 582:         io = creat(file, 0666);
 583: #endif
 584:         if (io < 0)
 585:             syserror();
 586:         writing = 1;
 587:         if (hush == 0)
 588:             if (nonexist)
 589:                 printf(" [New file]");
 590:             else if (value(WRITEANY) && edfile() != EDF)
 591:                 printf(" [Existing file]");
 592:         break;
 593: 
 594:     case 2:
 595:         io = open(file, 1);
 596:         if (io < 0) {
 597:             if (exclam || value(WRITEANY))
 598:                 goto cre;
 599:             syserror();
 600:         }
 601:         lseek(io, 0l, 2);
 602:         break;
 603:     }
 604:     putfile(0);
 605:     ignore(iostats());
 606:     if (c != 2 && addr1 == one && addr2 == dol) {
 607:         if (eq(file, savedfile))
 608:             edited = 1;
 609:         sync();
 610:     }
 611:     if (!dofname) {
 612:         addr1 = saddr1;
 613:         addr2 = saddr2;
 614:     }
 615:     writing = 0;
 616: }
 617: 
 618: /*
 619:  * Is file the edited file?
 620:  * Work here is that it is not considered edited
 621:  * if this is a partial buffer, and distinguish
 622:  * all cases.
 623:  */
 624: edfile()
 625: {
 626: 
 627:     if (!edited || !eq(file, savedfile))
 628:         return (NOTEDF);
 629:     return (addr1 == one && addr2 == dol ? EDF : PARTBUF);
 630: }
 631: 
 632: /*
 633:  * Extract the next line from the io stream.
 634:  */
 635: char *nextip;
 636: 
 637: getfile()
 638: {
 639:     register short c;
 640:     register char *lp, *fp;
 641: 
 642:     lp = linebuf;
 643:     fp = nextip;
 644:     do {
 645:         if (--ninbuf < 0) {
 646:             ninbuf = read(io, genbuf, bsize) - 1;
 647:             if (ninbuf < 0) {
 648:                 if (lp != linebuf) {
 649:                     lp++;
 650:                     printf(" [Incomplete last line]");
 651:                     break;
 652:                 }
 653:                 return (EOF);
 654:             }
 655:             fp = genbuf;
 656:             cntch += ninbuf+1;
 657:         }
 658:         if (lp >= &linebuf[LBSIZE]) {
 659:             error(" Line too long");
 660:         }
 661:         c = *fp++;
 662:         if (c == 0) {
 663:             cntnull++;
 664:             continue;
 665:         }
 666:         if (c & QUOTE) {
 667:             cntodd++;
 668:             c &= TRIM;
 669:             if (c == 0)
 670:                 continue;
 671:         }
 672:         *lp++ = c;
 673:     } while (c != '\n');
 674:     *--lp = 0;
 675:     nextip = fp;
 676:     cntln++;
 677:     return (0);
 678: }
 679: 
 680: /*
 681:  * Write a range onto the io stream.
 682:  */
 683: putfile(isfilter)
 684: int isfilter;
 685: {
 686:     line *a1;
 687:     register char *fp, *lp;
 688:     register int nib;
 689:     struct stat statb;
 690: 
 691:     a1 = addr1;
 692:     clrstats();
 693:     cntln = addr2 - a1 + 1;
 694:     if (cntln == 0)
 695:         return;
 696:     if (fstat(io, &statb) < 0)
 697:         bsize = LBSIZE;
 698:     else {
 699:         bsize = statb.st_blksize;
 700:         if (bsize <= 0)
 701:             bsize = LBSIZE;
 702:     }
 703:     nib = bsize;
 704:     fp = genbuf;
 705:     do {
 706:         getline(*a1++);
 707:         lp = linebuf;
 708:         for (;;) {
 709:             if (--nib < 0) {
 710:                 nib = fp - genbuf;
 711:                 if (write(io, genbuf, nib) != nib) {
 712:                     wrerror();
 713:                 }
 714:                 cntch += nib;
 715:                 nib = bsize - 1;
 716:                 fp = genbuf;
 717:             }
 718:             if ((*fp++ = *lp++) == 0) {
 719:                 fp[-1] = '\n';
 720:                 break;
 721:             }
 722:         }
 723:     } while (a1 <= addr2);
 724:     nib = fp - genbuf;
 725:     if (write(io, genbuf, nib) != nib) {
 726:         wrerror();
 727:     }
 728:     cntch += nib;
 729: }
 730: 
 731: /*
 732:  * A write error has occurred;  if the file being written was
 733:  * the edited file then we consider it to have changed since it is
 734:  * now likely scrambled.
 735:  */
 736: wrerror()
 737: {
 738: 
 739:     if (eq(file, savedfile) && edited)
 740:         change();
 741:     syserror();
 742: }
 743: 
 744: /*
 745:  * Source command, handles nested sources.
 746:  * Traps errors since it mungs unit 0 during the source.
 747:  */
 748: short slevel;
 749: short ttyindes;
 750: 
 751: source(fil, okfail)
 752:     char *fil;
 753:     bool okfail;
 754: {
 755:     jmp_buf osetexit;
 756:     register int saveinp, ointty, oerrno;
 757:     char *saveglobp;
 758:     short savepeekc;
 759: 
 760:     signal(SIGINT, SIG_IGN);
 761:     saveinp = dup(0);
 762:     savepeekc = peekc;
 763:     saveglobp = globp;
 764:     peekc = 0; globp = 0;
 765:     if (saveinp < 0)
 766:         error("Too many nested sources");
 767:     if (slevel <= 0)
 768:         ttyindes = saveinp;
 769:     close(0);
 770:     if (open(fil, 0) < 0) {
 771:         oerrno = errno;
 772:         setrupt();
 773:         dup(saveinp);
 774:         close(saveinp);
 775:         errno = oerrno;
 776:         if (!okfail)
 777:             filioerr(fil);
 778:         return;
 779:     }
 780:     slevel++;
 781:     ointty = intty;
 782:     intty = isatty(0);
 783:     oprompt = value(PROMPT);
 784:     value(PROMPT) &= intty;
 785:     getexit(osetexit);
 786:     setrupt();
 787:     if (setexit() == 0)
 788:         commands(1, 1);
 789:     else if (slevel > 1) {
 790:         close(0);
 791:         dup(saveinp);
 792:         close(saveinp);
 793:         slevel--;
 794:         resexit(osetexit);
 795:         reset();
 796:     }
 797:     intty = ointty;
 798:     value(PROMPT) = oprompt;
 799:     close(0);
 800:     dup(saveinp);
 801:     close(saveinp);
 802:     globp = saveglobp;
 803:     peekc = savepeekc;
 804:     slevel--;
 805:     resexit(osetexit);
 806: }
 807: 
 808: /*
 809:  * Clear io statistics before a read or write.
 810:  */
 811: clrstats()
 812: {
 813: 
 814:     ninbuf = 0;
 815:     cntch = 0;
 816:     cntln = 0;
 817:     cntnull = 0;
 818:     cntodd = 0;
 819: }
 820: 
 821: /*
 822:  * Io is finished, close the unit and print statistics.
 823:  */
 824: iostats()
 825: {
 826: 
 827:     (void) fsync(io);
 828:     close(io);
 829:     io = -1;
 830:     if (hush == 0) {
 831:         if (value(TERSE))
 832:             printf(" %d/%D", cntln, cntch);
 833:         else
 834:             printf(" %d line%s, %D character%s", cntln, plural((long) cntln),
 835:                 cntch, plural(cntch));
 836:         if (cntnull || cntodd) {
 837:             printf(" (");
 838:             if (cntnull) {
 839:                 printf("%D null", cntnull);
 840:                 if (cntodd)
 841:                     printf(", ");
 842:             }
 843:             if (cntodd)
 844:                 printf("%D non-ASCII", cntodd);
 845:             putchar(')');
 846:         }
 847:         noonl();
 848:         flush();
 849:     }
 850:     return (cntnull != 0 || cntodd != 0);
 851: }
 852: 
 853: #if USG | USG3TTY
 854: /* It's so wonderful how we all speak the same language... */
 855: # define index strchr
 856: # define rindex strrchr
 857: #endif
 858: 
 859: checkmodeline(line)
 860: char *line;
 861: {
 862:     char *beg, *end;
 863:     char cmdbuf[1024];
 864:     char *index(), *rindex();
 865: 
 866:     beg = index(line, ':');
 867:     if (beg == NULL)
 868:         return;
 869:     if (&beg[-3] < line)
 870:         return;
 871:     if (!(  ( (beg[-3] == ' ' || beg[-3] == '\t')
 872:             && beg[-2] == 'e'
 873:         && beg[-1] == 'x')
 874:          || ( (beg[-3] == ' ' || beg[-3] == '\t')
 875:             && beg[-2] == 'v'
 876:         && beg[-1] == 'i'))) return;
 877:     strncpy(cmdbuf, beg+1, sizeof cmdbuf);
 878:     end = rindex(cmdbuf, ':');
 879:     if (end == NULL)
 880:         return;
 881:     *end = 0;
 882:     globp = cmdbuf;
 883:     commands(1, 1);
 884: }

Defined functions

checkmodeline defined in line 859; used 1 times
clrstats defined in line 811; used 2 times
edfile defined in line 624; used 2 times
filename defined in line 44; used 7 times
getargs defined in line 122; used 2 times
getfile defined in line 637; used 3 times
getone defined in line 289; used 3 times
glob defined in line 194; used 2 times
gscan defined in line 275; used 1 times
iostats defined in line 824; used 2 times
putfile defined in line 683; used 1 times
rop defined in line 309; used 2 times
rop2 defined in line 417; used 2 times
rop3 defined in line 449; used 2 times
samei defined in line 490; used 3 times
source defined in line 751; used 3 times
wop defined in line 509; used 2 times
wrerror defined in line 736; used 2 times

Defined variables

G defined in line 288; used 4 times
altdot defined in line 25; used 5 times
cntch defined in line 30; used 7 times
cntln defined in line 34; used 7 times
cntnull defined in line 36; used 6 times
cntodd defined in line 37; used 7 times
isalt defined in line 28; used 3 times
nextip defined in line 635; used 2 times
oldadot defined in line 26; used 3 times
sccsid defined in line 8; never used
slevel defined in line 748; used 5 times
ttyindes defined in line 749; used 1 times
wasalt defined in line 27; used 3 times

Defined macros

EDF defined in line 502; used 2 times
NOTEDF defined in line 503; used 1 times
PARTBUF defined in line 504; used 1 times
index defined in line 855; used 2 times
rindex defined in line 856; used 2 times
Last modified: 1991-09-08
Generated: 2016-12-26
Generated by src2html V0.67
page hit count: 5470
Valid CSS Valid XHTML 1.0 Strict