home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 5 Edit / 05-Edit.zip / elvis184.zip / src / cut.c < prev    next >
C/C++ Source or Header  |  1995-05-26  |  16KB  |  797 lines

  1. /* cut.c */
  2.  
  3. /* Author:
  4.  *    Steve Kirkendall
  5.  *    1500 SW Park #326
  6.  *    Portland OR, 97201
  7.  *    kirkenda@cs.pdx.edu
  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 NANONS    9    /* number of anonymous 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    tmpnum;    /* ID number of the temp file */
  32.     char    lnmode;    /* boolean: line-mode cut? (as opposed to char-mode) */
  33. }
  34.     named[27],    /* cut buffers "a through "z and ". */
  35.     anon[NANONS];    /* anonymous cut buffers */
  36.  
  37. static char    cbname;    /* name chosen for next cut/paste operation */
  38. static char    dotcb;    /* cut buffer to use if "doingdot" is set */
  39.  
  40.  
  41. #ifndef NO_RECYCLE
  42. /* This function builds a list of all blocks needed in the current tmp file
  43.  * for the contents of cut buffers.
  44.  * !!! WARNING: if you have more than ~450000 bytes of text in all of the
  45.  * cut buffers, then this will fail disastrously, because buffer overflow
  46.  * is *not* allowed for.
  47.  */
  48. int cutneeds(need)
  49.     BLK        *need;    /* this is where we deposit the list */
  50. {
  51.     struct cutbuf    *cb;    /* used to count through cut buffers */
  52.     int        i;    /* used to count through blocks of a cut buffer */
  53.     int        n;    /* total number of blocks in list */
  54.  
  55.     n = 0;
  56.  
  57.     /* first the named buffers... */
  58.     for (cb = named; cb < &named[27]; cb++)
  59.     {
  60.         if (cb->tmpnum != tmpnum)
  61.             continue;
  62.  
  63.         for (i = cb->nblks; i-- > 0; )
  64.         {
  65.             need->n[n++] = cb->phys[i];
  66.         }
  67.     }
  68.  
  69.     /* then the anonymous buffers */
  70.     for (cb = anon; cb < &anon[NANONS]; cb++)
  71.     {
  72.         if (cb->tmpnum != tmpnum)
  73.             continue;
  74.  
  75.         for (i = cb->nblks; i-- > 0; )
  76.         {
  77.             need->n[n++] = cb->phys[i];
  78.         }
  79.     }
  80.  
  81.     /* return the length of the list */
  82.     return n;
  83. }
  84. #endif
  85.  
  86. static void maybezap(num)
  87.     int    num;    /* the tmpnum of the temporary file to [maybe] delete */
  88. {
  89.     char    cutfname[80];
  90.     int    i;
  91.  
  92.     /* if this is the current tmp file, then we'd better keep it! */
  93.     if (tmpfd >= 0 && num == tmpnum)
  94.     {
  95.         return;
  96.     }
  97.  
  98.     /* see if anybody else needs this tmp file */
  99.     for (i = 27; --i >= 0; )
  100.     {
  101.         if (named[i].nblks > 0 && named[i].tmpnum == num)
  102.         {
  103.             break;
  104.         }
  105.     }
  106.     if (i < 0)
  107.     {
  108.         for (i = NANONS; --i >= 0 ; )
  109.         {
  110.             if (anon[i].nblks > 0 && anon[i].tmpnum == num)
  111.             {
  112.                 break;
  113.             }
  114.         }
  115.     }
  116.  
  117.     /* if nobody else needs it, then discard the tmp file */
  118.     if (i < 0)
  119.     {
  120. #if MSDOS || TOS || OS2
  121.         strcpy(cutfname, o_directory);
  122.         if ((i = strlen(cutfname)) && !strchr(":/\\", cutfname[i - 1]))
  123.             cutfname[i++] = SLASH;
  124.         sprintf(cutfname + i, TMPNAME + 3, getpid(), num);
  125. #else
  126.         sprintf(cutfname, TMPNAME, o_directory, getpid(), num);
  127. #endif
  128.         unlink(cutfname);
  129.     }
  130. }
  131.  
  132. /* This function frees a cut buffer.  If it was the last cut buffer that
  133.  * referred to an old temp file, then it will delete the temp file. */
  134. static void cutfree(buf)
  135.     struct cutbuf    *buf;
  136. {
  137.     int    num;
  138.  
  139.     /* return immediately if the buffer is already empty */
  140.     if (buf->nblks <= 0)
  141.     {
  142.         return;
  143.     }
  144.  
  145.     /* else free up stuff */
  146.     num = buf->tmpnum;
  147.     buf->nblks = 0;
  148. #ifdef DEBUG
  149.     if (!buf->phys)
  150.         msg("cutfree() tried to free a NULL buf->phys pointer.");
  151.     else
  152. #endif
  153.     _free_((char *)buf->phys);
  154.  
  155.     /* maybe delete the temp file */
  156.     maybezap(num);
  157. }
  158.  
  159. /* This function is called when we are about to abort a tmp file.
  160.  *
  161.  * To minimize the number of extra files lying around, only named cut buffers
  162.  * are preserved in a file switch; the anonymous buffers just go away.
  163.  */
  164. void cutswitch()
  165. {
  166.     int    i;
  167.  
  168.     /* mark the current temp file as being "obsolete", and close it.  */
  169.     storename((char *)0);
  170.     close(tmpfd);
  171.     tmpfd = -1;
  172.  
  173.     /* discard all anonymous cut buffers */
  174. #ifndef NO_EXTENSIONS
  175.     if (!o_keepanon)
  176. #endif
  177.     {
  178.         for (i = 0; i < NANONS; i++)
  179.         {
  180.             cutfree(&anon[i]);
  181.         }
  182.     }
  183.  
  184.     /* delete the temp file, if we don't really need it */
  185.     maybezap(tmpnum);
  186. }
  187.  
  188. /* This function should be called just before termination of vi */
  189. void cutend()
  190. {
  191.     int    i;
  192.  
  193.     /* free the anonymous buffers, if they aren't already free */
  194.     cutswitch();
  195.  
  196.     /* free all named cut buffers, since they might be forcing an older
  197.      * tmp file to be retained.
  198.      */
  199.     for (i = 0; i < 27; i++)
  200.     {
  201.         cutfree(&named[i]);
  202.     }
  203.  
  204. #ifndef NO_EXTENSIONS
  205.     /* free all anonymous buffers, same reason */
  206.     for (i = 0; i < NANONS; i++)
  207.     {
  208.         cutfree(&anon[i]);
  209.     }
  210. #endif
  211.  
  212.     /* delete the temp file */
  213.     maybezap(tmpnum);
  214. }
  215.  
  216.  
  217. /* This function is used to select the cut buffer to be used next */
  218. void cutname(name)
  219.     int    name;    /* a single character */
  220. {
  221.     cbname = name;
  222. }
  223.  
  224.  
  225. #ifndef NO_LEARN
  226. /* This function appends a single character to a cut buffer; it is used
  227.  * during "learn" mode to record a keystroke.  The buffer to use is determined
  228.  * by an external variable, `learnbuf'; this variable contains the buffer's
  229.  * name while learning, or '\0' if not learning.
  230.  */
  231. void learnkey(key)
  232.     char        key;    /* keystroke to append to learning buffer */
  233. {
  234.     static char    prevlearn;    /* previously learned buffer name */
  235.     static char    buf[BLKSIZE];    /* used for storing keystrokes */
  236.     static int    nkeys;        /* number of keystrokes in buf[] */
  237.     struct cutbuf    *cb;        /* ptr to buffer being saved */
  238.     long        seekpos;    /* where saved cutbuf's text goes */
  239.  
  240.     /* if we're ending a learn operation, then save keystokes in a cutbuf */
  241.     if (learn != prevlearn && prevlearn)
  242.     {
  243.         /* choose the cutbuffer to use; free its old contents, if any */
  244.         cb = &named[prevlearn - 'a'];
  245.         cutfree(cb);
  246.  
  247.         /* delete the final "]a" (or whatever) from the keystoke buffer */
  248.         nkeys -= 2;
  249.  
  250.         /* allocate a BLK for storage of the keystrokes */
  251.         cb->phys = (short *)malloc(sizeof(short));
  252.         cb->nblks = 1;
  253.         cb->start = 0;
  254.         cb->end = nkeys;
  255.         cb->tmpnum = tmpnum;
  256. #ifndef NO_RECYCLE
  257.         seekpos = allocate();
  258.         lseek(tmpfd, seekpos, 0);
  259. #else
  260.         seekpos = lseek(tmpfd, 0L, 2);
  261. #endif
  262.         cb->phys[0] = (short)(seekpos / BLKSIZE);
  263.  
  264.         /* write the keystokes there */
  265.         if (write(tmpfd, buf, (unsigned)BLKSIZE) != BLKSIZE)
  266.         {
  267.             msg("Trouble writing to tmp file");
  268.             deathtrap(0);
  269.         }
  270.  
  271.         /* saving complete */
  272.         prevlearn = '\0';
  273.         nkeys = 0;
  274.     }
  275.  
  276.     /* if we're learning a buffer now, save the keystroke */
  277.     if (learn)
  278.     {
  279.         prevlearn = learn;
  280.         buf[nkeys++] = key;
  281.         if (nkeys >= BLKSIZE - 2)
  282.         {
  283.             msg("Learn buffer full");
  284.             learn = 0;
  285.             nkeys += 2; /* <- to fake "]a" in keystroke buffer */
  286.         }
  287.     }
  288. }
  289. #endif /* !NO_LEARN */
  290.  
  291.  
  292.  
  293. /* This function copies a selected segment of text to a cut buffer */
  294. void cut(from, to)
  295.     MARK    from;        /* start of text to cut */
  296.     MARK    to;        /* end of text to cut */
  297. {
  298.     int        first;    /* logical number of first block in cut */
  299.     int        last;    /* logical number of last block used in cut */
  300.     long        line;    /* a line number */
  301.     int        lnmode;    /* boolean: will this be a line-mode cut? */
  302.     MARK        delthru;/* end of text temporarily inserted for apnd */
  303.     REG struct cutbuf *cb;
  304.     REG long    l;
  305.     REG int        i;
  306.     REG char    *scan;
  307.     char        *blkc;
  308.  
  309.     /* detect whether this must be a line-mode cut or char-mode cut */
  310.     if (markidx(from) == 0 && markidx(to) == 0)
  311.         lnmode = TRUE;
  312.     else
  313.         lnmode = FALSE;
  314.  
  315.     /* by default, we don't "delthru" anything */
  316.     delthru = MARK_UNSET;
  317.  
  318.     /* handle the "doingdot" quirks */
  319.     if (doingdot)
  320.     {
  321.         if (!cbname)
  322.         {
  323.             cbname = dotcb;
  324.         }
  325.     }
  326.     else if (cbname != '.')
  327.     {
  328.         dotcb = cbname;
  329.     }
  330.  
  331.     /* decide which cut buffer to use */
  332.     if (!cbname)
  333.     {
  334.         /* free up the last anonymous cut buffer */
  335.         cutfree(&anon[NANONS - 1]);
  336.  
  337.         /* shift the anonymous cut buffers */
  338.         for (i = NANONS - 1; i > 0; i--)
  339.         {
  340.             anon[i] = anon[i - 1];
  341.         }
  342.  
  343.         /* use the first anonymous cut buffer */
  344.         cb = anon;
  345.         cb->nblks = 0;
  346.     }
  347.     else if (cbname >= 'a' && cbname <= 'z')
  348.     {
  349.         cb = &named[cbname - 'a'];
  350.         cutfree(cb);
  351.     }
  352. #ifndef CRUNCH
  353.     else if (cbname >= 'A' && cbname <= 'Z')
  354.     {
  355.         cb = &named[cbname - 'A'];
  356.         if (cb->nblks > 0)
  357.         {
  358.             /* resolve linemode/charmode differences */
  359.             if (!lnmode && cb->lnmode)
  360.             {
  361.                 from &= ~(BLKSIZE - 1);
  362.                 if (markidx(to) != 0 || to == from)
  363.                 {
  364.                     to = to + BLKSIZE - markidx(to);
  365.                 }
  366.                 lnmode = TRUE;
  367.             }
  368.  
  369.             /* insert the old cut-buffer before the new text */
  370.             mark[28] = to;
  371.             delthru = paste(from, FALSE, TRUE);
  372.             if (delthru == MARK_UNSET)
  373.             {
  374.                 return;
  375.             }
  376.             delthru++;
  377.             to = mark[28];
  378.         }
  379.         cutfree(cb);
  380.     }
  381. #endif /* not CRUNCH */
  382.     else if (cbname == '.')
  383.     {
  384.         cb = &named[26];
  385.         cutfree(cb);
  386.     }
  387.     else
  388.     {
  389.         msg("Invalid cut buffer name: \"%c", cbname);
  390.         dotcb = cbname = '\0';
  391.         return;
  392.     }
  393.     cbname = '\0';
  394.     cb->tmpnum = tmpnum;
  395.  
  396.     /* detect whether we're doing a line mode cut */
  397.     cb->lnmode = lnmode;
  398.  
  399.     /* ---------- */
  400.  
  401.     /* Reporting... */    
  402.     if (markidx(from) == 0 && markidx(to) == 0)
  403.     {
  404.         rptlines = markline(to) - markline(from);
  405.         rptlabel = "yanked";
  406.     }
  407.  
  408.     /* ---------- */
  409.  
  410.     /* make sure each block has a physical disk address */
  411.     blksync();
  412.  
  413.     /* find the first block in the cut */
  414.     line = markline(from);
  415.     for (first = 1; line > lnum[first]; first++)
  416.     {
  417.     }
  418.  
  419.     /* fetch text of the block containing that line */
  420.     blkc = scan = blkget(first)->c;
  421.  
  422.     /* find the mark in the block */
  423.     for (l = lnum[first - 1]; ++l < line; )
  424.     {
  425.         while (*scan++ != '\n')
  426.         {
  427.         }
  428.     }
  429.     scan += markidx(from);
  430.  
  431.     /* remember the offset of the start */
  432.     cb->start = scan - blkc;
  433.  
  434.     /* ---------- */
  435.  
  436.     /* find the last block in the cut */
  437.     line = markline(to);
  438.     for (last = first; line > lnum[last]; last++)
  439.     {
  440.     }
  441.  
  442.     /* fetch text of the block containing that line */
  443.     if (last != first)
  444.     {
  445.         blkc = scan = blkget(last)->c;
  446.     }
  447.     else
  448.     {
  449.         scan = blkc;
  450.     }
  451.  
  452.     /* find the mark in the block */
  453.     for (l = lnum[last - 1]; ++l < line; )
  454.     {
  455.         while (*scan++ != '\n')
  456.         {
  457.         }
  458.     }
  459.     if (markline(to) <= nlines)
  460.     {
  461.         scan += markidx(to);
  462.     }
  463.  
  464.     /* remember the offset of the end */
  465.     cb->end = scan - blkc;
  466.  
  467.     /* ------- */
  468.  
  469.     /* remember the physical block numbers of all included blocks */
  470.     cb->nblks = last - first;
  471.     if (cb->end > 0)
  472.     {
  473.         cb->nblks++;
  474.     }
  475.     else /*!!!*/
  476.     {
  477.         cb->end = BLKSIZE - 1;
  478.     }
  479. #ifdef lint
  480.     cb->phys = (short *)0;
  481. #else
  482.     cb->phys = (short *)malloc((unsigned)(cb->nblks * sizeof(short)));
  483. #endif
  484.     for (i = 0; i < cb->nblks; i++)
  485.     {
  486.         cb->phys[i] = hdr.n[first++];
  487.     }
  488.  
  489. #ifndef CRUNCH
  490.     /* if we temporarily inserted text for appending, then delete that
  491.      * text now -- before the user sees it.
  492.      */
  493.     if (delthru)
  494.     {
  495.         line = rptlines;
  496.         delete(from, delthru);
  497.         rptlines = line;
  498.         rptlabel = "yanked";
  499.     }
  500. #endif /* not CRUNCH */
  501. }
  502.  
  503.  
  504. static void readcutblk(cb, blkno)
  505.     struct cutbuf    *cb;
  506.     int        blkno;
  507. {
  508.     char        cutfname[50];/* name of an old temp file */
  509.     int        fd;    /* either tmpfd or the result of open() */
  510. #if MSDOS || TOS || OS2
  511.     int        i;
  512. #endif
  513.  
  514.     /* decide which fd to use */
  515.     if (cb->tmpnum == tmpnum)
  516.     {
  517.         fd = tmpfd;
  518.     }
  519.     else
  520.     {
  521. #if MSDOS || TOS || OS2
  522.         strcpy(cutfname, o_directory);
  523.         if ((i = strlen(cutfname)) && !strchr(":/\\", cutfname[i-1]))
  524.             cutfname[i++]=SLASH;
  525.         sprintf(cutfname+i, TMPNAME+3, getpid(), cb->tmpnum);
  526. #else
  527.         sprintf(cutfname, TMPNAME, o_directory, getpid(), cb->tmpnum);
  528. #endif
  529.         fd = open(cutfname, O_RDONLY);
  530.     }
  531.  
  532.     /* get the block */
  533.     lseek(fd, (long)cb->phys[blkno] * (long)BLKSIZE, 0);
  534.     if (read(fd, tmpblk.c, (unsigned)BLKSIZE) != BLKSIZE)
  535.     {
  536.         msg("Error reading back from tmp file for pasting!");
  537.     }
  538.  
  539.     /* close the fd, if it isn't tmpfd */
  540.     if (fd != tmpfd)
  541.     {
  542.         close(fd);
  543.     }
  544. }
  545.  
  546.  
  547. /* This function inserts text from a cut buffer, and returns the MARK where
  548.  * insertion ended.  Return MARK_UNSET on errors.
  549.  */
  550. MARK paste(at, after, retend)
  551.     MARK    at;    /* where to insert the text */
  552.     int    after;    /* boolean: insert after mark? (rather than before) */
  553.     int    retend;    /* boolean: return end of text? (rather than start) */
  554. {
  555.     REG struct cutbuf    *cb;
  556.     REG int            i;
  557.  
  558.     /* handle the "doingdot" quirks */
  559.     if (doingdot)
  560.     {
  561.         if (!cbname)
  562.         {
  563.             if (dotcb >= '1' && dotcb < '1' + NANONS - 1)
  564.             {
  565.                 dotcb++;
  566.             }
  567.             cbname = dotcb;
  568.         }
  569.     }
  570.     else if (cbname != '.')
  571.     {
  572.         dotcb = cbname;
  573.     }
  574.  
  575.     /* decide which cut buffer to use */
  576.     if (cbname >= 'A' && cbname <= 'Z')
  577.     {
  578.         cb = &named[cbname - 'A'];
  579.     }
  580.     else if (cbname >= 'a' && cbname <= 'z')
  581.     {
  582.         cb = &named[cbname - 'a'];
  583.     }
  584.     else if (cbname >= '1' && cbname <= '9')
  585.     {
  586.         cb = &anon[cbname - '1'];
  587.     }
  588.     else if (cbname == '.')
  589.     {
  590.         cb = &named[26];
  591.     }
  592.     else if (!cbname)
  593.     {
  594.         cb = anon;
  595.     }
  596.     else
  597.     {
  598.         msg("Invalid cut buffer name: \"%c", cbname);
  599.         cbname = '\0';
  600.         return MARK_UNSET;
  601.     }
  602.  
  603.     /* make sure it isn't empty */
  604.     if (cb->nblks == 0)
  605.     {
  606.         if (cbname)
  607.             msg("Cut buffer \"%c is empty", cbname);
  608.         else
  609.             msg("Cut buffer is empty");
  610.         cbname = '\0';
  611.         return MARK_UNSET;
  612.     }
  613.     cbname = '\0';
  614.  
  615.     /* adjust the insertion MARK for "after" and line-mode cuts */
  616.     if (cb->lnmode)
  617.     {
  618.         at &= ~(BLKSIZE - 1);
  619.         if (after)
  620.         {
  621.             at += BLKSIZE;
  622.         }
  623. #if 1
  624.         /* screen updates still have a glitch.  Force total redraw */
  625.         if (!o_optimize)
  626.         {
  627.             redrawrange(at, INFINITY, INFINITY);
  628.         }
  629. #endif
  630.     }
  631.     else if (after)
  632.     {
  633.         /* careful! if markidx(at) == 0 we might be pasting into an
  634.          * empty line -- so we can't blindly increment "at".
  635.          */
  636.         if (markidx(at) == 0)
  637.         {
  638.             pfetch(markline(at));
  639.             if (plen != 0)
  640.             {
  641.                 at++;
  642.             }
  643.         }
  644.         else
  645.         {
  646.             at++;
  647.         }
  648.     }
  649.  
  650.     /* put a copy of the "at" mark in the mark[] array, so it stays in
  651.      * sync with changes made via add().
  652.      */
  653.     mark[27] = at;
  654.  
  655.     /* simple one-block paste? */
  656.     if (cb->nblks == 1)
  657.     {
  658.         /* get the block */
  659.         readcutblk(cb, 0);
  660.  
  661.         /* isolate the text we need within it */
  662.         if (cb->end)
  663.         {
  664.             tmpblk.c[cb->end] = '\0';
  665.         }
  666.  
  667.         /* insert it */
  668.         ChangeText
  669.         {
  670.             add(at, &tmpblk.c[cb->start]);
  671.         }
  672.     }
  673.     else
  674.     {
  675.         /* multi-block paste */
  676.  
  677.         ChangeText
  678.         {
  679.             i = cb->nblks - 1;
  680.  
  681.             /* add text from the last block first */
  682.             if (cb->end > 0)
  683.             {
  684.                 readcutblk(cb, i);
  685.                 tmpblk.c[cb->end] = '\0';
  686.                 add(at, tmpblk.c);
  687.                 i--;
  688.             }
  689.  
  690.             /* add intervening blocks */
  691.             while (i > 0)
  692.             {
  693.                 readcutblk(cb, i);
  694.                 add(at, tmpblk.c);
  695.                 i--;
  696.             }
  697.  
  698.             /* add text from the first cut block */
  699.             readcutblk(cb, 0);
  700.             add(at, &tmpblk.c[cb->start]);
  701.         }
  702.     }
  703.  
  704.     /* Reporting... */
  705.     rptlines = markline(mark[27]) - markline(at);
  706.     rptlabel = "pasted";
  707.  
  708.     /* return the mark at the beginning/end of inserted text */
  709.     if (retend)
  710.     {
  711.         return mark[27] - 1L;
  712.     }
  713.     return at;
  714. }
  715.  
  716.  
  717.  
  718.  
  719. #ifndef NO_AT
  720.  
  721. /* This function copies characters from a cut buffer into a string.
  722.  * It returns the number of characters in the cut buffer.  If the cut
  723.  * buffer is too large to fit in the string (i.e. if cb2str() returns
  724.  * a number >= size) then the characters will not have been copied.
  725.  * It returns 0 if the cut buffer is empty, and -1 for invalid cut buffers.
  726.  */
  727. int cb2str(name, buf, size)
  728.     int    name;    /* the name of a cut-buffer to get: a-z only! */
  729.     char    *buf;    /* where to put the string */
  730.     unsigned size;    /* size of buf */
  731. {
  732.     REG struct cutbuf    *cb;
  733.     REG char        *src;
  734.     REG char        *dest;
  735.  
  736.     /* decide which cut buffer to use */
  737.     if (name >= 'a' && name <= 'z')
  738.     {
  739.         cb = &named[name - 'a'];
  740.     }
  741. #if 1 /* [sdw] allow selection of anonymous buffer */
  742.     else if (!name)
  743.     {
  744.         cb = anon;
  745.     }
  746. #endif
  747.     else
  748.     {
  749.         return -1;
  750.     }
  751.  
  752.     /* if the buffer is empty, return 0 */
  753.     if (cb->nblks == 0)
  754.     {
  755.         return 0;
  756.     }
  757.  
  758.     /* !!! if not a single-block cut, then fail */
  759.     if (cb->nblks != 1)
  760.     {
  761.         return size;
  762.     }
  763.  
  764.     /* if too big, return the size now, without doing anything */
  765.     if ((unsigned)(cb->end - cb->start) >= size)
  766.     {
  767.         return cb->end - cb->start;
  768.     }
  769.  
  770.     /* get the block */
  771.     readcutblk(cb, 0);
  772.  
  773.     /* isolate the string within that blk */
  774.     if (cb->start == 0)
  775.     {
  776.         tmpblk.c[cb->end] = '\0';
  777.     }
  778.     else
  779.     {
  780.         for (dest = tmpblk.c, src = dest + cb->start; src < tmpblk.c + cb->end; )
  781.         {
  782.             *dest++ = *src++;
  783.         }
  784.         *dest = '\0';
  785.     }
  786.  
  787.     /* copy the string into the buffer */
  788.     if (buf != tmpblk.c)
  789.     {
  790.         strcpy(buf, tmpblk.c);
  791.     }
  792.  
  793.     /* return the length */
  794.     return cb->end - cb->start;
  795. }
  796. #endif
  797.