"Fossies" - the Fresh Open Source Software Archive

Member "tin-2.4.4/src/getline.c" (20 Nov 2019, 18094 Bytes) of package /linux/misc/tin-2.4.4.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "getline.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 2.4.3_vs_2.4.4.

    1 /*
    2  *  Project   : tin - a Usenet reader
    3  *  Module    : getline.c
    4  *  Author    : Chris Thewalt & Iain Lea
    5  *  Created   : 1991-11-09
    6  *  Updated   : 2019-01-15
    7  *  Notes     : emacs style line editing input package.
    8  *  Copyright : (c) Copyright 1991-99 by Chris Thewalt & Iain Lea
    9  *              Permission to use, copy, modify, and distribute this
   10  *              software for any purpose and without fee is hereby
   11  *              granted, provided that the above copyright notices
   12  *              appear in all copies and that both the copyright
   13  *              notice and this permission notice appear in supporting
   14  *              documentation. This software is provided "as is" without
   15  *              express or implied warranty.
   16  */
   17 
   18 #ifndef TIN_H
   19 #   include "tin.h"
   20 #endif /* !TIN_H */
   21 #ifndef TCURSES_H
   22 #   include "tcurses.h"
   23 #endif /* !TCURSES_H */
   24 
   25 #define BUF_SIZE    1024
   26 #define TAB_SIZE        4
   27 
   28 #define CTRL_A  '\001'
   29 #define CTRL_B  '\002'
   30 #define CTRL_D  '\004'
   31 #define CTRL_E  '\005'
   32 #define CTRL_F  '\006'
   33 #define CTRL_H  '\010'
   34 #define CTRL_K  '\013'
   35 #define CTRL_L  '\014'
   36 #define CTRL_R  '\022'
   37 #define CTRL_N  '\016'
   38 #define CTRL_P  '\020'
   39 #define CTRL_U  '\025'
   40 #define CTRL_W  '\027'
   41 #define TAB '\t'
   42 #define DEL '\177'
   43 
   44 #if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
   45     static wchar_t gl_buf[BUF_SIZE];    /* wide-character input buffer */
   46     static char buf[BUF_SIZE];
   47 #else
   48     static char gl_buf[BUF_SIZE];   /* input buffer */
   49 #endif /* MULTIBYTE_ABLE && !NO_LOCALE */
   50 static const char *gl_prompt;   /* to save the prompt string */
   51 static int gl_width = 0;    /* net size available for input */
   52 static int gl_pos, gl_cnt = 0;  /* position and size of input */
   53 static t_bool is_passwd;
   54 
   55 /*
   56  * local prototypes
   57  */
   58 #if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
   59     static void gl_addwchar(wint_t wc);
   60     static int gl_tab(wchar_t *wbuf, int offset, int *loc);
   61 #else
   62     static void gl_addchar(int c);
   63     static int gl_tab(char *buf, int offset, int *loc);
   64 #endif /* MULTIBYTE_ABLE && !NO_LOCALE */
   65 static void gl_del(int loc);
   66 static void gl_fixup(int change, int cursor);
   67 static void gl_newline(int w);
   68 static void gl_kill(void);
   69 static void gl_kill_back_word(void);
   70 static void hist_add(int w);
   71 static void hist_next(int w);
   72 static void hist_prev(int w);
   73 
   74 #if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
   75     static int (*gl_in_hook) (wchar_t *) = 0;
   76     static int (*gl_out_hook) (wchar_t *) = 0;
   77     static int (*gl_tab_hook) (wchar_t *, int, int *) = gl_tab;
   78 #else
   79     static int (*gl_in_hook) (char *) = 0;
   80     static int (*gl_out_hook) (char *) = 0;
   81     static int (*gl_tab_hook) (char *, int, int *) = gl_tab;
   82 #endif /* MULTIBYTE_ABLE && !NO_LOCALE */
   83 
   84 char *
   85 tin_getline(
   86     const char *prompt,
   87     int number_only,    /* 1=positive numbers only, 2=negative too */
   88     const char *str,
   89     int max_chars,
   90     t_bool passwd,
   91     int which_hist)
   92 {
   93     int c, i, loc, tmp, gl_max;
   94     char *tprompt;
   95 #if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
   96     wint_t wc;
   97 #else
   98     char *buf = gl_buf;
   99 #endif /* MULTIBYTE_ABLE && !NO_LOCALE */
  100 
  101     input_context = cGetline;
  102 
  103     is_passwd = passwd;
  104 
  105     set_xclick_off();
  106     if (prompt == NULL)
  107         prompt = "";
  108 
  109     tprompt = strunc(prompt, cCOLS - 6);
  110 
  111     gl_buf[0] = 0;      /* used as end of input indicator */
  112     gl_fixup(-1, 0);    /* this resets gl_fixup */
  113     gl_width = cCOLS - MIN(cCOLS - 6, strwidth(tprompt));
  114     gl_prompt = tprompt;
  115     gl_pos = gl_cnt = 0;
  116 
  117     if (max_chars == 0) {
  118         if (number_only)
  119             gl_max = 6;
  120         else
  121             gl_max = BUF_SIZE;
  122     } else
  123         gl_max = max_chars;
  124 
  125     my_fputs(tprompt, stdout);
  126     cursoron();
  127     my_flush();
  128 
  129     if (gl_in_hook) {
  130         loc = gl_in_hook(gl_buf);
  131         if (loc >= 0)
  132             gl_fixup(0, BUF_SIZE);
  133     }
  134 
  135     if (!cmd_line && gl_max == BUF_SIZE)
  136         CleartoEOLN();
  137 
  138 #if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
  139     if (str != NULL) {
  140         wchar_t *wbuf;
  141 
  142         if ((wbuf = char2wchar_t(str)) != NULL) {
  143             for (i = 0; wbuf[i]; i++)
  144                 gl_addwchar(wbuf[i]);
  145             free(wbuf);
  146         }
  147     }
  148 
  149     while ((wc = ReadWch()) != WEOF) {
  150         if ((gl_cnt < gl_max) && iswprint(wc)) {
  151             if (number_only) {
  152                 if (iswdigit(wc)) {
  153                     gl_addwchar(wc);
  154                 /* Minus */
  155                 } else if (number_only == 2 && gl_pos == 0 && wc == (wint_t) '-') {
  156                     gl_addwchar(wc);
  157                 } else {
  158                     ring_bell();
  159                 }
  160             } else
  161                 gl_addwchar(wc);
  162         } else {
  163             c = (int) wc;
  164             switch (wc) {
  165 #else
  166     if (str != NULL) {
  167         for (i = 0; str[i]; i++)
  168             gl_addchar(str[i]);
  169     }
  170 
  171     while ((c = ReadCh()) != EOF) {
  172         c &= 0xff;
  173         if ((gl_cnt < gl_max) && my_isprint(c)) {
  174             if (number_only) {
  175                 if (isdigit(c)) {
  176                     gl_addchar(c);
  177                 /* Minus */
  178                 } else if (number_only == 2 && gl_pos == 0 && c == '-') {
  179                     gl_addchar(c);
  180                 } else {
  181                     ring_bell();
  182                 }
  183             } else
  184                 gl_addchar(c);
  185         } else {
  186             switch (c) {
  187 #endif /* MULTIBYTE_ABLE && !NO_LOCALE */
  188                 case ESC:   /* abort */
  189 #ifdef HAVE_KEY_PREFIX
  190                 case KEY_PREFIX:
  191 #endif /* HAVE_KEY_PREFIX */
  192                     switch (get_arrow_key(c)) {
  193                         case KEYMAP_UP:
  194                         case KEYMAP_PAGE_UP:
  195                             hist_prev(which_hist);
  196                             break;
  197 
  198                         case KEYMAP_PAGE_DOWN:
  199                         case KEYMAP_DOWN:
  200                             hist_next(which_hist);
  201                             break;
  202 
  203                         case KEYMAP_RIGHT:
  204                             gl_fixup(-1, gl_pos + 1);
  205                             break;
  206 
  207                         case KEYMAP_LEFT:
  208                             gl_fixup(-1, gl_pos - 1);
  209                             break;
  210 
  211                         case KEYMAP_HOME:
  212                             gl_fixup(-1, 0);
  213                             break;
  214 
  215                         case KEYMAP_END:
  216                             gl_fixup(-1, gl_cnt);
  217                             break;
  218 
  219                         case KEYMAP_DEL:
  220                             gl_del(0);
  221                             break;
  222 
  223                         case KEYMAP_INS:
  224 #if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
  225                             gl_addwchar((wint_t) ' ');
  226 #else
  227                             gl_addchar(' ');
  228 #endif /* MULTIBYTE_ABLE && !NO_LOCALE */
  229                             break;
  230 
  231                         default:
  232                             input_context = cNone;
  233                             free(tprompt);
  234                             return NULL;
  235                     }
  236                     break;
  237 
  238                 case '\n':  /* newline */
  239                 case '\r':
  240                     gl_newline(which_hist);
  241 #if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
  242                     wcstombs(buf, gl_buf, BUF_SIZE - 1);
  243 #endif /* MULTIBYTE_ABLE && !NO_LOCALE */
  244                     input_context = cNone;
  245                     free(tprompt);
  246                     return buf;
  247 
  248                 case CTRL_A:
  249                     gl_fixup(-1, 0);
  250                     break;
  251 
  252                 case CTRL_B:
  253                     gl_fixup(-1, gl_pos - 1);
  254                     break;
  255 
  256                 case CTRL_D:
  257                     if (gl_cnt == 0) {
  258                         gl_buf[0] = 0;
  259                         my_fputc('\n', stdout);
  260 #if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
  261                         wcstombs(buf, gl_buf, BUF_SIZE - 1);
  262 #endif /* MULTIBYTE_ABLE && !NO_LOCALE */
  263                         input_context = cNone;
  264                         free(tprompt);
  265                         return buf;
  266                     } else
  267                         gl_del(0);
  268                     break;
  269 
  270                 case CTRL_E:
  271                     gl_fixup(-1, gl_cnt);
  272                     break;
  273 
  274                 case CTRL_F:
  275                     gl_fixup(-1, gl_pos + 1);
  276                     break;
  277 
  278                 case CTRL_H:
  279                 case DEL:
  280                     gl_del(-1);
  281                     break;
  282 
  283                 case TAB:
  284                     if (gl_tab_hook) {
  285                         tmp = gl_pos;
  286                         loc = gl_tab_hook(gl_buf, strlen(gl_prompt), &tmp);
  287                         if (loc >= 0 || tmp != gl_pos)
  288                             gl_fixup(loc, tmp);
  289                     }
  290                     break;
  291 
  292                 case CTRL_W:
  293                     gl_kill_back_word();
  294                     break;
  295 
  296                 case CTRL_U:
  297                     gl_fixup(-1, 0);
  298                     /* FALLTHROUGH */
  299                 case CTRL_K:
  300                     gl_kill();
  301                     break;
  302 
  303                 case CTRL_L:
  304                 case CTRL_R:
  305                     gl_redraw();
  306                     break;
  307 
  308                 case CTRL_N:
  309                     hist_next(which_hist);
  310                     break;
  311 
  312                 case CTRL_P:
  313                     hist_prev(which_hist);
  314                     break;
  315 
  316                 default:
  317                     ring_bell();
  318                     break;
  319             }
  320         }
  321     }
  322 #if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
  323     wcstombs(buf, gl_buf, BUF_SIZE - 1);
  324 #endif /* MULTIBYTE_ABLE && !NO_LOCALE */
  325     input_context = cNone;
  326     free(tprompt);
  327     return buf;
  328 }
  329 
  330 
  331 /*
  332  * adds the character c to the input buffer at current location if
  333  * the character is in the allowed template of characters
  334  */
  335 static void
  336 #if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
  337 gl_addwchar(
  338     wint_t wc)
  339 #else
  340 gl_addchar(
  341     int c)
  342 #endif /* MULTIBYTE_ABLE && !NO_LOCALE */
  343 {
  344     int i;
  345 
  346     /*
  347      * Crashing is always the worst solution IMHO. So as a quick hack,
  348      * ignore characters silently, if buffer is full. To allow a final
  349      * newline, leave space for one more character. Just a hack too.
  350      * This was the original code:
  351      *
  352     if (gl_cnt >= BUF_SIZE - 1) {
  353         error_message(2, "tin_getline: input buffer overflow");
  354         giveup();
  355     }
  356      */
  357     if (gl_cnt >= BUF_SIZE - 2)
  358         return;
  359 
  360     for (i = gl_cnt; i >= gl_pos; i--)
  361         gl_buf[i + 1] = gl_buf[i];
  362 
  363 #if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
  364     gl_buf[gl_pos] = (wchar_t) wc;
  365 #else
  366     gl_buf[gl_pos] = c;
  367 #endif /* MULTIBYTE_ABLE && !NO_LOCALE */
  368     gl_fixup(gl_pos, gl_pos + 1);
  369 }
  370 
  371 
  372 /*
  373  * Cleans up entire line before returning to caller. A \n is appended.
  374  * If line longer than screen, we redraw starting at beginning
  375  */
  376 static void
  377 gl_newline(
  378     int w)
  379 {
  380     int change = gl_cnt;
  381     int len = gl_cnt;
  382     int loc = gl_width - 5; /* shifts line back to start position */
  383 
  384     if (gl_cnt >= BUF_SIZE - 1) {
  385         /*
  386          * Like above: avoid crashing if possible. gl_addchar() now
  387          * leaves one space left for the newline, so this part of the
  388          * code should never be reached. A proper implementation is
  389          * desirable though.
  390          */
  391         error_message(2, "tin_getline: input buffer overflow");
  392         free(tin_progname);
  393         giveup();
  394     }
  395     hist_add(w);        /* only adds if nonblank */
  396     if (gl_out_hook) {
  397         change = gl_out_hook(gl_buf);
  398 #if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
  399         len = wcslen(gl_buf);
  400 #else
  401         len = strlen(gl_buf);
  402 #endif /* MULTIBYTE_ABLE && !NO_LOCALE */
  403     }
  404     if (loc > len)
  405         loc = len;
  406     gl_fixup(change, loc);  /* must do this before appending \n */
  407 #if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
  408     gl_buf[len] = (wchar_t) '\0';
  409 #else
  410     gl_buf[len] = '\0';
  411 #endif /* MULTIBYTE_ABLE && !NO_LOCALE */
  412 }
  413 
  414 
  415 /*
  416  * Delete a character. The loc variable can be:
  417  *    -1 : delete character to left of cursor
  418  *     0 : delete character under cursor
  419  */
  420 static void
  421 gl_del(
  422     int loc)
  423 {
  424     int i;
  425 
  426     if ((loc == -1 && gl_pos > 0) || (loc == 0 && gl_pos < gl_cnt)) {
  427         for (i = gl_pos + loc; i < gl_cnt; i++)
  428             gl_buf[i] = gl_buf[i + 1];
  429         gl_fixup(gl_pos + loc, gl_pos + loc);
  430     } else
  431         ring_bell();
  432 }
  433 
  434 
  435 /*
  436  * delete from current position to the end of line
  437  */
  438 static void
  439 gl_kill(
  440     void)
  441 {
  442     if (gl_pos < gl_cnt) {
  443 #if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
  444         gl_buf[gl_pos] = (wchar_t) '\0';
  445 #else
  446         gl_buf[gl_pos] = '\0';
  447 #endif /* MULTIBYTE_ABLE && !NO_LOCALE */
  448         gl_fixup(gl_pos, gl_pos);
  449     } else
  450         ring_bell();
  451 }
  452 
  453 
  454 /*
  455  * delete from the start of current or last word to current position
  456  */
  457 static void
  458 gl_kill_back_word(
  459     void)
  460 {
  461     int i, j, cur;
  462 
  463 #if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
  464     /* delete spaces */
  465     for (i = gl_pos - 1; i >= 0 && iswspace((wint_t) gl_buf[i]); --i)
  466         ;
  467 
  468     /* delete not alnum characters but graph characters */
  469     for (; i >= 0 && iswgraph((wint_t) gl_buf[i]) && !iswalnum((wint_t) gl_buf[i]); --i)
  470         ;
  471 
  472     /* delete all graph characters except '/' */
  473     for (; i >= 0 && gl_buf[i] != (wchar_t) '/' && iswgraph((wint_t) gl_buf[i]); --i)
  474         ;
  475 #else
  476     /* delete spaces */
  477     for (i = gl_pos - 1; i >= 0 && isspace((int) gl_buf[i]); --i)
  478         ;
  479 
  480     /* delete not alnum characters but graph characters */
  481     for (; i >= 0 && isgraph((int) gl_buf[i]) && !isalnum((int) gl_buf[i]); --i)
  482         ;
  483 
  484     /* delete all graph characters except '/' */
  485     for (; i >= 0 && gl_buf[i] != '/' && isgraph((int) gl_buf[i]); --i)
  486         ;
  487 #endif /* MULTIBYTE_ABLE && !NO_LOCALE */
  488 
  489     i++;
  490     if (i != gl_pos) {
  491         j = i;
  492         cur = gl_pos;
  493         gl_fixup(-1, i);
  494 
  495         while (gl_buf[cur])
  496             gl_buf[j++] = gl_buf[cur++];
  497 #if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
  498         gl_buf[j] = (wchar_t) '\0';
  499 #else
  500         gl_buf[j] = '\0';
  501 #endif /* MULTIBYTE_ABLE && !NO_LOCALE */
  502         gl_fixup(i, i);
  503     } else
  504         ring_bell();
  505 }
  506 
  507 
  508 /*
  509  * emit a newline, reset and redraw prompt and current input line
  510  */
  511 void
  512 gl_redraw(
  513     void)
  514 {
  515     clear_message();
  516     my_fputs(gl_prompt, stdout);
  517     gl_pos = 0;
  518     gl_fixup(0, BUF_SIZE);
  519     cursoron();
  520 }
  521 
  522 
  523 /*
  524  * This function is used both for redrawing when input changes or for
  525  * moving within the input line. The parameters are:
  526  *   change : the index of the start of changes in the input buffer,
  527  *            with -1 indicating no changes.
  528  *   cursor : the desired location of the cursor after the call.
  529  *            A value of BUF_SIZE can be used to indicate the cursor
  530  *            should move just past the end of the input line.
  531  */
  532 static void
  533 gl_fixup(
  534     int change,
  535     int cursor)
  536 {
  537     static int gl_shift;    /* index of first on screen character */
  538     static int off_right;   /* true if more text right of screen */
  539     static int off_left;    /* true if more text left of screen */
  540     int left = 0, right = -1;   /* bounds for redraw */
  541     int pad;        /* how much to erase at end of line */
  542     int backup;     /* how far to backup before fixing */
  543     int new_shift;      /* value of shift based on cursor */
  544     int extra;      /* adjusts when shift (scroll) happens */
  545     int i;
  546     /* FIXME: there are some small problems if SCROLL >= gl_width - 2 */
  547     int SCROLL = MIN(30, gl_width - 3);
  548 
  549     if (change == -1 && cursor == 0 && gl_buf[0] == 0) {    /* reset */
  550         gl_shift = off_right = off_left = 0;
  551         return;
  552     }
  553     pad = (off_right) ? gl_width - 1 : gl_cnt - gl_shift;   /* old length */
  554     backup = gl_pos - gl_shift;
  555     if (change >= 0) {
  556 #if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
  557         gl_cnt = wcslen(gl_buf);
  558 #else
  559         gl_cnt = strlen(gl_buf);
  560 #endif /* MULTIBYTE_ABLE && !NO_LOCALE */
  561         if (change > gl_cnt)
  562             change = gl_cnt;
  563     }
  564     if (cursor > gl_cnt) {
  565         if (cursor != BUF_SIZE)     /* BUF_SIZE means end of line */
  566             ring_bell();
  567         cursor = gl_cnt;
  568     }
  569     if (cursor < 0) {
  570         ring_bell();
  571         cursor = 0;
  572     }
  573     if (!is_passwd) {
  574         if (off_right || (off_left && (cursor < gl_shift + gl_width - SCROLL / 2)))
  575             extra = 2;  /* shift the scrolling boundary */
  576         else
  577             extra = 0;
  578         new_shift = cursor + extra + SCROLL - gl_width;
  579         if (new_shift > 0) {
  580             new_shift /= SCROLL;
  581             new_shift *= SCROLL;
  582         } else
  583             new_shift = 0;
  584         if (new_shift != gl_shift) {    /* scroll occurs */
  585             gl_shift = new_shift;
  586             off_left = (gl_shift) ? 1 : 0;
  587             off_right = (gl_cnt > gl_shift + gl_width - 1) ? 1 : 0;
  588             left = gl_shift;
  589             right = (off_right) ? gl_shift + gl_width - 2 : gl_cnt;
  590         } else if (change >= 0) {   /* no scroll, but text changed */
  591             if (change < gl_shift + off_left)
  592                 left = gl_shift;
  593             else {
  594                 left = change;
  595                 backup = gl_pos - change;
  596             }
  597             off_right = (gl_cnt > gl_shift + gl_width - 1) ? 1 : 0;
  598             right = (off_right) ? gl_shift + gl_width - 2 : gl_cnt;
  599         }
  600         pad -= (off_right ? gl_width - 1 : gl_cnt - gl_shift);
  601         pad = (pad < 0) ? 0 : pad;
  602         if (left <= right) {    /* clean up screen */
  603             for (i = 0; i < backup; i++)
  604                 my_fputc('\b', stdout);
  605             if (left == gl_shift && off_left) {
  606                 my_fputc('$', stdout);
  607                 left++;
  608             }
  609             for (i = left; i < right; i++) {
  610 #if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
  611                 my_fputwc((wint_t) gl_buf[i], stdout);
  612 #else
  613                 my_fputc(gl_buf[i], stdout);
  614 #endif /* MULTIBYTE_ABLE && !NO_LOCALE */
  615             }
  616             if (off_right) {
  617                 my_fputc('$', stdout);
  618                 gl_pos = right + 1;
  619             } else {
  620                 for (i = 0; i < pad; i++)   /* erase remains of prev line */
  621                     my_fputc(' ', stdout);
  622                 gl_pos = right + pad;
  623             }
  624         }
  625         i = gl_pos - cursor;    /* move to final cursor location */
  626         if (i > 0) {
  627             while (i--)
  628                 my_fputc('\b', stdout);
  629         } else {
  630             for (i = gl_pos; i < cursor; i++) {
  631 #if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
  632                 my_fputwc((wint_t) gl_buf[i], stdout);
  633 #else
  634                 my_fputc(gl_buf[i], stdout);
  635 #endif /* MULTIBYTE_ABLE && !NO_LOCALE */
  636             }
  637         }
  638         my_flush();
  639     }
  640     gl_pos = cursor;
  641 }
  642 
  643 
  644 /*
  645  * default tab handler, acts like tabstops every TAB_SIZE cols
  646  */
  647 static int
  648 gl_tab(
  649 #if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
  650     wchar_t *wbuf,
  651 #else
  652     char *buf,
  653 #endif /* MULTIBYTE_ABLE && !NO_LOCALE */
  654     int offset,
  655     int *loc)
  656 {
  657     int i, count, len;
  658 
  659 #if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
  660     len = wcslen(wbuf);
  661     count = TAB_SIZE - (offset + *loc) % TAB_SIZE;
  662     for (i = len; i >= *loc; i--)
  663         wbuf[i + count] = wbuf[i];
  664     for (i = 0; i < count; i++)
  665         wbuf[*loc + i] = (wchar_t) ' ';
  666 #else
  667     len = strlen(buf);
  668     count = TAB_SIZE - (offset + *loc) % TAB_SIZE;
  669     for (i = len; i >= *loc; i--)
  670         buf[i + count] = buf[i];
  671     for (i = 0; i < count; i++)
  672         buf[*loc + i] = ' ';
  673 #endif /* MULTIBYTE_ABLE && !NO_LOCALE */
  674     i = *loc;
  675     *loc = i + count;
  676     return i;
  677 }
  678 
  679 
  680 static void
  681 hist_add(
  682     int w)
  683 {
  684     char *p;
  685     char *tmp;
  686 
  687     if (w == HIST_NONE)
  688         return;
  689 
  690 #if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
  691     if ((tmp = wchar_t2char(gl_buf)) == NULL)
  692         return;
  693 #else
  694     tmp = my_strdup(gl_buf);
  695 #endif /* MULTIBYTE_ABLE && !NO_LOCALE */
  696 
  697     p = tmp;
  698     while (*p == ' ' || *p == '\t')     /* only save nonblank line */
  699         p++;
  700 
  701     if (*p) {
  702         input_history[w][hist_last[w]] = tmp;
  703         hist_last[w] = (hist_last[w] + 1) % HIST_SIZE;
  704         FreeAndNull(input_history[w][hist_last[w]]);    /* erase next location */
  705     } else  /* we didn't need tmp, so free it */
  706         free(tmp);
  707 
  708     hist_pos[w] = hist_last[w];
  709 }
  710 
  711 
  712 /*
  713  * loads previous hist entry into input buffer, sticks on first
  714  */
  715 static void
  716 hist_prev(
  717     int w)
  718 {
  719     int next;
  720 #if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
  721     size_t size;
  722 #endif /* MULTIBYTE_ABLE && !NO_LOCALE */
  723 
  724     if (w == HIST_NONE)
  725         return;
  726 
  727     next = (hist_pos[w] - 1 + HIST_SIZE) % HIST_SIZE;
  728     if (next != hist_last[w]) {
  729         if (input_history[w][next]) {
  730             hist_pos[w] = next;
  731 #if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
  732             if ((size = mbstowcs(gl_buf, input_history[w][hist_pos[w]], BUF_SIZE - 1)) == (size_t) -1)
  733                 size = 0;
  734             gl_buf[size] = (wchar_t) '\0';
  735 #else
  736             strcpy(gl_buf, input_history[w][hist_pos[w]]);
  737 #endif /* MULTIBYTE_ABLE && !NO_LOCALE */
  738         } else
  739             ring_bell();
  740     } else
  741         ring_bell();
  742 
  743     if (gl_in_hook)
  744         gl_in_hook(gl_buf);
  745     gl_fixup(0, BUF_SIZE);
  746 }
  747 
  748 
  749 /*
  750  * loads next hist entry into input buffer, clears on last
  751  */
  752 static void
  753 hist_next(
  754     int w)
  755 {
  756     if (w == HIST_NONE)
  757         return;
  758 
  759     if (hist_pos[w] != hist_last[w]) {
  760         hist_pos[w] = (hist_pos[w] + 1) % HIST_SIZE;
  761         if (input_history[w][hist_pos[w]]) {
  762 #if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
  763             size_t size;
  764 
  765             if ((size = mbstowcs(gl_buf, input_history[w][hist_pos[w]], BUF_SIZE - 1)) == (size_t) -1)
  766                 size = 0;
  767             gl_buf[size] = (wchar_t) '\0';
  768 #else
  769             strcpy(gl_buf, input_history[w][hist_pos[w]]);
  770 #endif /* MULTIBYTE_ABLE && !NO_LOCALE */
  771         } else
  772             gl_buf[0] = 0;
  773     } else
  774         ring_bell();
  775 
  776     if (gl_in_hook)
  777         gl_in_hook(gl_buf);
  778     gl_fixup(0, BUF_SIZE);
  779 }