home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / usenet / altsrcs / 1 / 1708 / cut.c < prev    next >
C/C++ Source or Header  |  1990-12-28  |  10KB  |  524 lines

  1. /* cut.c */
  2.  
  3. /* Author:
  4.  *    Steve Kirkendall
  5.  *    16820 SW Tallac Way
  6.  *    Beaverton, OR 97006
  7.  *    kirkenda@jove.cs.pdx.edu, 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 is called when we are about to abort a tmp file.  If any
  85.  * cut buffers still need the file, then a copy of the file should be
  86.  * created for use by the cut buffers.
  87.  *
  88.  * To minimize the number of extra files lying around, only named cut buffers
  89.  * are preserved in a file switch; the annonymous buffers just go away.
  90.  */
  91. cutswitch(tmpname)
  92.     char    *tmpname; /* name of the tmp file */
  93. {
  94.     char    cutname[50];    /* used to build a new name for the tmp file */
  95.     int    fd;        /* a new fd for the current tmp file */
  96.     int    i, j;
  97.  
  98.     /* discard all annonymous cut buffers */
  99.     for (i = 0; i < NANNONS; i++)
  100.     {
  101.         cutfree(&annon[i]);
  102.     }
  103.  
  104.     /* find the first named buffer that uses this tmp file */
  105.     for (i = 0; i < 27; i++)
  106.     {
  107.         if (named[i].nblks > 0 && named[i].fd < 0)
  108.         {
  109.             break;
  110.         }
  111.     }
  112.  
  113.     /* if none of them use this tmp file, then we're done */
  114.     if (i == 27)
  115.     {
  116.         return;
  117.     }
  118.  
  119.     /* else we'll need this file and an fd a little longer */
  120.         /* !!! we could use some error checking here */
  121. #if    TOS
  122.     /* Hack! Tos allows dup for standard handles only. */
  123.     fd = tmpfd;
  124.     tmpfd = -1;
  125. #else
  126.     fd = dup(tmpfd);
  127. #endif
  128. #if MSDOS || TOS
  129.     strcpy(cutname, o_directory);
  130.     if ((j = strlen(cutname)) && !strchr(":/\\", cutname[j-1]))
  131.         cutname[j++]=SLASH;
  132.     sprintf(cutname+j, CUTNAME+3, getpid(), fd);
  133.     rename(tmpname, cutname);
  134. #else
  135.     sprintf(cutname, CUTNAME, o_directory, getpid(), fd);
  136.     link(tmpname, cutname) || unlink(tmpname);
  137. #endif
  138.  
  139.     /* have all cut buffers use the new fd instead */
  140.     for (; i < 27; i++)
  141.     {
  142.         if (named[i].nblks > 0 && named[i].fd < 0)
  143.         {
  144.             named[i].fd = fd;
  145.         }
  146.     }
  147. }
  148.  
  149. /* This function frees a cut buffer */
  150. static cutfree(buf)
  151.     struct cutbuf    *buf;
  152. {
  153.     char    cutname[50];
  154.     int    i;
  155.  
  156.     /* return immediately if the buffer is already empty */
  157.     if (buf->nblks <= 0)
  158.     {
  159.         return;
  160.     }
  161.  
  162.     /* else free up stuff */
  163.     buf->nblks = 0;
  164.     free(buf->phys);
  165.  
  166.     /* see if anybody else needs this tmp file */
  167.     if (buf->fd >= 0)
  168.     {
  169.         for (i = 0; i < 27; i++)
  170.         {
  171. #if    0
  172.             if (named[i].nblks > 0 && named[i].fd >= 0)
  173. #else
  174.             if (named[i].nblks > 0 && named[i].fd == buf->fd)
  175. #endif
  176.             {
  177.                 break;
  178.             }
  179.         }
  180.     }
  181.  
  182.     /* if nobody else needs it, then discard the tmp file */
  183.     if (buf->fd >= 0 && i == 27)
  184.     {
  185.         close(buf->fd);
  186. #if    MSDOS || TOS
  187.         strcpy(cutname, o_directory);
  188.         if ((i = strlen(cutname)) && !strchr(":/\\", cutname[i-1]))
  189.             cutname[i++]=SLASH;
  190.         sprintf(cutname+i, CUTNAME+3, getpid(), buf->fd);
  191. #else
  192.         sprintf(cutname, CUTNAME, o_directory, getpid(), buf->fd);
  193. #endif
  194.         unlink(cutname);
  195.     }
  196. }
  197.  
  198. /* This function should be called just before termination of vi */
  199. cutend()
  200. {
  201.     int    i;
  202.  
  203.     /* free all named cut buffers, since they might be forcing an older
  204.      * tmp file to be retained.
  205.      */
  206.     for (i = 0; i < 27; i++)
  207.     {
  208.         cutfree(&named[i]);
  209.     }
  210. }
  211.  
  212.  
  213. /* This function is used to select the cut buffer to be used next */
  214. cutname(name)
  215.     int    name;    /* a single character */
  216. {
  217.     cbname = name;
  218. }
  219.  
  220.  
  221.  
  222.  
  223. /* This function copies a selected segment of text to a cut buffer */
  224. cut(from, to)
  225.     MARK    from;        /* start of text to cut */
  226.     MARK    to;        /* end of text to cut */
  227. {
  228.     int        first;    /* logical number of first block in cut */
  229.     int        last;    /* logical number of last block used in cut */
  230.     long        line;    /* a line number */
  231.     register struct cutbuf *cb;
  232.     register long    l;
  233.     register int    i;
  234.     register char    *scan;
  235.     char        *blkc;
  236.  
  237.     /* decide which cut buffer to use */
  238.     if (!cbname)
  239.     {
  240.         /* free up the last annonymous cut buffer */
  241.         cutfree(&annon[NANNONS - 1]);
  242.  
  243.         /* shift the annonymous cut buffers */
  244.         for (i = NANNONS - 1; i > 0; i--)
  245.         {
  246.             annon[i] = annon[i - 1];
  247.         }
  248.  
  249.         /* use the first annonymous cut buffer */
  250.         cb = annon;
  251.         cb->nblks = 0;
  252.     }
  253.     else if (cbname >= 'a' && cbname <= 'z')
  254.     {
  255.         cb = &named[cbname - 'a'];
  256.         cutfree(cb);
  257.     }
  258.     else if (cbname == '.')
  259.     {
  260.         cb = &named[26];
  261.         cutfree(cb);
  262.     }
  263.     else
  264.     {
  265.         msg("Invalid cut buffer name: \"%c", cbname);
  266.         cbname = '\0';
  267.         return;
  268.     }
  269.     cbname = '\0';
  270.     cb->fd = -1;
  271.  
  272.     /* detect whether we're doing a line mode cut */
  273.     cb->lnmode = (markidx(from) == 0 && markidx(to) == 0);
  274.  
  275.     /* ---------- */
  276.  
  277.     /* Reporting... */    
  278.     if (markidx(from) == 0 && markidx(to) == 0)
  279.     {
  280.         rptlines = markline(to) - markline(from);
  281.         rptlabel = "yanked";
  282.     }
  283.  
  284.     /* ---------- */
  285. blksync();
  286.     /* find the first block in the cut */
  287.     line = markline(from);
  288.     for (first = 1; line > lnum[first]; first++)
  289.     {
  290.     }
  291.  
  292.     /* fetch text of the block containing that line */
  293.     blkc = scan = blkget(first)->c;
  294.  
  295.     /* find the mark in the block */
  296.     for (l = lnum[first - 1]; ++l < line; )
  297.     {
  298.         while (*scan++ != '\n')
  299.         {
  300.         }
  301.     }
  302.     scan += markidx(from);
  303.  
  304.     /* remember the offset of the start */
  305.     cb->start = scan - blkc;
  306.  
  307.     /* ---------- */
  308.  
  309.     /* find the last block in the cut */
  310.     line = markline(to);
  311.     for (last = first; line > lnum[last]; last++)
  312.     {
  313.     }
  314.  
  315.     /* fetch text of the block containing that line */
  316.     if (last != first)
  317.     {
  318.         blkc = scan = blkget(last)->c;
  319.     }
  320.     else
  321.     {
  322.         scan = blkc;
  323.     }
  324.  
  325.     /* find the mark in the block */
  326.     for (l = lnum[last - 1]; ++l < line; )
  327.     {
  328.         while (*scan++ != '\n')
  329.         {
  330.         }
  331.     }
  332.     if (markline(to) <= nlines)
  333.     {
  334.         scan += markidx(to);
  335.     }
  336.  
  337.     /* remember the offset of the end */
  338.     cb->end = scan - blkc;
  339.  
  340.     /* ------- */
  341.  
  342.     /* remember the physical block numbers of all included blocks */
  343.     cb->nblks = last - first;
  344.     if (cb->end > 0)
  345.     {
  346.         cb->nblks++;
  347.     }
  348.     cb->phys = (short *)malloc((unsigned)(cb->nblks * sizeof(short)));
  349.     for (i = 0; i < cb->nblks; i++)
  350.     {
  351.         cb->phys[i] = hdr.n[first++];
  352.     }
  353. }
  354.  
  355.  
  356. static readcutblk(cb, blkno)
  357.     struct cutbuf    *cb;
  358.     int        blkno;
  359. {
  360.     int        fd;    /* either tmpfd or cb->fd */
  361.  
  362.     /* decide which fd to use */
  363.     if (cb->fd >= 0)
  364.     {
  365.         fd = cb->fd;
  366.     }
  367.     else
  368.     {
  369.         fd = tmpfd;
  370.     }
  371.  
  372.     /* get the block */
  373.     lseek(fd, (long)cb->phys[blkno] * (long)BLKSIZE, 0);
  374.     if (read(fd, tmpblk.c, BLKSIZE) != BLKSIZE)
  375.     {
  376.         msg("Error reading back from tmp file for pasting!");
  377.     }
  378. }
  379.  
  380.  
  381. /* This function inserts text from a cut buffer, and returns the MARK where
  382.  * insertion ended.  Return MARK_UNSET on errors.
  383.  */
  384. MARK paste(at, after, retend)
  385.     MARK    at;    /* where to insert the text */
  386.     int    after;    /* boolean: insert after mark? (rather than before) */
  387.     int    retend;    /* boolean: return end marker (rather than start) */
  388. {
  389.     register struct cutbuf    *cb;
  390.     register int        i;
  391.  
  392.     /* decide which cut buffer to use */
  393.     if (cbname >= 'a' && cbname <= 'z')
  394.     {
  395.         cb = &named[cbname - 'a'];
  396.     }
  397.     else if (cbname >= '1' && cbname <= '9')
  398.     {
  399.         cb = &annon[cbname - '1'];
  400.     }
  401.     else if (cbname == '.')
  402.     {
  403.         cb = &named[26];
  404.     }
  405.     else if (!cbname)
  406.     {
  407.         cb = annon;
  408.     }
  409.     else
  410.     {
  411.         msg("Invalid cut buffer name: \"%c", cbname);
  412.         return MARK_UNSET;
  413.     }
  414.  
  415.     /* make sure it isn't empty */
  416.     if (cb->nblks == 0)
  417.     {
  418.         if (cbname)
  419.             msg("Cut buffer \"%c is empty", cbname);
  420.         else
  421.             msg("Cut buffer is empty");
  422.         cbname = '\0';
  423.         return MARK_UNSET;
  424.     }
  425.     cbname = '\0';
  426.  
  427.     /* adjust the insertion MARK for "after" and line-mode cuts */
  428.     if (cb->lnmode)
  429.     {
  430.         at &= ~(BLKSIZE - 1);
  431.         if (after)
  432.         {
  433.             at += BLKSIZE;
  434.         }
  435.     }
  436.     else if (after)
  437.     {
  438.         /* careful! if markidx(at) == 0 we might be pasting into an
  439.          * empty line -- so we can't blindly increment "at".
  440.          */
  441.         if (markidx(at) == 0)
  442.         {
  443.             pfetch(markline(at));
  444.             if (plen != 0)
  445.             {
  446.                 at++;
  447.             }
  448.         }
  449.         else
  450.         {
  451.             at++;
  452.         }
  453.     }
  454.  
  455.     /* put a copy of the "at" mark in the mark[] array, so it stays in
  456.      * sync with changes made via add().
  457.      */
  458.     mark[27] = at;
  459.  
  460.     /* simple one-block paste? */
  461.     if (cb->nblks == 1)
  462.     {
  463.         /* get the block */
  464.         readcutblk(cb, 0);
  465.  
  466.         /* isolate the text we need within it */
  467.         if (cb->end)
  468.         {
  469.             tmpblk.c[cb->end] = '\0';
  470.         }
  471.  
  472.         /* insert it */
  473.         ChangeText
  474.         {
  475.             add(at, &tmpblk.c[cb->start]);
  476.         }
  477.     }
  478.     else
  479.     {
  480.         /* multi-block paste */
  481.  
  482.         ChangeText
  483.         {
  484.             i = cb->nblks - 1;
  485.  
  486.             /* add text from the last block first */
  487.             if (cb->end > 0)
  488.             {
  489.                 readcutblk(cb, i);
  490.                 tmpblk.c[cb->end] = '\0';
  491.                 add(at, tmpblk.c);
  492.                 i--;
  493.             }
  494.  
  495.             /* add intervening blocks */
  496.             while (i > 0)
  497.             {
  498.                 readcutblk(cb, i);
  499.                 add(at, tmpblk.c);
  500.                 i--;
  501.             }
  502.  
  503.             /* add text from the first cut block */
  504.             readcutblk(cb, 0);
  505.             add(at, &tmpblk.c[cb->start]);
  506.         }
  507.     }
  508.  
  509.     /* Reporting... */
  510.     rptlines = markline(mark[27]) - markline(at);
  511.     rptlabel = "pasted";
  512.  
  513.     /* correct the redraw range */
  514.     redrawafter = preredraw = markline(at);
  515.     postredraw = markline(mark[27]);
  516.  
  517.     /* return the mark at the beginning of inserted text */
  518.     if (retend)
  519.     {
  520.         return mark[27] - 1L;
  521.     }
  522.     return at;
  523. }
  524.