home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 5 Edit / 05-Edit.zip / vile-src.zip / vile-8.1 / insert.c < prev    next >
C/C++ Source or Header  |  1998-07-15  |  28KB  |  1,302 lines

  1. /*
  2.  *
  3.  *    insert.c
  4.  *
  5.  * Do various types of character insertion, including most of insert mode.
  6.  *
  7.  * Most code probably by Dan Lawrence or Dave Conroy for MicroEMACS
  8.  * Extensions for vile by Paul Fox
  9.  *
  10.  * $Header: /usr/build/vile/vile/RCS/insert.c,v 1.110 1998/07/15 23:41:13 tom Exp $
  11.  *
  12.  */
  13.  
  14. #include    "estruct.h"
  15. #include    "edef.h"
  16.  
  17. #define    DOT_ARGUMENT    ((dotcmdmode == PLAY) && dotcmdarg)
  18.  
  19. #define    BackspaceLimit() (b_val(curbp,MDBACKLIMIT) && autoindented <= 0)\
  20.             ? DOT.o\
  21.             : w_left_margin(curwp)
  22.  
  23. static    int    backspace (void);
  24. static    int    doindent(int ind);
  25. static    int    indented_newline (void);
  26. static    int    indented_newline_above (void);
  27. static    int    ins_anytime(int playback, int cur_count, int max_count, int *splice);
  28. static    int    insbrace(int n, int c);
  29. static    int    inspound (void);
  30. static    int    nextindent(int *bracefp);
  31. static    int    openlines(int n);
  32. static    int    shiftwidth(int f, int n);
  33. static    int    tab(int f, int n);
  34. #if !SMALLER
  35. static    int    istring(int f, int n, int mode);
  36. #endif
  37.  
  38. /* value of insertmode maintained through subfuncs */
  39. static    int    savedmode;
  40.  
  41. static int allow_aindent = TRUE;
  42. static int skipindent;
  43.  
  44. /*--------------------------------------------------------------------------*/
  45.  
  46. /* If the wrapmargin mode is active (i.e., nonzero), and if it's not wider than
  47.  * the screen, return the difference from the current position to the wrap
  48.  * margin.  Otherwise, return negative.
  49.  */
  50. static int
  51. past_wrapmargin(int c)
  52. {
  53.     register int    n;
  54.  
  55.     if ((n = b_val(curbp, VAL_WRAPMARGIN)) > 0
  56.      && (n = (term.t_ncol - (nu_width(curwp) + n))) >= 0) {
  57.         int list = w_val(curwp,WMDLIST);
  58.         int used = getccol(list);
  59.  
  60.         /* compute the effective screen column after adding the
  61.          * latest character
  62.          */
  63.         return NEXT_COLUMN(used, c, list, curtabval) - n;
  64.     }
  65.     return -1;
  66. }
  67.  
  68. /* Returns true iff wrap-margin or wrap-words is active and we'll wrap at the
  69.  * current column.
  70.  */
  71. static int
  72. wrap_at_col(int c)
  73. {
  74.     register int    n;
  75.  
  76.     if (past_wrapmargin(c) >= 0)
  77.         return TRUE;
  78.  
  79.     if (b_val(curbp, MDWRAP)
  80.      && (n = b_val(curbp, VAL_FILL)) > 0)
  81.          return (getccol(FALSE) > n);
  82.  
  83.     return FALSE;
  84. }
  85.  
  86. /* advance one character past the current position, for 'append()' */
  87. static void
  88. advance_one_char(void)
  89. {
  90.     if (! is_header_line(DOT,curbp) && !is_at_end_of_line(DOT))
  91.         forwchar(TRUE,1); /* END OF LINE HACK */
  92. }
  93.  
  94. /* common logic for i,I,a,A commands */
  95. static int
  96. ins_n_times(int f, int n, int advance)
  97. {
  98.     register int status = TRUE;
  99.     register int i;
  100.     int    flag    = FALSE;
  101.  
  102.     if (!f || n < 0)
  103.         n = 1;
  104.  
  105.     for (i = 0; i < n; i++) {
  106.         if ((status = ins_anytime((i != 0), i, n, &flag)) != TRUE)
  107.             break;
  108.         if (advance && !flag)
  109.             advance_one_char();
  110.     }
  111.     return status;
  112. }
  113.  
  114. /* open lines up before this one */
  115. int
  116. openup(int f, int n)
  117. {
  118.     register int s;
  119.  
  120.     if (!f) n = 1;
  121.     if (n < 0) return (FALSE);
  122.     if (n == 0) return ins();
  123.  
  124.     (void)gotobol(TRUE,1);
  125.  
  126.     /* if we are in C mode and this is a default <NL> */
  127.     if (allow_aindent && n == 1 &&
  128.         (is_c_mode(curbp) || b_val(curbp,MDAIND)) &&
  129.                     !is_header_line(DOT,curbp)) {
  130.         s = indented_newline_above();
  131.         if (s != TRUE) return (s);
  132.  
  133.         return(ins());
  134.     }
  135.     s = lnewline();
  136.     if (s != TRUE) return s;
  137.  
  138.     (void)backline(TRUE,1);        /* back to the blank line */
  139.  
  140.     if ( n > 1) {
  141.         s = openlines(n-1);
  142.         if (s != TRUE) return s;
  143.         s = backline(TRUE, 1);    /* backup over the first one */
  144.         if (s != TRUE) return s;
  145.     }
  146.  
  147.     return(ins());
  148. }
  149.  
  150. /*
  151.  * as above, but override all autoindenting and cmode-ing
  152.  */
  153. int
  154. openup_no_aindent(int f, int n)
  155. {
  156.         int s;
  157.     int oallow = allow_aindent;
  158.     allow_aindent = FALSE;
  159.     s = openup(f,n);
  160.     allow_aindent = oallow;
  161.     return s;
  162. }
  163.  
  164. /* open lines up after this one */
  165. int
  166. opendown(int f, int n)
  167. {
  168.     register int    s;
  169.  
  170.     if (!f) n = 1;
  171.     if (n < 0) return (FALSE);
  172.     if (n == 0) return ins();
  173.  
  174.     s = openlines(n);
  175.     if (s != TRUE)
  176.         return (s);
  177.  
  178.     return(ins());
  179. }
  180.  
  181. /*
  182.  * as above, but override all autoindenting and cmode-ing
  183.  */
  184. int
  185. opendown_no_aindent(int f, int n)
  186. {
  187.         int s;
  188.     int oallow = allow_aindent;
  189.     allow_aindent = FALSE;
  190.     s = opendown(f,n);
  191.     allow_aindent = oallow;
  192.     return s;
  193. }
  194.  
  195. /*
  196.  * Open up some blank space. The basic plan is to insert a bunch of newlines,
  197.  * and then back up over them.
  198.  *
  199.  * This interprets the repeat-count for the 'o' and 'O' commands.  Unlike vi
  200.  * (which does not use the repeat-count), this specifies the number of blank
  201.  * lines to create before proceeding with inserting the string-argument of the
  202.  * command.
  203.  */
  204. static int
  205. openlines(int n)
  206. {
  207.     register int i = n;            /* Insert newlines. */
  208.     register int s = TRUE;
  209.     while (i-- && s==TRUE) {
  210.         (void)gotoeol(FALSE,1);
  211.         s = newline(TRUE,1);
  212.     }
  213.     if (s == TRUE && n)            /* Then back up over top */
  214.         (void)backline(TRUE, n-1);    /* of them all.         */
  215.  
  216.     curgoal = -1;
  217.  
  218.     return s;
  219. }
  220.  
  221. /*
  222.  * Implements the vi 'i' command.
  223.  */
  224. int
  225. insert(int f, int n)
  226. {
  227.     return ins_n_times(f,n,TRUE);
  228. }
  229.  
  230. /*
  231.  * as above, but override all autoindenting and cmode-ing
  232.  */
  233. int
  234. insert_no_aindent(int f, int n)
  235. {
  236.         int s;
  237.     int oallow = allow_aindent;
  238.     allow_aindent = FALSE;
  239.     s = ins_n_times(f,n,TRUE);
  240.     allow_aindent = oallow;
  241.     return s;
  242. }
  243.  
  244. /*
  245.  * Implements the vi 'I' command.
  246.  */
  247. int
  248. insertbol(int f, int n)
  249. {
  250.     if (!DOT_ARGUMENT || (dotcmdrep == dotcmdcnt))
  251.         (void)firstnonwhite(FALSE,1);
  252.     return ins_n_times(f,n,TRUE);
  253. }
  254.  
  255. /*
  256.  * Implements the vi 'a' command.
  257.  */
  258. int
  259. append(int f, int n)
  260. {
  261.     advance_one_char();
  262.  
  263.     return ins_n_times(f,n, !DOT_ARGUMENT);
  264. }
  265.  
  266. /*
  267.  * Implements the vi 'A' command.
  268.  */
  269. int
  270. appendeol(int f, int n)
  271. {
  272.     if (!is_header_line(DOT,curbp))
  273.         (void)gotoeol(FALSE,0);
  274.  
  275.     return ins_n_times(f,n,TRUE);
  276. }
  277.  
  278. /*
  279.  * Unlike most flags on the mode-line, we'll only show the insertion-mode on
  280.  * the current window.
  281.  */
  282. static void
  283. set_insertmode(int mode)
  284. {
  285.     insertmode = mode;
  286.     if (b_val(curbp, MDSHOWMODE))
  287.         curwp->w_flag |= WFMODE;
  288. }
  289.  
  290. /*
  291.  * Function that returns the insertion mode if we're inserting into a given
  292.  * window
  293.  */
  294. int
  295. ins_mode(WINDOW *wp)
  296. {
  297.     return (wp == curwp) ? insertmode : FALSE;
  298. }
  299.  
  300. /*
  301.  * Implements the vi 'R' command.
  302.  *
  303.  * This takes an optional repeat-count and a string-argument.  The repeat-count
  304.  * (default 1) specifies the number of times that the string argument is
  305.  * inserted.  The length of the string-argument itself determines the number of
  306.  * characters (beginning with the cursor position) to delete before beginning
  307.  * the insertion.
  308.  */
  309. int
  310. overwritechars(int f, int n)
  311. {
  312.     set_insertmode(OVERWRITE);
  313.     return ins_n_times(f,n, TRUE);
  314. }
  315.  
  316. /*
  317.  * Implements the vi 'r' command.
  318.  *
  319.  * This takes an optional repeat-count and a single-character argument.  The
  320.  * repeat-count (default 1) specifies the number of characters beginning with
  321.  * the cursor position that are replaced by the argument.  Newline is treated
  322.  * differently from the other characters (only one newline is inserted).
  323.  *
  324.  * Unlike vi, the number of characters replaced can be longer than a line.
  325.  * Also, vile allows quoted characters.
  326.  */
  327. int
  328. replacechar(int f, int n)
  329. {
  330.     register int    s = TRUE;
  331.     register int    t = FALSE;
  332.     register int    c;
  333.  
  334.     if (!f && is_empty_line(DOT))
  335.         return FALSE;
  336.  
  337.     if (clexec || isnamedcmd) {
  338.         int status;
  339.         static char cbuf[NLINE];
  340.         if ((status=mlreply("Replace with: ", cbuf, 2)) != TRUE)
  341.             return status;
  342.         c = cbuf[0];
  343.     } else {
  344.         set_insertmode(REPLACECHAR);  /* need to fool SPEC prefix code */
  345.         if (dotcmdmode != PLAY)
  346.             (void)update(FALSE);
  347.         c = keystroke();
  348.         if (ABORTED(c)) {
  349.             set_insertmode(FALSE);
  350.             return ABORT;
  351.         }
  352.  
  353.     }
  354.     c = kcod2key(c);
  355.  
  356.     if (!f || !n)
  357.         n = 1;
  358.     if (n < 0)
  359.         s = FALSE;
  360.     else {
  361.         int    vi_fix = (!DOT_ARGUMENT || (dotcmdrep <= 1));
  362.  
  363.         (void)ldelete((B_COUNT)n, FALSE);
  364.         if (c == quotec) {
  365.             t = s = quote(f,n);
  366.         } else {
  367.             if (isreturn(c)) {
  368.                 if (vi_fix)
  369.                     s = lnewline();
  370.             } else {
  371.                 if (isbackspace(c)) {    /* vi beeps here */
  372.                     s = TRUE;    /* replaced with nothing */
  373.                 } else {
  374.                     t = s = linsert(n, c);
  375.                 }
  376.             }
  377.         }
  378.         if ((t == TRUE) && (DOT.o > w_left_margin(curwp)) && vi_fix)
  379.             s = backchar(FALSE,1);
  380.     }
  381.     set_insertmode(FALSE);
  382.     return s;
  383. }
  384.  
  385. /*
  386.  * This routine performs the principal decoding for insert mode (i.e.., the
  387.  * i,I,a,A,R commands).  It is invoked via 'ins_n_times()', which loops over
  388.  * the repeat-count for direct commands.  One complicating factor is that
  389.  * 'ins_n_times()' (actually its callers) is called once for each repetition in
  390.  * a '.' command.  At this level we compute the effective loop counter (the '.'
  391.  * and the direct commands), because we have to handle the vi-compatibilty case
  392.  * of inserting a newline.
  393.  *
  394.  * We stop repeating insert after the first newline in the insertion-string
  395.  * (that's what vi does).  If a user types
  396.  *
  397.  *    3iABC<nl>foo<esc>
  398.  *
  399.  * then we want to insert
  400.  *
  401.  *    ABCABCABC<nl>foo
  402.  */
  403. static int last_insert_char;
  404.  
  405. static int
  406. ins_anytime(int playback, int cur_count, int max_count, int *splice)
  407. {
  408. #if OPT_MOUSE || OPT_B_LIMITS
  409.     WINDOW    *wp0 = curwp;
  410. #endif
  411.     register int status;
  412.     int    c;        /* command character */
  413.     int backsp_limit;
  414.     static ITBUFF *insbuff;
  415.     int osavedmode;
  416.  
  417.     if (DOT_ARGUMENT) {
  418.         max_count = cur_count + dotcmdcnt;
  419.         cur_count += dotcmdcnt - dotcmdrep;
  420.     }
  421.  
  422.     if (playback && (insbuff != 0))
  423.         itb_first(insbuff);
  424.     else if (!itb_init(&insbuff, abortc))
  425.         return FALSE;
  426.  
  427.     if (insertmode == FALSE)
  428.         set_insertmode(INSERT);
  429.     osavedmode = savedmode;
  430.     savedmode = insertmode;
  431.  
  432.     backsp_limit = BackspaceLimit();
  433.  
  434.     last_insert_char = EOS;
  435.  
  436.     for_ever {
  437.  
  438.         /*
  439.          * Read another character from the insertion-string.
  440.          */
  441.         c = abortc;
  442.         if (playback) {
  443.             if (*splice && !itb_more(insbuff))
  444.                 playback = FALSE;
  445.             else
  446.                 c = itb_next(insbuff);
  447.         }
  448.         if (!playback) {
  449.             if (dotcmdmode != PLAY)
  450.                 (void)update(FALSE);
  451.  
  452.             c = mapped_keystroke();
  453.  
  454. #if OPT_MOUSE
  455.             /*
  456.              * Prevent user from starting insertion into a
  457.              * modifiable buffer, then clicking on another
  458.              * buffer to continue inserting.  This assumes that
  459.              * 'setcursor()' handles entry into the other
  460.              * buffer.
  461.              */
  462.             if (curwp != wp0) {
  463.                     /* end insert mode for window we started in */
  464.                     wp0->w_traits.insmode = FALSE;
  465.                 if (b_val(wp0->w_bufp, MDSHOWMODE))
  466.                     wp0->w_flag |= WFMODE;
  467.                 unkeystroke(c);
  468.                 goto leave;
  469.             }
  470. #endif
  471.             if (!itb_append(&insbuff, c)) {
  472.                 status = FALSE;
  473.                 break;
  474.             }
  475.         }
  476.  
  477.  
  478.         if (isspecial(c)) {
  479.                 /* if we're allowed to honor SPEC bindings,
  480.                 then see if it's bound to something, and
  481.                 execute it */
  482.             const CMDFUNC *cfp = kcod2fnc(c);
  483.             if (cfp) {
  484.                 int savedexecmode = insertmode;
  485.  
  486.                 backsp_limit = w_left_margin(curwp);
  487.                 if (curgoal < 0)
  488.                     curgoal = getccol(FALSE);
  489.                 (void)execute(cfp,FALSE,1);
  490.                 insertmode = savedexecmode;
  491.             }
  492.             continue;
  493.         }
  494.  
  495.         if (!isident(c))
  496.             abbr_check(&backsp_limit);
  497.  
  498.         if (isreturn(c)) {
  499.             if ((cur_count+1) < max_count) {
  500.                 if (DOT_ARGUMENT) {
  501.                     while (itb_more(dotcmd))
  502.                         (void)mapped_keystroke();
  503.                 }
  504.                 *splice = TRUE;
  505.                 status = TRUE;
  506.                 break;
  507.             } else if (DOT_ARGUMENT) {
  508.                 *splice = TRUE;
  509.             }
  510.         }
  511.         /*
  512.          * Decode the character
  513.          */
  514.         if (ABORTED(c)) {
  515. #if OPT_MOUSE
  516.     leave:
  517. #endif
  518.              /* an unfortunate Vi-ism that ensures one
  519.                 can always type "ESC a" if you're not sure
  520.                 you're in insert mode. */
  521.             if (DOT.o > w_left_margin(wp0))
  522.                 backchar(TRUE,1);
  523.             if (autoindented >= 0) {
  524.                 (void)trimline((void *)0,0,0);
  525.                 autoindented = -1;
  526.             }
  527.             if (cur_count+1 == max_count)
  528.                 *splice = TRUE;
  529.             status = TRUE;
  530.             break;
  531.         } else if ((c & HIGHBIT) && b_val(curbp, MDMETAINSBIND)) {
  532.                 /* if we're allowed to honor meta-character bindings,
  533.                 then see if it's bound to something, and
  534.                 insert it if not */
  535.             const CMDFUNC *cfp = kcod2fnc(c);
  536.             if (cfp) {
  537.                 int savedexecmode = insertmode;
  538.  
  539.                 backsp_limit = w_left_margin(curwp);
  540.                 if (curgoal < 0)
  541.                     curgoal = getccol(FALSE);
  542.                 (void)execute(cfp,FALSE,1);
  543.                 insertmode = savedexecmode;
  544.                 continue;
  545.             }
  546.         }
  547.  
  548.         if (c == startc || c == stopc) {  /* ^Q and ^S */
  549.             continue;
  550.         }
  551. #if OPT_SHELL && SYS_UNIX && defined(SIGTSTP)    /* job control, ^Z */
  552.         else if (c == suspc) {
  553.             status = bktoshell(FALSE,1);
  554.         }
  555. #endif
  556.         else {
  557.             status = inschar(c,&backsp_limit);
  558.             curgoal = -1;
  559.         }
  560.  
  561.         if (status != TRUE)
  562.             break;
  563.  
  564. #if OPT_CFENCE
  565.         /* check for CMODE fence matching */
  566.             if (b_val(curbp, MDSHOWMAT))
  567.             fmatch(c);
  568. #endif
  569.  
  570.         /* check auto-save mode */
  571.         if (b_val(curbp, MDASAVE)) {
  572.             if (--curbp->b_acount <= 0) {
  573.                 /* and save the file if needed */
  574.                 (void)update(TRUE);
  575.                 filesave(FALSE, 0);
  576.                 curbp->b_acount = b_val(curbp,VAL_ASAVECNT);
  577.             }
  578.         }
  579.     }
  580.  
  581.     set_insertmode(FALSE);
  582.     savedmode = osavedmode;
  583.     return (status);
  584. }
  585.  
  586. /* grunt routine for insert mode */
  587. int
  588. ins(void)
  589. {
  590.     int    flag;
  591.     return ins_anytime(FALSE,1,1,&flag);
  592. }
  593.  
  594. static int
  595. isallspace(LINEPTR ln, int lb, int ub)
  596. {
  597.     while (lb <= ub) {
  598.         if (!isSpace(lgetc(ln,ub)))
  599.             return FALSE;
  600.         ub--;
  601.     }
  602.     return TRUE;
  603. }
  604.  
  605. /*
  606.  * This function is used when testing for wrapping, to see if there are any
  607.  * blanks already on the line.  We explicitly exclude blanks before the
  608.  * autoindent margin, if any, to avoid inserting unnecessary blank lines.
  609.  */
  610. static int
  611. blanks_on_line(void)
  612. {
  613.     int    code = FALSE;
  614.     int    indentwas = b_val(curbp,MDAIND) ? previndent((int *)0) : 0;
  615.     int    save = DOT.o;
  616.     int    list = w_val(curwp,WMDLIST);
  617.  
  618.     for (DOT.o = 0; DOT.o < llength(DOT.l); DOT.o++) {
  619.         if (isSpace(char_at(DOT))
  620.          && getccol(list) >= indentwas) {
  621.             code = TRUE;
  622.             break;
  623.         }
  624.     }
  625.     DOT.o = save;
  626.     return code;
  627. }
  628.  
  629. int
  630. inschar(int c, int *backsp_limit_p)
  631. {
  632.     CmdFunc execfunc;    /* ptr to function to execute */
  633.  
  634.     execfunc = NULL;
  635.     if (c == quotec) {
  636.         execfunc = quote;
  637.     } else {
  638.         /*
  639.          * If a space was typed, fill column is defined, the
  640.          * argument is non- negative, wrap mode is enabled, and
  641.          * we are now past fill column, perform word wrap.
  642.          */
  643.         if (wrap_at_col(c)) {
  644.             int offset = past_wrapmargin(c);
  645.             int wm_flag = (offset >= 0);
  646.             int is_print = (!isspecial(c) && isPrint(c));
  647.             int is_space = (!isspecial(c) && isSpace(c));
  648.  
  649.             if (is_space
  650.              || (is_print && (offset >= 1) && blanks_on_line())) {
  651.                 int status = wrapword(wm_flag, is_space);
  652.                 *backsp_limit_p = w_left_margin(curwp);
  653.                 if (wm_flag && is_space)
  654.                     return status;
  655.             } else if (wm_flag
  656.                 && !blanks_on_line()
  657.                 && (c == '\t' || is_print)) {
  658.                 kbd_alarm();    /* vi beeps past the margin */
  659.             }
  660.         }
  661.  
  662.         if ( c == '\t') { /* tab */
  663.             execfunc = tab;
  664.             autoindented = -1;
  665.         } else if (isreturn(c)) {
  666.             execfunc = newline;
  667.             if (autoindented >= 0) {
  668.                 (void)trimline((void *)0,0,0);
  669.                 autoindented = -1;
  670.             }
  671.             *backsp_limit_p = w_left_margin(curwp);
  672.         } else if ( isbackspace(c) ||
  673.                 c == tocntrl('D') ||
  674.                 c == killc ||
  675.                 c == wkillc) { /* ^U and ^W */
  676.             execfunc = nullproc;
  677.             /* all this says -- "is this a regular ^D for
  678.                 backing up a shiftwidth?".  otherwise,
  679.                 we treat it as ^U, below */
  680.             if (c == tocntrl('D')
  681.              && !(DOT.o > *backsp_limit_p
  682.                   && ((lgetc(DOT.l,DOT.o-1) == '0'
  683.                   && last_insert_char == '0')
  684.                   || (lgetc(DOT.l,DOT.o-1) == '^'
  685.                   && last_insert_char == '^'))
  686.                   && isallspace(DOT.l,w_left_margin(curwp),
  687.                                       DOT.o-2))) {
  688.                 int goal, col, sw;
  689.  
  690.                 sw = shiftwid_val(curbp);
  691.                 if (autoindented >=0)
  692.                     *backsp_limit_p = w_left_margin(curwp);
  693.                 col = getccol(FALSE);
  694.                 if (col > 0)
  695.                     goal = ((col-1)/sw)*sw;
  696.                 else
  697.                     goal = 0;
  698.                 while (col > goal &&
  699.                     DOT.o > *backsp_limit_p) {
  700.                     backspace();
  701.                     col = getccol(FALSE);
  702.                 }
  703.                 if (col < goal)
  704.                     linsert(goal - col,' ');
  705.             } else {
  706.                 /* have we backed thru a "word" yet? */
  707.                 int saw_word = FALSE;
  708.  
  709.                 /* was it '^^D'?  then set the flag
  710.                     that tells us to skip a line
  711.                     when calculating the autoindent
  712.                     on the next newline */
  713.                 if (c == tocntrl('D') &&
  714.                     last_insert_char == '^')
  715.                     skipindent = 1;
  716.  
  717.                 while (DOT.o > *backsp_limit_p) {
  718.                     if (c == wkillc) {
  719.                         if (isSpace( lgetc(DOT.l,
  720.                                 DOT.o-1))) {
  721.                             if (saw_word)
  722.                                 break;
  723.                         } else {
  724.                             saw_word = TRUE;
  725.                         }
  726.                     }
  727.                     backspace();
  728.                     autoindented--;
  729.                     if (c != wkillc && c != killc
  730.                         && c != tocntrl('D'))
  731.                         break;
  732.                 }
  733.             }
  734.         } else if ( c ==  tocntrl('T')) { /* ^T */
  735.             execfunc = shiftwidth;
  736.         }
  737.  
  738.         last_insert_char = c;
  739.  
  740.     }
  741.  
  742.     if (execfunc != NULL)
  743.         return (*execfunc)(FALSE, 1);
  744.  
  745.     /* make it a real character again */
  746.     c = kcod2key(c);
  747.  
  748.     /* if we are in overwrite mode, not at eol,
  749.        and next char is not a tab or we are at a tab stop,
  750.        delete a char forword            */
  751.     if ((insertmode == OVERWRITE)
  752.      && (!DOT_ARGUMENT || (dotcmdrep <= 1))
  753.      && (DOT.o < llength(DOT.l))
  754.      && (char_at(DOT) != '\t' || DOT.o % curtabval == curtabval-1)) {
  755.         autoindented = -1;
  756.         (void)ldelete(1L, FALSE);
  757.     }
  758.  
  759.     /* do the appropriate insertion */
  760.     if (allow_aindent && is_c_mode(curbp)) {
  761.         int dir;
  762.         if (is_user_fence(c, &dir) && dir == REVERSE) {
  763.             return insbrace(1, c);
  764.         } else if (c == '#') {
  765.             return inspound();
  766.         }
  767.     }
  768.  
  769.     autoindented = -1;
  770.     return linsert(1, c);
  771.  
  772. }
  773.  
  774. #if ! SMALLER
  775. int
  776. appstring(int f, int n)
  777. {
  778.     advance_one_char();
  779.     return istring(f,n,INSERT);
  780. }
  781.  
  782. int
  783. insstring(int f, int n)
  784. {
  785.     return istring(f,n,INSERT);
  786. }
  787.  
  788. int
  789. overwstring(int f, int n)
  790. {
  791.     return istring(f,n,OVERWRITE);
  792. }
  793.  
  794. /* ask for and insert or overwrite a string into the current */
  795. /* buffer at the current point */
  796. static int
  797. istring(int f, int n, int mode)
  798. {
  799.     register char *tp;    /* pointer into string to add */
  800.     register int status;    /* status return code */
  801.     int backsp_limit;
  802.     static char tstring[NPAT+1];    /* string to add */
  803.  
  804.     /* ask for string to insert */
  805.     status = mlreply("String to insert: ", tstring, NPAT);
  806.     if (status != TRUE)
  807.         return(status);
  808.  
  809.  
  810.     if (f == FALSE)
  811.         n = 1;
  812.  
  813.     if (n < 0)
  814.         n = - n;
  815.  
  816.     set_insertmode(mode);
  817.  
  818.     backsp_limit = BackspaceLimit();
  819.  
  820.     /* insert it */
  821.     while (n--) {
  822.         tp = &tstring[0];
  823.         while (*tp) {
  824.             status = inschar(*tp++,&backsp_limit);
  825.             if (status != TRUE) {
  826.                 set_insertmode(FALSE);
  827.                 return(status);
  828.             }
  829.         }
  830.     }
  831.  
  832.     set_insertmode(FALSE);
  833.     return(TRUE);
  834. }
  835. #endif
  836.  
  837. static int
  838. backspace(void)
  839. {
  840.     register int    s;
  841.  
  842.     if ((s=backchar(TRUE, 1)) == TRUE && insertmode != OVERWRITE)
  843.         s = ldelete(1L, FALSE);
  844.     return (s);
  845. }
  846.  
  847. /*
  848.  * Insert a newline. If we are in CMODE, do automatic
  849.  * indentation as specified.
  850.  */
  851. int
  852. newline(int f, int n)
  853. {
  854.     register int    s;
  855.  
  856.     if (!f)
  857.         n = 1;
  858.     else if (n < 0)
  859.         return (FALSE);
  860.  
  861.     /* if we are in C or auto-indent modes and this is a default <NL> */
  862.     if (allow_aindent
  863.      && (n == 1)
  864.      && (is_c_mode(curbp) || b_val(curbp,MDAIND))
  865.      && !is_header_line(DOT,curbp))
  866.         return indented_newline();
  867.  
  868.     /* insert some lines */
  869.     while (n--) {
  870.         if ((s=lnewline()) != TRUE)
  871.             return (s);
  872.         curwp->w_flag |= WFINS;
  873.     }
  874.     return (TRUE);
  875. }
  876.  
  877. /* insert a newline and indentation for C */
  878. static int
  879. indented_newline(void)
  880. {
  881.     int cmode = allow_aindent && is_c_mode(curbp);
  882.     register int indentwas; /* indent to reproduce */
  883.     int bracef; /* was there a brace at the end of line? */
  884.  
  885.     if (lnewline() == FALSE)
  886.         return FALSE;
  887.  
  888.     indentwas = previndent(&bracef);
  889.     skipindent = 0;
  890.  
  891.     if (cmode && bracef)
  892.         indentwas = next_sw(indentwas);
  893.  
  894.     return doindent(indentwas);
  895. }
  896.  
  897. /* insert a newline and indentation for autoindent */
  898. static int
  899. indented_newline_above(void)
  900. {
  901.     int cmode = allow_aindent && is_c_mode(curbp);
  902.     register int indentwas;    /* indent to reproduce */
  903.     int bracef; /* was there a brace at the beginning of line? */
  904.  
  905.     indentwas = nextindent(&bracef);
  906.     if (lnewline() == FALSE)
  907.         return FALSE;
  908.     if (backline(TRUE,1) == FALSE)
  909.         return FALSE;
  910.     if (cmode && bracef)
  911.         indentwas = next_sw(indentwas);
  912.  
  913.     return doindent(indentwas);
  914. }
  915.  
  916. /* get the indent of the last previous non-blank line.    also, if arg
  917.     is non-null, check if line ended in a brace */
  918. int
  919. previndent(int *bracefp)
  920. {
  921.     int ind;
  922.     int cmode = allow_aindent && is_c_mode(curbp);
  923.  
  924.     if (bracefp) *bracefp = FALSE;
  925.  
  926.     MK = DOT;
  927.  
  928.     /* backword() will leave us either on this line, if there's something
  929.         non-blank here, or on the nearest previous non-blank line. */
  930.     /* (at start of buffer, may leave us on empty line) */
  931.     do {
  932.         if (backword(FALSE,1) == FALSE || is_empty_line(DOT)) {
  933.             (void)gomark(FALSE,1);
  934.             return 0;
  935.         }
  936.         DOT.o = 0;
  937.     /* if the line starts with a #, then don't copy its indent */
  938.     } while ((skipindent-- > 0) || (cmode && lgetc(DOT.l, 0) == '#'));
  939.  
  940.     ind = indentlen(DOT.l);
  941.     if (bracefp) {
  942.         int lc = lastchar(DOT.l);
  943.         int c = lgetc(DOT.l,lc);
  944.         int dir;
  945.         *bracefp = (lc >= 0 && (c == ':' ||
  946.                 (is_user_fence(c, &dir) && dir == FORWARD)));
  947.  
  948.     }
  949.  
  950.     (void)gomark(FALSE,1);
  951.  
  952.     return ind;
  953. }
  954.  
  955. /* get the indent of the next non-blank line.    also, if arg
  956.     is non-null, check if line starts in a brace */
  957. static int
  958. nextindent(int *bracefp)
  959. {
  960.     int ind;
  961.     int fc;
  962.  
  963.     MK = DOT;
  964.  
  965.     /* we want the indent of this line if it's non-blank, or the indent
  966.         of the next non-blank line otherwise */
  967.     fc = firstchar(DOT.l);
  968.     if (fc < 0 && (   forwword(FALSE,1) == FALSE
  969.                    || (fc = firstchar(DOT.l)) < 0)) {
  970.         if (bracefp)
  971.             *bracefp = FALSE;
  972.         DOT = MK;
  973.         return 0;
  974.     }
  975.     ind = indentlen(DOT.l);
  976.     if (bracefp) {
  977.         *bracefp = ((lgetc(DOT.l,fc) == RBRACE) ||
  978.                 (lgetc(DOT.l,fc) == RPAREN) ||
  979.                 (lgetc(DOT.l,fc) == RBRACK));
  980.     }
  981.  
  982.     DOT = MK;
  983.  
  984.     return ind;
  985. }
  986.  
  987. static int
  988. doindent(int ind)
  989. {
  990.     register int i, j;
  991.  
  992.     /* first clean up existing leading whitespace */
  993.     if ((i = firstchar(DOT.l)) >= 0)
  994.         j = DOT.o - i;
  995.     else
  996.         j = 0;
  997.     if (j < 0)
  998.         j = 0;
  999.     DOT.o = w_left_margin(curwp);
  1000.     if (i > 0)
  1001.         (void)ldelete((B_COUNT)i,FALSE);
  1002.  
  1003.     autoindented = 0;
  1004.     /* if no indent was asked for, we're done */
  1005.     if (ind > 0) {
  1006.         i = ind/curtabval;  /* how many tabs? */
  1007.         if (i && b_val(curbp,MDTABINSERT)) {
  1008.             autoindented += i;
  1009.             if (tab(TRUE,i) == FALSE)
  1010.                 return FALSE;
  1011.             ind %= curtabval; /* how many spaces remaining */
  1012.         }
  1013.         if (ind > 0) {  /* only spaces now */
  1014.             autoindented += ind;
  1015.             if (linsert(ind, ' ') == FALSE)
  1016.                 return FALSE;
  1017.         }
  1018.     }
  1019.     if (!autoindented)
  1020.         autoindented = -1;
  1021.  
  1022.     DOT.o += j;    /* put dot back pointing to the same text as before */
  1023.     return TRUE;
  1024. }
  1025.  
  1026. /* return the column indent of the specified line */
  1027. int
  1028. indentlen(LINE *lp)
  1029. {
  1030.     register int ind, i, c;
  1031.     ind = 0;
  1032.     for (i=0; i<llength(lp); ++i) {
  1033.         c = lgetc(lp, i);
  1034.         if (!isSpace(c))
  1035.             break;
  1036.         if (c == '\t')
  1037.             ind = nextab(ind);
  1038.         else
  1039.             ++ind;
  1040.     }
  1041.     return ind;
  1042. }
  1043.  
  1044.  
  1045. /* insert a brace or paren into the text here... we are in CMODE */
  1046. static int
  1047. insbrace(
  1048. int n,    /* repeat count */
  1049. int c)    /* brace/paren to insert (always '}' or ')' for now) */
  1050. {
  1051.  
  1052. #if ! OPT_CFENCE
  1053.     /* wouldn't want to back up from here, but fences might take us
  1054.         forward */
  1055.     /* if we are at the beginning of the line, no go */
  1056.     if (DOT.o <= w_left_margin(curwp))
  1057.         return(linsert(n,c));
  1058. #endif
  1059.  
  1060.     if (autoindented >= 0) {
  1061.         (void)trimline((void *)0,0,0);
  1062.     } else {
  1063.         return linsert(n,c);
  1064.     }
  1065.     skipindent = 0;
  1066. #if ! OPT_CFENCE /* no fences?    then put brace one tab in from previous line */
  1067.     doindent(((previndent(NULL)-1) / curtabval) * curtabval);
  1068. #else /* line up brace with the line containing its match */
  1069.     doindent(fmatchindent(c));
  1070. #endif
  1071.     autoindented = -1;
  1072.  
  1073.     /* and insert the required brace(s) */
  1074.     return(linsert(n, c));
  1075. }
  1076.  
  1077. static int
  1078. inspound(void)    /* insert a # into the text here...we are in CMODE */
  1079. {
  1080.     /* if we are at the beginning of the line, no go */
  1081.     if (DOT.o <= w_left_margin(curwp))
  1082.         return(linsert(1,'#'));
  1083.  
  1084.     if (autoindented > 0) { /* must all be whitespace before us */
  1085.         if (autoindented > llength(DOT.l))
  1086.             autoindented = llength(DOT.l);
  1087.         DOT.o = w_left_margin(curwp);
  1088.         (void)ldelete((B_COUNT)autoindented,FALSE);
  1089.     }
  1090.     autoindented = -1;
  1091.  
  1092.     /* and insert the required pound */
  1093.     return(linsert(1, '#'));
  1094. }
  1095.  
  1096. /* insert a tab into the file */
  1097. static int
  1098. tab(int f, int n)
  1099. {
  1100.     int ccol;
  1101.     if (!f) n = 1;
  1102.     if (n <= 0)
  1103.         return FALSE;
  1104.  
  1105.     if (b_val(curbp,MDTABINSERT))
  1106.         return linsert(n, '\t');
  1107.  
  1108.     ccol = getccol(FALSE);
  1109.     return linsert((nextab(ccol) - ccol) + (n-1)*curtabval,' ');
  1110. }
  1111.  
  1112. /*ARGSUSED*/
  1113. static int
  1114. shiftwidth(int f GCC_UNUSED, int n GCC_UNUSED)
  1115. {
  1116.     int logical_col;
  1117.     int char_index;
  1118.     int space_count;
  1119.     int all_white;
  1120.     int add_spaces;
  1121.     int c;
  1122.     int s;
  1123.  
  1124.     char_index = DOT.o;
  1125.  
  1126.     /*
  1127.      * Compute the "logical" column; i.e. the column the cursor is
  1128.      * in on the screen.
  1129.      *
  1130.      * While we're at it, compute the spaces just before the insert
  1131.      * point.
  1132.      */
  1133.  
  1134.     (void) gocol(0);
  1135.         logical_col = 0;
  1136.     space_count = 0;
  1137.     all_white = TRUE;
  1138.     while (DOT.o < char_index) {
  1139.         c = char_at(DOT);
  1140.         if (c == ' ') {
  1141.         space_count++;
  1142.         } else {
  1143.         space_count = 0;
  1144.         }
  1145.         if (!isSpace(c)) {
  1146.         all_white = FALSE;
  1147.         }
  1148.  
  1149.         if (c == '\t') {
  1150.         logical_col += curtabval - (logical_col % curtabval);
  1151.             } else {
  1152.         logical_col++;
  1153.         }
  1154.  
  1155.         DOT.o++;
  1156.     }
  1157.  
  1158.     DOT.o = char_index;
  1159.  
  1160.     /*
  1161.      * Now we can compute the destination column. If this is the same
  1162.      * as the tab column, delete the spaces before the insert point
  1163.      * & insert a tab; otherwise, insert spaces as required.
  1164.      */
  1165.     add_spaces = next_sw(logical_col) - logical_col;
  1166.     if (space_count + add_spaces > curtabval) {
  1167.         space_count = curtabval - add_spaces;
  1168.     }
  1169.     if (b_val(curbp,MDTABINSERT) &&
  1170.         ((add_spaces + logical_col) % curtabval == 0)) {
  1171.         if (space_count > 0) {
  1172.         DOT.o -= space_count;
  1173.         s = ldelete((B_COUNT)space_count, FALSE);
  1174.         } else {
  1175.         space_count = 0;
  1176.         s = TRUE;
  1177.         }
  1178.         if (s) {
  1179.         space_count += add_spaces;
  1180.         s = linsert((space_count + curtabval - 1) / curtabval, '\t');
  1181.         }
  1182.     } else {
  1183.         s = linsert(add_spaces, ' ');
  1184.     }
  1185.  
  1186.     if (all_white && s) {
  1187.         if (autoindented >= 0) {
  1188.         int fc = firstchar(DOT.l);
  1189.         if (fc >= 0)
  1190.             autoindented = fc;
  1191.         else /* all white */
  1192.             autoindented = llength(DOT.l);
  1193.         }
  1194.     }
  1195.     return s;
  1196. }
  1197.  
  1198. /*
  1199.  * Quote the next character, and insert it into the buffer. All the characters
  1200.  * are taken literally, with the exception of a) the newline, which always has
  1201.  * its line splitting meaning, and b) decimal digits, which are accumulated
  1202.  * (up to three of them) and the resulting value put in the buffer.
  1203.  *
  1204.  * A character is always read, even if it is inserted 0 times, for regularity.
  1205.  */
  1206. int
  1207. quote(int f, int n)
  1208. {
  1209.     int  s, c, digs, base, i, num, delta;
  1210.     const char *str;
  1211.  
  1212.     i = digs = 0;
  1213.     num = 0;
  1214.  
  1215.     c = keystroke_raw8();
  1216.     if (!f)
  1217.         n = 1;
  1218.     if (n < 0)
  1219.         return FALSE;
  1220.     if (n == 0)
  1221.         return TRUE;
  1222.  
  1223.     /* accumulate up to 3 digits */
  1224.     if (isDigit(c) || c == 'x') {
  1225.         if (c == '0') {
  1226.             digs = 4; /* including the leading '0' */
  1227.             base = 8;
  1228.             str = "octal";
  1229.         } else if (c == 'x') {
  1230.             digs = 3; /* including the leading 'x' */
  1231.             base = 16;
  1232.             c = '0';
  1233.             str = "hex";
  1234.         } else {
  1235.             digs = 3;
  1236.             base = 10;
  1237.             str = "decimal";
  1238.         }
  1239.         do {
  1240.             if (isbackspace(c)) {
  1241.                 num /= base;
  1242.                 if (--i < 0)
  1243.                     break;
  1244.             } else {
  1245.                 if (c >= 'a' && c <= 'f')
  1246.                     delta = ('a' - 10);
  1247.                 else if (c >= 'A' && c <= 'F')
  1248.                     delta = ('A' - 10);
  1249.                 else
  1250.                     delta = '0';
  1251.                 num = num * base + c - delta;
  1252.                 i++;
  1253.             }
  1254.             mlwrite("Enter %s digits... %d", str, num);
  1255.             if (i >= digs)
  1256.                 break;
  1257.             (void)update(FALSE);
  1258.             c = keystroke_raw8();
  1259.         } while (isbackspace(c) ||
  1260.             (isDigit(c) && base >= 10) ||
  1261.             (base == 8 && c < '8') ||
  1262.             (base == 16 && (c >= 'a' && c <= 'f')) ||
  1263.             (base == 16 && (c >= 'A' && c <= 'F')));
  1264.     }
  1265.  
  1266.     mlerase();
  1267.     /* did we get any digits at all? */
  1268.     if (i > 0) {
  1269.         if (ABORTED(c)) /* ESC gets us out painlessly */
  1270.             return ABORT;
  1271.         if (i < digs) /* any other character will be pushed back */
  1272.             unkeystroke(c);
  1273.         /* the accumulated value gets inserted */
  1274.         c = (num > 255) ? 255 : num;
  1275.     }
  1276.     if (c == '\n') {
  1277.         do {
  1278.             s = lnewline();
  1279.         } while (s==TRUE && --n);
  1280.         return s;
  1281.     } else  {
  1282.         return linsert(n, c);
  1283.     }
  1284. }
  1285.  
  1286. #if OPT_EVAL
  1287. char *
  1288. current_modename(void)
  1289. {
  1290.     switch (savedmode) {
  1291.         default:
  1292.             return "command";
  1293.         case INSERT:
  1294.             return "insert";
  1295.         case OVERWRITE:
  1296.             return "overwrite";
  1297.         case REPLACECHAR:
  1298.             return "replace";
  1299.     }
  1300. }
  1301. #endif
  1302.