1: /*************************************************************************** 2: * This program is Copyright (C) 1986, 1987, 1988 by Jonathan Payne. JOVE * 3: * is provided to you without charge, and with no warranty. You may give * 4: * away copies of JOVE, including sources, provided that this notice is * 5: * included in all the files. * 6: ***************************************************************************/ 7: 8: #include "jove.h" 9: #include "io.h" 10: #include "termcap.h" 11: 12: #ifdef IPROCS 13: # include <signal.h> 14: #endif 15: 16: #ifdef MAC 17: # include "mac.h" 18: #else 19: # include <sys/stat.h> 20: #endif 21: 22: #ifdef UNIX 23: #include <sys/file.h> 24: #ifdef YP_PASSWD 25: #include <rpcsvc/ypclnt.h> 26: #endif 27: #endif 28: 29: #ifdef MSDOS 30: #include <fcntl.h> 31: #include <io.h> 32: #ifdef CHDIR 33: #include <direct.h> 34: #include <dos.h> 35: #endif 36: #endif /* MSDOS */ 37: #include <errno.h> 38: 39: #ifdef MAC 40: # undef private 41: # define private 42: #endif 43: 44: #ifdef LINT_ARGS 45: private struct block 46: * b_unlink(struct block *), 47: * lookup(short); 48: 49: private char 50: * dbackup(char *, char *, char), 51: #if defined(MSDOS) && defined(CHDIR) 52: * fixpath(char *), 53: #endif 54: * getblock(disk_line, int); 55: 56: private void 57: #if defined(MSDOS) && defined(CHDIR) 58: abspath(char *, char *), 59: #endif 60: fake_blkio(struct block *, int (*)()), 61: LRUunlink(struct block *), 62: real_blkio(struct block *, int (*)()); 63: 64: private int 65: #if defined(MSDOS) && defined(CHDIR) 66: Dchdir(char *), 67: #endif 68: dfollow(char *, char *); 69: 70: #else 71: private struct block 72: * b_unlink(), 73: * lookup(); 74: 75: private char 76: * dbackup(), 77: #if defined(MSDOS) && defined(CHDIR) 78: * fixpath(), 79: #endif 80: * getblock(); 81: 82: private void 83: #if defined(MSDOS) && defined(CHDIR) 84: abspath(), 85: #endif 86: fake_blkio(), 87: LRUunlink(), 88: real_blkio(); 89: 90: private int 91: #if defined(MSDOS) && defined(CHDIR) 92: Dchdir(), 93: #endif 94: dfollow(); 95: #endif /* LINT_ARGS */ 96: 97: #ifdef MAC 98: # undef private 99: # define private static 100: #endif 101: 102: 103: #ifndef W_OK 104: # define W_OK 2 105: # define F_OK 0 106: #endif 107: 108: long io_chars; /* number of chars in this open_file */ 109: int io_lines; /* number of lines in this open_file */ 110: 111: #if defined(VMUNIX)||defined(MSDOS) 112: char iobuff[LBSIZE], 113: genbuf[LBSIZE], 114: linebuf[LBSIZE]; 115: #else 116: char *iobuff, 117: *genbuf, 118: *linebuf; 119: #endif 120: 121: #ifdef BACKUPFILES 122: int BkupOnWrite = 0; 123: #endif 124: 125: void 126: close_file(fp) 127: File *fp; 128: { 129: if (fp) { 130: if (fp->f_flags & F_TELLALL) 131: add_mess(" %d lines, %D characters.", 132: io_lines, 133: io_chars); 134: f_close(fp); 135: } 136: } 137: 138: /* Write the region from line1/char1 to line2/char2 to FP. This 139: never CLOSES the file since we don't know if we want to. */ 140: 141: int EndWNewline = 1; 142: 143: void 144: putreg(fp, line1, char1, line2, char2, makesure) 145: register File *fp; 146: Line *line1, 147: *line2; 148: { 149: register int c; 150: register char *lp; 151: 152: if (makesure) 153: (void) fixorder(&line1, &char1, &line2, &char2); 154: while (line1 != line2->l_next) { 155: lp = lcontents(line1) + char1; 156: if (line1 == line2) { 157: fputnchar(lp, (char2 - char1), fp); 158: io_chars += (char2 - char1); 159: } else while (c = *lp++) { 160: putc(c, fp); 161: io_chars += 1; 162: } 163: if (line1 != line2) { 164: io_lines += 1; 165: io_chars += 1; 166: #ifdef MSDOS 167: putc('\r', fp); 168: #endif /* MSDOS */ 169: putc('\n', fp); 170: } 171: line1 = line1->l_next; 172: char1 = 0; 173: } 174: flush(fp); 175: } 176: 177: void 178: read_file(file, is_insert) 179: char *file; 180: { 181: Bufpos save; 182: File *fp; 183: if (!is_insert) { 184: curbuf->b_ntbf = 0; 185: set_ino(curbuf); 186: } 187: fp = open_file(file, iobuff, F_READ, !COMPLAIN, !QUIET); 188: if (fp == NIL) { 189: if (!is_insert && errno == ENOENT) 190: s_mess("(new file)"); 191: else 192: s_mess(IOerr("open", file)); 193: return; 194: } 195: DOTsave(&save); 196: dofread(fp); 197: if (is_insert && io_chars > 0) { 198: modify(); 199: set_mark(); 200: } 201: SetDot(&save); 202: getDOT(); 203: close_file(fp); 204: } 205: 206: void 207: dofread(fp) 208: register File *fp; 209: { 210: char end[LBSIZE]; 211: int xeof = 0; 212: Line *savel = curline; 213: int savec = curchar; 214: extern disk_line f_getputl(); 215: 216: strcpy(end, linebuf + curchar); 217: xeof = f_gets(fp, linebuf + curchar, LBSIZE - curchar); 218: SavLine(curline, linebuf); 219: if (!xeof) do { 220: curline = listput(curbuf, curline); 221: xeof = f_getputl(curline, fp); 222: } while (!xeof); 223: getDOT(); 224: linecopy(linebuf, (curchar = strlen(linebuf)), end); 225: SavLine(curline, linebuf); 226: IFixMarks(savel, savec, curline, curchar); 227: } 228: 229: void 230: SaveFile() 231: { 232: if (IsModified(curbuf)) { 233: if (curbuf->b_fname == 0) 234: WriteFile(); 235: else { 236: filemunge(curbuf->b_fname); 237: #if !defined(MAC) && !defined(MSDOS) 238: chk_mtime(curbuf, curbuf->b_fname, "save"); 239: #endif /* !MAC && !MSDOS */ 240: file_write(curbuf->b_fname, 0); 241: unmodify(); 242: } 243: } else 244: message("No changes need to be written."); 245: } 246: 247: char *HomeDir; /* home directory */ 248: int HomeLen = -1; /* length of home directory string */ 249: 250: #ifndef CHDIR 251: 252: char * 253: pr_name(fname, okay_home) 254: char *fname; 255: { 256: if (fname == 0) 257: return 0; 258: 259: if (okay_home == YES && strncmp(fname, HomeDir, HomeLen) == 0) { 260: static char name_buf[100]; 261: 262: sprintf(name_buf, "~%s", fname + HomeLen); 263: return name_buf; 264: } 265: 266: return fname; 267: } 268: 269: #else 270: 271: #define NDIRS 5 272: 273: private char *DirStack[NDIRS] = {0}; 274: private int DirSP = 0; /* Directory stack pointer */ 275: #define PWD (DirStack[DirSP]) 276: 277: char * 278: pwd() 279: { 280: return PWD; 281: } 282: 283: char * 284: pr_name(fname, okay_home) 285: char *fname; 286: { 287: int n; 288: 289: if (fname == 0) 290: return 0; 291: n = numcomp(fname, PWD); 292: 293: if ((PWD[n] == 0) && /* Matched to end of PWD */ 294: (fname[n] == '/')) 295: return fname + n + 1; 296: 297: if (okay_home == YES && strcmp(HomeDir, "/") != 0 && strncmp(fname, HomeDir, HomeLen) == 0) { 298: static char name_buf[100]; 299: 300: sprintf(name_buf, "~%s", fname + HomeLen); 301: return name_buf; 302: } 303: 304: return fname; /* return entire path name */ 305: } 306: 307: extern unsigned int fmask; 308: 309: Chdir() 310: { 311: char dirbuf[FILESIZE]; 312: 313: #ifdef MSDOS 314: fmask = 0x10; 315: #endif 316: (void) ask_file((char *) 0, PWD, dirbuf); 317: #ifdef MSDOS 318: fmask = 0x13; 319: if (Dchdir(dirbuf) == -1) 320: #else 321: if (chdir(dirbuf) == -1) 322: #endif 323: { 324: s_mess("cd: cannot change into %s.", dirbuf); 325: return; 326: } 327: UpdModLine = YES; 328: setCWD(dirbuf); 329: prCWD(); 330: #ifdef MAC 331: Bufchange++; 332: #endif 333: } 334: 335: #if defined(UNIX) && !defined(JOB_CONTROL) 336: char * 337: getwd(buffer) 338: char *buffer; 339: { 340: Buffer *old = curbuf; 341: char *ret_val; 342: 343: SetBuf(do_select((Window *) 0, "pwd-output")); 344: curbuf->b_type = B_PROCESS; 345: (void) UnixToBuf("pwd-output", NO, 0, YES, "/bin/pwd", (char *) 0); 346: ToFirst(); 347: strcpy(buffer, linebuf); 348: SetBuf(old); 349: return buffer; 350: } 351: #endif /* UNIX && !JOB_CONTROL */ 352: 353: setCWD(d) 354: char *d; 355: { 356: if (PWD == 0) 357: PWD = malloc((unsigned) strlen(d) + 1); 358: else { 359: extern char *ralloc(); 360: 361: PWD = ralloc(PWD, strlen(d) + 1); 362: } 363: strcpy(PWD, d); 364: } 365: 366: getCWD() 367: { 368: char *cwd; 369: char pathname[FILESIZE]; 370: #if defined(UNIX) && defined(JOB_CONTROL) 371: extern char *getwd(); 372: #endif 373: #if defined(MSDOS) 374: extern char *getcwd(); 375: #endif 376: 377: #ifndef MSDOS 378: cwd = getenv("CWD"); 379: if (cwd == 0) 380: cwd = getenv("PWD"); 381: if (cwd == 0) 382: cwd = getwd(pathname); 383: #else /* MSDOS */ 384: cwd = fixpath(getcwd(pathname, FILESIZE)); 385: #endif /* MSDOS */ 386: 387: setCWD(cwd); 388: } 389: 390: prDIRS() 391: { 392: register int i; 393: 394: s_mess(": %f "); 395: for (i = DirSP; i >= 0; i--) 396: add_mess("%s ", pr_name(DirStack[i], YES)); 397: } 398: 399: prCWD() 400: { 401: s_mess(": %f => \"%s\"", PWD); 402: } 403: 404: Pushd() 405: { 406: char *newdir, 407: dirbuf[FILESIZE]; 408: 409: #ifdef MSDOS 410: fmask = 0x10; 411: #endif 412: newdir = ask_file((char *) 0, NullStr, dirbuf); 413: #ifdef MSDOS 414: fmask = 0x13; 415: #endif 416: UpdModLine = YES; 417: if (*newdir == 0) { /* Wants to swap top two entries */ 418: char *old_top; 419: 420: if (DirSP == 0) 421: complain("pushd: no other directory."); 422: old_top = PWD; 423: DirStack[DirSP] = DirStack[DirSP - 1]; 424: DirStack[DirSP - 1] = old_top; 425: #ifdef MSDOS 426: (void) Dchdir(PWD); 427: #else 428: (void) chdir(PWD); 429: #endif 430: } else { 431: #ifdef MSDOS 432: if (Dchdir(dirbuf) == -1) { 433: #else 434: if (chdir(dirbuf) == -1) { 435: #endif 436: s_mess("pushd: cannot change into %s.", dirbuf); 437: return; 438: } 439: 440: if (DirSP + 1 >= NDIRS) 441: complain("pushd: full stack; max of %d pushes.", NDIRS); 442: DirSP += 1; 443: setCWD(dirbuf); 444: } 445: prDIRS(); 446: } 447: 448: Popd() 449: { 450: if (DirSP == 0) 451: complain("popd: directory stack is empty."); 452: UpdModLine = YES; 453: free(PWD); 454: PWD = 0; 455: DirSP -= 1; 456: #ifdef MSDOS 457: (void) Dchdir(PWD); /* If this doesn't work, we's in deep shit. */ 458: #else 459: (void) chdir(PWD); /* If this doesn't work, we's in deep shit. */ 460: #endif 461: prDIRS(); 462: } 463: 464: private char * 465: dbackup(base, offset, c) 466: register char *base, 467: *offset, 468: c; 469: { 470: while (offset > base && *--offset != c) 471: ; 472: return offset; 473: } 474: 475: private 476: dfollow(file, into) 477: char *file, 478: *into; 479: { 480: char *dp, 481: #ifdef MSDOS 482: filefix[FILESIZE], 483: #endif 484: *sp; 485: 486: #ifndef MSDOS 487: if (*file == '/') { /* Absolute pathname */ 488: strcpy(into, "/"); 489: file += 1; 490: } else 491: strcpy(into, PWD); 492: #else 493: abspath(file, filefix); /* convert to absolute pathname */ 494: strcpy(into, filefix); /* and forget about drives */ 495: into[3] = 0; 496: into = &(into[2]); 497: file = &(filefix[3]); 498: #endif 499: dp = into + strlen(into); 500: 501: sp = file; 502: do { 503: if (*file == 0) 504: break; 505: if (sp = index(file, '/')) 506: *sp = 0; 507: if (strcmp(file, ".") == 0) 508: ; /* So it will get to the end of the loop */ 509: else if (strcmp(file, "..") == 0) { 510: *(dp = dbackup(into, dp, '/')) = 0; 511: if (dp == into) 512: strcpy(into, "/"), dp = into + 1; 513: } else { 514: if (into[strlen(into) - 1] != '/') 515: (void) strcat(into, "/"), dp += 1; 516: (void) strcat(into, file); 517: dp += strlen(file); /* stay at the end */ 518: } 519: file = sp + 1; 520: } while (sp != 0); 521: } 522: 523: #endif /* CHDIR */ 524: 525: #ifdef UNIX 526: 527: #ifndef NOGETPWENT 528: 529: #include <pwd.h> 530: private 531: get_hdir(user, buf) 532: register char *user, 533: *buf; 534: { 535: register struct passwd *pw; 536: 537: setpwent(); 538: while ((pw = getpwent()) != (struct passwd *)NULL) 539: if (strcmp(pw->pw_name, user) == 0) { 540: strncpy(buf, pw->pw_dir, FILESIZE); 541: buf[FILESIZE-1] = '\0'; 542: endpwent(); 543: return; 544: } 545: endpwent(); 546: complain("[unknown user: %s]", user); 547: } 548: 549: #else /* NOGETPWENT */ 550: 551: private 552: get_hdir(user, buf) 553: register char *user, 554: *buf; 555: { 556: char fbuf[LBSIZE], 557: pattern[100]; 558: register int u_len; 559: File *fp; 560: 561: u_len = strlen(user); 562: fp = open_file("/etc/passwd", fbuf, F_READ, COMPLAIN, QUIET); 563: sprintf(pattern, "%s:[^:]*:[^:]*:[^:]*:[^:]*:\\([^:]*\\):", user); 564: while (f_gets(fp, genbuf, LBSIZE) != EOF) 565: if ((strncmp(genbuf, user, u_len) == 0) && 566: (LookingAt(pattern, genbuf, 0))) { 567: putmatch(1, buf, FILESIZE); 568: close_file(fp); 569: return; 570: } 571: f_close(fp); 572: complain("[unknown user: %s]", user); 573: } 574: 575: #endif /* NOGETPWENT */ 576: #endif /* UNIX */ 577: 578: void 579: PathParse(name, intobuf) 580: char *name, 581: *intobuf; 582: { 583: char localbuf[FILESIZE]; 584: 585: intobuf[0] = localbuf[0] = '\0'; 586: if (*name == '\0') 587: return; 588: if (*name == '~') { 589: if (name[1] == '/' || name[1] == '\0') { 590: strcpy(localbuf, HomeDir); 591: name += 1; 592: } 593: #if !(defined(MSDOS) || defined(MAC)) /* may add for mac in future */ 594: else { 595: char *uendp = index(name, '/'), 596: unamebuf[30]; 597: 598: if (uendp == 0) 599: uendp = name + strlen(name); 600: name += 1; 601: null_ncpy(unamebuf, name, uendp - name); 602: get_hdir(unamebuf, localbuf); 603: name = uendp; 604: } 605: #endif 606: } 607: #ifndef MSDOS 608: else if (*name == '\\') 609: name += 1; 610: #endif /* MSDOS */ 611: (void) strcat(localbuf, name); 612: #ifdef CHDIR 613: dfollow(localbuf, intobuf); 614: #else 615: strcpy(intobuf, localbuf); 616: #endif 617: } 618: 619: void 620: filemunge(newname) 621: char *newname; 622: { 623: struct stat stbuf; 624: 625: if (newname == 0) 626: return; 627: if (stat(newname, &stbuf)) 628: return; 629: #ifndef MSDOS 630: if (((stbuf.st_dev != curbuf->b_dev) || 631: (stbuf.st_ino != curbuf->b_ino)) && 632: #else /* MSDOS */ 633: if ( /* (stbuf.st_ino != curbuf->b_ino) && */ 634: #endif /* MSDOS */ 635: #ifndef MAC 636: ((stbuf.st_mode & S_IFMT) != S_IFCHR) && 637: #endif 638: (strcmp(newname, curbuf->b_fname) != 0)) { 639: rbell(); 640: confirm("\"%s\" already exists; overwrite it? ", newname); 641: } 642: } 643: 644: void 645: WrtReg() 646: { 647: DoWriteReg(NO); 648: } 649: 650: void 651: AppReg() 652: { 653: DoWriteReg(YES); 654: } 655: 656: int CreatMode = DFLT_MODE; 657: 658: void 659: DoWriteReg(app) 660: { 661: char fnamebuf[FILESIZE], 662: *fname; 663: Mark *mp = CurMark(); 664: File *fp; 665: 666: /* Won't get here if there isn't a Mark */ 667: fname = ask_file((char *) 0, (char *) 0, fnamebuf); 668: 669: #ifdef BACKUPFILES 670: if (app == NO) { 671: filemunge(fname); 672: 673: if (BkupOnWrite) 674: file_backup(fname); 675: } 676: #else 677: if (!app) 678: filemunge(fname); 679: #endif 680: 681: fp = open_file(fname, iobuff, app ? F_APPEND : F_WRITE, COMPLAIN, !QUIET); 682: putreg(fp, mp->m_line, mp->m_char, curline, curchar, YES); 683: close_file(fp); 684: } 685: 686: int OkayBadChars = 0; 687: 688: void 689: WriteFile() 690: { 691: char *fname, 692: fnamebuf[FILESIZE]; 693: #ifdef MAC 694: if (Macmode) { 695: if(!(fname = pfile(fnamebuf))) return; 696: } 697: else 698: #endif /* MAC */ 699: 700: fname = ask_file((char *) 0, curbuf->b_fname, fnamebuf); 701: /* Don't allow bad characters when creating new files. */ 702: if (!OkayBadChars && strcmp(curbuf->b_fname, fnamebuf) != 0) { 703: #ifdef UNIX 704: static char *badchars = "!$^&*()~`{}\"'\\|<>? "; 705: #endif /* UNIX */ 706: #ifdef MSDOS 707: static char *badchars = "*|<>? "; 708: #endif /* MSDOS */ 709: #ifdef MAC 710: static char *badchars = ":"; 711: #endif /* MAC */ 712: register char *cp = fnamebuf; 713: register int c; 714: 715: while (c = *cp++ & CHARMASK) /* avoid sign extension... */ 716: if (c < ' ' || c == '\177' || index(badchars, c)) 717: complain("'%p': bad character in filename.", c); 718: } 719: 720: #ifndef MAC 721: #ifndef MSDOS 722: chk_mtime(curbuf, fname, "write"); 723: #endif /* MSDOS */ 724: #endif /* MAC */ 725: filemunge(fname); 726: curbuf->b_type = B_FILE; /* in case it wasn't before */ 727: setfname(curbuf, fname); 728: file_write(fname, 0); 729: unmodify(); 730: } 731: 732: /* Open file FNAME supplying the buffer IO routine with buffer BUF. 733: HOW is F_READ, F_WRITE or F_APPEND. IFBAD == COMPLAIN means that 734: if we fail at opening the file, call complain. LOUDNESS says 735: whether or not to print the "reading ..." message on the message 736: line. 737: 738: NOTE: This opens the pr_name(fname, NO) of fname. That is, FNAME 739: is usually an entire pathname, which can be slow when the 740: pathname is long and there are lots of symbolic links along 741: the way (which has become very common in my experience). So, 742: this speeds up opens file names in the local directory. It 743: will not speed up things like "../scm/foo.scm" simple because 744: by the time we get here that's already been expanded to an 745: absolute pathname. But this is a start. 746: */ 747: 748: File * 749: open_file(fname, buf, how, ifbad, loudness) 750: register char *fname; 751: char *buf; 752: register int how; 753: { 754: register File *fp; 755: 756: io_chars = 0; 757: io_lines = 0; 758: 759: fp = f_open(pr_name(fname, NO), how, buf, LBSIZE); 760: if (fp == NIL) { 761: message(IOerr((how == F_READ) ? "open" : "create", fname)); 762: if (ifbad == COMPLAIN) 763: complain((char *) 0); 764: } else { 765: int readonly = FALSE; 766: #ifndef MAC 767: if (access(pr_name(fname, NO), W_OK) == -1 && errno != ENOENT) 768: readonly = TRUE; 769: #endif 770: if (loudness != QUIET) { 771: fp->f_flags |= F_TELLALL; 772: f_mess("\"%s\"%s", pr_name(fname, YES), 773: readonly ? " [Read only]" : NullStr); 774: } 775: } 776: return fp; 777: } 778: 779: #ifndef MSDOS 780: /* Check to see if the file has been modified since it was 781: last written. If so, make sure they know what they're 782: doing. 783: 784: I hate to use another stat(), but to use confirm we gotta 785: do this before we open the file. 786: 787: NOTE: This stats FNAME after converting it to a path-relative 788: name. I can't see why this would cause a problem ... 789: */ 790: 791: chk_mtime(thisbuf, fname, how) 792: Buffer *thisbuf; 793: char *fname, 794: *how; 795: { 796: struct stat stbuf; 797: Buffer *b; 798: char *mesg = "Shall I go ahead and %s anyway? "; 799: 800: if ((thisbuf->b_mtime != 0) && /* if we care ... */ 801: (b = file_exists(fname)) && /* we already have this file */ 802: (b == thisbuf) && /* and it's the current buffer */ 803: (stat(pr_name(fname, NO), &stbuf) != -1) && /* and we can stat it */ 804: (stbuf.st_mtime != b->b_mtime)) { /* and there's trouble. */ 805: rbell(); 806: redisplay(); /* Ring that bell! */ 807: TOstart("Warning", TRUE); 808: Typeout("\"%s\" now saved on disk is not what you last", pr_name(fname, YES)); 809: Typeout("visited or saved. Probably someone else is editing"); 810: Typeout("your file at the same time."); 811: if (how) { 812: Typeout(""); 813: Typeout("Type \"y\" if I should %s, anyway.", how); 814: f_mess(mesg, how); 815: } 816: TOstop(); 817: if (how) 818: confirm(mesg, how); 819: } 820: } 821: 822: #endif /* MSDOS */ 823: 824: void 825: file_write(fname, app) 826: char *fname; 827: { 828: File *fp; 829: 830: #ifdef BACKUPFILES 831: if (!app && BkupOnWrite) 832: file_backup(fname); 833: #endif 834: 835: fp = open_file(fname, iobuff, app ? F_APPEND : F_WRITE, COMPLAIN, !QUIET); 836: 837: if (EndWNewline) { /* Make sure file ends with a newLine */ 838: Bufpos save; 839: 840: DOTsave(&save); 841: ToLast(); 842: if (length(curline)) /* Not a blank Line */ 843: LineInsert(1); 844: SetDot(&save); 845: } 846: putreg(fp, curbuf->b_first, 0, curbuf->b_last, length(curbuf->b_last), NO); 847: close_file(fp); 848: set_ino(curbuf); 849: } 850: 851: void 852: ReadFile() 853: { 854: Buffer *bp; 855: char *fname, 856: fnamebuf[FILESIZE]; 857: int lineno; 858: 859: #ifdef MAC 860: if(Macmode) { 861: if(!(fname = gfile(fnamebuf))) return; 862: } 863: else 864: #endif /* MAC */ 865: fname = ask_file((char *) 0, curbuf->b_fname, fnamebuf); 866: #if !(defined(MSDOS) || defined(MAC)) 867: chk_mtime(curbuf, fname, "read"); 868: #endif /* MSDOS || MAC */ 869: 870: if (IsModified(curbuf)) { 871: char *y_or_n; 872: int c; 873: 874: for (;;) { 875: rbell(); 876: y_or_n = ask(NullStr, "Shall I make your changes to \"%s\" permanent? ", curbuf->b_name); 877: c = CharUpcase(*y_or_n); 878: if (c == 'Y' || c == 'N') 879: break; 880: } 881: if (c == 'Y') 882: SaveFile(); 883: } 884: 885: if ((bp = file_exists(fnamebuf)) && 886: (bp == curbuf)) 887: lineno = pnt_line() - 1; 888: else 889: lineno = 0; 890: 891: unmodify(); 892: initlist(curbuf); 893: setfname(curbuf, fname); 894: read_file(fname, 0); 895: SetLine(next_line(curbuf->b_first, lineno)); 896: } 897: 898: void 899: InsFile() 900: { 901: char *fname, 902: fnamebuf[FILESIZE]; 903: #ifdef MAC 904: if(Macmode) { 905: if(!(fname = gfile(fnamebuf))) return; 906: } 907: else 908: #endif /* MAC */ 909: fname = ask_file((char *) 0, curbuf->b_fname, fnamebuf); 910: read_file(fname, 1); 911: } 912: 913: #include "temp.h" 914: 915: int DOLsave = 0; /* Do Lsave flag. If lines aren't being save 916: when you think they should have been, this 917: flag is probably not being set, or is being 918: cleared before lsave() was called. */ 919: 920: private int nleft, /* number of good characters left in current block */ 921: tmpfd = -1; 922: disk_line DFree = 1; 923: /* pointer to end of tmp file */ 924: private char *tfname; 925: 926: void 927: tmpinit() 928: { 929: char buf[FILESIZE]; 930: 931: #ifdef MAC 932: sprintf(buf, "%s/%s", HomeDir, d_tempfile); 933: #else 934: sprintf(buf, "%s/%s", TmpFilePath, d_tempfile); 935: #endif 936: tfname = copystr(buf); 937: tfname = mktemp(tfname); 938: (void) close(creat(tfname, 0600)); 939: #ifndef MSDOS 940: tmpfd = open(tfname, 2); 941: #else /* MSDOS */ 942: tmpfd = open(tfname, 0x8002); /* MSDOS fix */ 943: #endif /* MSDOS */ 944: if (tmpfd == -1) 945: complain("Warning: cannot create tmp file!"); 946: } 947: 948: void 949: tmpclose() 950: { 951: if (tmpfd == -1) 952: return; 953: (void) close(tmpfd); 954: tmpfd = -1; 955: (void) unlink(tfname); 956: } 957: 958: /* get a line at `tl' in the tmp file into `buf' which should be LBSIZE 959: long */ 960: 961: int Jr_Len; /* length of Just Read Line */ 962: 963: #ifdef MAC /* The Lighspeed compiler can't copy with static here */ 964: char *getblock(); 965: #else 966: private char *getblock(); 967: #endif 968: void 969: getline(addr, buf) 970: disk_line addr; 971: register char *buf; 972: { 973: register char *bp, 974: *lp; 975: 976: lp = buf; 977: bp = getblock(addr >> 1, READ); 978: while (*lp++ = *bp++) 979: ; 980: Jr_Len = (lp - buf) - 1; 981: } 982: 983: /* Put `buf' and return the disk address */ 984: 985: disk_line 986: putline(buf) 987: char *buf; 988: { 989: register char *bp, 990: *lp; 991: register int nl; 992: disk_line free_ptr; 993: 994: lp = buf; 995: free_ptr = DFree; 996: bp = getblock(free_ptr, WRITE); 997: nl = nleft; 998: free_ptr = blk_round(free_ptr); 999: while (*bp = *lp++) { 1000: if (*bp++ == '\n') { 1001: *--bp = 0; 1002: break; 1003: } 1004: if (--nl == 0) { 1005: free_ptr = forward_block(free_ptr); 1006: DFree = free_ptr; 1007: bp = getblock(free_ptr, WRITE); 1008: lp = buf; /* start over ... */ 1009: nl = nleft; 1010: } 1011: } 1012: free_ptr = DFree; 1013: DFree += (((lp - buf) + CH_SIZE - 1) / CH_SIZE); 1014: /* (lp - buf) includes the null */ 1015: return (free_ptr << 1); 1016: } 1017: 1018: /* The theory is that critical section of code inside this procedure 1019: will never cause a problem to occur. Basically, we need to ensure 1020: that two blocks are in memory at the same time, but I think that 1021: this can never screw up. */ 1022: 1023: #define lockblock(addr) 1024: #define unlockblock(addr) 1025: 1026: disk_line 1027: f_getputl(line, fp) 1028: Line *line; 1029: register File *fp; 1030: { 1031: register char *bp; 1032: register int c, 1033: nl, 1034: max = LBSIZE; 1035: disk_line free_ptr; 1036: char *base; 1037: #ifdef MSDOS 1038: char crleft = 0; 1039: #endif /* MSDOS */ 1040: 1041: free_ptr = DFree; 1042: base = bp = getblock(free_ptr, WRITE); 1043: nl = nleft; 1044: free_ptr = blk_round(free_ptr); 1045: while (--max > 0) { 1046: #ifdef MSDOS 1047: if (crleft) { 1048: c = crleft; 1049: crleft = 0; 1050: } else 1051: #endif /* MSDOS */ 1052: c = getc(fp); 1053: if (c == EOF || c == '\n') 1054: break; 1055: #ifdef MSDOS 1056: if (c == '\r') 1057: if ((crleft = getc(fp)) == '\n') { 1058: crleft = 0; 1059: break; 1060: } 1061: #endif /* MSDOS */ 1062: if (--nl == 0) { 1063: char *newbp; 1064: int nbytes; 1065: 1066: lockblock(free_ptr); 1067: DFree = free_ptr = forward_block(free_ptr); 1068: nbytes = bp - base; 1069: newbp = getblock(free_ptr, WRITE); 1070: nl = nleft; 1071: byte_copy(base, newbp, nbytes); 1072: bp = newbp + nbytes; 1073: base = newbp; 1074: unlockblock(free_ptr); 1075: } 1076: *bp++ = c; 1077: } 1078: *bp++ = '\0'; 1079: free_ptr = DFree; 1080: DFree += (((bp - base) + CH_SIZE - 1) / CH_SIZE); 1081: line->l_dline = (free_ptr << 1); 1082: if (max == 0) { 1083: add_mess(" [Line too long]"); 1084: rbell(); 1085: return EOF; 1086: } 1087: if (c == EOF) { 1088: if (--bp != base) 1089: add_mess(" [Incomplete last line]"); 1090: return EOF; 1091: } 1092: io_lines += 1; 1093: return 0; 1094: } 1095: 1096: typedef struct block { 1097: short b_dirty, 1098: b_bno; 1099: char b_buf[BUFSIZ]; 1100: struct block 1101: *b_LRUnext, 1102: *b_LRUprev, 1103: *b_HASHnext; 1104: } Block; 1105: 1106: #define HASHSIZE 7 /* Primes work best (so I'm told) */ 1107: #define B_HASH(bno) (bno % HASHSIZE) 1108: 1109: #ifdef MAC 1110: private Block *b_cache, 1111: #else 1112: private Block b_cache[NBUF], 1113: #endif 1114: *bht[HASHSIZE] = {0}, /* Block hash table */ 1115: *f_block = 0, 1116: *l_block = 0; 1117: private int max_bno = -1, 1118: NBlocks; 1119: 1120: #ifdef MAC 1121: void (*blkio)(); 1122: #else 1123: #ifdef LINT_ARGS 1124: private int (*blkio)(Block *, int (*)()); 1125: #else 1126: private int (*blkio)(); 1127: #endif 1128: #endif /* MAC */ 1129: 1130: #ifdef MAC 1131: make_cache() /* Only 32K of static space on Mac, so... */ 1132: { 1133: return((b_cache = (Block *) calloc(NBUF,sizeof(Block))) == 0 ? 0 : 1); 1134: } 1135: #endif /* MAC */ 1136: 1137: extern int read(), write(); 1138: 1139: private void 1140: real_blkio(b, iofcn) 1141: register Block *b; 1142: #ifdef MAC 1143: register int (*iofcn)(); 1144: #else 1145: #ifdef LINT_ARGS 1146: register int (*iofcn)(int, char *, unsigned int); 1147: #else 1148: register int (*iofcn)(); 1149: #endif 1150: #endif /* MAC */ 1151: { 1152: (void) lseek(tmpfd, (long) ((unsigned) b->b_bno) * BUFSIZ, 0); 1153: if ((*iofcn)(tmpfd, b->b_buf, BUFSIZ) != BUFSIZ) 1154: error("[Tmp file %s error; to continue editing would be dangerous]", (iofcn == read) ? "READ" : "WRITE"); 1155: } 1156: 1157: private void 1158: fake_blkio(b, iofcn) 1159: register Block *b; 1160: register int (*iofcn)(); 1161: { 1162: tmpinit(); 1163: blkio = real_blkio; 1164: real_blkio(b, iofcn); 1165: } 1166: 1167: void 1168: d_cache_init() 1169: { 1170: register Block *bp, /* Block pointer */ 1171: **hp; /* Hash pointer */ 1172: register short bno; 1173: 1174: for (bp = b_cache, bno = NBUF; --bno >= 0; bp++) { 1175: NBlocks += 1; 1176: bp->b_dirty = 0; 1177: bp->b_bno = bno; 1178: if (l_block == 0) 1179: l_block = bp; 1180: bp->b_LRUprev = 0; 1181: bp->b_LRUnext = f_block; 1182: if (f_block != 0) 1183: f_block->b_LRUprev = bp; 1184: f_block = bp; 1185: 1186: bp->b_HASHnext = *(hp = &bht[B_HASH(bno)]); 1187: *hp = bp; 1188: } 1189: blkio = fake_blkio; 1190: } 1191: 1192: void 1193: SyncTmp() 1194: { 1195: register Block *b; 1196: #ifdef IBMPC 1197: register int bno = 0; 1198: Block *lookup(); 1199: 1200: /* sync the blocks in order, for floppy disks */ 1201: for (bno = 0; bno <= max_bno; ) { 1202: if ((b = lookup(bno++)) && b->b_dirty) { 1203: (*blkio)(b, write); 1204: b->b_dirty = 0; 1205: } 1206: } 1207: #else 1208: for (b = f_block; b != 0; b = b->b_LRUnext) 1209: if (b->b_dirty) { 1210: (*blkio)(b, write); 1211: b->b_dirty = 0; 1212: } 1213: #endif 1214: } 1215: 1216: private Block * 1217: lookup(bno) 1218: register short bno; 1219: { 1220: register Block *bp; 1221: 1222: for (bp = bht[B_HASH(bno)]; bp != 0; bp = bp->b_HASHnext) 1223: if (bp->b_bno == bno) 1224: break; 1225: return bp; 1226: } 1227: 1228: private void 1229: LRUunlink(b) 1230: register Block *b; 1231: { 1232: if (b->b_LRUprev == 0) 1233: f_block = b->b_LRUnext; 1234: else 1235: b->b_LRUprev->b_LRUnext = b->b_LRUnext; 1236: if (b->b_LRUnext == 0) 1237: l_block = b->b_LRUprev; 1238: else 1239: b->b_LRUnext->b_LRUprev = b->b_LRUprev; 1240: } 1241: 1242: private Block * 1243: b_unlink(bp) 1244: register Block *bp; 1245: { 1246: register Block *hp, 1247: *prev = 0; 1248: 1249: LRUunlink(bp); 1250: /* Now that we have the block, we remove it from its position 1251: in the hash table, so we can THEN put it somewhere else with 1252: it's new block assignment. */ 1253: 1254: for (hp = bht[B_HASH(bp->b_bno)]; hp != 0; prev = hp, hp = hp->b_HASHnext) 1255: if (hp == bp) 1256: break; 1257: if (hp == 0) { 1258: printf("\rBlock %d missing!", bp->b_bno); 1259: finish(0); 1260: } 1261: if (prev) 1262: prev->b_HASHnext = hp->b_HASHnext; 1263: else 1264: bht[B_HASH(bp->b_bno)] = hp->b_HASHnext; 1265: 1266: if (bp->b_dirty) { /* do, now, the delayed write */ 1267: (*blkio)(bp, write); 1268: bp->b_dirty = 0; 1269: } 1270: 1271: return bp; 1272: } 1273: 1274: /* Get a block which contains at least part of the line with the address 1275: atl. Returns a pointer to the block and sets the global variable 1276: nleft (number of good characters left in the buffer). */ 1277: 1278: private char * 1279: getblock(atl, iof) 1280: disk_line atl; 1281: { 1282: register int bno, 1283: off; 1284: register Block *bp; 1285: static Block *lastb = 0; 1286: 1287: bno = da_to_bno(atl); 1288: off = da_to_off(atl); 1289: if (da_too_huge(atl)) 1290: error("Tmp file too large. Get help!"); 1291: nleft = BUFSIZ - off; 1292: if (lastb != 0 && lastb->b_bno == bno) { 1293: lastb->b_dirty |= iof; 1294: return lastb->b_buf + off; 1295: } 1296: 1297: /* The requested block already lives in memory, so we move 1298: it to the end of the LRU list (making it Most Recently Used) 1299: and then return a pointer to it. */ 1300: if (bp = lookup(bno)) { 1301: if (bp != l_block) { 1302: LRUunlink(bp); 1303: if (l_block == 0) 1304: f_block = l_block = bp; 1305: else 1306: l_block->b_LRUnext = bp; 1307: bp->b_LRUprev = l_block; 1308: l_block = bp; 1309: bp->b_LRUnext = 0; 1310: } 1311: if (bp->b_bno > max_bno) 1312: max_bno = bp->b_bno; 1313: bp->b_dirty |= iof; 1314: lastb = bp; 1315: return bp->b_buf + off; 1316: } 1317: 1318: /* The block we want doesn't reside in memory so we take the 1319: least recently used clean block (if there is one) and use 1320: it. */ 1321: bp = f_block; 1322: if (bp->b_dirty) /* The best block is dirty ... */ 1323: SyncTmp(); 1324: 1325: bp = b_unlink(bp); 1326: if (l_block == 0) 1327: l_block = f_block = bp; 1328: else 1329: l_block->b_LRUnext = bp; /* Place it at the end ... */ 1330: bp->b_LRUprev = l_block; 1331: l_block = bp; 1332: bp->b_LRUnext = 0; /* so it's Most Recently Used */ 1333: 1334: bp->b_dirty = iof; 1335: bp->b_bno = bno; 1336: bp->b_HASHnext = bht[B_HASH(bno)]; 1337: bht[B_HASH(bno)] = bp; 1338: 1339: /* Get the current contents of the block UNLESS this is a new 1340: block that's never been looked at before, i.e., it's past 1341: the end of the tmp file. */ 1342: 1343: if (bp->b_bno <= max_bno) 1344: (*blkio)(bp, read); 1345: else 1346: max_bno = bno; 1347: 1348: lastb = bp; 1349: return bp->b_buf + off; 1350: } 1351: 1352: char * 1353: lbptr(line) 1354: Line *line; 1355: { 1356: return getblock(line->l_dline >> 1, READ); 1357: } 1358: 1359: /* save the current contents of linebuf, if it has changed */ 1360: 1361: void 1362: lsave() 1363: { 1364: if (curbuf == 0 || !DOLsave) /* Nothing modified recently */ 1365: return; 1366: 1367: if (strcmp(lbptr(curline), linebuf) != 0) 1368: SavLine(curline, linebuf); /* Put linebuf on the disk. */ 1369: DOLsave = 0; 1370: } 1371: 1372: #ifdef BACKUPFILES 1373: void 1374: file_backup(fname) 1375: char *fname; 1376: { 1377: #ifndef MSDOS 1378: char *s; 1379: register int i; 1380: int fd1, 1381: fd2; 1382: char tmp1[BUFSIZ], 1383: tmp2[BUFSIZ]; 1384: struct stat buf; 1385: int mode; 1386: 1387: strcpy(tmp1, fname); 1388: if ((s = rindex(tmp1, '/')) == NULL) 1389: sprintf(tmp2, "#%s", fname); 1390: else { 1391: *s++ = '\0'; 1392: sprintf(tmp2, "%s/#%s", tmp1, s); 1393: } 1394: 1395: if ((fd1 = open(fname, 0)) < 0) 1396: return; 1397: 1398: /* create backup file with same mode as input file */ 1399: #ifndef MAC 1400: if (fstat(fd1, &buf) != 0) 1401: mode = CreatMode; 1402: else 1403: #endif 1404: mode = buf.st_mode; 1405: 1406: if ((fd2 = creat(tmp2, mode)) < 0) { 1407: (void) close(fd1); 1408: return; 1409: } 1410: while ((i = read(fd1, tmp1, sizeof(tmp1))) > 0) 1411: write(fd2, tmp1, i); 1412: #ifdef BSD4_2 1413: (void) fsync(fd2); 1414: #endif 1415: (void) close(fd2); 1416: (void) close(fd1); 1417: #else /* MSDOS */ 1418: char *dot, 1419: *slash, 1420: tmp[FILESIZE]; 1421: 1422: strcpy(tmp, fname); 1423: slash = basename(tmp); 1424: if (dot = rindex(slash, '.')) { 1425: if (!stricmp(dot,".bak")) 1426: return; 1427: else *dot = 0; 1428: } 1429: strcat(tmp, ".bak"); 1430: unlink(tmp); 1431: rename(fname, tmp); 1432: #endif /* MSDOS */ 1433: } 1434: #endif 1435: 1436: #if defined(MSDOS) && defined (CHDIR) 1437: 1438: private int /* chdir + drive */ 1439: Dchdir(to) 1440: char *to; 1441: { 1442: unsigned d, dd, n; 1443: 1444: if (to[1] == ':') { 1445: d = to[0]; 1446: if (d >= 'a') d = d - 'a' + 1; 1447: if (d >= 'A') d = d - 'A' + 1; 1448: _dos_getdrive(&dd); 1449: if (dd != d) 1450: _dos_setdrive(d, &n); 1451: if (to[2] == 0) 1452: return 0; 1453: } 1454: return chdir(to); 1455: } 1456: 1457: private char * 1458: fixpath(p) 1459: char *p; 1460: { 1461: char *pp = p; 1462: 1463: while (*p) { 1464: if (*p == '\\') 1465: *p = '/'; 1466: p++; 1467: } 1468: return(strlwr(pp)); 1469: } 1470: 1471: 1472: private void 1473: abspath(so, dest) 1474: char *so, *dest; 1475: { 1476: char cwd[FILESIZE], cwdD[3], cwdDIR[FILESIZE], cwdF[9], cwdEXT[5], 1477: soD[3], soDIR[FILESIZE], soF[9], soEXT[5]; 1478: char *drive, *path; 1479: 1480: _splitpath(fixpath(so), soD, soDIR, soF, soEXT); 1481: getcwd(cwd, FILESIZE); 1482: if (*soD != 0) { 1483: Dchdir(soD); /* this is kinda messy */ 1484: getcwd(cwdDIR, FILESIZE); /* should probably just */ 1485: Dchdir(cwd); /* call DOS to do it */ 1486: strcpy(cwd, cwdDIR); 1487: } 1488: (void) fixpath(cwd); 1489: if (cwd[strlen(cwd)-1] != '/') 1490: strcat(cwd, "/x.x"); /* need dummy filename */ 1491: 1492: _splitpath(fixpath(cwd), cwdD, cwdDIR, cwdF, cwdEXT); 1493: 1494: drive = (*soD == 0) ? cwdD : soD; 1495: 1496: if (*soDIR != '/') 1497: path = strcat(cwdDIR, soDIR); 1498: else 1499: path = soDIR; 1500: _makepath(dest, drive, path, soF, soEXT); 1501: fixpath(dest); /* can't do it often enough */ 1502: } 1503: 1504: #endif