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