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, source, 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: #ifdef RDONLY
  88:             if (value(READONLY))
  89:                 printf(" [Read only]");
  90: #endif
  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: #ifdef ROOMTOBURN
 308:     switch (stbuf.st_mode & S_IFMT) {
 309: 
 310:     case S_IFBLK:
 311:         error(" Block special file");
 312: 
 313:     case S_IFCHR:
 314:         if (isatty(io))
 315:             error(" Teletype");
 316:         if (samei(&stbuf, "/dev/null"))
 317:             break;
 318:         error(" Character special file");
 319: 
 320:     case S_IFDIR:
 321:         error(" Directory");
 322: 
 323:     case S_IFREG:
 324:         i = read(io, (char *) &magic, sizeof(magic));
 325:         lseek(io, 0l, 0);
 326:         if (i != sizeof(magic))
 327:             break;
 328:         switch (magic) {
 329: 
 330:         case 0405:
 331:         case 0407:
 332:         case 0410:
 333:         case 0411:
 334:             error(" Executable");
 335: 
 336:         case 0177545:
 337:         case 0177555:
 338:             error(" Archive");
 339: 
 340:         default:
 341:             if (magic & 0100200)
 342:                 error(" Non-ascii file");
 343:             break;
 344:         }
 345:     }
 346: #endif
 347:     if (c == 'r')
 348:         setdot();
 349:     else
 350:         setall();
 351:     if (inopen && c == 'r')
 352:         undap1 = undap2 = dot + 1;
 353:     rop2();
 354:     rop3(c);
 355: }
 356: 
 357: rop2()
 358: {
 359: 
 360:     deletenone();
 361:     clrstats();
 362:     ignore(append(getfile, addr2));
 363: }
 364: 
 365: rop3(c)
 366:     int c;
 367: {
 368: 
 369:     if (iostats() == 0 && c == 'e')
 370:         edited++;
 371:     if (c == 'e') {
 372:         if (wasalt || firstpat) {
 373:             register line *addr = zero + oldadot;
 374: 
 375:             if (addr > dol)
 376:                 addr = dol;
 377:             if (firstpat) {
 378:                 globp = (*firstpat) ? firstpat : "$";
 379:                 commands(1,1);
 380:                 firstpat = 0;
 381:             } else if (addr >= one) {
 382:                 if (inopen)
 383:                     dot = addr;
 384:                 markpr(addr);
 385:             } else
 386:                 goto other;
 387:         } else
 388: other:
 389:             if (dol > zero) {
 390:                 if (inopen)
 391:                     dot = one;
 392:                 markpr(one);
 393:             }
 394:         undkind = UNDNONE;
 395:         if (inopen) {
 396:             vcline = 0;
 397:             vreplace(0, LINES, lineDOL());
 398:         }
 399:     }
 400:     if (laste) {
 401:         laste = 0;
 402:         sync();
 403:     }
 404: }
 405: 
 406: /*
 407:  * Are these two really the same inode?
 408:  */
 409: samei(sp, cp)
 410:     struct stat *sp;
 411:     char *cp;
 412: {
 413:     struct stat stb;
 414: 
 415:     if (stat(cp, &stb) < 0 || sp->st_dev != stb.st_dev)
 416:         return (0);
 417:     return (sp->st_ino == stb.st_ino);
 418: }
 419: 
 420: /* Returns from edited() */
 421: #define EDF 0       /* Edited file */
 422: #define NOTEDF  -1      /* Not edited file */
 423: #define PARTBUF 1       /* Write of partial buffer to Edited file */
 424: 
 425: /*
 426:  * Write a file.
 427:  */
 428: wop(dofname)
 429: bool dofname;   /* if 1 call filename, else use savedfile */
 430: {
 431:     register int c, exclam, nonexist;
 432:     line *saddr1, *saddr2;
 433:     struct stat stbuf;
 434: 
 435:     c = 0;
 436:     exclam = 0;
 437:     if (dofname) {
 438:         if (peekchar() == '!')
 439:             exclam++, ignchar();
 440:         ignore(skipwh());
 441:         while (peekchar() == '>')
 442:             ignchar(), c++, ignore(skipwh());
 443:         if (c != 0 && c != 2)
 444:             error("Write forms are 'w' and 'w>>'");
 445:         filename('w');
 446:     } else {
 447:         if (savedfile[0] == 0)
 448:             error("No file|No current filename");
 449:         saddr1=addr1;
 450:         saddr2=addr2;
 451:         addr1=one;
 452:         addr2=dol;
 453:         CP(file, savedfile);
 454:         if (inopen) {
 455:             vclrech(0);
 456:             splitw++;
 457:         }
 458:         lprintf("\"%s\"", file);
 459:     }
 460:     nonexist = stat(file, &stbuf);
 461:     switch (c) {
 462: 
 463:     case 0:
 464:         if (!exclam && (!value(WRITEANY) || value(READONLY)))
 465:         switch (edfile()) {
 466: 
 467:         case NOTEDF:
 468:             if (nonexist)
 469:                 break;
 470:             if ((stbuf.st_mode & S_IFMT) == S_IFCHR) {
 471:                 if (samei(&stbuf, "/dev/null"))
 472:                     break;
 473:                 if (samei(&stbuf, "/dev/tty"))
 474:                     break;
 475:             }
 476:             io = open(file, 1);
 477:             if (io < 0)
 478:                 syserror();
 479:             if (!isatty(io))
 480:                 serror(" File exists| File exists - use \"w! %s\" to overwrite", file);
 481:             close(io);
 482:             break;
 483: 
 484:         case EDF:
 485:             if (value(READONLY))
 486:                 error(" File is read only");
 487:             break;
 488:         case PARTBUF:
 489:             if (value(READONLY))
 490:                 error(" File is read only");
 491:             error(" Use \"w!\" to write partial buffer");
 492:         }
 493: cre:
 494:         synctmp();
 495: #ifdef V6
 496:         io = creat(file, 0644);
 497: #else
 498:         io = creat(file, 0666);
 499: #endif
 500:         if (io < 0)
 501:             syserror();
 502:         if (hush == 0)
 503:             if (nonexist)
 504:                 printf(" [New file]");
 505:             else if (value(WRITEANY) && edfile() != EDF)
 506:                 printf(" [Existing file]");
 507:         break;
 508: 
 509:     case 2:
 510:         io = open(file, 1);
 511:         if (io < 0) {
 512:             if (exclam || value(WRITEANY))
 513:                 goto cre;
 514:             syserror();
 515:         }
 516:         lseek(io, 0l, 2);
 517:         break;
 518:     }
 519:     putfile();
 520:     ignore(iostats());
 521:     if (c != 2 && addr1 == one && addr2 == dol) {
 522:         if (eq(file, savedfile))
 523:             edited = 1;
 524:         sync();
 525:     }
 526:     if (!dofname) {
 527:         addr1 = saddr1;
 528:         addr2 = saddr2;
 529:     }
 530: }
 531: 
 532: /*
 533:  * Is file the edited file?
 534:  * Work here is that it is not considered edited
 535:  * if this is a partial buffer, and distinguish
 536:  * all cases.
 537:  */
 538: edfile()
 539: {
 540: 
 541:     if (!edited || !eq(file, savedfile))
 542:         return (NOTEDF);
 543:     return (addr1 == one && addr2 == dol ? EDF : PARTBUF);
 544: }
 545: 
 546: /*
 547:  * Extract the next line from the io stream.
 548:  */
 549: static  char *nextip;
 550: 
 551: getfile()
 552: {
 553:     register short c;
 554:     register char *lp, *fp;
 555: 
 556:     lp = linebuf;
 557:     fp = nextip;
 558:     do {
 559:         if (--ninbuf < 0) {
 560:             ninbuf = read(io, genbuf, LBSIZE) - 1;
 561:             if (ninbuf < 0) {
 562:                 if (lp != linebuf) {
 563:                     printf(" [Incomplete last line]");
 564:                     break;
 565:                 }
 566:                 return (EOF);
 567:             }
 568:             fp = genbuf;
 569:         }
 570:         if (lp >= &linebuf[LBSIZE]) {
 571:             error(" Line too long");
 572:         }
 573:         c = *fp++;
 574:         if (c == 0) {
 575:             cntnull++;
 576:             continue;
 577:         }
 578:         if (c & QUOTE) {
 579:             cntodd++;
 580:             c &= TRIM;
 581:             if (c == 0)
 582:                 continue;
 583:         }
 584:         *lp++ = c;
 585:     } while (c != '\n');
 586:     cntch += lp - linebuf;
 587:     *--lp = 0;
 588:     nextip = fp;
 589:     cntln++;
 590:     return (0);
 591: }
 592: 
 593: /*
 594:  * Write a range onto the io stream.
 595:  */
 596: putfile()
 597: {
 598:     line *a1;
 599:     register char *fp, *lp;
 600:     register int nib;
 601: 
 602:     a1 = addr1;
 603:     clrstats();
 604:     cntln = addr2 - a1 + 1;
 605:     if (cntln == 0)
 606:         return;
 607:     nib = BUFSIZ;
 608:     fp = genbuf;
 609:     do {
 610:         getline(*a1++);
 611:         lp = linebuf;
 612:         for (;;) {
 613:             if (--nib < 0) {
 614:                 nib = fp - genbuf;
 615:                 if (write(io, genbuf, nib) != nib) {
 616:                     wrerror();
 617:                 }
 618:                 cntch += nib;
 619:                 nib = BUFSIZ - 1;
 620:                 fp = genbuf;
 621:             }
 622:             if ((*fp++ = *lp++) == 0) {
 623:                 fp[-1] = '\n';
 624:                 break;
 625:             }
 626:         }
 627:     } while (a1 <= addr2);
 628:     nib = fp - genbuf;
 629:     if (write(io, genbuf, nib) != nib) {
 630:         wrerror();
 631:     }
 632:     cntch += nib;
 633: }
 634: 
 635: /*
 636:  * A write error has occurred;  if the file being written was
 637:  * the edited file then we consider it to have changed since it is
 638:  * now likely scrambled.
 639:  */
 640: wrerror()
 641: {
 642: 
 643:     if (eq(file, savedfile) && edited)
 644:         change();
 645:     syserror();
 646: }
 647: 
 648: /*
 649:  * Source command, handles nested sources.
 650:  * Traps errors since it mungs unit 0 during the source.
 651:  */
 652: static  short slevel;
 653: 
 654: source(fil, okfail)
 655:     char *fil;
 656:     bool okfail;
 657: {
 658:     jmp_buf osetexit;
 659:     register int saveinp, ointty, oerrno;
 660: 
 661:     signal(SIGINT, SIG_IGN);
 662:     saveinp = dup(0);
 663:     if (saveinp < 0)
 664:         error("Too many nested sources");
 665:     close(0);
 666:     if (open(fil, 0) < 0) {
 667:         oerrno = errno;
 668:         setrupt();
 669:         dup(saveinp);
 670:         close(saveinp);
 671:         errno = oerrno;
 672:         if (!okfail)
 673:             filioerr(fil);
 674:         return;
 675:     }
 676:     slevel++;
 677:     ointty = intty;
 678:     intty = isatty(0);
 679:     oprompt = value(PROMPT);
 680:     value(PROMPT) &= intty;
 681:     getexit(osetexit);
 682:     setrupt();
 683:     if (setexit() == 0)
 684:         commands(1, 1);
 685:     else if (slevel > 1) {
 686:         close(0);
 687:         dup(saveinp);
 688:         close(saveinp);
 689:         slevel--;
 690:         resexit(osetexit);
 691:         reset();
 692:     }
 693:     intty = ointty;
 694:     value(PROMPT) = oprompt;
 695:     close(0);
 696:     dup(saveinp);
 697:     close(saveinp);
 698:     slevel--;
 699:     resexit(osetexit);
 700: }
 701: 
 702: /*
 703:  * Clear io statistics before a read or write.
 704:  */
 705: clrstats()
 706: {
 707: 
 708:     ninbuf = 0;
 709:     cntch = 0;
 710:     cntln = 0;
 711:     cntnull = 0;
 712:     cntodd = 0;
 713: }
 714: 
 715: /*
 716:  * Io is finished, close the unit and print statistics.
 717:  */
 718: iostats()
 719: {
 720: 
 721:     close(io);
 722:     io = -1;
 723:     if (hush == 0) {
 724:         if (value(TERSE))
 725:             printf(" %d/%D", cntln, cntch);
 726:         else
 727:             printf(" %d line%s, %D character%s", cntln, plural((long) cntln),
 728:                 cntch, plural(cntch));
 729:         if (cntnull || cntodd) {
 730:             printf(" (");
 731:             if (cntnull) {
 732:                 printf("%D null", cntnull);
 733:                 if (cntodd)
 734:                     printf(", ");
 735:             }
 736:             if (cntodd)
 737:                 printf("%D non-ASCII", cntodd);
 738:             putchar(')');
 739:         }
 740:         noonl();
 741:         flush();
 742:     }
 743:     return (cntnull != 0 || cntodd != 0);
 744: }

Defined functions

clrstats defined in line 705; used 2 times
edfile defined in line 538; used 2 times
filename defined in line 30; used 7 times
getargs defined in line 107; used 2 times
getfile defined in line 551; used 3 times
getone defined in line 271; used 3 times
glob defined in line 179; used 2 times
gscan defined in line 258; used 1 times
iostats defined in line 718; used 2 times
putfile defined in line 596; used 1 times
rop defined in line 292; used 2 times
rop2 defined in line 357; used 2 times
rop3 defined in line 365; used 2 times
samei defined in line 409; used 3 times
source defined in line 654; used 2 times
wop defined in line 428; used 2 times
wrerror defined in line 640; used 2 times

Defined variables

altdot defined in line 16; used 4 times
cntch defined in line 20; used 7 times
cntln defined in line 21; used 7 times
cntnull defined in line 22; used 6 times
cntodd defined in line 23; used 7 times
nextip defined in line 549; used 2 times
oldadot defined in line 17; used 3 times
slevel defined in line 652; used 4 times
wasalt defined in line 18; used 3 times

Defined macros

EDF defined in line 421; used 2 times
NOTEDF defined in line 422; used 1 times
PARTBUF defined in line 423; used 1 times
Last modified: 1980-09-13
Generated: 2016-12-26
Generated by src2html V0.67
page hit count: 1871
Valid CSS Valid XHTML 1.0 Strict