1: /* Copyright (c) 1981 Regents of the University of California */ 2: static char *sccsid = "@(#)ex_io.c 7.3 9/3/81"; 3: #include "ex.h" 4: #include "ex_argv.h" 5: #include "ex_temp.h" 6: #include "ex_tty.h" 7: #include "ex_vis.h" 8: 9: /* 10: * File input/output, source, preserve and recover 11: */ 12: 13: /* 14: * Following remember where . was in the previous file for return 15: * on file switching. 16: */ 17: int altdot; 18: int oldadot; 19: bool wasalt; 20: short isalt; 21: 22: long cntch; /* Count of characters on unit io */ 23: #ifndef VMUNIX 24: short cntln; /* Count of lines " */ 25: #else 26: int cntln; 27: #endif 28: long cntnull; /* Count of nulls " */ 29: long cntodd; /* Count of non-ascii characters " */ 30: 31: /* 32: * Parse file name for command encoded by comm. 33: * If comm is E then command is doomed and we are 34: * parsing just so user won't have to retype the name. 35: */ 36: filename(comm) 37: int comm; 38: { 39: register int c = comm, d; 40: register int i; 41: 42: d = getchar(); 43: if (endcmd(d)) { 44: if (savedfile[0] == 0 && comm != 'f') 45: error("No file|No current filename"); 46: CP(file, savedfile); 47: wasalt = (isalt > 0) ? isalt-1 : 0; 48: isalt = 0; 49: oldadot = altdot; 50: if (c == 'e' || c == 'E') 51: altdot = lineDOT(); 52: if (d == EOF) 53: ungetchar(d); 54: } else { 55: ungetchar(d); 56: getone(); 57: eol(); 58: if (savedfile[0] == 0 && c != 'E' && c != 'e') { 59: c = 'e'; 60: edited = 0; 61: } 62: wasalt = strcmp(file, altfile) == 0; 63: oldadot = altdot; 64: switch (c) { 65: 66: case 'f': 67: edited = 0; 68: /* fall into ... */ 69: 70: case 'e': 71: if (savedfile[0]) { 72: altdot = lineDOT(); 73: CP(altfile, savedfile); 74: } 75: CP(savedfile, file); 76: break; 77: 78: default: 79: if (file[0]) { 80: if (c != 'E') 81: altdot = lineDOT(); 82: CP(altfile, file); 83: } 84: break; 85: } 86: } 87: if (hush && comm != 'f' || comm == 'E') 88: return; 89: if (file[0] != 0) { 90: lprintf("\"%s\"", file); 91: if (comm == 'f') { 92: if (value(READONLY)) 93: printf(" [Read only]"); 94: if (!edited) 95: printf(" [Not edited]"); 96: if (tchng) 97: printf(" [Modified]"); 98: } 99: flush(); 100: } else 101: printf("No file "); 102: if (comm == 'f') { 103: if (!(i = lineDOL())) 104: i++; 105: printf(" line %d of %d --%ld%%--", lineDOT(), lineDOL(), 106: (long) 100 * lineDOT() / i); 107: } 108: } 109: 110: /* 111: * Get the argument words for a command into genbuf 112: * expanding # and %. 113: */ 114: getargs() 115: { 116: register int c; 117: register char *cp, *fp; 118: static char fpatbuf[32]; /* hence limit on :next +/pat */ 119: 120: pastwh(); 121: if (peekchar() == '+') { 122: for (cp = fpatbuf;;) { 123: c = *cp++ = getchar(); 124: if (cp >= &fpatbuf[sizeof(fpatbuf)]) 125: error("Pattern too long"); 126: if (c == '\\' && isspace(peekchar())) 127: c = getchar(); 128: if (c == EOF || isspace(c)) { 129: ungetchar(c); 130: *--cp = 0; 131: firstpat = &fpatbuf[1]; 132: break; 133: } 134: } 135: } 136: if (skipend()) 137: return (0); 138: CP(genbuf, "echo "); cp = &genbuf[5]; 139: for (;;) { 140: c = getchar(); 141: if (endcmd(c)) { 142: ungetchar(c); 143: break; 144: } 145: switch (c) { 146: 147: case '\\': 148: if (any(peekchar(), "#%|")) 149: c = getchar(); 150: /* fall into... */ 151: 152: default: 153: if (cp > &genbuf[LBSIZE - 2]) 154: flong: 155: error("Argument buffer overflow"); 156: *cp++ = c; 157: break; 158: 159: case '#': 160: fp = altfile; 161: if (*fp == 0) 162: error("No alternate filename@to substitute for #"); 163: goto filexp; 164: 165: case '%': 166: fp = savedfile; 167: if (*fp == 0) 168: error("No current filename@to substitute for %%"); 169: filexp: 170: while (*fp) { 171: if (cp > &genbuf[LBSIZE - 2]) 172: goto flong; 173: *cp++ = *fp++; 174: } 175: break; 176: } 177: } 178: *cp = 0; 179: return (1); 180: } 181: 182: /* 183: * Glob the argument words in genbuf, or if no globbing 184: * is implied, just split them up directly. 185: */ 186: glob(gp) 187: struct glob *gp; 188: { 189: int pvec[2]; 190: register char **argv = gp->argv; 191: register char *cp = gp->argspac; 192: register int c; 193: char ch; 194: int nleft = NCARGS; 195: 196: gp->argc0 = 0; 197: if (gscan() == 0) { 198: register char *v = genbuf + 5; /* strlen("echo ") */ 199: 200: for (;;) { 201: while (isspace(*v)) 202: v++; 203: if (!*v) 204: break; 205: *argv++ = cp; 206: while (*v && !isspace(*v)) 207: *cp++ = *v++; 208: *cp++ = 0; 209: gp->argc0++; 210: } 211: *argv = 0; 212: return; 213: } 214: if (pipe(pvec) < 0) 215: error("Can't make pipe to glob"); 216: pid = fork(); 217: io = pvec[0]; 218: if (pid < 0) { 219: close(pvec[1]); 220: error("Can't fork to do glob"); 221: } 222: if (pid == 0) { 223: int oerrno; 224: 225: close(1); 226: dup(pvec[1]); 227: close(pvec[0]); 228: close(2); /* so errors don't mess up the screen */ 229: open("/dev/null", 1); 230: execl(svalue(SHELL), "sh", "-c", genbuf, 0); 231: oerrno = errno; close(1); dup(2); errno = oerrno; 232: filioerr(svalue(SHELL)); 233: } 234: close(pvec[1]); 235: do { 236: *argv = cp; 237: for (;;) { 238: if (read(io, &ch, 1) != 1) { 239: close(io); 240: c = -1; 241: } else 242: c = ch & TRIM; 243: if (c <= 0 || isspace(c)) 244: break; 245: *cp++ = c; 246: if (--nleft <= 0) 247: error("Arg list too long"); 248: } 249: if (cp != *argv) { 250: --nleft; 251: *cp++ = 0; 252: gp->argc0++; 253: if (gp->argc0 >= NARGS) 254: error("Arg list too long"); 255: argv++; 256: } 257: } while (c >= 0); 258: waitfor(); 259: if (gp->argc0 == 0) 260: error("No match"); 261: } 262: 263: /* 264: * Scan genbuf for shell metacharacters. 265: * Set is union of v7 shell and csh metas. 266: */ 267: gscan() 268: { 269: register char *cp; 270: 271: for (cp = genbuf; *cp; cp++) 272: if (any(*cp, "~{[*?$`'\"\\")) 273: return (1); 274: return (0); 275: } 276: 277: /* 278: * Parse one filename into file. 279: */ 280: struct glob G; 281: getone() 282: { 283: register char *str; 284: 285: if (getargs() == 0) 286: error("Missing filename"); 287: glob(&G); 288: if (G.argc0 > 1) 289: error("Ambiguous|Too many file names"); 290: str = G.argv[G.argc0 - 1]; 291: if (strlen(str) > FNSIZE - 4) 292: error("Filename too long"); 293: samef: 294: CP(file, str); 295: } 296: 297: /* 298: * Read a file from the world. 299: * C is command, 'e' if this really an edit (or a recover). 300: */ 301: rop(c) 302: int c; 303: { 304: register int i; 305: struct stat stbuf; 306: short magic; 307: static int ovro; /* old value(READONLY) */ 308: static int denied; /* 1 if READONLY was set due to file permissions */ 309: 310: io = open(file, 0); 311: if (io < 0) { 312: if (c == 'e' && errno == ENOENT) { 313: edited++; 314: /* 315: * If the user just did "ex foo" he is probably 316: * creating a new file. Don't be an error, since 317: * this is ugly, and it screws up the + option. 318: */ 319: if (!seenprompt) { 320: printf(" [New file]"); 321: noonl(); 322: return; 323: } 324: } 325: syserror(); 326: } 327: if (fstat(io, &stbuf)) 328: syserror(); 329: switch (stbuf.st_mode & S_IFMT) { 330: 331: case S_IFBLK: 332: error(" Block special file"); 333: 334: case S_IFCHR: 335: if (isatty(io)) 336: error(" Teletype"); 337: if (samei(&stbuf, "/dev/null")) 338: break; 339: error(" Character special file"); 340: 341: case S_IFDIR: 342: error(" Directory"); 343: 344: case S_IFREG: 345: #ifdef CRYPT 346: if (xflag) 347: break; 348: #endif 349: i = read(io, (char *) &magic, sizeof(magic)); 350: lseek(io, 0l, 0); 351: if (i != sizeof(magic)) 352: break; 353: switch (magic) { 354: 355: case 0405: /* data overlay on exec */ 356: case 0407: /* unshared */ 357: case 0410: /* shared text */ 358: case 0411: /* separate I/D */ 359: case 0413: /* VM/Unix demand paged */ 360: case 0430: /* PDP-11 Overlay shared */ 361: case 0431: /* PDP-11 Overlay sep I/D */ 362: error(" Executable"); 363: 364: /* 365: * We do not forbid the editing of portable archives 366: * because it is reasonable to edit them, especially 367: * if they are archives of text files. This is 368: * especially useful if you archive source files together 369: * and copy them to another system with ~%take, since 370: * the files sometimes show up munged and must be fixed. 371: */ 372: case 0177545: 373: case 0177555: 374: error(" Archive"); 375: 376: default: 377: #ifdef mbb 378: /* C/70 has a 10 bit byte */ 379: if (magic & 03401600) 380: #else 381: /* Everybody else has an 8 bit byte */ 382: if (magic & 0100200) 383: #endif 384: error(" Non-ascii file"); 385: break; 386: } 387: } 388: if (c != 'r') { 389: if (value(READONLY) && denied) { 390: value(READONLY) = ovro; 391: denied = 0; 392: } 393: if ((stbuf.st_mode & 0222) == 0 || access(file, 2) < 0) { 394: ovro = value(READONLY); 395: denied = 1; 396: value(READONLY) = 1; 397: } 398: } 399: if (value(READONLY)) { 400: printf(" [Read only]"); 401: flush(); 402: } 403: if (c == 'r') 404: setdot(); 405: else 406: setall(); 407: if (FIXUNDO && inopen && c == 'r') 408: undap1 = undap2 = dot + 1; 409: rop2(); 410: rop3(c); 411: } 412: 413: rop2() 414: { 415: line *first, *last, *a; 416: 417: deletenone(); 418: clrstats(); 419: first = addr2 + 1; 420: ignore(append(getfile, addr2)); 421: last = dot; 422: for (a=first; a<=last; a++) { 423: if (a==first+5 && last-first > 10) 424: a = last - 4; 425: getline(*a); 426: checkmodeline(linebuf); 427: } 428: } 429: 430: rop3(c) 431: int c; 432: { 433: 434: if (iostats() == 0 && c == 'e') 435: edited++; 436: if (c == 'e') { 437: if (wasalt || firstpat) { 438: register line *addr = zero + oldadot; 439: 440: if (addr > dol) 441: addr = dol; 442: if (firstpat) { 443: globp = (*firstpat) ? firstpat : "$"; 444: commands(1,1); 445: firstpat = 0; 446: } else if (addr >= one) { 447: if (inopen) 448: dot = addr; 449: markpr(addr); 450: } else 451: goto other; 452: } else 453: other: 454: if (dol > zero) { 455: if (inopen) 456: dot = one; 457: markpr(one); 458: } 459: if(FIXUNDO) 460: undkind = UNDNONE; 461: if (inopen) { 462: vcline = 0; 463: vreplace(0, LINES, lineDOL()); 464: } 465: } 466: if (laste) { 467: #ifdef VMUNIX 468: tlaste(); 469: #endif 470: laste = 0; 471: sync(); 472: } 473: } 474: 475: /* 476: * Are these two really the same inode? 477: */ 478: samei(sp, cp) 479: struct stat *sp; 480: char *cp; 481: { 482: struct stat stb; 483: 484: if (stat(cp, &stb) < 0 || sp->st_dev != stb.st_dev) 485: return (0); 486: return (sp->st_ino == stb.st_ino); 487: } 488: 489: /* Returns from edited() */ 490: #define EDF 0 /* Edited file */ 491: #define NOTEDF -1 /* Not edited file */ 492: #define PARTBUF 1 /* Write of partial buffer to Edited file */ 493: 494: /* 495: * Write a file. 496: */ 497: wop(dofname) 498: bool dofname; /* if 1 call filename, else use savedfile */ 499: { 500: register int c, exclam, nonexist; 501: line *saddr1, *saddr2; 502: struct stat stbuf; 503: 504: c = 0; 505: exclam = 0; 506: if (dofname) { 507: if (peekchar() == '!') 508: exclam++, ignchar(); 509: ignore(skipwh()); 510: while (peekchar() == '>') 511: ignchar(), c++, ignore(skipwh()); 512: if (c != 0 && c != 2) 513: error("Write forms are 'w' and 'w>>'"); 514: filename('w'); 515: } else { 516: if (savedfile[0] == 0) 517: error("No file|No current filename"); 518: saddr1=addr1; 519: saddr2=addr2; 520: addr1=one; 521: addr2=dol; 522: CP(file, savedfile); 523: if (inopen) { 524: vclrech(0); 525: splitw++; 526: } 527: lprintf("\"%s\"", file); 528: } 529: nonexist = stat(file, &stbuf); 530: switch (c) { 531: 532: case 0: 533: if (!exclam && (!value(WRITEANY) || value(READONLY))) 534: switch (edfile()) { 535: 536: case NOTEDF: 537: if (nonexist) 538: break; 539: if ((stbuf.st_mode & S_IFMT) == S_IFCHR) { 540: if (samei(&stbuf, "/dev/null")) 541: break; 542: if (samei(&stbuf, "/dev/tty")) 543: break; 544: } 545: io = open(file, 1); 546: if (io < 0) 547: syserror(); 548: if (!isatty(io)) 549: serror(" File exists| File exists - use \"w! %s\" to overwrite", file); 550: close(io); 551: break; 552: 553: case EDF: 554: if (value(READONLY)) 555: error(" File is read only"); 556: break; 557: 558: case PARTBUF: 559: if (value(READONLY)) 560: error(" File is read only"); 561: error(" Use \"w!\" to write partial buffer"); 562: } 563: cre: 564: /* 565: synctmp(); 566: */ 567: #ifdef V6 568: io = creat(file, 0644); 569: #else 570: io = creat(file, 0666); 571: #endif 572: if (io < 0) 573: syserror(); 574: writing = 1; 575: if (hush == 0) 576: if (nonexist) 577: printf(" [New file]"); 578: else if (value(WRITEANY) && edfile() != EDF) 579: printf(" [Existing file]"); 580: break; 581: 582: case 2: 583: io = open(file, 1); 584: if (io < 0) { 585: if (exclam || value(WRITEANY)) 586: goto cre; 587: syserror(); 588: } 589: lseek(io, 0l, 2); 590: break; 591: } 592: putfile(0); 593: ignore(iostats()); 594: if (c != 2 && addr1 == one && addr2 == dol) { 595: if (eq(file, savedfile)) 596: edited = 1; 597: sync(); 598: } 599: if (!dofname) { 600: addr1 = saddr1; 601: addr2 = saddr2; 602: } 603: writing = 0; 604: } 605: 606: /* 607: * Is file the edited file? 608: * Work here is that it is not considered edited 609: * if this is a partial buffer, and distinguish 610: * all cases. 611: */ 612: edfile() 613: { 614: 615: if (!edited || !eq(file, savedfile)) 616: return (NOTEDF); 617: return (addr1 == one && addr2 == dol ? EDF : PARTBUF); 618: } 619: 620: /* 621: * Extract the next line from the io stream. 622: */ 623: char *nextip; 624: 625: getfile() 626: { 627: register short c; 628: register char *lp, *fp; 629: 630: lp = linebuf; 631: fp = nextip; 632: do { 633: if (--ninbuf < 0) { 634: ninbuf = read(io, genbuf, LBSIZE) - 1; 635: if (ninbuf < 0) { 636: if (lp != linebuf) { 637: lp++; 638: printf(" [Incomplete last line]"); 639: break; 640: } 641: return (EOF); 642: } 643: #ifdef CRYPT 644: fp = genbuf; 645: while(fp < &genbuf[ninbuf]) { 646: if (*fp++ & 0200) { 647: if (kflag) 648: crblock(perm, genbuf, ninbuf+1, 649: cntch); 650: break; 651: } 652: } 653: #endif 654: fp = genbuf; 655: cntch += ninbuf+1; 656: } 657: if (lp >= &linebuf[LBSIZE]) { 658: error(" Line too long"); 659: } 660: c = *fp++; 661: if (c == 0) { 662: cntnull++; 663: continue; 664: } 665: if (c & QUOTE) { 666: cntodd++; 667: c &= TRIM; 668: if (c == 0) 669: continue; 670: } 671: *lp++ = c; 672: } while (c != '\n'); 673: *--lp = 0; 674: nextip = fp; 675: cntln++; 676: return (0); 677: } 678: 679: /* 680: * Write a range onto the io stream. 681: */ 682: putfile(isfilter) 683: int isfilter; 684: { 685: line *a1; 686: register char *fp, *lp; 687: register int nib; 688: 689: a1 = addr1; 690: clrstats(); 691: cntln = addr2 - a1 + 1; 692: if (cntln == 0) 693: return; 694: nib = BUFSIZ; 695: fp = genbuf; 696: do { 697: getline(*a1++); 698: lp = linebuf; 699: for (;;) { 700: if (--nib < 0) { 701: nib = fp - genbuf; 702: #ifdef CRYPT 703: if(kflag && !isfilter) 704: crblock(perm, genbuf, nib, cntch); 705: #endif 706: if (write(io, genbuf, nib) != nib) { 707: wrerror(); 708: } 709: cntch += nib; 710: nib = BUFSIZ - 1; 711: fp = genbuf; 712: } 713: if ((*fp++ = *lp++) == 0) { 714: fp[-1] = '\n'; 715: break; 716: } 717: } 718: } while (a1 <= addr2); 719: nib = fp - genbuf; 720: #ifdef CRYPT 721: if(kflag && !isfilter) 722: crblock(perm, genbuf, nib, cntch); 723: #endif 724: if (write(io, genbuf, nib) != nib) { 725: wrerror(); 726: } 727: cntch += nib; 728: } 729: 730: /* 731: * A write error has occurred; if the file being written was 732: * the edited file then we consider it to have changed since it is 733: * now likely scrambled. 734: */ 735: wrerror() 736: { 737: 738: if (eq(file, savedfile) && edited) 739: change(); 740: syserror(); 741: } 742: 743: /* 744: * Source command, handles nested sources. 745: * Traps errors since it mungs unit 0 during the source. 746: */ 747: short slevel; 748: short ttyindes; 749: 750: source(fil, okfail) 751: char *fil; 752: bool okfail; 753: { 754: jmp_buf osetexit; 755: register int saveinp, ointty, oerrno; 756: char savepeekc, *saveglobp; 757: 758: signal(SIGINT, SIG_IGN); 759: saveinp = dup(0); 760: savepeekc = peekc; 761: saveglobp = globp; 762: peekc = 0; globp = 0; 763: if (saveinp < 0) 764: error("Too many nested sources"); 765: if (slevel <= 0) 766: ttyindes = saveinp; 767: close(0); 768: if (open(fil, 0) < 0) { 769: oerrno = errno; 770: setrupt(); 771: dup(saveinp); 772: close(saveinp); 773: errno = oerrno; 774: if (!okfail) 775: filioerr(fil); 776: return; 777: } 778: slevel++; 779: ointty = intty; 780: intty = isatty(0); 781: oprompt = value(PROMPT); 782: value(PROMPT) &= intty; 783: getexit(osetexit); 784: setrupt(); 785: if (setexit() == 0) 786: commands(1, 1); 787: else if (slevel > 1) { 788: close(0); 789: dup(saveinp); 790: close(saveinp); 791: slevel--; 792: resexit(osetexit); 793: reset(); 794: } 795: intty = ointty; 796: value(PROMPT) = oprompt; 797: close(0); 798: dup(saveinp); 799: close(saveinp); 800: globp = saveglobp; 801: peekc = savepeekc; 802: slevel--; 803: resexit(osetexit); 804: } 805: 806: /* 807: * Clear io statistics before a read or write. 808: */ 809: clrstats() 810: { 811: 812: ninbuf = 0; 813: cntch = 0; 814: cntln = 0; 815: cntnull = 0; 816: cntodd = 0; 817: } 818: 819: /* 820: * Io is finished, close the unit and print statistics. 821: */ 822: iostats() 823: { 824: 825: close(io); 826: io = -1; 827: if (hush == 0) { 828: if (value(TERSE)) 829: printf(" %d/%D", cntln, cntch); 830: else 831: printf(" %d line%s, %D character%s", cntln, plural((long) cntln), 832: cntch, plural(cntch)); 833: if (cntnull || cntodd) { 834: printf(" ("); 835: if (cntnull) { 836: printf("%D null", cntnull); 837: if (cntodd) 838: printf(", "); 839: } 840: if (cntodd) 841: printf("%D non-ASCII", cntodd); 842: putchar(')'); 843: } 844: noonl(); 845: flush(); 846: } 847: return (cntnull != 0 || cntodd != 0); 848: } 849: 850: #if USG | USG3TTY 851: /* It's so wonderful how we all speak the same language... */ 852: # define index strchr 853: # define rindex strrchr 854: #endif 855: 856: checkmodeline(line) 857: char *line; 858: { 859: char *beg, *end; 860: char cmdbuf[1024]; 861: char *index(), *rindex(); 862: 863: beg = index(line, ':'); 864: if (beg == NULL) 865: return; 866: if (beg[-2] != 'e' && beg[-2] != 'v') return; 867: if (beg[-1] != 'x' && beg[-1] != 'i') return; 868: 869: strncpy(cmdbuf, beg+1, sizeof cmdbuf); 870: end = rindex(cmdbuf, ':'); 871: if (end == NULL) 872: return; 873: *end = 0; 874: globp = cmdbuf; 875: commands(1, 1); 876: }