home *** CD-ROM | disk | FTP | other *** search
/ The World of Computer Software / World_Of_Computer_Software-02-387-Vol-3of3.iso / e / elv17src.zip / CUT.C < prev    next >
C/C++ Source or Header  |  1992-12-30  |  14KB  |  701 lines

  1. /* cut.c */
  2.  
  3. /* Author:
  4.  *    Steve Kirkendall
  5.  *    14407 SW Teal Blvd. #C
  6.  *    Beaverton, OR 97005
  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
  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.  * refered 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.     for (i = 0; i < NANONS; i++)
  175.     {
  176.         cutfree(&anon[i]);
  177.     }
  178.  
  179.     /* delete the temp file, if we don't really need it */
  180.     maybezap(tmpnum);
  181. }
  182.  
  183. /* This function should be called just before termination of vi */
  184. void cutend()
  185. {
  186.     int    i;
  187.  
  188.     /* free the anonymous buffers, if they aren't already free */
  189.     cutswitch();
  190.  
  191.     /* free all named cut buffers, since they might be forcing an older
  192.      * tmp file to be retained.
  193.      */
  194.     for (i = 0; i < 27; i++)
  195.     {
  196.         cutfree(&named[i]);
  197.     }
  198.  
  199.     /* delete the temp file */
  200.     maybezap(tmpnum);
  201. }
  202.  
  203.  
  204. /* This function is used to select the cut buffer to be used next */
  205. void cutname(name)
  206.     int    name;    /* a single character */
  207. {
  208.     cbname = name;
  209. }
  210.  
  211.  
  212.  
  213.  
  214. /* This function copies a selected segment of text to a cut buffer */
  215. void cut(from, to)
  216.     MARK    from;        /* start of text to cut */
  217.     MARK    to;        /* end of text to cut */
  218. {
  219.     int        first;    /* logical number of first block in cut */
  220.     int        last;    /* logical number of last block used in cut */
  221.     long        line;    /* a line number */
  222.     int        lnmode;    /* boolean: will this be a line-mode cut? */
  223.     MARK        delthru;/* end of text temporarily inserted for apnd */
  224.     REG struct cutbuf *cb;
  225.     REG long    l;
  226.     REG int        i;
  227.     REG char    *scan;
  228.     char        *blkc;
  229.  
  230.     /* detect whether this must be a line-mode cut or char-mode cut */
  231.     if (markidx(from) == 0 && markidx(to) == 0)
  232.         lnmode = TRUE;
  233.     else
  234.         lnmode = FALSE;
  235.  
  236.     /* by default, we don't "delthru" anything */
  237.     delthru = MARK_UNSET;
  238.  
  239.     /* handle the "doingdot" quirks */
  240.     if (doingdot)
  241.     {
  242.         if (!cbname)
  243.         {
  244.             cbname = dotcb;
  245.         }
  246.     }
  247.     else if (cbname != '.')
  248.     {
  249.         dotcb = cbname;
  250.     }
  251.  
  252.     /* decide which cut buffer to use */
  253.     if (!cbname)
  254.     {
  255.         /* free up the last anonymous cut buffer */
  256.         cutfree(&anon[NANONS - 1]);
  257.  
  258.         /* shift the anonymous cut buffers */
  259.         for (i = NANONS - 1; i > 0; i--)
  260.         {
  261.             anon[i] = anon[i - 1];
  262.         }
  263.  
  264.         /* use the first anonymous cut buffer */
  265.         cb = anon;
  266.         cb->nblks = 0;
  267.     }
  268.     else if (cbname >= 'a' && cbname <= 'z')
  269.     {
  270.         cb = &named[cbname - 'a'];
  271.         cutfree(cb);
  272.     }
  273. #ifndef CRUNCH
  274.     else if (cbname >= 'A' && cbname <= 'Z')
  275.     {
  276.         cb = &named[cbname - 'A'];
  277.         if (cb->nblks > 0)
  278.         {
  279.             /* resolve linemode/charmode differences */
  280.             if (!lnmode && cb->lnmode)
  281.             {
  282.                 from &= ~(BLKSIZE - 1);
  283.                 if (markidx(to) != 0 || to == from)
  284.                 {
  285.                     to = to + BLKSIZE - markidx(to);
  286.                 }
  287.                 lnmode = TRUE;
  288.             }
  289.  
  290.             /* insert the old cut-buffer before the new text */
  291.             mark[28] = to;
  292.             delthru = paste(from, FALSE, TRUE);
  293.             if (delthru == MARK_UNSET)
  294.             {
  295.                 return;
  296.             }
  297.             delthru++;
  298.             to = mark[28];
  299.         }
  300.         cutfree(cb);
  301.     }
  302. #endif /* not CRUNCH */
  303.     else if (cbname == '.')
  304.     {
  305.         cb = &named[26];
  306.         cutfree(cb);
  307.     }
  308.     else
  309.     {
  310.         msg("Invalid cut buffer name: \"%c", cbname);
  311.         dotcb = cbname = '\0';
  312.         return;
  313.     }
  314.     cbname = '\0';
  315.     cb->tmpnum = tmpnum;
  316.  
  317.     /* detect whether we're doing a line mode cut */
  318.     cb->lnmode = lnmode;
  319.  
  320.     /* ---------- */
  321.  
  322.     /* Reporting... */    
  323.     if (markidx(from) == 0 && markidx(to) == 0)
  324.     {
  325.         rptlines = markline(to) - markline(from);
  326.         rptlabel = "yanked";
  327.     }
  328.  
  329.     /* ---------- */
  330.  
  331.     /* make sure each block has a physical disk address */
  332.     blksync();
  333.  
  334.     /* find the first block in the cut */
  335.     line = markline(from);
  336.     for (first = 1; line > lnum[first]; first++)
  337.     {
  338.     }
  339.  
  340.     /* fetch text of the block containing that line */
  341.     blkc = scan = blkget(first)->c;
  342.  
  343.     /* find the mark in the block */
  344.     for (l = lnum[first - 1]; ++l < line; )
  345.     {
  346.         while (*scan++ != '\n')
  347.         {
  348.         }
  349.     }
  350.     scan += markidx(from);
  351.  
  352.     /* remember the offset of the start */
  353.     cb->start = scan - blkc;
  354.  
  355.     /* ---------- */
  356.  
  357.     /* find the last block in the cut */
  358.     line = markline(to);
  359.     for (last = first; line > lnum[last]; last++)
  360.     {
  361.     }
  362.  
  363.     /* fetch text of the block containing that line */
  364.     if (last != first)
  365.     {
  366.         blkc = scan = blkget(last)->c;
  367.     }
  368.     else
  369.     {
  370.         scan = blkc;
  371.     }
  372.  
  373.     /* find the mark in the block */
  374.     for (l = lnum[last - 1]; ++l < line; )
  375.     {
  376.         while (*scan++ != '\n')
  377.         {
  378.         }
  379.     }
  380.     if (markline(to) <= nlines)
  381.     {
  382.         scan += markidx(to);
  383.     }
  384.  
  385.     /* remember the offset of the end */
  386.     cb->end = scan - blkc;
  387.  
  388.     /* ------- */
  389.  
  390.     /* remember the physical block numbers of all included blocks */
  391.     cb->nblks = last - first;
  392.     if (cb->end > 0)
  393.     {
  394.         cb->nblks++;
  395.     }
  396. #ifdef lint
  397.     cb->phys = (short *)0;
  398. #else
  399.     cb->phys = (short *)malloc((unsigned)(cb->nblks * sizeof(short)));
  400. #endif
  401.     for (i = 0; i < cb->nblks; i++)
  402.     {
  403.         cb->phys[i] = hdr.n[first++];
  404.     }
  405.  
  406. #ifndef CRUNCH
  407.     /* if we temporarily inserted text for appending, then delete that
  408.      * text now -- before the user sees it.
  409.      */
  410.     if (delthru)
  411.     {
  412.         line = rptlines;
  413.         delete(from, delthru);
  414.         rptlines = line;
  415.         rptlabel = "yanked";
  416.     }
  417. #endif /* not CRUNCH */
  418. }
  419.  
  420.  
  421. static void readcutblk(cb, blkno)
  422.     struct cutbuf    *cb;
  423.     int        blkno;
  424. {
  425.     char        cutfname[50];/* name of an old temp file */
  426.     int        fd;    /* either tmpfd or the result of open() */
  427. #if MSDOS || TOS
  428.     int        i;
  429. #endif
  430.  
  431.     /* decide which fd to use */
  432.     if (cb->tmpnum == tmpnum)
  433.     {
  434.         fd = tmpfd;
  435.     }
  436.     else
  437.     {
  438. #if MSDOS || TOS
  439.         strcpy(cutfname, o_directory);
  440.         if ((i = strlen(cutfname)) && !strchr(":/\\", cutfname[i-1]))
  441.             cutfname[i++]=SLASH;
  442.         sprintf(cutfname+i, TMPNAME+3, getpid(), cb->tmpnum);
  443. #else
  444.         sprintf(cutfname, TMPNAME, o_directory, getpid(), cb->tmpnum);
  445. #endif
  446.         fd = open(cutfname, O_RDONLY);
  447.     }
  448.  
  449.     /* get the block */
  450.     lseek(fd, (long)cb->phys[blkno] * (long)BLKSIZE, 0);
  451.     if (read(fd, tmpblk.c, (unsigned)BLKSIZE) != BLKSIZE)
  452.     {
  453.         msg("Error reading back from tmp file for pasting!");
  454.     }
  455.  
  456.     /* close the fd, if it isn't tmpfd */
  457.     if (fd != tmpfd)
  458.     {
  459.         close(fd);
  460.     }
  461. }
  462.  
  463.  
  464. /* This function inserts text from a cut buffer, and returns the MARK where
  465.  * insertion ended.  Return MARK_UNSET on errors.
  466.  */
  467. MARK paste(at, after, retend)
  468.     MARK    at;    /* where to insert the text */
  469.     int    after;    /* boolean: insert after mark? (rather than before) */
  470.     int    retend;    /* boolean: return end of text? (rather than start) */
  471. {
  472.     REG struct cutbuf    *cb;
  473.     REG int            i;
  474.  
  475.     /* handle the "doingdot" quirks */
  476.     if (doingdot)
  477.     {
  478.         if (!cbname)
  479.         {
  480.             if (dotcb >= '1' && dotcb < '1' + NANONS - 1)
  481.             {
  482.                 dotcb++;
  483.             }
  484.             cbname = dotcb;
  485.         }
  486.     }
  487.     else if (cbname != '.')
  488.     {
  489.         dotcb = cbname;
  490.     }
  491.  
  492.     /* decide which cut buffer to use */
  493.     if (cbname >= 'A' && cbname <= 'Z')
  494.     {
  495.         cb = &named[cbname - 'A'];
  496.     }
  497.     else if (cbname >= 'a' && cbname <= 'z')
  498.     {
  499.         cb = &named[cbname - 'a'];
  500.     }
  501.     else if (cbname >= '1' && cbname <= '9')
  502.     {
  503.         cb = &anon[cbname - '1'];
  504.     }
  505.     else if (cbname == '.')
  506.     {
  507.         cb = &named[26];
  508.     }
  509.     else if (!cbname)
  510.     {
  511.         cb = anon;
  512.     }
  513.     else
  514.     {
  515.         msg("Invalid cut buffer name: \"%c", cbname);
  516.         cbname = '\0';
  517.         return MARK_UNSET;
  518.     }
  519.  
  520.     /* make sure it isn't empty */
  521.     if (cb->nblks == 0)
  522.     {
  523.         if (cbname)
  524.             msg("Cut buffer \"%c is empty", cbname);
  525.         else
  526.             msg("Cut buffer is empty");
  527.         cbname = '\0';
  528.         return MARK_UNSET;
  529.     }
  530.     cbname = '\0';
  531.  
  532.     /* adjust the insertion MARK for "after" and line-mode cuts */
  533.     if (cb->lnmode)
  534.     {
  535.         at &= ~(BLKSIZE - 1);
  536.         if (after)
  537.         {
  538.             at += BLKSIZE;
  539.         }
  540.     }
  541.     else if (after)
  542.     {
  543.         /* careful! if markidx(at) == 0 we might be pasting into an
  544.          * empty line -- so we can't blindly increment "at".
  545.          */
  546.         if (markidx(at) == 0)
  547.         {
  548.             pfetch(markline(at));
  549.             if (plen != 0)
  550.             {
  551.                 at++;
  552.             }
  553.         }
  554.         else
  555.         {
  556.             at++;
  557.         }
  558.     }
  559.  
  560.     /* put a copy of the "at" mark in the mark[] array, so it stays in
  561.      * sync with changes made via add().
  562.      */
  563.     mark[27] = at;
  564.  
  565.     /* simple one-block paste? */
  566.     if (cb->nblks == 1)
  567.     {
  568.         /* get the block */
  569.         readcutblk(cb, 0);
  570.  
  571.         /* isolate the text we need within it */
  572.         if (cb->end)
  573.         {
  574.             tmpblk.c[cb->end] = '\0';
  575.         }
  576.  
  577.         /* insert it */
  578.         ChangeText
  579.         {
  580.             add(at, &tmpblk.c[cb->start]);
  581.         }
  582.     }
  583.     else
  584.     {
  585.         /* multi-block paste */
  586.  
  587.         ChangeText
  588.         {
  589.             i = cb->nblks - 1;
  590.  
  591.             /* add text from the last block first */
  592.             if (cb->end > 0)
  593.             {
  594.                 readcutblk(cb, i);
  595.                 tmpblk.c[cb->end] = '\0';
  596.                 add(at, tmpblk.c);
  597.                 i--;
  598.             }
  599.  
  600.             /* add intervening blocks */
  601.             while (i > 0)
  602.             {
  603.                 readcutblk(cb, i);
  604.                 add(at, tmpblk.c);
  605.                 i--;
  606.             }
  607.  
  608.             /* add text from the first cut block */
  609.             readcutblk(cb, 0);
  610.             add(at, &tmpblk.c[cb->start]);
  611.         }
  612.     }
  613.  
  614.     /* Reporting... */
  615.     rptlines = markline(mark[27]) - markline(at);
  616.     rptlabel = "pasted";
  617.  
  618.     /* return the mark at the beginning/end of inserted text */
  619.     if (retend)
  620.     {
  621.         return mark[27] - 1L;
  622.     }
  623.     return at;
  624. }
  625.  
  626.  
  627.  
  628.  
  629. #ifndef NO_AT
  630.  
  631. /* This function copies characters from a cut buffer into a string.
  632.  * It returns the number of characters in the cut buffer.  If the cut
  633.  * buffer is too large to fit in the string (i.e. if cb2str() returns
  634.  * a number >= size) then the characters will not have been copied.
  635.  * It returns 0 if the cut buffer is empty, and -1 for invalid cut buffers.
  636.  */
  637. int cb2str(name, buf, size)
  638.     int    name;    /* the name of a cut-buffer to get: a-z only! */
  639.     char    *buf;    /* where to put the string */
  640.     unsigned size;    /* size of buf */
  641. {
  642.     REG struct cutbuf    *cb;
  643.     REG char        *src;
  644.     REG char        *dest;
  645.  
  646.     /* decide which cut buffer to use */
  647.     if (name >= 'a' && name <= 'z')
  648.     {
  649.         cb = &named[name - 'a'];
  650.     }
  651.     else
  652.     {
  653.         return -1;
  654.     }
  655.  
  656.     /* if the buffer is empty, return 0 */
  657.     if (cb->nblks == 0)
  658.     {
  659.         return 0;
  660.     }
  661.  
  662.     /* !!! if not a single-block cut, then fail */
  663.     if (cb->nblks != 1)
  664.     {
  665.         return size;
  666.     }
  667.  
  668.     /* if too big, return the size now, without doing anything */
  669.     if ((unsigned)(cb->end - cb->start) >= size)
  670.     {
  671.         return cb->end - cb->start;
  672.     }
  673.  
  674.     /* get the block */
  675.     readcutblk(cb, 0);
  676.  
  677.     /* isolate the string within that blk */
  678.     if (cb->start == 0)
  679.     {
  680.         tmpblk.c[cb->end] = '\0';
  681.     }
  682.     else
  683.     {
  684.         for (dest = tmpblk.c, src = dest + cb->start; src < tmpblk.c + cb->end; )
  685.         {
  686.             *dest++ = *src++;
  687.         }
  688.         *dest = '\0';
  689.     }
  690.  
  691.     /* copy the string into the buffer */
  692.     if (buf != tmpblk.c)
  693.     {
  694.         strcpy(buf, tmpblk.c);
  695.     }
  696.  
  697.     /* return the length */
  698.     return cb->end - cb->start;
  699. }
  700. #endif
  701.