home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 5 Edit / 05-Edit.zip / STVI369G.ZIP / OPS.C < prev    next >
C/C++ Source or Header  |  1990-05-01  |  13KB  |  659 lines

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