home *** CD-ROM | disk | FTP | other *** search
/ The C Users' Group Library 1994 August / wc-cdrom-cusersgrouplibrary-1994-08.iso / vol_300 / 313_01 / ops.c < prev    next >
C/C++ Source or Header  |  1990-04-22  |  13KB  |  649 lines

  1. /* $Header: /nw2/tony/src/stevie/src/RCS/ops.c,v 1.5 89/08/06 09:50:42 tony Exp $
  2.  *
  3.  * Contains routines that implement the operators in vi. Everything in this
  4.  * file is called only from code in normal.c
  5.  */
  6.  
  7. #include "stevie.h"
  8. #include "ops.h"
  9.  
  10. /*
  11.  * doshift - handle a shift operation
  12.  */
  13. void
  14. doshift(op, c1, c2, num)
  15. int    op;
  16. char    c1, c2;
  17. int    num;
  18. {
  19.     void    tabinout();
  20.     LPTR    top, bot;
  21.     int    nlines;
  22.     char    opchar;
  23.  
  24.     top = startop;
  25.     bot = *Curschar;
  26.  
  27.     if (lt(&bot, &top))
  28.         pswap(&top, &bot);
  29.  
  30.     u_save(top.linep->prev, bot.linep->next);
  31.  
  32.     nlines = cntllines(&top, &bot);
  33.     *Curschar = top;
  34.     tabinout((op == LSHIFT), nlines);
  35.  
  36.     /* construct Redo buff */
  37.     opchar = (op == LSHIFT) ? '<' : '>';
  38.     if (num != 0)
  39.         sprintf(Redobuff, "%c%d%c%c", opchar, num, c1, c2);
  40.     else
  41.         sprintf(Redobuff, "%c%c%c", opchar, c1, c2);
  42.  
  43.     /*
  44.      * The cursor position afterward is the prior of the two positions.
  45.      */
  46.     *Curschar = top;
  47.  
  48.     /*
  49.      * If we were on the last char of a line that got shifted left,
  50.      * then move left one so we aren't beyond the end of the line
  51.      */
  52.     if (gchar(Curschar) == NUL && Curschar->index > 0)
  53.         Curschar->index--;
  54.  
  55.     updatescreen();
  56.  
  57.     if (nlines > P(P_RP))
  58.         smsg("%d lines %ced", nlines, opchar);
  59. }
  60.  
  61. /*
  62.  * dodelete - handle a delete operation
  63.  */
  64. void
  65. dodelete(c1, c2, num)
  66. char    c1, c2;
  67. int    num;
  68. {
  69.     LPTR    top, bot;
  70.     int    nlines;
  71.     register int    n;
  72.  
  73.     /*
  74.      * Do a yank of whatever we're about to delete. If there's too much
  75.      * stuff to fit in the yank buffer, then get a confirmation before
  76.      * doing the delete. This is crude, but simple. And it avoids doing
  77.      * a delete of something we can't put back if we want.
  78.      */
  79.     if (!doyank()) {
  80.         msg("yank buffer exceeded: press <y> to confirm");
  81.         if (vgetc() != 'y') {
  82.             msg("delete aborted");
  83.             *Curschar = startop;
  84.             return;
  85.         }
  86.     }
  87.  
  88.     top = startop;
  89.     bot = *Curschar;
  90.  
  91.     if (lt(&bot, &top))
  92.         pswap(&top, &bot);
  93.  
  94.     u_save(top.linep->prev, bot.linep->next);
  95.  
  96.     nlines = cntllines(&top, &bot);
  97.     *Curschar = top;
  98.     cursupdate();
  99.  
  100.     if (mtype == MLINE) {
  101.         delline(nlines, TRUE);
  102.     } else {
  103.         if (!mincl && bot.index != 0)
  104.             dec(&bot);
  105.  
  106.         if (top.linep == bot.linep) {        /* del. within line */
  107.             n = bot.index - top.index + 1;
  108.             while (n--)
  109.                 if (!delchar(TRUE))
  110.                     break;
  111.         } else {                /* del. between lines */
  112.             n = Curschar->index;
  113.             while (Curschar->index >= n)
  114.                 if (!delchar(TRUE))
  115.                     break;
  116.  
  117.             top = *Curschar;
  118.             *Curschar = *nextline(Curschar);
  119.             delline(nlines-2, TRUE);
  120.             Curschar->index = 0;
  121.             n = bot.index + 1;
  122.             while (n--)
  123.                 if (!delchar(TRUE))
  124.                     break;
  125.             *Curschar = top;
  126.             (void) dojoin(FALSE);
  127.             oneright();    /* we got bumped left up above */
  128.         }
  129.     }
  130.  
  131.     /* construct Redo buff */
  132.     if (num != 0)
  133.         sprintf(Redobuff, "d%d%c%c", num, c1, c2);
  134.     else
  135.         sprintf(Redobuff, "d%c%c", c1, c2);
  136.  
  137.     if (mtype == MCHAR && nlines == 1)
  138.         updateline();
  139.     else
  140.         updatescreen();
  141.  
  142.     if (nlines > P(P_RP))
  143.         smsg("%d fewer lines", nlines);
  144. }
  145.  
  146. /*
  147.  * dofilter - handle a filter operation
  148.  */
  149.  
  150. #define    ITMP    "viXXXXXX"
  151. #define    OTMP    "voXXXXXX"
  152.  
  153. static    char    itmp[32];
  154. static    char    otmp[32];
  155.  
  156.  
  157. /*
  158.  * dofilter - filter lines through a command given by the user
  159.  *
  160.  * We use temp files and the system() routine here. This would normally
  161.  * be done using pipes on a UNIX machine, but this is more portable to
  162.  * the machines we usually run on. The system() routine needs to be able
  163.  * to deal with redirection somehow, and should handle things like looking
  164.  * at the PATH env. variable, and adding reasonable extensions to the
  165.  * command name given by the user. All reasonable versions of system()
  166.  * do this.
  167.  */
  168. void
  169. dofilter(c1, c2, num)
  170. char    c1, c2;
  171. int    num;
  172. {
  173.     char    *mktemp();
  174.     static    char    *lastcmd = NULL;/* the last thing we did */
  175.     char    *buff;            /* cmd buffer from getcmdln() */
  176.     char    cmdln[200];        /* filtering command line */
  177.     LPTR    top, bot;
  178.     int    nlines;
  179.  
  180.     top = startop;
  181.     bot = *Curschar;
  182.  
  183.     buff = getcmdln('!');
  184.  
  185.     if (buff == NULL)    /* user backed out of the command prompt */
  186.         return;
  187.  
  188.     if (*buff == '!') {        /* use the 'last' command */
  189.         if (lastcmd == NULL) {
  190.             emsg("No previous command");
  191.             return;
  192.         }
  193.         buff = lastcmd;
  194.     }
  195.  
  196.     /*
  197.      * Remember the current command
  198.      */
  199.     if (lastcmd != NULL)
  200.         free(lastcmd);
  201.     lastcmd = strsave(buff);
  202.  
  203.     if (lt(&bot, &top))
  204.         pswap(&top, &bot);
  205.  
  206.     u_save(top.linep->prev, bot.linep->next);
  207.  
  208.     nlines = cntllines(&top, &bot);
  209.     *Curschar = top;
  210.     cursupdate();
  211.  
  212.     /*
  213.      * 1. Form temp file names
  214.      * 2. Write the lines to a temp file
  215.      * 3. Run the filter command on the temp file
  216.      * 4. Read the output of the command into the buffer
  217.      * 5. Delete the original lines to be filtered
  218.      * 6. Remove the temp files
  219.      */
  220.  
  221. #ifdef    TMPDIR
  222.     strcpy(itmp, TMPDIR);
  223.     strcpy(otmp, TMPDIR);
  224. #else
  225.     itmp[0] = otmp[0] = NUL;
  226. #endif
  227.     strcat(itmp, ITMP);
  228.     strcat(otmp, OTMP);
  229.  
  230.     if (mktemp(itmp) == NULL || mktemp(otmp) == NULL) {
  231.         emsg("Can't get temp file names");
  232.         return;
  233.     }
  234.  
  235.     if (!writeit(itmp, &top, &bot)) {
  236.         emsg("Can't create input temp file");
  237.         return;
  238.     }
  239.  
  240.     sprintf(cmdln, "%s <%s >%s", buff, itmp, otmp);
  241.  
  242.     if (system(cmdln) != 0) {
  243.         emsg("Filter command failed");
  244.         remove(ITMP);
  245.         return;
  246.     }
  247.  
  248.     if (readfile(otmp, &bot, TRUE)) {
  249.         emsg("Can't read filter output");
  250.         return;
  251.     }
  252.  
  253.     delline(nlines, TRUE);
  254.  
  255.     remove(itmp);
  256.     remove(otmp);
  257.  
  258.     /* construct Redo buff */
  259.     if (num != 0)
  260.         sprintf(Redobuff, "d%d%c%c", num, c1, c2);
  261.     else
  262.         sprintf(Redobuff, "d%c%c", c1, c2);
  263.  
  264.     updatescreen();
  265.  
  266.     if (nlines > P(P_RP))
  267.         smsg("%d lines filtered", nlines);
  268. }
  269.  
  270. #ifdef    TILDEOP
  271. void
  272. dotilde(c1, c2, num)
  273. char    c1, c2;
  274. int    num;
  275. {
  276.     LPTR    top, bot;
  277.     register char    c;
  278.  
  279.     /* construct Redo buff */
  280.     if (num != 0)
  281.         sprintf(Redobuff, "~%d%c%c", num, c1, c2);
  282.     else
  283.         sprintf(Redobuff, "~%c%c", c1, c2);
  284.  
  285.     top = startop;
  286.     bot = *Curschar;
  287.  
  288.     if (lt(&bot, &top))
  289.         pswap(&top, &bot);
  290.  
  291.     u_save(top.linep->prev, bot.linep->next);
  292.  
  293.     if (mtype == MLINE) {
  294.         top.index = 0;
  295.         bot.index = strlen(bot.linep->s);
  296.     } else {
  297.         if (!mincl) {
  298.             if (bot.index)
  299.                 bot.index--;
  300.         }
  301.     }
  302.  
  303.     for (; ltoreq(&top, &bot) ;inc(&top)) {
  304.         /*
  305.          * Swap case through the range
  306.          */
  307.         c = gchar(&top);
  308.         if (isalpha(c)) {
  309.             if (islower(c))
  310.                 c = toupper(c);
  311.             else
  312.                 c = tolower(c);
  313.  
  314.             pchar(&top, c);        /* Change current character. */
  315.             CHANGED;
  316.         }
  317.     }
  318.     *Curschar = startop;
  319.     updatescreen();
  320. }
  321. #endif
  322.  
  323. /*
  324.  * dochange - handle a change operation
  325.  */
  326. void
  327. dochange(c1, c2, num)
  328. char    c1, c2;
  329. int    num;
  330. {
  331.     char    sbuf[16];
  332.     bool_t    doappend;    /* true if we should do append, not insert */
  333.     bool_t    at_eof;        /* changing through the end of file */
  334.     LPTR    top, bot;
  335.  
  336.     top = startop;
  337.     bot = *Curschar;
  338.  
  339.     if (lt(&bot, &top))
  340.         pswap(&top, &bot);
  341.  
  342.     doappend = endofline(&bot);
  343.     at_eof = (bot.linep->next == Fileend->linep);
  344.  
  345.     dodelete(c1, c2, num);
  346.  
  347.     if (mtype == MLINE) {
  348.         /*
  349.          * If we made a change through the last line of the file,
  350.          * then the cursor got backed up, and we need to open a
  351.          * new line forward, otherwise we go backward.
  352.          */
  353.         if (at_eof)
  354.             opencmd(FORWARD, FALSE);
  355.         else
  356.             opencmd(BACKWARD, FALSE);
  357.     } else {
  358.         if (doappend && !lineempty())
  359.             inc(Curschar);
  360.     }
  361.  
  362.     if (num)
  363.         sprintf(sbuf, "c%d%c%c", num, c1, c2);
  364.     else
  365.         sprintf(sbuf, "c%c%c", c1, c2);
  366.  
  367.     startinsert(sbuf, mtype == MLINE);
  368. }
  369.  
  370. #ifndef    YBSIZE
  371. #define    YBSIZE    4096
  372. #endif
  373.  
  374. static    char    ybuf[YBSIZE];
  375. static    int    ybtype = MBAD;
  376.  
  377. bool_t
  378. doyank()
  379. {
  380.     LPTR    top, bot;
  381.     char    *yptr = ybuf;
  382.     char    *ybend = &ybuf[YBSIZE-1];
  383.     int    nlines;
  384.  
  385.     top = startop;
  386.     bot = *Curschar;
  387.  
  388.     if (lt(&bot, &top))
  389.         pswap(&top, &bot);
  390.  
  391.     nlines = cntllines(&top, &bot);
  392.  
  393.     ybtype = mtype;            /* set the yank buffer type */
  394.  
  395.     if (mtype == MLINE) {
  396.         top.index = 0;
  397.         bot.index = strlen(bot.linep->s);
  398.         /*
  399.          * The following statement checks for the special case of
  400.          * yanking a blank line at the beginning of the file. If
  401.          * not handled right, we yank an extra char (a newline).
  402.          */
  403.         if (dec(&bot) == -1) {
  404.             ybuf[0] = NUL;
  405.             if (operator == YANK)
  406.                 *Curschar = startop;
  407.             return TRUE;
  408.         }
  409.     } else {
  410.         if (!mincl) {
  411.             if (bot.index)
  412.                 bot.index--;
  413.         }
  414.     }
  415.  
  416.     for (; ltoreq(&top, &bot) ;inc(&top)) {
  417.         *yptr = (gchar(&top) != NUL) ? gchar(&top) : NL;
  418.         if (++yptr >= ybend) {
  419.             msg("yank too big for buffer");
  420.             ybtype = MBAD;
  421.             return FALSE;
  422.         }
  423.     }
  424.  
  425.     *yptr = NUL;
  426.  
  427.     if (operator == YANK) {    /* restore Curschar if really doing yank */
  428.         *Curschar = startop;
  429.  
  430.         if (nlines > P(P_RP))
  431.             smsg("%d lines yanked", nlines);
  432.     }
  433.  
  434.     return TRUE;
  435. }
  436.  
  437. /*
  438.  * doput(dir)
  439.  *
  440.  * Put the yank buffer at the current location, using the direction given
  441.  * by 'dir'.
  442.  */
  443. void
  444. doput(dir)
  445. int    dir;
  446. {
  447.     void    inslines();
  448.  
  449.     if (ybtype == MBAD) {
  450.         beep();
  451.         return;
  452.     }
  453.     
  454.     u_saveline();
  455.  
  456.     if (ybtype == MLINE)
  457.         inslines(Curschar->linep, dir, ybuf);
  458.     else {
  459.         /*
  460.          * If we did a character-oriented yank, and the buffer
  461.          * contains multiple lines, the situation is more complex.
  462.          * For the moment, we punt, and pretend the user did a
  463.          * line-oriented yank. This doesn't actually happen that
  464.          * often.
  465.          */
  466.         if (strchr(ybuf, NL) != NULL)
  467.             inslines(Curschar->linep, dir, ybuf);
  468.         else {
  469.             char    *s;
  470.             int    len;
  471.  
  472.             len = strlen(Curschar->linep->s) + strlen(ybuf) + 1;
  473.             s = alloc((unsigned) len);
  474.             strcpy(s, Curschar->linep->s);
  475.             if (dir == FORWARD)
  476.                 Curschar->index++;
  477.             strcpy(s + Curschar->index, ybuf);
  478.             strcat(s, &Curschar->linep->s[Curschar->index]);
  479.             free(Curschar->linep->s);
  480.             Curschar->linep->s = s;
  481.             Curschar->linep->size = len;
  482.             updateline();
  483.         }
  484.     }
  485.  
  486.     CHANGED;
  487. }
  488.  
  489. bool_t
  490. dojoin(join_cmd)
  491. bool_t    join_cmd;        /* handling a real "join" command? */
  492. {
  493.     int    scol;        /* save cursor column */
  494.     int    size;        /* size of the joined line */
  495.  
  496.     if (nextline(Curschar) == NULL)        /* on last line */
  497.         return FALSE;
  498.  
  499.     if (!canincrease(size = strlen(Curschar->linep->next->s)))
  500.         return FALSE;
  501.  
  502.     while (oneright())            /* to end of line */
  503.         ;
  504.  
  505.     strcat(Curschar->linep->s, Curschar->linep->next->s);
  506.  
  507.     /*
  508.      * Delete the following line. To do this we move the cursor
  509.      * there briefly, and then move it back. Don't back up if the
  510.      * delete made us the last line.
  511.      */
  512.     Curschar->linep = Curschar->linep->next;
  513.     scol = Curschar->index;
  514.  
  515.     if (nextline(Curschar) != NULL) {
  516.         delline(1, TRUE);
  517.         Curschar->linep = Curschar->linep->prev;
  518.     } else
  519.         delline(1, TRUE);
  520.  
  521.     Curschar->index = scol;
  522.  
  523.     if (join_cmd)
  524.         oneright();    /* go to first char. of joined line */
  525.  
  526.     if (join_cmd && size != 0) {
  527.         /*
  528.          * Delete leading white space on the joined line
  529.          * and insert a single space.
  530.          */
  531.         while (gchar(Curschar) == ' ' || gchar(Curschar) == TAB)
  532.             delchar(TRUE);
  533.         inschar(' ');
  534.     }
  535.  
  536.     return TRUE;
  537. }
  538.  
  539. void
  540. startinsert(initstr, startln)
  541. char    *initstr;
  542. int    startln;    /* if set, insert point really at start of line */
  543. {
  544.     register char    *p, c;
  545.  
  546.     *Insstart = *Curschar;
  547.     if (startln)
  548.         Insstart->index = 0;
  549.     Ninsert = 0;
  550.     Insptr = Insbuff;
  551.     for (p=initstr; (c=(*p++))!='\0'; )
  552.         *Insptr++ = c;
  553.  
  554.     if (*initstr == 'R')
  555.         State = REPLACE;
  556.     else
  557.         State = INSERT;
  558.  
  559.     if (P(P_MO))
  560.         msg((State == INSERT) ? "Insert Mode" : "Replace Mode");
  561. }
  562. /*
  563.  * tabinout(inout,num)
  564.  *
  565.  * If inout==0, add a tab to the begining of the next num lines.
  566.  * If inout==1, delete a tab from the beginning of the next num lines.
  567.  */
  568. static void
  569. tabinout(inout, num)
  570. int    inout;
  571. int    num;
  572. {
  573.     int    ntodo = num;
  574.     LPTR    *p;
  575.  
  576.     beginline(FALSE);
  577.     while (ntodo-- > 0) {
  578.         beginline(FALSE);
  579.         if (inout == 0)
  580.             inschar(TAB);
  581.         else {
  582.             if (gchar(Curschar) == TAB)
  583.                 delchar(TRUE);
  584.         }
  585.         if ( ntodo > 0 ) {
  586.             if ((p = nextline(Curschar)) != NULL)
  587.                 *Curschar = *p;
  588.             else
  589.                 break;
  590.         }
  591.     }
  592. }
  593.  
  594. /*
  595.  * inslines(lp, dir, buf)
  596.  *
  597.  * Inserts lines in the file from the given buffer. Lines are inserted
  598.  * before or after "lp" according to the given direction flag. Newlines
  599.  * in the buffer result in multiple lines being inserted. The cursor
  600.  * is left on the first of the inserted lines.
  601.  */
  602. static void
  603. inslines(lp, dir, buf)
  604. LINE    *lp;
  605. int    dir;
  606. char    *buf;
  607. {
  608.     register char    *cp = buf;
  609.     register int    len;
  610.     char    *ep;
  611.     LINE    *l, *nc = NULL;
  612.  
  613.     if (dir == BACKWARD)
  614.         lp = lp->prev;
  615.  
  616.     do {
  617.         if ((ep = strchr(cp, NL)) == NULL)
  618.             len = strlen(cp);
  619.         else
  620.             len = ep - cp;
  621.  
  622.         l = newline(len);
  623.         if (len != 0)
  624.             strncpy(l->s, cp, len);
  625.         l->s[len] = NUL;
  626.  
  627.         l->next = lp->next;
  628.         l->prev = lp;
  629.         lp->next->prev = l;
  630.         lp->next = l;
  631.  
  632.         if (nc == NULL)
  633.             nc = l;
  634.  
  635.         lp = lp->next;
  636.  
  637.         cp = ep + 1;
  638.     } while (ep != NULL);
  639.  
  640.     if (dir == BACKWARD)    /* fix the top line in case we were there */
  641.         Filemem->linep = Filetop->linep->next;
  642.  
  643.     renum();
  644.  
  645.     updatescreen();
  646.     Curschar->linep = nc;
  647.     Curschar->index = 0;
  648. }
  649.