home *** CD-ROM | disk | FTP | other *** search
/ The World of Computer Software / World_Of_Computer_Software-02-387-Vol-3of3.iso / x / xvisrc.zoo / yankput.c < prev   
C/C++ Source or Header  |  1992-07-28  |  12KB  |  553 lines

  1. /* Copyright (c) 1990,1991,1992 Chris and John Downey */
  2. #ifndef lint
  3. static char *sccsid = "@(#)yankput.c    2.4 (Chris & John Downey) 8/6/92";
  4. #endif
  5.  
  6. /***
  7.  
  8. * program name:
  9.     xvi
  10. * function:
  11.     PD version of UNIX "vi" editor, with extensions.
  12. * module name:
  13.     yankput.c
  14. * module function:
  15.     Functions to handle "yank" and "put" commands.
  16.  
  17.     Note that there is still some code in normal.c to do
  18.     some of the work - this will have to be changed later.
  19.  
  20.     Some of the routines and data structures herein assume ASCII
  21.     order, so I don't know if they are particularly portable.
  22. * history:
  23.     STEVIE - ST Editor for VI Enthusiasts, Version 3.10
  24.     Originally by Tim Thompson (twitch!tjt)
  25.     Extensive modifications by Tony Andrews (onecom!wldrdg!tony)
  26.     Heavily modified by Chris & John Downey
  27.  
  28. ***/
  29.  
  30. #include "xvi.h"
  31.  
  32. /*
  33.  * Structure to store yanked text or yanked lines.
  34.  */
  35. typedef struct yankbuffer {
  36.     enum {
  37.     y_none,
  38.     y_chars,
  39.     y_lines
  40.     } y_type;
  41.  
  42.     char    *y_1st_text;
  43.     char    *y_2nd_text;
  44.     Line    *y_line_buf;
  45. } Yankbuffer;
  46.  
  47. /*
  48.  * For named buffers, we have an array of yankbuffer structures,
  49.  * mapped by printable ascii characters. Only the alphabetic
  50.  * characters, and '@', are directly settable by the user.
  51.  * The uppercase versions mean "append" rather than "replace".
  52.  */
  53. #define    LOWEST_NAME    ' '
  54. #define    HIGHEST_NAME    'Z'
  55. #define    NBUFS        (HIGHEST_NAME - LOWEST_NAME + 1)
  56. #define    bufno(c)    ((c) - LOWEST_NAME)
  57. #define    validname(c)    ((c) >= LOWEST_NAME && (c) < 'A')
  58.  
  59. static    Yankbuffer    yb[NBUFS];
  60.  
  61. static    void        put P((char *, bool_t, bool_t));
  62. static    Yankbuffer    *yp_get_buffer P((int));
  63. static    Line        *copy_lines P((Line *, Line *));
  64. static    char        *yanktext P((Posn *, Posn *));
  65. static    void        yp_free P((Yankbuffer *));
  66.  
  67. void
  68. init_yankput()
  69. {
  70. }
  71.  
  72. /*
  73.  * Set the buffer name to be used for the next yank/put operation to
  74.  * the given character. The character '@' is used as a synonym for the
  75.  * default (unnamed) buffer.
  76.  */
  77. static Yankbuffer *
  78. yp_get_buffer(name)
  79. int    name;
  80. {
  81.     int    i;
  82.  
  83.     if (validname(name)) {
  84.     i = bufno(name);
  85.     } else if (is_alpha(name)) {
  86.     if (is_upper(name)) {
  87.         show_message(curwin, "Appending to named buffers not supported");
  88.     }
  89.     i = bufno(to_upper(name));
  90.     } else {
  91.     show_error(curwin, "Illegal buffer name");
  92.     return(NULL);
  93.     }
  94.     return(&yb[i]);
  95. }
  96.  
  97. /*
  98.  * Yank the text specified by the given start/end positions.
  99.  * The fourth parameter is TRUE if we are doing a character-
  100.  * based, rather than a line-based, yank.
  101.  *
  102.  * For line-based yanks, the range of positions is inclusive.
  103.  *
  104.  * Returns TRUE if successfully yanked.
  105.  *
  106.  * Positions must be ordered properly, i.e. "from" <= "to".
  107.  */
  108. /*ARGSUSED*/
  109. bool_t
  110. do_yank(buffer, from, to, charbased, name)
  111. Buffer    *buffer;
  112. Posn    *from, *to;
  113. bool_t    charbased;
  114. int    name;
  115. {
  116.     Yankbuffer    *yp_buf;
  117.     long    nlines;
  118.  
  119.     yp_buf = yp_get_buffer(name);
  120.     if (yp_buf == NULL) {
  121.     return(FALSE);
  122.     }
  123.     yp_free(yp_buf);
  124.  
  125.     nlines = cntllines(from->p_line, to->p_line);
  126.  
  127.     if (charbased) {
  128.     Posn        ptmp;
  129.  
  130.     /*
  131.      * First yank either the whole of the text string
  132.      * specified (if from and to are on the same line),
  133.      * or from "from" to the end of the line.
  134.      */
  135.     ptmp.p_line = from->p_line;
  136.     if (to->p_line == from->p_line) {
  137.         ptmp.p_index = to->p_index;
  138.     } else {
  139.         ptmp.p_index = strlen(from->p_line->l_text) - 1;
  140.     }
  141.     yp_buf->y_1st_text = yanktext(from, &ptmp);
  142.     if (yp_buf->y_1st_text == NULL) {
  143.         return(FALSE);
  144.     }
  145.  
  146.     /*
  147.      * Next, determine if it is a multi-line character-based
  148.      * yank, in which case we have to yank from the start of
  149.      * the line containing "to" up to "to" itself.
  150.      */
  151.     if (nlines > 1) {
  152.         ptmp.p_line = to->p_line;
  153.         ptmp.p_index = 0;
  154.         yp_buf->y_2nd_text = yanktext(&ptmp, to);
  155.         if (yp_buf->y_1st_text == NULL) {
  156.         free(yp_buf->y_1st_text);
  157.         return(FALSE);
  158.         }
  159.     }
  160.  
  161.     /*
  162.      * Finally, we may need to yank any lines between "from"
  163.      * and "to".
  164.      */
  165.     if (nlines > 2) {
  166.         yp_buf->y_line_buf =
  167.         copy_lines(from->p_line->l_next, to->p_line);
  168.         if (yp_buf->y_line_buf == NULL) {
  169.         free(yp_buf->y_1st_text);
  170.         free(yp_buf->y_2nd_text);
  171.         return(FALSE);
  172.         }
  173.     }
  174.  
  175.     yp_buf->y_type = y_chars;
  176.     } else {
  177.     /*
  178.      * Yank lines starting at "from", ending at "to".
  179.      */
  180.     yp_buf->y_line_buf = copy_lines(from->p_line,
  181.             to->p_line->l_next);
  182.     if (yp_buf->y_line_buf == NULL) {
  183.         return(FALSE);
  184.     }
  185.     yp_buf->y_type = y_lines;
  186.     }
  187.     return(TRUE);
  188. }
  189.  
  190. /*
  191.  * Yank the given string.
  192.  *
  193.  * Third parameter indicates whether to do it as a line or a string.
  194.  *
  195.  * Returns TRUE if successfully yanked.
  196.  */
  197. bool_t
  198. yank_str(name, str, line_based)
  199. int    name;
  200. char    *str;
  201. bool_t    line_based;
  202. {
  203.     Yankbuffer        *yp_buf;
  204.     register Line    *tmp;
  205.     register char    *cp;
  206.  
  207.     yp_buf = yp_get_buffer(name);
  208.     if (yp_buf == NULL) {
  209.     return(FALSE);
  210.     }
  211.     yp_free(yp_buf);
  212.  
  213.     /*
  214.      * Obtain space to store the string.
  215.      */
  216.     if (line_based) {
  217.     /*
  218.      * First try to save the string. If no can do,
  219.      * return FALSE without affecting the current
  220.      * contents of the yank buffer.
  221.      */
  222.     tmp = newline(strlen(str) + 1);
  223.     if (tmp == NULL) {
  224.         return(FALSE);
  225.     }
  226.     tmp->l_prev = tmp->l_next = NULL;
  227.     (void) strcpy(tmp->l_text, str);
  228.     } else {
  229.         cp = strsave(str);
  230.     if (cp == NULL) {
  231.         return(FALSE);
  232.     }
  233.     }
  234.  
  235.     /*
  236.      * Set up the yank structure.
  237.      */
  238.     if (line_based) {
  239.     yp_buf->y_type = y_lines;
  240.     yp_buf->y_line_buf = tmp;
  241.     } else {
  242.         yp_buf->y_type = y_chars;
  243.     yp_buf->y_1st_text = cp;
  244.     }
  245.  
  246.     return(TRUE);
  247. }
  248.  
  249. /*
  250.  * Put back the last yank at the specified position,
  251.  * in the specified direction.
  252.  */
  253. void
  254. do_put(win, location, direction, name)
  255. Xviwin    *win;
  256. Posn    *location;
  257. int    direction;
  258. int    name;
  259. {
  260.     Yankbuffer        *yp_buf;
  261.     register Line    *currline;    /* line we are on now */
  262.     register Line    *nextline;    /* line after currline */
  263.     Buffer        *buffer;
  264.  
  265.     yp_buf = yp_get_buffer(name);
  266.     if (yp_buf == NULL) {
  267.     return;
  268.     }
  269.  
  270.     buffer = win->w_buffer;
  271.  
  272.     /*
  273.      * Set up current and next line pointers.
  274.      */
  275.     currline = location->p_line;
  276.     nextline = currline->l_next;
  277.  
  278.     /*
  279.      * See which type of yank it was ...
  280.      */
  281.     if (yp_buf->y_type == y_chars) {
  282.     int    l;
  283.  
  284.     l = win->w_cursor->p_index;
  285.     if (direction == FORWARD && currline->l_text[l] != '\0') {
  286.         ++l;
  287.     }
  288.  
  289.     if (!start_command(win)) {
  290.         return;
  291.     }
  292.  
  293.     /*
  294.      * Firstly, insert the 1st_text buffer, since this is
  295.      * always present. We may wish to split the line after
  296.      * the inserted text if this was a multi-line yank.
  297.      */
  298.     replchars(win, currline, l, 0, yp_buf->y_1st_text);
  299.     updateline(win);
  300.  
  301.     if (yp_buf->y_2nd_text != NULL) {
  302.         int    end_of_1st_text;
  303.         Line    *newl;
  304.  
  305.         end_of_1st_text = l + strlen(yp_buf->y_1st_text);
  306.         newl = newline(strlen(yp_buf->y_1st_text) + SLOP);
  307.         if (newl == NULL)
  308.         return;
  309.  
  310.         /*
  311.          * Link the new line into the list.
  312.          */
  313.         repllines(win, nextline, 0L, newl);
  314.         nextline = newl;
  315.         replchars(win, nextline, 0, 0,
  316.                 currline->l_text + end_of_1st_text);
  317.         replchars(win, currline, end_of_1st_text,
  318.             strlen(currline->l_text + end_of_1st_text), "");
  319.  
  320.     }
  321.  
  322.     if (yp_buf->y_line_buf != NULL) {
  323.         Line    *newlines;
  324.  
  325.         newlines = copy_lines(yp_buf->y_line_buf, (Line *) NULL);
  326.         if (newlines != NULL) {
  327.         repllines(win, nextline, 0L, newlines);
  328.         }
  329.     }
  330.  
  331.     if (yp_buf->y_2nd_text != NULL) {
  332.         if (nextline == buffer->b_lastline) {
  333.         Line    *new;
  334.  
  335.         /*
  336.          * Can't put the remainder of the text
  337.          * on the following line, 'cos there
  338.          * isn't one, so we have to create a
  339.          * new line.
  340.          */
  341.         new = newline(strlen(yp_buf->y_2nd_text) + 1);
  342.         if (new == NULL) {
  343.             end_command(win);
  344.             return;
  345.         }
  346.         repllines(win, nextline, 0L, new);
  347.         nextline = new;
  348.         }
  349.         replchars(win, nextline, 0, 0, yp_buf->y_2nd_text);
  350.     }
  351.  
  352.     end_command(win);
  353.  
  354.     /*
  355.      * Move on to the last character of the inserted text.
  356.      */
  357.     if (direction == BACKWARD) {
  358.         (void) one_left(curwin, FALSE);
  359.     }
  360.  
  361.     cursupdate(win);
  362.     update_buffer(buffer);
  363.  
  364.     } else if (yp_buf->y_type == y_lines) {
  365.  
  366.     Line    *new;        /* first line of lines to be put */
  367.  
  368.     /*
  369.      * Make a new copy of the saved lines.
  370.      */
  371.     new = copy_lines(yp_buf->y_line_buf, (Line *) NULL);
  372.     if (new == NULL) {
  373.         return;
  374.     }
  375.  
  376.     repllines(win, (direction == FORWARD) ? nextline : currline, 0L, new);
  377.  
  378.     /*
  379.      * Put the cursor at the "right" place
  380.      * (i.e. the place the "real" vi uses).
  381.      */
  382.     move_cursor(win, new, 0);
  383.     begin_line(win, TRUE);
  384.     move_window_to_cursor(win);
  385.     cursupdate(win);
  386.     update_buffer(buffer);
  387.     } else {
  388.     show_error(win, "Nothing to put!");
  389.     }
  390. }
  391.  
  392. /*
  393.  * Stuff the specified buffer into the input stream.
  394.  * Called by the '@' command.
  395.  *
  396.  * The "vi_mode" parameter will be FALSE if the buffer should
  397.  * be preceded by a ':' and followed by a '\n', i.e. it is the
  398.  * result of a :@ command rather than a vi-mode @ command.
  399.  */
  400. void
  401. yp_stuff_input(win, name, vi_mode)
  402. Xviwin    *win;
  403. int    name;
  404. bool_t    vi_mode;
  405. {
  406.     Yankbuffer    *yp_buf;
  407.  
  408.     yp_buf = yp_get_buffer(name);
  409.     if (yp_buf == NULL) {
  410.     show_error(win, "Nothing in buffer %c", name);
  411.     return;
  412.     }
  413.  
  414.     switch (yp_buf->y_type) {
  415.     case y_chars:
  416.     put(yp_buf->y_1st_text, vi_mode, FALSE);
  417.     break;
  418.  
  419.     case y_lines:
  420.     break;
  421.  
  422.     default:
  423.     show_error(win, "Nothing to put!");
  424.     return;
  425.     }
  426.  
  427.     if (yp_buf->y_line_buf != NULL) {
  428.     Line    *lp;
  429.  
  430.     for (lp = yp_buf->y_line_buf; lp != NULL; lp = lp->l_next) {
  431.         put(lp->l_text, vi_mode, TRUE);
  432.     }
  433.     }
  434.  
  435.     if (yp_buf->y_type == y_chars && yp_buf->y_2nd_text != NULL) {
  436.     put(yp_buf->y_2nd_text, vi_mode, FALSE);
  437.     }
  438. }
  439.  
  440. static void
  441. put(str, vi_mode, newline)
  442. char    *str;
  443. bool_t    vi_mode;
  444. bool_t    newline;
  445. {
  446.     stuff("%s%s%s",
  447.         (!vi_mode && str[0] != ':') ? ":" : "",
  448.         str,
  449.         (!vi_mode || newline) ? "\n" : "");
  450. }
  451.  
  452. /*
  453.  * Copy the lines pointed at by "from", up to but not including
  454.  * pointer "to" (which might be NULL), into new memory and return
  455.  * a pointer to the start of the new list.
  456.  *
  457.  * Returns NULL for errors.
  458.  */
  459. static Line *
  460. copy_lines(from, to)
  461. Line    *from, *to;
  462. {
  463.     Line    *src;
  464.     Line    head;
  465.     Line    *dest = &head;
  466.  
  467.     for (src = from; src != to; src = src->l_next) {
  468.     Line    *tmp;
  469.  
  470.     tmp = newline(strlen(src->l_text) + 1);
  471.     if (tmp == NULL) {
  472.         throw(head.l_next);
  473.         return(NULL);
  474.     }
  475.  
  476.     /*
  477.      * Copy the line's text over, and advance
  478.      * "dest" to point to the new line structure.
  479.      */
  480.     (void) strcpy(tmp->l_text, src->l_text);
  481.     tmp->l_next = NULL;
  482.     tmp->l_prev = dest;
  483.     dest->l_next = tmp;
  484.     dest = tmp;
  485.     }
  486.  
  487.     return(head.l_next);
  488. }
  489.  
  490. static char *
  491. yanktext(from, to)
  492. Posn    *from, *to;
  493. {
  494.     int        nchars;
  495.     char    *cp;
  496.  
  497.     nchars = to->p_index - from->p_index + 1;
  498.     cp = (char *) alloc((unsigned) nchars + 1);
  499.     if (cp == NULL) {
  500.     return(NULL);
  501.     }
  502.  
  503.     (void) strncpy(cp, from->p_line->l_text + from->p_index, nchars);
  504.     cp[nchars] = '\0';
  505.  
  506.     return(cp);
  507. }
  508.  
  509. static void
  510. yp_free(yp)
  511. Yankbuffer    *yp;
  512. {
  513.     if (yp->y_type == y_lines) {
  514.     throw(yp->y_line_buf);
  515.     yp->y_line_buf = NULL;
  516.     } else if (yp->y_type == y_chars) {
  517.     free(yp->y_1st_text);
  518.     yp->y_1st_text = NULL;
  519.     if (yp->y_2nd_text != NULL)
  520.         free(yp->y_2nd_text);
  521.     yp->y_2nd_text = NULL;
  522.     if (yp->y_line_buf != NULL)
  523.         throw(yp->y_line_buf);
  524.     yp->y_line_buf = NULL;
  525.     }
  526.     yp->y_type = y_none;
  527. }
  528.  
  529. /*
  530.  * Push up buffers 1..8 by one, spilling 9 off the top.
  531.  * Then move '@' into '1'.
  532.  *
  533.  * This routine assumes contiguity of characters '0' to '9',
  534.  * i.e. probably ASCII, but what the hell.
  535.  */
  536. void
  537. yp_push_deleted()
  538. {
  539.     Yankbuffer    *atp;
  540.     int        c;
  541.  
  542.     yp_free(&yb[bufno('9')]);
  543.     for (c = '9'; c > '1'; --c) {
  544.         yb[bufno(c)] = yb[bufno(c - 1)];
  545.     }
  546.     atp = &yb[bufno('@')];
  547.     yb[bufno('1')] = *atp;
  548.     atp->y_type = y_none;
  549.     atp->y_line_buf = NULL;
  550.     atp->y_1st_text = NULL;
  551.     atp->y_2nd_text = NULL;
  552. }
  553.