Annotation of coherent/d/usr/bin/elvis/cut.c, revision 1.1.1.1

1.1       root        1: /* cut.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 function which manipulate the cut buffers. */
                     12: 
                     13: #include "config.h"
                     14: #include "vi.h"
                     15: #if TURBOC
                     16: #include <process.h>           /* needed for getpid */
                     17: #endif
                     18: #if TOS
                     19: #include <osbind.h>
                     20: #define        rename(a,b)     Frename(0,a,b)
                     21: #endif
                     22: 
                     23: # define NANNONS       9       /* number of annonymous buffers */
                     24: 
                     25: static struct cutbuf
                     26: {
                     27:        short   *phys;  /* pointer to an array of #s of BLKs containing text */
                     28:        int     nblks;  /* number of blocks in phys[] array */
                     29:        int     start;  /* offset into first block of start of cut */
                     30:        int     end;    /* offset into last block of end of cut */
                     31:        int     fd;     /* fd of tmp file, or -1 to use tmpfd */
                     32:        char    lnmode; /* boolean: line-mode cut? (as opposed to char-mode) */
                     33: }
                     34:        named[27],      /* cut buffers "a through "z and ". */
                     35:        annon[NANNONS]; /* annonymous cut buffers */
                     36: 
                     37: static char    cbname; /* name chosen for next cut/paste operation */
                     38: 
                     39: 
                     40: #ifndef NO_RECYCLE
                     41: /* This function builds a list of all blocks needed in the current tmp file
                     42:  * for the contents of cut buffers.
                     43:  * !!! WARNING: if you have more than ~450000 bytes of text in all of the
                     44:  * cut buffers, then this will fail disastrously, because buffer overflow
                     45:  * is *not* allowed for.
                     46:  */
                     47: int cutneeds(need)
                     48:        BLK             *need;  /* this is where we deposit the list */
                     49: {
                     50:        struct cutbuf   *cb;    /* used to count through cut buffers */
                     51:        int             i;      /* used to count through blocks of a cut buffer */
                     52:        int             n;      /* total number of blocks in list */
                     53: 
                     54:        n = 0;
                     55: 
                     56:        /* first the named buffers... */
                     57:        for (cb = named; cb < &named[27]; cb++)
                     58:        {
                     59:                if (cb->fd > 0)
                     60:                        continue;
                     61: 
                     62:                for (i = cb->nblks; i-- > 0; )
                     63:                {
                     64:                        need->n[n++] = cb->phys[i];
                     65:                }
                     66:        }
                     67: 
                     68:        /* then the anonymous buffers */
                     69:        for (cb = annon; cb < &annon[NANNONS]; cb++)
                     70:        {
                     71:                if (cb->fd > 0)
                     72:                        continue;
                     73: 
                     74:                for (i = cb->nblks; i-- > 0; )
                     75:                {
                     76:                        need->n[n++] = cb->phys[i];
                     77:                }
                     78:        }
                     79: 
                     80:        return n;
                     81: }
                     82: #endif
                     83: 
                     84: /* This function frees a cut buffer */
                     85: static void cutfree(buf)
                     86:        struct cutbuf   *buf;
                     87: {
                     88:        char    cutfname[50];
                     89:        int     i;
                     90: 
                     91:        /* return immediately if the buffer is already empty */
                     92:        if (buf->nblks <= 0)
                     93:        {
                     94:                return;
                     95:        }
                     96: 
                     97:        /* else free up stuff */
                     98:        buf->nblks = 0;
                     99: #ifdef DEBUG
                    100:        if (!buf->phys)
                    101:                msg("cutfree() tried to free an NULL buf->phys pointer.");
                    102: #endif
                    103:        free((char *)buf->phys);
                    104: 
                    105:        /* see if anybody else needs this tmp file */
                    106:        if (buf->fd >= 0)
                    107:        {
                    108:                for (i = 0; i < 27; i++)
                    109:                {
                    110:                        if (named[i].nblks > 0 && named[i].fd == buf->fd)
                    111:                        {
                    112:                                break;
                    113:                        }
                    114:                }
                    115:        }
                    116: 
                    117:        /* if nobody else needs it, then discard the tmp file */
                    118:        if (buf->fd >= 0 && i == 27)
                    119:        {
                    120:                close(buf->fd);
                    121: #if MSDOS || TOS
                    122:                strcpy(cutfname, o_directory);
                    123:                if ((i = strlen(cutfname)) && !strchr(":/\\", cutfname[i-1]))
                    124:                        cutfname[i++]=SLASH;
                    125:                sprintf(cutfname+i, CUTNAME+3, getpid(), buf->fd);
                    126: #else
                    127:                sprintf(cutfname, CUTNAME, o_directory, getpid(), buf->fd);
                    128: #endif
                    129:                unlink(cutfname);
                    130:        }
                    131: }
                    132: 
                    133: /* This function is called when we are about to abort a tmp file.  If any
                    134:  * cut buffers still need the file, then a copy of the file should be
                    135:  * created for use by the cut buffers.
                    136:  *
                    137:  * To minimize the number of extra files lying around, only named cut buffers
                    138:  * are preserved in a file switch; the annonymous buffers just go away.
                    139:  */
                    140: void cutswitch(tmpname)
                    141:        char    *tmpname; /* name of the tmp file */
                    142: {
                    143:        char    cutfname[50];   /* used to build a new name for the tmp file */
                    144:        int     fd;             /* a new fd for the current tmp file */
                    145:        int     i;
                    146: #if MSDOS || TOS
                    147:        int     j;
                    148: #endif
                    149: 
                    150:        /* discard all annonymous cut buffers */
                    151:        for (i = 0; i < NANNONS; i++)
                    152:        {
                    153:                cutfree(&annon[i]);
                    154:        }
                    155: 
                    156:        /* find the first named buffer that uses this tmp file */
                    157:        for (i = 0; i < 27; i++)
                    158:        {
                    159:                if (named[i].nblks > 0 && named[i].fd < 0)
                    160:                {
                    161:                        break;
                    162:                }
                    163:        }
                    164: 
                    165:        /* if none of them use this tmp file, then we're done */
                    166:        if (i == 27)
                    167:        {
                    168:                return;
                    169:        }
                    170: 
                    171:        /* else we'll need this file and an fd a little longer */
                    172: #if MSDOS || TOS
                    173:        strcpy(cutfname, o_directory);
                    174:        if ((j = strlen(cutfname)) && !strchr(":/\\", cutfname[j-1]))
                    175:                cutfname[j++]=SLASH;
                    176:        close(tmpfd);
                    177:        fd = open(tmpname, O_RDONLY|O_BINARY);
                    178:        close(fd);
                    179:        sprintf(cutfname+j, CUTNAME+3, getpid(), fd);
                    180:        rename(tmpname, cutfname);
                    181:        fd = open(cutfname, O_RDONLY|O_BINARY);
                    182:        tmpfd = -1; /* we'll try to close this in tmp.c, but who cares? */
                    183: #else
                    184:        fd = dup(tmpfd);
                    185: # if OSK
                    186:        sprintf(cutfname, CUTNAME, "", getpid(), fd);
                    187:        if (!link(tmpname, &cutfname[1])) /* skip slash */
                    188:                unlink(tmpname);
                    189: # else 
                    190:        sprintf(cutfname, CUTNAME, o_directory, getpid(), fd);
                    191:        link(tmpname, cutfname) || unlink(tmpname);
                    192: # endif
                    193: #endif
                    194: 
                    195:        /* have all cut buffers use the new fd instead */
                    196:        for (; i < 27; i++)
                    197:        {
                    198:                if (named[i].nblks > 0 && named[i].fd < 0)
                    199:                {
                    200:                        named[i].fd = fd;
                    201:                }
                    202:        }
                    203: }
                    204: 
                    205: /* This function should be called just before termination of vi */
                    206: void cutend()
                    207: {
                    208:        int     i;
                    209: 
                    210:        /* free all named cut buffers, since they might be forcing an older
                    211:         * tmp file to be retained.
                    212:         */
                    213:        for (i = 0; i < 27; i++)
                    214:        {
                    215:                cutfree(&named[i]);
                    216:        }
                    217: }
                    218: 
                    219: 
                    220: /* This function is used to select the cut buffer to be used next */
                    221: void cutname(name)
                    222:        int     name;   /* a single character */
                    223: {
                    224:        cbname = name;
                    225: }
                    226: 
                    227: 
                    228: 
                    229: 
                    230: /* This function copies a selected segment of text to a cut buffer */
                    231: void cut(from, to)
                    232:        MARK    from;           /* start of text to cut */
                    233:        MARK    to;             /* end of text to cut */
                    234: {
                    235:        int             first;  /* logical number of first block in cut */
                    236:        int             last;   /* logical number of last block used in cut */
                    237:        long            line;   /* a line number */
                    238:        int             lnmode; /* boolean: will this be a line-mode cut? */
                    239:        MARK            delthru;/* end of text temporarily inserted for apnd */
                    240:        REG struct cutbuf *cb;
                    241:        REG long        l;
                    242:        REG int         i;
                    243:        REG char        *scan;
                    244:        char            *blkc;
                    245: 
                    246:        /* detect whether this must be a line-mode cut or char-mode cut */
                    247:        if (markidx(from) == 0 && markidx(to) == 0)
                    248:                lnmode = TRUE;
                    249:        else
                    250:                lnmode = FALSE;
                    251: 
                    252:        /* by default, we don't "delthru" anything */
                    253:        delthru = MARK_UNSET;
                    254: 
                    255:        /* decide which cut buffer to use */
                    256:        if (!cbname)
                    257:        {
                    258:                /* free up the last annonymous cut buffer */
                    259:                cutfree(&annon[NANNONS - 1]);
                    260: 
                    261:                /* shift the annonymous cut buffers */
                    262:                for (i = NANNONS - 1; i > 0; i--)
                    263:                {
                    264:                        annon[i] = annon[i - 1];
                    265:                }
                    266: 
                    267:                /* use the first annonymous cut buffer */
                    268:                cb = annon;
                    269:                cb->nblks = 0;
                    270:        }
                    271:        else if (cbname >= 'a' && cbname <= 'z')
                    272:        {
                    273:                cb = &named[cbname - 'a'];
                    274:                cutfree(cb);
                    275:        }
                    276: #ifndef CRUNCH
                    277:        else if (cbname >= 'A' && cbname <= 'Z')
                    278:        {
                    279:                cb = &named[cbname - 'A'];
                    280:                if (cb->nblks > 0)
                    281:                {
                    282:                        /* resolve linemode/charmode differences */
                    283:                        if (!lnmode && cb->lnmode)
                    284:                        {
                    285:                                from &= ~(BLKSIZE - 1);
                    286:                                if (markidx(to) != 0 || to == from)
                    287:                                {
                    288:                                        to = to + BLKSIZE - markidx(to);
                    289:                                }
                    290:                                lnmode = TRUE;
                    291:                        }
                    292: 
                    293:                        /* insert the old cut-buffer before the new text */
                    294:                        mark[28] = to;
                    295:                        delthru = paste(from, FALSE, TRUE);
                    296:                        if (delthru == MARK_UNSET)
                    297:                        {
                    298:                                return;
                    299:                        }
                    300:                        delthru++;
                    301:                        to = mark[28];
                    302:                }
                    303:                cutfree(cb);
                    304:        }
                    305: #endif /* not CRUNCH */
                    306:        else if (cbname == '.')
                    307:        {
                    308:                cb = &named[26];
                    309:                cutfree(cb);
                    310:        }
                    311:        else
                    312:        {
                    313:                msg("Invalid cut buffer name: \"%c", cbname);
                    314:                cbname = '\0';
                    315:                return;
                    316:        }
                    317:        cbname = '\0';
                    318:        cb->fd = -1;
                    319: 
                    320:        /* detect whether we're doing a line mode cut */
                    321:        cb->lnmode = lnmode;
                    322: 
                    323:        /* ---------- */
                    324: 
                    325:        /* Reporting... */      
                    326:        if (markidx(from) == 0 && markidx(to) == 0)
                    327:        {
                    328:                rptlines = markline(to) - markline(from);
                    329:                rptlabel = "yanked";
                    330:        }
                    331: 
                    332:        /* ---------- */
                    333: 
                    334:        /* make sure each block has a physical disk address */
                    335:        blksync();
                    336: 
                    337:        /* find the first block in the cut */
                    338:        line = markline(from);
                    339:        for (first = 1; line > lnum[first]; first++)
                    340:        {
                    341:        }
                    342: 
                    343:        /* fetch text of the block containing that line */
                    344:        blkc = scan = blkget(first)->c;
                    345: 
                    346:        /* find the mark in the block */
                    347:        for (l = lnum[first - 1]; ++l < line; )
                    348:        {
                    349:                while (*scan++ != '\n')
                    350:                {
                    351:                }
                    352:        }
                    353:        scan += markidx(from);
                    354: 
                    355:        /* remember the offset of the start */
                    356:        cb->start = scan - blkc;
                    357: 
                    358:        /* ---------- */
                    359: 
                    360:        /* find the last block in the cut */
                    361:        line = markline(to);
                    362:        for (last = first; line > lnum[last]; last++)
                    363:        {
                    364:        }
                    365: 
                    366:        /* fetch text of the block containing that line */
                    367:        if (last != first)
                    368:        {
                    369:                blkc = scan = blkget(last)->c;
                    370:        }
                    371:        else
                    372:        {
                    373:                scan = blkc;
                    374:        }
                    375: 
                    376:        /* find the mark in the block */
                    377:        for (l = lnum[last - 1]; ++l < line; )
                    378:        {
                    379:                while (*scan++ != '\n')
                    380:                {
                    381:                }
                    382:        }
                    383:        if (markline(to) <= nlines)
                    384:        {
                    385:                scan += markidx(to);
                    386:        }
                    387: 
                    388:        /* remember the offset of the end */
                    389:        cb->end = scan - blkc;
                    390: 
                    391:        /* ------- */
                    392: 
                    393:        /* remember the physical block numbers of all included blocks */
                    394:        cb->nblks = last - first;
                    395:        if (cb->end > 0)
                    396:        {
                    397:                cb->nblks++;
                    398:        }
                    399: #ifdef lint
                    400:        cb->phys = (short *)0;
                    401: #else
                    402:        cb->phys = (short *)malloc((unsigned)(cb->nblks * sizeof(short)));
                    403: #endif
                    404:        for (i = 0; i < cb->nblks; i++)
                    405:        {
                    406:                cb->phys[i] = hdr.n[first++];
                    407:        }
                    408: 
                    409: #ifndef CRUNCH
                    410:        /* if we temporarily inserted text for appending, then delete that
                    411:         * text now -- before the user sees it.
                    412:         */
                    413:        if (delthru)
                    414:        {
                    415:                line = rptlines;
                    416:                delete(from, delthru);
                    417:                rptlines = line;
                    418:                rptlabel = "yanked";
                    419:        }
                    420: #endif /* not CRUNCH */
                    421: }
                    422: 
                    423: 
                    424: static void readcutblk(cb, blkno)
                    425:        struct cutbuf   *cb;
                    426:        int             blkno;
                    427: {
                    428:        int             fd;     /* either tmpfd or cb->fd */
                    429: 
                    430:        /* decide which fd to use */
                    431:        if (cb->fd >= 0)
                    432:        {
                    433:                fd = cb->fd;
                    434:        }
                    435:        else
                    436:        {
                    437:                fd = tmpfd;
                    438:        }
                    439: 
                    440:        /* get the block */
                    441:        lseek(fd, (long)cb->phys[blkno] * (long)BLKSIZE, 0);
                    442:        if (read(fd, tmpblk.c, (unsigned)BLKSIZE) != BLKSIZE)
                    443:        {
                    444:                msg("Error reading back from tmp file for pasting!");
                    445:        }
                    446: }
                    447: 
                    448: 
                    449: /* This function inserts text from a cut buffer, and returns the MARK where
                    450:  * insertion ended.  Return MARK_UNSET on errors.
                    451:  */
                    452: MARK paste(at, after, retend)
                    453:        MARK    at;     /* where to insert the text */
                    454:        int     after;  /* boolean: insert after mark? (rather than before) */
                    455:        int     retend; /* boolean: return end of text? (rather than start) */
                    456: {
                    457:        REG struct cutbuf       *cb;
                    458:        REG int                 i;
                    459: 
                    460:        /* decide which cut buffer to use */
                    461:        if (cbname >= 'A' && cbname <= 'Z')
                    462:        {
                    463:                cb = &named[cbname - 'A'];
                    464:        }
                    465:        else if (cbname >= 'a' && cbname <= 'z')
                    466:        {
                    467:                cb = &named[cbname - 'a'];
                    468:        }
                    469:        else if (cbname >= '1' && cbname <= '9')
                    470:        {
                    471:                cb = &annon[cbname - '1'];
                    472:        }
                    473:        else if (cbname == '.')
                    474:        {
                    475:                cb = &named[26];
                    476:        }
                    477:        else if (!cbname)
                    478:        {
                    479:                cb = annon;
                    480:        }
                    481:        else
                    482:        {
                    483:                msg("Invalid cut buffer name: \"%c", cbname);
                    484:                cbname = '\0';
                    485:                return MARK_UNSET;
                    486:        }
                    487: 
                    488:        /* make sure it isn't empty */
                    489:        if (cb->nblks == 0)
                    490:        {
                    491:                if (cbname)
                    492:                        msg("Cut buffer \"%c is empty", cbname);
                    493:                else
                    494:                        msg("Cut buffer is empty");
                    495:                cbname = '\0';
                    496:                return MARK_UNSET;
                    497:        }
                    498:        cbname = '\0';
                    499: 
                    500:        /* adjust the insertion MARK for "after" and line-mode cuts */
                    501:        if (cb->lnmode)
                    502:        {
                    503:                at &= ~(BLKSIZE - 1);
                    504:                if (after)
                    505:                {
                    506:                        at += BLKSIZE;
                    507:                }
                    508:        }
                    509:        else if (after)
                    510:        {
                    511:                /* careful! if markidx(at) == 0 we might be pasting into an
                    512:                 * empty line -- so we can't blindly increment "at".
                    513:                 */
                    514:                if (markidx(at) == 0)
                    515:                {
                    516:                        pfetch(markline(at));
                    517:                        if (plen != 0)
                    518:                        {
                    519:                                at++;
                    520:                        }
                    521:                }
                    522:                else
                    523:                {
                    524:                        at++;
                    525:                }
                    526:        }
                    527: 
                    528:        /* put a copy of the "at" mark in the mark[] array, so it stays in
                    529:         * sync with changes made via add().
                    530:         */
                    531:        mark[27] = at;
                    532: 
                    533:        /* simple one-block paste? */
                    534:        if (cb->nblks == 1)
                    535:        {
                    536:                /* get the block */
                    537:                readcutblk(cb, 0);
                    538: 
                    539:                /* isolate the text we need within it */
                    540:                if (cb->end)
                    541:                {
                    542:                        tmpblk.c[cb->end] = '\0';
                    543:                }
                    544: 
                    545:                /* insert it */
                    546:                ChangeText
                    547:                {
                    548:                        add(at, &tmpblk.c[cb->start]);
                    549:                }
                    550:        }
                    551:        else
                    552:        {
                    553:                /* multi-block paste */
                    554: 
                    555:                ChangeText
                    556:                {
                    557:                        i = cb->nblks - 1;
                    558: 
                    559:                        /* add text from the last block first */
                    560:                        if (cb->end > 0)
                    561:                        {
                    562:                                readcutblk(cb, i);
                    563:                                tmpblk.c[cb->end] = '\0';
                    564:                                add(at, tmpblk.c);
                    565:                                i--;
                    566:                        }
                    567: 
                    568:                        /* add intervening blocks */
                    569:                        while (i > 0)
                    570:                        {
                    571:                                readcutblk(cb, i);
                    572:                                add(at, tmpblk.c);
                    573:                                i--;
                    574:                        }
                    575: 
                    576:                        /* add text from the first cut block */
                    577:                        readcutblk(cb, 0);
                    578:                        add(at, &tmpblk.c[cb->start]);
                    579:                }
                    580:        }
                    581: 
                    582:        /* Reporting... */
                    583:        rptlines = markline(mark[27]) - markline(at);
                    584:        rptlabel = "pasted";
                    585: 
                    586:        /* return the mark at the beginning/end of inserted text */
                    587:        if (retend)
                    588:        {
                    589:                return mark[27] - 1L;
                    590:        }
                    591:        return at;
                    592: }
                    593: 
                    594: 
                    595: 
                    596: 
                    597: #ifndef NO_AT
                    598: 
                    599: /* This function copies characters from a cut buffer into a string.
                    600:  * It returns the number of characters in the cut buffer.  If the cut
                    601:  * buffer is too large to fit in the string (i.e. if cb2str() returns
                    602:  * a number >= size) then the characters will not have been copied.
                    603:  * It returns 0 if the cut buffer is empty, and -1 for invalid cut buffers.
                    604:  */
                    605: int cb2str(name, buf, size)
                    606:        int     name;   /* the name of a cut-buffer to get: a-z only! */
                    607:        char    *buf;   /* where to put the string */
                    608:        unsigned size;  /* size of buf */
                    609: {
                    610:        REG struct cutbuf       *cb;
                    611:        REG char                *src;
                    612:        REG char                *dest;
                    613: 
                    614:        /* decide which cut buffer to use */
                    615:        if (name >= 'a' && name <= 'z')
                    616:        {
                    617:                cb = &named[name - 'a'];
                    618:        }
                    619:        else
                    620:        {
                    621:                return -1;
                    622:        }
                    623: 
                    624:        /* if the buffer is empty, return 0 */
                    625:        if (cb->nblks == 0)
                    626:        {
                    627:                return 0;
                    628:        }
                    629: 
                    630:        /* !!! if not a single-block cut, then fail */
                    631:        if (cb->nblks != 1)
                    632:        {
                    633:                return size;
                    634:        }
                    635: 
                    636:        /* if too big, return the size now, without doing anything */
                    637:        if (cb->end - cb->start >= size)
                    638:        {
                    639:                return cb->end - cb->start;
                    640:        }
                    641: 
                    642:        /* get the block */
                    643:        readcutblk(cb, 0);
                    644: 
                    645:        /* isolate the string within that blk */
                    646:        if (cb->start == 0)
                    647:        {
                    648:                tmpblk.c[cb->end] = '\0';
                    649:        }
                    650:        else
                    651:        {
                    652:                for (dest = tmpblk.c, src = dest + cb->start; src < tmpblk.c + cb->end; )
                    653:                {
                    654:                        *dest++ = *src++;
                    655:                }
                    656:                *dest = '\0';
                    657:        }
                    658: 
                    659:        /* copy the string into the buffer */
                    660:        if (buf != tmpblk.c)
                    661:        {
                    662:                strcpy(buf, tmpblk.c);
                    663:        }
                    664: 
                    665:        /* return the length */
                    666:        return cb->end - cb->start;
                    667: }
                    668: #endif

unix.superglobalmegacorp.com

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