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