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: }