|
|
1.1 ! root 1: #ifndef lint ! 2: static char rcs_id[] = "$Header: EDiskSrc.c,v 1.13 87/09/11 08:19:00 toddb Exp $"; ! 3: #endif lint ! 4: /* ! 5: * COPYRIGHT 1987 ! 6: * DIGITAL EQUIPMENT CORPORATION ! 7: * MAYNARD, MASSACHUSETTS ! 8: * ALL RIGHTS RESERVED. ! 9: * ! 10: * THE INFORMATION IN THIS SOFTWARE IS SUBJECT TO CHANGE WITHOUT NOTICE AND ! 11: * SHOULD NOT BE CONSTRUED AS A COMMITMENT BY DIGITAL EQUIPMENT CORPORATION. ! 12: * DIGITAL MAKES NO REPRESENTATIONS ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ! 13: * ANY PURPOSE. IT IS SUPPLIED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. ! 14: * ! 15: * IF THE SOFTWARE IS MODIFIED IN A MANNER CREATING DERIVATIVE COPYRIGHT RIGHTS, ! 16: * APPROPRIATE LEGENDS MAY BE PLACED ON THE DERIVATIVE WORK IN ADDITION TO THAT ! 17: * SET FORTH ABOVE. ! 18: * ! 19: * ! 20: * Permission to use, copy, modify, and distribute this software and its ! 21: * documentation for any purpose and without fee is hereby granted, provided ! 22: * that the above copyright notice appear in all copies and that both that ! 23: * copyright notice and this permission notice appear in supporting documentation, ! 24: * and that the name of Digital Equipment Corporation not be used in advertising ! 25: * or publicity pertaining to distribution of the software without specific, ! 26: * written prior permission. ! 27: */ ! 28: ! 29: /* File: EDiskSource.c */ ! 30: ! 31: /* This is a rather simplistic text source. It allows editing of a disk file. ! 32: It divides the file into blocks of BUFSIZE characters. No block is ! 33: actually read in from disk until referenced; each block will be read at ! 34: most once. Thus, the entire file can eventually be read into memory and ! 35: stored there. When the save function is called, everything gets written ! 36: back. */ ! 37: ! 38: ! 39: ! 40: #include <sys/types.h> ! 41: #include <sys/stat.h> ! 42: #include <sys/file.h> ! 43: ! 44: #include "xmh.h" ! 45: ! 46: ! 47: /* Private definitions. */ ! 48: ! 49: #define BUFSIZE 512 /* Number of chars to store in each piece. */ ! 50: ! 51: extern char *strcpy(); ! 52: extern long lseek(); ! 53: ! 54: /* The structure corresponding to each piece of the file in memory. */ ! 55: ! 56: typedef struct { ! 57: char *buf; /* Pointer to this piece's data. */ ! 58: int length; /* Number of chars used in buf. */ ! 59: int maxlength; /* Number of chars allocated in buf. */ ! 60: long start; /* Position in file corresponding to piece. */ ! 61: } PieceRec, *PiecePtr; ! 62: ! 63: ! 64: ! 65: /* The master structure for the source. */ ! 66: ! 67: typedef struct { ! 68: int file; /* File descriptor. */ ! 69: char *name; /* Name of file. */ ! 70: int length; /* Number of characters in file. */ ! 71: int origlength; /* Number of characters originally in file. */ ! 72: int numpieces; /* How many pieces the file is divided in. */ ! 73: PiecePtr piece; /* Pointer to array of pieces. */ ! 74: short changed; /* If changes have not been written to disk. */ ! 75: short everchanged; /* If changes have ever been made. */ ! 76: short startsvalid; /* True iff the start fields in pieces ! 77: are correct. */ ! 78: short eversaved; /* If we've ever saved changes to the file. */ ! 79: XtEditType editMode; /* Whether we are editing or read-only. */ ! 80: void (*func)(); /* Function to call when a change is made. */ ! 81: caddr_t param; /* Parameter to pass to above function. */ ! 82: int checkpointed; /* Filename to store checkpoints. */ ! 83: int checkpointchange; /* TRUE if we've changed since checkpointed. */ ! 84: } EDiskRec, *EDiskPtr; ! 85: ! 86: ! 87: ! 88: /* Refigure the starting location of each piece, which is the sum of the ! 89: lengths of all the preceding pieces. */ ! 90: ! 91: static CalculateStarts(data) ! 92: EDiskPtr data; ! 93: { ! 94: int i; ! 95: PiecePtr piece; ! 96: long start = 0; ! 97: for (i = 0, piece = data->piece; i < data->numpieces; i++, piece++) { ! 98: piece->start = start; ! 99: start += piece->length; ! 100: } ! 101: data->startsvalid = TRUE; ! 102: } ! 103: ! 104: ! 105: ! 106: /* Read in the text for the ith piece. */ ! 107: ! 108: static ReadPiece(data, i) ! 109: EDiskPtr data; ! 110: int i; ! 111: { ! 112: PiecePtr piece = data->piece + i; ! 113: piece->maxlength = piece->length; ! 114: piece->buf = XtMalloc((unsigned) piece->maxlength); ! 115: (void)lseek(data->file, (long) i * BUFSIZE, 0); ! 116: (void)read(data->file, piece->buf, piece->length); ! 117: } ! 118: ! 119: ! 120: ! 121: /* Figure out which piece corresponds to the given position. If this source ! 122: has never had any changes, then this is a simple matter of division; ! 123: otherwise, we have to search for the specified piece. (Currently, this is ! 124: a simple linear search; a binary one would probably be better.) */ ! 125: ! 126: static PiecePtr PieceFromPosition(data, position) ! 127: EDiskPtr data; ! 128: XtTextPosition position; ! 129: { ! 130: int i; ! 131: PiecePtr piece; ! 132: if (!data->everchanged) { ! 133: i = position / BUFSIZE; ! 134: if (i >= data->numpieces) i = data->numpieces - 1; ! 135: piece = data->piece + i; ! 136: } else { ! 137: if (!data->startsvalid) ! 138: CalculateStarts(data); ! 139: for (i = 0, piece = data->piece; i < data->numpieces - 1; i++, piece++) ! 140: if (position < piece->start + piece->length) ! 141: break; ! 142: } ! 143: if (!piece->buf) ReadPiece(data, i); ! 144: return piece; ! 145: } ! 146: ! 147: ! 148: /* Return the given position, coerced to be within the range of legal positions ! 149: for the given source. */ ! 150: ! 151: static XtTextPosition CoerceToLegalPosition(data, position) ! 152: EDiskPtr data; ! 153: XtTextPosition position; ! 154: { ! 155: return (position < 0) ? 0 : ! 156: ((position > data->length) ? data->length : position); ! 157: } ! 158: ! 159: ! 160: ! 161: /* Semi-public definitions */ ! 162: ! 163: /* Reads in the text for the given range. Will not read past a piece boundary. ! 164: Returns the position after the last character that was read. */ ! 165: ! 166: static EDiskReadText(src, position, text, maxRead) ! 167: XtTextSource *src; ! 168: XtTextPosition position; /* Starting position to read. */ ! 169: XtTextBlock *text; /* RETURN - the text read. */ ! 170: int maxRead; /* Number of positions to read. */ ! 171: { ! 172: EDiskPtr data = (EDiskPtr) src->data; ! 173: PiecePtr piece; ! 174: int count; ! 175: text->firstPos = position; ! 176: if (position < data->length) { ! 177: piece = PieceFromPosition(data, position); ! 178: text->ptr = piece->buf + (position - piece->start); ! 179: count = piece->length - (position - piece->start); ! 180: text->length = (count < maxRead) ? count : maxRead; ! 181: } ! 182: else { ! 183: text->length = 0; ! 184: text->ptr = ""; ! 185: } ! 186: return position + text->length; ! 187: } ! 188: ! 189: ! 190: ! 191: ! 192: /* Replace the text between startPos and endPos with the given text. Returns ! 193: EDITDONE on success, or EDITERROR if this source is read-only. */ ! 194: ! 195: ! 196: static int EDiskReplaceText(src, startPos, endPos, text, delta) ! 197: XtTextSource *src; ! 198: XtTextPosition startPos, endPos; ! 199: XtTextBlock *text; ! 200: int *delta; /* RETURN - change in length of source. */ ! 201: { ! 202: EDiskPtr data = (EDiskPtr) src->data; ! 203: PiecePtr piece, piece2; ! 204: int oldlength = endPos - startPos; ! 205: int i; ! 206: int del = text->length - oldlength; ! 207: if (del == 0 && oldlength == 0) { ! 208: *delta = 0; ! 209: return EDITDONE; ! 210: } ! 211: if (data->editMode != XttextEdit) ! 212: return EDITERROR; ! 213: data->length += del; ! 214: piece = PieceFromPosition(data, startPos); ! 215: if (oldlength > 0) { ! 216: piece2 = PieceFromPosition(data, endPos - 1); ! 217: if (piece != piece2) { ! 218: oldlength = endPos - piece2->start; ! 219: piece2->length -= oldlength; ! 220: for (i = 0; i < piece2->length; i++) ! 221: piece2->buf[i] = piece2->buf[i + oldlength]; ! 222: for (piece2--; piece2 > piece; piece2--) ! 223: piece2->length = 0; ! 224: oldlength = piece->length - (startPos - piece->start); ! 225: del = text->length - oldlength; ! 226: } ! 227: } ! 228: data->changed = data->everchanged = data->checkpointchange = TRUE; ! 229: data->startsvalid = FALSE; ! 230: piece->length += del; ! 231: if (piece->length > piece->maxlength) { ! 232: do ! 233: piece->maxlength *= 2; ! 234: while (piece->length > piece->maxlength); ! 235: piece->buf = XtRealloc(piece->buf, (unsigned)piece->maxlength); ! 236: } ! 237: if (del < 0) /* insert shorter than delete, text getting ! 238: shorter */ ! 239: for (i = startPos - piece->start; i < piece->length; i++) ! 240: piece->buf[i] = piece->buf[i - del]; ! 241: else ! 242: if (del > 0) /* insert longer than delete, text getting ! 243: longer */ ! 244: for (i = piece->length - del - 1; ! 245: i >= startPos - piece->start; ! 246: i--) ! 247: piece->buf[i + del] = piece->buf[i]; ! 248: if (text->length) /* do insert */ ! 249: for (i = 0; i < text->length; ++i) ! 250: if ((piece->buf[startPos - piece->start + i] = text->ptr[i]) == CR) ! 251: piece->buf[startPos - piece->start + i] = LF; ! 252: *delta = text->length - (endPos - startPos); ! 253: if (data->func) (*data->func)(data->param); ! 254: return EDITDONE; ! 255: } ! 256: ! 257: ! 258: /* Returns the last legal position in the source. */ ! 259: ! 260: static XtTextPosition EDiskGetLastPos(src) ! 261: XtTextSource *src; ! 262: { ! 263: return ((EDiskPtr) src->data)->length; ! 264: } ! 265: ! 266: ! 267: /* Sets the last legal position in the source. This is actually an obsolete ! 268: source request, and is therefore a noop. */ ! 269: ! 270: /*ARGSUSED*/ /* -- keeps lint from complaining. */ ! 271: static EDiskSetLastPos(src) ! 272: XtTextSource *src; ! 273: { ! 274: } ! 275: ! 276: ! 277: /* Utility macro used in EDiskScan. Used to determine what the next character ! 278: "after" index is, where "after" is either left or right, depending on ! 279: what kind of search we're doing. */ ! 280: ! 281: #define Look(index, c)\ ! 282: { \ ! 283: if ((dir == XtsdLeft && index <= 0) || \ ! 284: (dir == XtsdRight && index >= data->length)) \ ! 285: c = 0; \ ! 286: else { \ ! 287: if (index + doff < piece->start || \ ! 288: index + doff >= piece->start + piece->length) \ ! 289: piece = PieceFromPosition(data, index + doff); \ ! 290: c = piece->buf[index + doff - piece->start]; \ ! 291: } \ ! 292: } ! 293: ! 294: ! 295: ! 296: /* Scan the source. This gets complicated, but should be explained wherever ! 297: it is that we documented sources. */ ! 298: ! 299: static XtTextPosition EDiskScan(src, position, sType, dir, count, include) ! 300: XtTextSource *src; ! 301: XtTextPosition position; ! 302: ScanType sType; ! 303: ScanDirection dir; ! 304: int count, include; ! 305: { ! 306: EDiskPtr data = (EDiskPtr) src->data; ! 307: XtTextPosition index; ! 308: PiecePtr piece; ! 309: char c; ! 310: int ddir, doff, i, whiteSpace; ! 311: ddir = (dir == XtsdRight) ? 1 : -1; ! 312: doff = (dir == XtsdRight) ? 0 : -1; ! 313: ! 314: index = position; ! 315: piece = data->piece; ! 316: if (!piece->buf) ReadPiece(data, 0); ! 317: switch (sType) { ! 318: case XtstPositions: ! 319: if (!include && count > 0) ! 320: count--; ! 321: index = CoerceToLegalPosition(data, index + count * ddir); ! 322: break; ! 323: case XtstWhiteSpace: ! 324: for (i = 0; i < count; i++) { ! 325: whiteSpace = -1; ! 326: while (index >= 0 && index <= data->length) { ! 327: Look(index, c); ! 328: if ((c == ' ') || (c == '\t') || (c == '\n')) { ! 329: if (whiteSpace < 0) whiteSpace = index; ! 330: } else if (whiteSpace >= 0) ! 331: break; ! 332: index += ddir; ! 333: } ! 334: } ! 335: if (!include) { ! 336: if (whiteSpace < 0 && dir == XtsdRight) ! 337: whiteSpace = data->length; ! 338: index = whiteSpace; ! 339: } ! 340: index = CoerceToLegalPosition(data, index); ! 341: break; ! 342: case XtstEOL: ! 343: for (i = 0; i < count; i++) { ! 344: while (index >= 0 && index <= data->length) { ! 345: Look(index, c); ! 346: if (c == '\n') ! 347: break; ! 348: index += ddir; ! 349: } ! 350: if (i < count - 1) ! 351: index += ddir; ! 352: } ! 353: if (include) ! 354: index += ddir; ! 355: index = CoerceToLegalPosition(data, index); ! 356: break; ! 357: case XtstFile: ! 358: if (dir == XtsdLeft) ! 359: index = 0; ! 360: else ! 361: index = data->length; ! 362: break; ! 363: } ! 364: return index; ! 365: } ! 366: ! 367: ! 368: /* Return what the editting mode of this source is. */ ! 369: ! 370: static XtEditType EDiskGetEditType(src) ! 371: XtTextSource *src; ! 372: { ! 373: EDiskPtr data = (EDiskPtr) src->data; ! 374: return data->editMode; ! 375: } ! 376: ! 377: ! 378: ! 379: /* Public definitions. */ ! 380: ! 381: /* Create a new text source from the given file, and the given edit mode. */ ! 382: ! 383: XtTextSource *XtCreateEDiskSource(name, mode) ! 384: char *name; ! 385: XtEditType mode; ! 386: { ! 387: XtTextSource * src; ! 388: EDiskPtr data; ! 389: struct stat stat; ! 390: int i; ! 391: src = (XtTextSource *) XtMalloc(sizeof(XtTextSource)); ! 392: src->read = EDiskReadText; ! 393: src->replace = EDiskReplaceText; ! 394: src->getLastPos = EDiskGetLastPos; ! 395: src->setLastPos = EDiskSetLastPos; ! 396: src->editType = EDiskGetEditType; ! 397: src->scan = EDiskScan; ! 398: data = (EDiskPtr) XtMalloc(sizeof(EDiskRec)); ! 399: src->data = (int *) data; ! 400: data->editMode = mode; ! 401: data->file = myopen(name, O_RDONLY | O_CREAT, 0666); ! 402: data->name = strcpy(XtMalloc((unsigned)strlen(name) + 1), name); ! 403: (void)fstat(data->file, &stat); ! 404: data->length = data->origlength = stat.st_size; ! 405: data->numpieces = (data->length + BUFSIZE - 1) / BUFSIZE; ! 406: if (data->numpieces < 1) ! 407: data->numpieces = 1; ! 408: data->piece = (PiecePtr) ! 409: XtMalloc((unsigned) data->numpieces * sizeof(PieceRec)); ! 410: for (i = 0; i < data->numpieces; i++) { ! 411: data->piece[i].buf = 0; ! 412: data->piece[i].length = BUFSIZE; ! 413: data->piece[i].start = i * BUFSIZE; ! 414: } ! 415: data->piece[data->numpieces - 1].length -= ! 416: data->numpieces * BUFSIZE - data->length; ! 417: data->startsvalid = TRUE; ! 418: data->changed = data->everchanged = data->eversaved = FALSE; ! 419: data->checkpointed = data->checkpointchange = FALSE; ! 420: data->func = NULL; ! 421: if (data->length == 0) { ! 422: data->piece[0].buf = XtMalloc(1); ! 423: data->piece[0].maxlength = 1; ! 424: } ! 425: return src; ! 426: } ! 427: ! 428: ! 429: ! 430: /* Change the editing mode of a source. */ ! 431: ! 432: XtEDiskChangeEditMode(src, editMode) ! 433: XtTextSource *src; ! 434: XtEditType editMode; ! 435: { ! 436: EDiskPtr data = (EDiskPtr) src->data; ! 437: data->editMode = editMode; ! 438: } ! 439: ! 440: ! 441: ! 442: /* Call the given function with the given parameter whenever the source is ! 443: changed. */ ! 444: ! 445: XtEDiskSetCallbackWhenChanged(src, func, param) ! 446: XtTextSource *src; ! 447: void (*func)(); ! 448: caddr_t param; ! 449: { ! 450: EDiskPtr data = (EDiskPtr) src->data; ! 451: data->func = func; ! 452: data->param = param; ! 453: } ! 454: ! 455: ! 456: /* Return whether any unsaved changes have been made. */ ! 457: ! 458: XtEDiskChanged(src) ! 459: XtTextSource *src; ! 460: { ! 461: return ((EDiskPtr) src->data)->changed; ! 462: } ! 463: ! 464: ! 465: /* Save any unsaved changes. */ ! 466: ! 467: XtEDiskSaveFile(src) ! 468: XtTextSource *src; ! 469: { ! 470: EDiskPtr data = (EDiskPtr) src->data; ! 471: PiecePtr piece; ! 472: int i, needlseek; ! 473: char str[1024]; ! 474: if (!data->changed) ! 475: return; ! 476: data->changed = data->checkpointchange = FALSE; ! 477: if (!data->eversaved) { ! 478: data->eversaved = TRUE; /* Open file in a mode where we can write. */ ! 479: (void)myclose(data->file); ! 480: data->file = myopen(data->name, O_RDWR, 0666); ! 481: } ! 482: if (!data->startsvalid) ! 483: CalculateStarts(data); ! 484: for (i = 0, piece = data->piece; i < data->numpieces; i++, piece++) { ! 485: if (!(piece->buf) && i * BUFSIZE != piece->start) ! 486: ReadPiece(data, i); ! 487: } ! 488: needlseek = TRUE; ! 489: for (i = 0, piece = data->piece; i < data->numpieces; i++, piece++) { ! 490: if (piece->buf) { ! 491: if (needlseek) { ! 492: (void) lseek(data->file, (long) piece->start, 0); ! 493: needlseek = FALSE; ! 494: } ! 495: (void) write(data->file, piece->buf, piece->length); ! 496: } ! 497: else ! 498: needlseek = TRUE; ! 499: } ! 500: if (data->length < data->origlength) ! 501: (void)ftruncate(data->file, data->length); ! 502: data->origlength = data->length; ! 503: if (data->checkpointed) { ! 504: (void) sprintf(str, "%s.CKP", data->name); ! 505: (void) unlink(str); ! 506: data->checkpointed = FALSE; ! 507: } ! 508: } ! 509: ! 510: ! 511: /* Save into a new file. */ ! 512: ! 513: XtEDiskSaveAsFile(src, filename) ! 514: XtTextSource *src; ! 515: char *filename; ! 516: { ! 517: EDiskPtr data = (EDiskPtr) src->data; ! 518: int i; ! 519: PiecePtr piece; ! 520: if (strcmp(filename, data->name) != 0) { ! 521: for (i = 0, piece = data->piece; i < data->numpieces; i++, piece++) ! 522: if (!(piece->buf)) ! 523: ReadPiece(data, i); ! 524: data->eversaved = FALSE; ! 525: data->origlength = 0; ! 526: data->file = creat(filename, 0666); ! 527: data->eversaved = TRUE; ! 528: data->origlength = 0; ! 529: XtFree(data->name); ! 530: data->name = strcpy(XtMalloc((unsigned) strlen(filename) + 1), ! 531: filename); ! 532: data->changed = TRUE; ! 533: } ! 534: XtEDiskSaveFile(src); ! 535: } ! 536: ! 537: ! 538: ! 539: /* Checkpoint this file. */ ! 540: ! 541: XtEDiskMakeCheckpoint(src) ! 542: XtTextSource *src; ! 543: { ! 544: EDiskPtr data = (EDiskPtr) src->data; ! 545: char name[1024]; ! 546: int fid, i; ! 547: PiecePtr piece; ! 548: if (!data->checkpointchange) return; /* No changes to save. */ ! 549: (void) sprintf(name, "%s.CKP", data->name); ! 550: fid = creat(name, 0600); ! 551: if (fid < 0) return; ! 552: for (i = 0, piece = data->piece; i < data->numpieces; i++, piece++) { ! 553: if (!(piece->buf)) ReadPiece(data, i); ! 554: (void) write(fid, piece->buf, piece->length); ! 555: } ! 556: (void)close(fid); ! 557: data->checkpointed = TRUE; ! 558: data->checkpointchange = FALSE; ! 559: } ! 560: ! 561: ! 562: /* We're done with this source, XtFree its storage. */ ! 563: ! 564: XtDestroyEDiskSource(src) ! 565: XtTextSource *src; ! 566: { ! 567: EDiskPtr data = (EDiskPtr) src->data; ! 568: int i; ! 569: char *ptr; ! 570: (void)myclose(data->file); ! 571: for (i=0 ; i<data->numpieces ; i++) ! 572: if (ptr = data->piece[i].buf) XtFree(ptr); ! 573: XtFree((char *)data->piece); ! 574: XtFree((char *)data->name); ! 575: XtFree((char *)data); ! 576: XtFree((char *)src); ! 577: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.