Annotation of researchv9/X11/src/X.V11R1/clients/xmh/EDiskSrc.c, revision 1.1.1.1

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

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.