home *** CD-ROM | disk | FTP | other *** search
/ ARM Club 1 / ARM_CLUB_CD.iso / contents / apps / program / d / elvis / Source / c / cut < prev    next >
Encoding:
Text File  |  1990-04-14  |  9.3 KB  |  495 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 "vi.h"
  14. extern char    *malloc();
  15.  
  16. # define NANNONS    9    /* number of annonymous buffers */
  17.  
  18. static struct cutbuf
  19. {
  20.     short    *phys;    /* pointer to an array of #s of BLKs containing text */
  21.     int    nblks;    /* number of blocks in phys[] array */
  22.     int    start;    /* offset into first block of start of cut */
  23.     int    end;    /* offset into last block of end of cut */
  24.     int    fd;    /* fd of tmp file, or -1 to use tmpfd */
  25.     char    lnmode;    /* boolean: line-mode cut? (as opposed to char-mode) */
  26. }
  27.     named[27],    /* cut buffers "a through "z and ". */
  28.     annon[NANNONS];    /* annonymous cut buffers */
  29.  
  30. static char    cbname;    /* name chosen for next cut/paste operation */
  31.  
  32.  
  33. #ifndef NO_RECYCLE
  34. /* This function builds a list of all blocks needed in the current tmp file
  35.  * for the contents of cut buffers.
  36.  * !!! WARNING: if you have more than ~450000 bytes of text in all of the
  37.  * cut buffers, then this will fail disastrously, because buffer overflow
  38.  * is *not* allowed for.
  39.  */
  40. int cutneeds(need)
  41.     BLK        *need;    /* this is where we deposit the list */
  42. {
  43.     struct cutbuf    *cb;    /* used to count through cut buffers */
  44.     int        i;    /* used to count through blocks of a cut buffer */
  45.     int        n;    /* total number of blocks in list */
  46.  
  47.     n = 0;
  48.  
  49.     /* first the named buffers... */
  50.     for (cb = named; cb < &named[27]; cb++)
  51.     {
  52.         if (cb->fd > 0)
  53.             continue;
  54.  
  55.         for (i = cb->nblks; i-- > 0; )
  56.         {
  57.             need->n[n++] = cb->phys[i];
  58.         }
  59.     }
  60.  
  61.     /* then the anonymous buffers */
  62.     for (cb = annon; cb < &annon[NANNONS]; cb++)
  63.     {
  64.         if (cb->fd > 0)
  65.             continue;
  66.  
  67.         for (i = cb->nblks; i-- > 0; )
  68.         {
  69.             need->n[n++] = cb->phys[i];
  70.         }
  71.     }
  72.  
  73.     return n;
  74. }
  75. #endif
  76.  
  77. /* This function frees a cut buffer */
  78. static cutfree(buf)
  79.     struct cutbuf    *buf;
  80. {
  81.     char    cutname[50];
  82.     int    i;
  83.  
  84.     /* return immediately if the buffer is already empty */
  85.     if (buf->nblks <= 0)
  86.     {
  87.         return;
  88.     }
  89.  
  90.     /* else free up stuff */
  91.     buf->nblks = 0;
  92.     free(buf->phys);
  93.  
  94.     /* see if anybody else needs this tmp file */
  95.     if (buf->fd >= 0)
  96.     {
  97.         for (i = 0; i < 27; i++)
  98.         {
  99.             if (named[i].nblks > 0 && named[i].fd >= 0)
  100.             {
  101.                 break;
  102.             }
  103.         }
  104.     }
  105.  
  106.     /* if nobody else needs it, then discard the tmp file */
  107.     if (buf->fd >= 0 && i == 27)
  108.     {
  109.         sprintf(cutname, CUTNAME, getpid(), buf->fd);
  110.         unlink(cutname);
  111.         close(buf->fd);
  112.     }
  113. }
  114.  
  115. /* This function is called when we are about to abort a tmp file.  If any
  116.  * cut buffers still need the file, then a copy of the file should be
  117.  * created for use by the cut buffers.
  118.  *
  119.  * To minimize the number of extra files lying around, only named cut buffers
  120.  * are preserved in a file switch; the annonymous buffers just go away.
  121.  */
  122. cutswitch(tmpname)
  123.     char    *tmpname; /* name of the tmp file */
  124. {
  125.     char    cutname[50];    /* used to build a new name for the tmp file */
  126.     int    fd;        /* a new fd for the current tmp file */
  127.     int    i;
  128.  
  129.     /* discard all annonymous cut buffers */
  130.     for (i = 0; i < NANNONS; i++)
  131.     {
  132.         cutfree(&annon[i]);
  133.     }
  134.  
  135.     /* find the first named buffer that uses this tmp file */
  136.     for (i = 0; i < 27; i++)
  137.     {
  138.         if (named[i].nblks > 0 && named[i].fd < 0)
  139.         {
  140.             break;
  141.         }
  142.     }
  143.  
  144.     /* if none of them use this tmp file, then we're done */
  145.     if (i == 27)
  146.     {
  147.         return;
  148.     }
  149.  
  150.     /* else we'll need this file and an fd a little longer */
  151.         /* !!! we could use some error checking here */
  152.     fd = dup(tmpfd);
  153.     if (fd>0) {
  154.         sprintf(cutname, CUTNAME, getpid(), fd);
  155.         link(tmpname, cutname);
  156.     }
  157.  
  158.     /* have all cut buffers use the new fd instead */
  159.     for (; i < 27; i++)
  160.     {
  161.         if (named[i].nblks > 0 && named[i].fd < 0)
  162.         {
  163.             named[i].fd = fd;
  164.         }
  165.     }
  166. }
  167.  
  168.  
  169. /* This function should be called just before termination of vi */
  170. cutend()
  171. {
  172.     int    i;
  173.  
  174.     /* free all named cut buffers, since they might be forcing an older
  175.      * tmp file to be retained.
  176.      */
  177.     for (i = 0; i < 27; i++)
  178.     {
  179.         cutfree(&named[i]);
  180.     }
  181. }
  182.  
  183.  
  184. /* This function is used to select the cut buffer to be used next */
  185. cutname(name)
  186.     int    name;    /* a single character */
  187. {
  188.     cbname = name;
  189. }
  190.  
  191.  
  192.  
  193.  
  194. /* This function copies a selected segment of text to a cut buffer */
  195. cut(from, to)
  196.     MARK    from;        /* start of text to cut */
  197.     MARK    to;        /* end of text to cut */
  198. {
  199.     int        first;    /* logical number of first block in cut */
  200.     int        last;    /* logical number of last block used in cut */
  201.     long        line;    /* a line number */
  202.     register struct cutbuf *cb;
  203.     register long    l;
  204.     register int    i;
  205.     register char    *scan;
  206.     char        *blkc;
  207.  
  208.     /* decide which cut buffer to use */
  209.     if (!cbname)
  210.     {
  211.         /* free up the last annonymous cut buffer */
  212.         cutfree(&annon[NANNONS - 1]);
  213.  
  214.         /* shift the annonymous cut buffers */
  215.         for (i = NANNONS - 1; i > 0; i--)
  216.         {
  217.             annon[i] = annon[i - 1];
  218.         }
  219.  
  220.         /* use the first annonymous cut buffer */
  221.         cb = annon;
  222.         cb->nblks = 0;
  223.     }
  224.     else if (cbname >= 'a' && cbname <= 'z')
  225.     {
  226.         cb = &named[cbname - 'a'];
  227.         cutfree(cb);
  228.     }
  229.     else if (cbname == '.')
  230.     {
  231.         cb = &named[26];
  232.         cutfree(cb);
  233.     }
  234.     else
  235.     {
  236.         msg("Invalid cut buffer name: \"%c", cbname);
  237.         cbname = '\0';
  238.         return;
  239.     }
  240.     cbname = '\0';
  241.     cb->fd = -1;
  242.  
  243.     /* detect whether we're doing a line mode cut */
  244.     cb->lnmode = (markidx(from) == 0 && markidx(to) == 0);
  245.  
  246.     /* ---------- */
  247.  
  248.     /* Reporting... */    
  249.     if (markidx(from) == 0 && markidx(to) == 0)
  250.     {
  251.         rptlines = markline(to) - markline(from);
  252.         rptlabel = "yanked";
  253.     }
  254.  
  255.     /* ---------- */
  256. blksync();
  257.     /* find the first block in the cut */
  258.     line = markline(from);
  259.     for (first = 1; line > lnum[first]; first++)
  260.     {
  261.     }
  262.  
  263.     /* fetch text of the block containing that line */
  264.     blkc = scan = blkget(first)->c;
  265.  
  266.     /* find the mark in the block */
  267.     for (l = lnum[first - 1]; ++l < line; )
  268.     {
  269.         while (*scan++ != '\n')
  270.         {
  271.         }
  272.     }
  273.     scan += markidx(from);
  274.  
  275.     /* remember the offset of the start */
  276.     cb->start = scan - blkc;
  277.  
  278.     /* ---------- */
  279.  
  280.     /* find the last block in the cut */
  281.     line = markline(to);
  282.     for (last = first; line > lnum[last]; last++)
  283.     {
  284.     }
  285.  
  286.     /* fetch text of the block containing that line */
  287.     if (last != first)
  288.     {
  289.         blkc = scan = blkget(last)->c;
  290.     }
  291.     else
  292.     {
  293.         scan = blkc;
  294.     }
  295.  
  296.     /* find the mark in the block */
  297.     for (l = lnum[last - 1]; ++l < line; )
  298.     {
  299.         while (*scan++ != '\n')
  300.         {
  301.         }
  302.     }
  303.     if (markline(to) <= nlines)
  304.     {
  305.         scan += markidx(to);
  306.     }
  307.  
  308.     /* remember the offset of the end */
  309.     cb->end = scan - blkc;
  310.  
  311.     /* ------- */
  312.  
  313.     /* remember the physical block numbers of all included blocks */
  314.     cb->nblks = last - first;
  315.     if (cb->end > 0)
  316.     {
  317.         cb->nblks++;
  318.     }
  319.     cb->phys = (short *)malloc((unsigned)(cb->nblks * sizeof(short)));
  320.     for (i = 0; i < cb->nblks; i++)
  321.     {
  322.         cb->phys[i] = hdr.n[first++];
  323.     }
  324. }
  325.  
  326.  
  327. static readcutblk(cb, blkno)
  328.     struct cutbuf    *cb;
  329.     int        blkno;
  330. {
  331.     int        fd;    /* either tmpfd or cb->fd */
  332.  
  333.     /* decide which fd to use */
  334.     if (cb->fd >= 0)
  335.     {
  336.         fd = cb->fd;
  337.     }
  338.     else
  339.     {
  340.         fd = tmpfd;
  341.     }
  342.  
  343.     /* get the block */
  344.     lseek(fd, (long)cb->phys[blkno] * (long)BLKSIZE, 0);
  345.     if (read(fd, tmpblk.c, BLKSIZE) != BLKSIZE)
  346.     {
  347.         msg("Error reading back from tmp file for pasting!");
  348.     }
  349. }
  350.  
  351.  
  352. /* This function inserts text from a cut buffer, and returns the MARK where
  353.  * insertion ended.  Return MARK_UNSET on errors.
  354.  */
  355. MARK paste(at, after, retend)
  356.     MARK    at;    /* where to insert the text */
  357.     int    after;    /* boolean: insert after mark? (rather than before) */
  358.     int    retend;    /* boolean: return end marker (rather than start) */
  359. {
  360.     register struct cutbuf    *cb;
  361.     register int        i;
  362.  
  363.     /* decide which cut buffer to use */
  364.     if (cbname >= 'a' && cbname <= 'z')
  365.     {
  366.         cb = &named[cbname - 'a'];
  367.     }
  368.     else if (cbname >= '1' && cbname <= '9')
  369.     {
  370.         cb = &annon[cbname - '1'];
  371.     }
  372.     else if (cbname == '.')
  373.     {
  374.         cb = &named[26];
  375.     }
  376.     else if (!cbname)
  377.     {
  378.         cb = annon;
  379.     }
  380.     else
  381.     {
  382.         msg("Invalid cut buffer name: \"%c", cbname);
  383.         return MARK_UNSET;
  384.     }
  385.  
  386.     /* make sure it isn't empty */
  387.     if (cb->nblks == 0)
  388.     {
  389.         if (cbname)
  390.             msg("Cut buffer \"%c is empty", cbname);
  391.         else
  392.             msg("Cut buffer is empty");
  393.         cbname = '\0';
  394.         return MARK_UNSET;
  395.     }
  396.     cbname = '\0';
  397.  
  398.     /* adjust the insertion MARK for "after" and line-mode cuts */
  399.     if (cb->lnmode)
  400.     {
  401.         at &= ~(BLKSIZE - 1);
  402.         if (after)
  403.         {
  404.             at += BLKSIZE;
  405.         }
  406.     }
  407.     else if (after)
  408.     {
  409.         /* careful! if markidx(at) == 0 we might be pasting into an
  410.          * empty line -- so we can't blindly increment "at".
  411.          */
  412.         if (markidx(at) == 0)
  413.         {
  414.             pfetch(markline(at));
  415.             if (plen != 0)
  416.             {
  417.                 at++;
  418.             }
  419.         }
  420.         else
  421.         {
  422.             at++;
  423.         }
  424.     }
  425.  
  426.     /* put a copy of the "at" mark in the mark[] array, so it stays in
  427.      * sync with changes made via add().
  428.      */
  429.     mark[27] = at;
  430.  
  431.     /* simple one-block paste? */
  432.     if (cb->nblks == 1)
  433.     {
  434.         /* get the block */
  435.         readcutblk(cb, 0);
  436.  
  437.         /* isolate the text we need within it */
  438.         if (cb->end)
  439.         {
  440.             tmpblk.c[cb->end] = '\0';
  441.         }
  442.  
  443.         /* insert it */
  444.         ChangeText
  445.         {
  446.             add(at, &tmpblk.c[cb->start]);
  447.         }
  448.     }
  449.     else
  450.     {
  451.         /* multi-block paste */
  452.  
  453.         ChangeText
  454.         {
  455.             i = cb->nblks - 1;
  456.  
  457.             /* add text from the last block first */
  458.             if (cb->end > 0)
  459.             {
  460.                 readcutblk(cb, i);
  461.                 tmpblk.c[cb->end] = '\0';
  462.                 add(at, tmpblk.c);
  463.                 i--;
  464.             }
  465.  
  466.             /* add intervening blocks */
  467.             while (i > 0)
  468.             {
  469.                 readcutblk(cb, i);
  470.                 add(at, tmpblk.c);
  471.                 i--;
  472.             }
  473.  
  474.             /* add text from the first cut block */
  475.             readcutblk(cb, 0);
  476.             add(at, &tmpblk.c[cb->start]);
  477.         }
  478.     }
  479.  
  480.     /* Reporting... */
  481.     rptlines = markline(mark[27]) - markline(at);
  482.     rptlabel = "pasted";
  483.  
  484.     /* correct the redraw range */
  485.     redrawafter = redrawpre = markline(at);
  486.     redrawpost = markline(mark[27]);
  487.  
  488.     /* return the mark at the beginning of inserted text */
  489.     if (retend)
  490.     {
  491.         return mark[27] - 1L;
  492.     }
  493.     return at;
  494. }
  495.