|
|
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.