|
|
1.1 ! root 1: /* tmpfile.c */ ! 2: ! 3: /* Author: ! 4: * Steve Kirkendall ! 5: * 16820 SW Tallac Way ! 6: * Beaverton, OR 97006 ! 7: * [email protected], or ...uunet!tektronix!psueea!jove!kirkenda ! 8: */ ! 9: ! 10: ! 11: /* This file contains functions which create & readback a TMPFILE */ ! 12: ! 13: ! 14: #include "config.h" ! 15: #include <ctype.h> ! 16: #include "vi.h" ! 17: #if TOS ! 18: # include <stat.h> ! 19: #else ! 20: # if OSK ! 21: # include "osk.h" ! 22: # else ! 23: # include <sys/stat.h> ! 24: # endif ! 25: #endif ! 26: ! 27: ! 28: #ifndef NO_MODELINE ! 29: static void do_modeline(l, stop) ! 30: long l; /* line number to start at */ ! 31: long stop; /* line number to stop at */ ! 32: { ! 33: char *str; /* used to scan through the line */ ! 34: char *start; /* points to the start of the line */ ! 35: char buf[80]; ! 36: ! 37: /* if modelines are disabled, then do nothing */ ! 38: if (!*o_modeline) ! 39: { ! 40: return; ! 41: } ! 42: ! 43: /* for each line... */ ! 44: for (l = 1; l <= stop; l++) ! 45: { ! 46: /* for each position in the line.. */ ! 47: for (str = fetchline(l); *str; str++) ! 48: { ! 49: /* if it is the start of a modeline command... */ ! 50: if ((str[0] == 'e' && str[1] == 'x' ! 51: || str[0] == 'v' && str[1] == 'i') ! 52: && str[2] == ':') ! 53: { ! 54: start = str += 3; ! 55: ! 56: /* find the end */ ! 57: while (*str && *str != ':') ! 58: { ! 59: str++; ! 60: } ! 61: ! 62: /* if it is a well-formed modeline, execute it */ ! 63: if (*str && str - start < sizeof buf) ! 64: { ! 65: strncpy(buf, start, (int)(str - start)); ! 66: buf[str - start] = '\0'; ! 67: doexcmd(buf); ! 68: break; ! 69: } ! 70: } ! 71: } ! 72: } ! 73: } ! 74: #endif ! 75: ! 76: ! 77: /* The FAIL() macro prints an error message and then exits. */ ! 78: #define FAIL(why,arg) mode = MODE_EX; msg(why, arg); endwin(); exit(9) ! 79: ! 80: /* This is the name of the temp file */ ! 81: static char tmpname[80]; ! 82: ! 83: /* This function creates the temp file and copies the original file into it. ! 84: * Returns if successful, or stops execution if it fails. ! 85: */ ! 86: int tmpstart(filename) ! 87: char *filename; /* name of the original file */ ! 88: { ! 89: int origfd; /* fd used for reading the original file */ ! 90: struct stat statb; /* stat buffer, used to examine inode */ ! 91: REG BLK *this; /* pointer to the current block buffer */ ! 92: REG BLK *next; /* pointer to the next block buffer */ ! 93: int inbuf; /* number of characters in a buffer */ ! 94: int nread; /* number of bytes read */ ! 95: REG int j, k; ! 96: int i; ! 97: int sum; /* used for calculating a checksum for this */ ! 98: char *scan; ! 99: long nbytes; ! 100: ! 101: /* switching to a different file certainly counts as a change */ ! 102: changes++; ! 103: redraw(MARK_UNSET, FALSE); ! 104: ! 105: /* open the original file for reading */ ! 106: *origname = '\0'; ! 107: if (filename && *filename) ! 108: { ! 109: strcpy(origname, filename); ! 110: origfd = open(origname, O_RDONLY); ! 111: if (origfd < 0 && errno != ENOENT) ! 112: { ! 113: msg("Can't open \"%s\"", origname); ! 114: return tmpstart(""); ! 115: } ! 116: if (origfd >= 0) ! 117: { ! 118: if (stat(origname, &statb) < 0) ! 119: { ! 120: FAIL("Can't stat \"%s\"", origname); ! 121: } ! 122: #if TOS ! 123: if (origfd >= 0 && (statb.st_mode & S_IJDIR)) ! 124: #else ! 125: # if OSK ! 126: if (origfd >= 0 && (statb.st_mode & S_IFDIR)) ! 127: # else ! 128: if (origfd >= 0 && (statb.st_mode & S_IFMT) != S_IFREG) ! 129: # endif ! 130: #endif ! 131: { ! 132: msg("\"%s\" is not a regular file", origname); ! 133: return tmpstart(""); ! 134: } ! 135: } ! 136: else ! 137: { ! 138: stat(".", &statb); ! 139: } ! 140: if (origfd >= 0) ! 141: { ! 142: origtime = statb.st_mtime; ! 143: #if MSDOS || OSK ! 144: if (*o_readonly || !(statb.st_mode & S_IWRITE)) ! 145: #endif ! 146: #if TOS ! 147: if (*o_readonly || (statb.st_mode & S_IJRON)) ! 148: #endif ! 149: #if ANY_UNIX ! 150: if (*o_readonly || !(statb.st_mode & ! 151: (statb.st_uid != geteuid() ? 0022 : 0200))) ! 152: #endif ! 153: { ! 154: setflag(file, READONLY); ! 155: } ! 156: } ! 157: else ! 158: { ! 159: origtime = 0L; ! 160: } ! 161: } ! 162: else ! 163: { ! 164: setflag(file, NOFILE); ! 165: origfd = -1; ! 166: origtime = 0L; ! 167: stat(".", &statb); ! 168: } ! 169: ! 170: /* generate a checksum from the file's name */ ! 171: for (sum = 0, scan = origname + strlen(origname); ! 172: --scan >= origname && (isascii(*scan) && isalnum(*scan) || *scan == '.'); ! 173: sum = sum + *scan) ! 174: { ! 175: } ! 176: sum &= 0xf; ! 177: ! 178: /* make a name for the tmp file */ ! 179: #if MSDOS || TOS ! 180: /* MS-Dos doesn't allow multiple slashes, but supports drives ! 181: * with current directories. ! 182: * This relies on TMPNAME beginning with "%s\\"!!!! ! 183: */ ! 184: strcpy(tmpname, o_directory); ! 185: if ((i = strlen(tmpname)) && !strchr(":/\\", tmpname[i-1])) ! 186: tmpname[i++]=SLASH; ! 187: sprintf(tmpname+i, TMPNAME+3, sum, statb.st_ino, statb.st_dev); ! 188: #else ! 189: sprintf(tmpname, TMPNAME, o_directory, sum, statb.st_ino, statb.st_dev); ! 190: #endif ! 191: ! 192: /* make sure nobody else is editing the same file */ ! 193: if (access(tmpname, 0) == 0) ! 194: { ! 195: if (*origname) ! 196: { ! 197: msg("\"%s\" is busy", filename); ! 198: return tmpstart(""); ! 199: } ! 200: FAIL("\"%s\" is busy", filename); ! 201: } ! 202: ! 203: /* create the temp file */ ! 204: #if ANY_UNIX ! 205: close(creat(tmpname, 0600)); /* only we can read it */ ! 206: #else ! 207: close(creat(tmpname, FILEPERMS)); /* anybody body can read it, alas */ ! 208: #endif ! 209: tmpfd = open(tmpname, O_RDWR | O_BINARY); ! 210: if (tmpfd < 0) ! 211: { ! 212: FAIL("Can't create temporary file, errno=%d", errno); ! 213: return 1; ! 214: } ! 215: ! 216: /* allocate space for the header in the file */ ! 217: write(tmpfd, hdr.c, (unsigned)BLKSIZE); ! 218: ! 219: #ifndef NO_RECYCLE ! 220: /* initialize the block allocator */ ! 221: /* This must already be done here, before the first attempt ! 222: * to write to the new file! GB */ ! 223: garbage(); ! 224: #endif ! 225: ! 226: /* initialize lnum[] */ ! 227: for (i = 1; i < MAXBLKS; i++) ! 228: { ! 229: lnum[i] = INFINITY; ! 230: } ! 231: lnum[0] = 0; ! 232: ! 233: /* if there is no original file, then create a 1-line file */ ! 234: if (origfd < 0) ! 235: { ! 236: hdr.n[0] = 0; /* invalid inode# denotes new file */ ! 237: ! 238: this = blkget(1); /* get the new text block */ ! 239: strcpy(this->c, "\n"); /* put a line in it */ ! 240: ! 241: lnum[1] = 1L; /* block 1 ends with line 1 */ ! 242: nlines = 1L; /* there is 1 line in the file */ ! 243: nbytes = 1L; ! 244: ! 245: if (*origname) ! 246: { ! 247: msg("\"%s\" [NEW FILE] 1 line, 1 char", origname); ! 248: } ! 249: else ! 250: { ! 251: msg("\"[NO FILE]\" 1 line, 1 char"); ! 252: } ! 253: } ! 254: else /* there is an original file -- read it in */ ! 255: { ! 256: hdr.n[0] = statb.st_ino; ! 257: nbytes = nlines = 0; ! 258: ! 259: /* preallocate 1 "next" buffer */ ! 260: i = 1; ! 261: next = blkget(i); ! 262: inbuf = 0; ! 263: ! 264: /* loop, moving blocks from orig to tmp */ ! 265: for (;;) ! 266: { ! 267: /* "next" buffer becomes "this" buffer */ ! 268: this = next; ! 269: ! 270: /* read [more] text into this block */ ! 271: nread = tread(origfd, &this->c[inbuf], BLKSIZE - 1 - inbuf); ! 272: if (nread < 0) ! 273: { ! 274: close(origfd); ! 275: close(tmpfd); ! 276: tmpfd = -1; ! 277: unlink(tmpname); ! 278: FAIL("Error reading \"%s\"", origname); ! 279: } ! 280: ! 281: /* convert NUL characters to something else */ ! 282: for (k = inbuf; k < inbuf + nread; k++) ! 283: { ! 284: if (!this->c[k]) ! 285: { ! 286: setflag(file, HADNUL); ! 287: this->c[k] = 0x80; ! 288: } ! 289: } ! 290: inbuf += nread; ! 291: ! 292: /* if the buffer is empty, quit */ ! 293: if (inbuf == 0) ! 294: { ! 295: goto FoundEOF; ! 296: } ! 297: ! 298: #if MSDOS || TOS ! 299: /* BAH! MS text mode read fills inbuf, then compresses eliminating \r ! 300: but leaving garbage at end of buf. The same is true for TURBOC. GB. */ ! 301: ! 302: memset(this->c + inbuf, '\0', BLKSIZE - inbuf); ! 303: #endif ! 304: ! 305: /* search backward for last newline */ ! 306: for (k = inbuf; --k >= 0 && this->c[k] != '\n';) ! 307: { ! 308: } ! 309: if (k++ < 0) ! 310: { ! 311: if (inbuf >= BLKSIZE - 1) ! 312: { ! 313: k = 80; ! 314: } ! 315: else ! 316: { ! 317: k = inbuf; ! 318: } ! 319: } ! 320: ! 321: /* allocate next buffer */ ! 322: next = blkget(++i); ! 323: ! 324: /* move fragmentary last line to next buffer */ ! 325: inbuf -= k; ! 326: for (j = 0; k < BLKSIZE; j++, k++) ! 327: { ! 328: next->c[j] = this->c[k]; ! 329: this->c[k] = 0; ! 330: } ! 331: ! 332: /* if necessary, add a newline to this buf */ ! 333: for (k = BLKSIZE - inbuf; --k >= 0 && !this->c[k]; ) ! 334: { ! 335: } ! 336: if (this->c[k] != '\n') ! 337: { ! 338: setflag(file, ADDEDNL); ! 339: this->c[k + 1] = '\n'; ! 340: } ! 341: ! 342: /* count the lines in this block */ ! 343: for (k = 0; k < BLKSIZE && this->c[k]; k++) ! 344: { ! 345: if (this->c[k] == '\n') ! 346: { ! 347: nlines++; ! 348: } ! 349: nbytes++; ! 350: } ! 351: lnum[i - 1] = nlines; ! 352: } ! 353: FoundEOF: ! 354: ! 355: /* if this is a zero-length file, add 1 line */ ! 356: if (nlines == 0) ! 357: { ! 358: this = blkget(1); /* get the new text block */ ! 359: strcpy(this->c, "\n"); /* put a line in it */ ! 360: ! 361: lnum[1] = 1; /* block 1 ends with line 1 */ ! 362: nlines = 1; /* there is 1 line in the file */ ! 363: nbytes = 1; ! 364: } ! 365: ! 366: #if MSDOS || TOS ! 367: /* each line has an extra CR that we didn't count yet */ ! 368: nbytes += nlines; ! 369: #endif ! 370: ! 371: /* report the number of lines in the file */ ! 372: msg("\"%s\" %s %ld line%s, %ld char%s", ! 373: origname, ! 374: (tstflag(file, READONLY) ? "[READONLY]" : ""), ! 375: nlines, ! 376: nlines == 1 ? "" : "s", ! 377: nbytes, ! 378: nbytes == 1 ? "" : "s"); ! 379: } ! 380: ! 381: /* initialize the cursor to start of line 1 */ ! 382: cursor = MARK_FIRST; ! 383: ! 384: /* close the original file */ ! 385: close(origfd); ! 386: ! 387: /* any other messages? */ ! 388: if (tstflag(file, HADNUL)) ! 389: { ! 390: msg("This file contained NULs. They've been changed to \\x80 chars"); ! 391: } ! 392: if (tstflag(file, ADDEDNL)) ! 393: { ! 394: msg("Newline characters have been inserted to break up long lines"); ! 395: } ! 396: ! 397: #ifndef NO_MODELINE ! 398: if (nlines > 10) ! 399: { ! 400: do_modeline(1L, 5L); ! 401: do_modeline(nlines - 4L, nlines); ! 402: } ! 403: else ! 404: { ! 405: do_modeline(1L, nlines); ! 406: } ! 407: #endif ! 408: return 0; ! 409: } ! 410: ! 411: ! 412: ! 413: /* This function copies the temp file back onto an original file. ! 414: * Returns TRUE if successful, or FALSE if the file could NOT be saved. ! 415: */ ! 416: int tmpsave(filename, bang) ! 417: char *filename; /* the name to save it to */ ! 418: int bang; /* forced write? */ ! 419: { ! 420: int fd; /* fd of the file we're writing to */ ! 421: REG int len; /* length of a text block */ ! 422: REG BLK *this; /* a text block */ ! 423: long bytes; /* byte counter */ ! 424: REG int i; ! 425: ! 426: /* if no filename is given, assume the original file name */ ! 427: if (!filename || !*filename) ! 428: { ! 429: filename = origname; ! 430: } ! 431: ! 432: /* if still no file name, then fail */ ! 433: if (!*filename) ! 434: { ! 435: msg("Don't know a name for this file -- NOT WRITTEN"); ! 436: return FALSE; ! 437: } ! 438: ! 439: /* can't rewrite a READONLY file */ ! 440: if (!strcmp(filename, origname) && *o_readonly && !bang) ! 441: { ! 442: msg("\"%s\" [READONLY] -- NOT WRITTEN", filename); ! 443: return FALSE; ! 444: } ! 445: ! 446: /* open the file */ ! 447: if (*filename == '>' && filename[1] == '>') ! 448: { ! 449: filename += 2; ! 450: while (*filename == ' ' || *filename == '\t') ! 451: { ! 452: filename++; ! 453: } ! 454: #ifdef O_APPEND ! 455: fd = open(filename, O_WRONLY|O_APPEND); ! 456: #else ! 457: fd = open(filename, O_WRONLY); ! 458: lseek(fd, 0L, 2); ! 459: #endif ! 460: } ! 461: else ! 462: { ! 463: /* either the file must not exist, or it must be the original ! 464: * file, or we must have a bang ! 465: */ ! 466: if (strcmp(filename, origname) && access(filename, 0) == 0 && !bang) ! 467: { ! 468: msg("File already exists - Use :w! to overwrite"); ! 469: return FALSE; ! 470: } ! 471: fd = creat(filename, FILEPERMS); ! 472: } ! 473: if (fd < 0) ! 474: { ! 475: msg("Can't write to \"%s\" -- NOT WRITTEN", filename); ! 476: return FALSE; ! 477: } ! 478: ! 479: /* write each text block to the file */ ! 480: bytes = 0L; ! 481: for (i = 1; i < MAXBLKS && (this = blkget(i)) && this->c[0]; i++) ! 482: { ! 483: for (len = 0; len < BLKSIZE && this->c[len]; len++) ! 484: { ! 485: } ! 486: twrite(fd, this->c, len); ! 487: bytes += len; ! 488: } ! 489: ! 490: /* reset the "modified" flag */ ! 491: clrflag(file, MODIFIED); ! 492: significant = FALSE; ! 493: ! 494: /* report lines & characters */ ! 495: #if MSDOS || TOS ! 496: bytes += nlines; /* for the inserted carriage returns */ ! 497: #endif ! 498: if (strncmp(filename, o_directory, strlen(o_directory))) ! 499: { ! 500: msg("Wrote \"%s\" %ld lines, %ld characters", filename, nlines, bytes); ! 501: } ! 502: ! 503: /* close the file */ ! 504: close(fd); ! 505: ! 506: return TRUE; ! 507: } ! 508: ! 509: ! 510: /* This function deletes the temporary file. If the file has been modified ! 511: * and "bang" is FALSE, then it returns FALSE without doing anything; else ! 512: * it returns TRUE. ! 513: * ! 514: * If the "autowrite" option is set, then instead of returning FALSE when ! 515: * the file has been modified and "bang" is false, it will call tmpend(). ! 516: */ ! 517: int tmpabort(bang) ! 518: int bang; ! 519: { ! 520: /* if there is no file, return successfully */ ! 521: if (tmpfd < 0) ! 522: { ! 523: return TRUE; ! 524: } ! 525: ! 526: /* see if we must return FALSE -- can't quit */ ! 527: if (!bang && tstflag(file, MODIFIED)) ! 528: { ! 529: /* if "autowrite" is set, then act like tmpend() */ ! 530: if (*o_autowrite) ! 531: return tmpend(bang); ! 532: else ! 533: return FALSE; ! 534: } ! 535: ! 536: /* delete the tmp file */ ! 537: cutswitch(tmpname); ! 538: close(tmpfd); ! 539: tmpfd = -1; ! 540: unlink(tmpname); ! 541: strcpy(prevorig, origname); ! 542: prevline = markline(cursor); ! 543: *origname = '\0'; ! 544: origtime = 0L; ! 545: blkinit(); ! 546: nlines = 0; ! 547: initflags(); ! 548: return TRUE; ! 549: } ! 550: ! 551: /* This function saves the file if it has been modified, and then deletes ! 552: * the temporary file. Returns TRUE if successful, or FALSE if the file ! 553: * needs to be saved but can't be. When it returns FALSE, it will not have ! 554: * deleted the tmp file, either. ! 555: */ ! 556: int tmpend(bang) ! 557: int bang; ! 558: { ! 559: /* save the file if it has been modified */ ! 560: if (tstflag(file, MODIFIED) && !tmpsave((char *)0, FALSE) && !bang) ! 561: { ! 562: return FALSE; ! 563: } ! 564: ! 565: /* delete the tmp file */ ! 566: tmpabort(TRUE); ! 567: ! 568: return TRUE; ! 569: } ! 570: ! 571: ! 572: /* If the tmp file has been changed, then this function will force those ! 573: * changes to be written to the disk, so that the tmp file will survive a ! 574: * system crash or power failure. ! 575: */ ! 576: #if MSDOS || TOS || OSK ! 577: sync() ! 578: { ! 579: # if OSK ! 580: /* OS9 doesn't need an explicit sync operation, but the linker ! 581: * demands something called sync(), so this is a dummy function. ! 582: */ ! 583: #else ! 584: /* MS-DOS and TOS don't flush their buffers until the file is closed, ! 585: * so here we close the tmp file and then immediately reopen it. ! 586: */ ! 587: close(tmpfd); ! 588: tmpfd = open(tmpname, O_RDWR | O_BINARY); ! 589: #endif ! 590: } ! 591: #endif
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.