"Fossies" - the Fresh Open Source Software Archive

Member "tin-2.4.1/src/getline.c" (21 Nov 2016, 18194 Bytes) of archive /linux/misc/tin-2.4.1.tar.gz:


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.0_vs_2.4.1.

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