summaryrefslogtreecommitdiffstats
path: root/smenu.c
diff options
context:
space:
mode:
Diffstat (limited to 'smenu.c')
-rw-r--r--smenu.c3737
1 files changed, 3737 insertions, 0 deletions
diff --git a/smenu.c b/smenu.c
new file mode 100644
index 0000000..dcad3d2
--- /dev/null
+++ b/smenu.c
@@ -0,0 +1,3737 @@
+/* ################################################################### */
+/* Copyright 2015 - Pierre Gentile */
+/* */
+/* This Software is licensed under the GPL licensed Version 2, */
+/* please read http://www.gnu.org/copyleft/gpl.html */
+/* */
+/* you can redistribute it and/or modify it under the terms of the GNU */
+/* General Public License as published by the Free Software */
+/* Foundation; either version 2 of the License. */
+/* */
+/* This software is distributed in the hope that it will be useful, */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU */
+/* General Public License for more details. */
+/* ################################################################### */
+
+#define CHARSCHUNK 8
+#define WORDSCHUNK 8
+#define COLSCHUNK 16
+
+#define _XOPEN_SOURCE 600
+#include <termios.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <ctype.h>
+#include <string.h>
+#include <unistd.h>
+#include <locale.h>
+#include <langinfo.h>
+#include <term.h>
+#include <errno.h>
+#include <wchar.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include "smenu.h"
+
+int bar_color; /* scrollbar color */
+int count = 0; /* number of words read from stdin */
+int current; /* index the current selection *
+ * (under the cursor) */
+int new_current; /* final current position, (used in *
+ * search function) */
+int *line_nb_of_word_a; /* array containig the line number *
+ * (from 0) of each word read */
+int *first_word_in_line_a; /* array containing the index of *
+ * the first word of each lines */
+int search_mode = 0; /* 1 if in search mode else 0 */
+int help_mode = 0; /* 1 if help is display else 0 */
+
+int (*my_isprint) (int);
+
+static int delims_cmp(const void *a, const void *b);
+
+/* UTF-8 usefull symbols */
+/* """"""""""""""""""""" */
+char *sbar_broken_line = "\xc2\xa6"; /* broken_bar */
+char *sbar_arr_left = "\xe2\x86\x90"; /* leftwards_arrow */
+char *sbar_arr_right = "\xe2\x86\x92"; /* rightwards_arrow */
+
+char *sbar_line = "\xe2\x94\x82"; /* box_drawings_light_vertical */
+char *sbar_top = "\xe2\x94\x90"; /* box_drawings_light_down_and_left */
+char *sbar_down = "\xe2\x94\x98"; /* box_drawings_light_up_and_left */
+char *sbar_curs = "\xe2\x95\x91"; /* box_drawings_double_vertical */
+char *sbar_arr_up = "\xe2\x96\xb2"; /* black_up_pointing_triangle */
+char *sbar_arr_down = "\xe2\x96\xbc"; /* black_down_pointing_triangle */
+
+/* Locale informations */
+/* """"""""""""""""""" */
+struct langinfo_s
+{
+ int utf8; /* charset is UTF-8 */
+ size_t bits; /* number of bits in the charset */
+};
+
+struct charsetinfo_s
+{
+ char *name; /* canonical name of the allowed charset */
+ size_t bits; /* number of bits in this charset */
+};
+
+struct toggle_s
+{
+ int del_line; /* 1 if the clean option is set (-d) else 0 */
+ int enter_val_in_search; /* 1 if ENTER validate in search mode else 0 */
+ int no_scrollbar; /* 1 to disable the scrollbar display else 0 */
+ int blank_nonprintable; /* 1 to try to display non-blanks in *
+ * symbolic form else 0 */
+};
+
+/* Mapping of supported charset and the number of bits used in them */
+/* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
+charsetinfo_t all_supported_charsets[] = {
+ {"UTF-8", 8},
+ {"ANSI_X3.4-1968", 7},
+ {"ANSI_X3.4-1986", 7},
+ {"ISO_646.BASIC", 7},
+ {"ISO_646.IRV", 7},
+ {"ISO-8859-1", 8},
+ {"ISO-8859-2", 8},
+ {"ISO-8859-3", 8},
+ {"ISO-8859-4", 8},
+ {"ISO-8859-5", 8},
+ {"ISO-8859-6", 8},
+ {"ISO-8859-7", 8},
+ {"ISO-8859-8", 8},
+ {"ISO-8859-9", 8},
+ {"ISO-8859-10", 8},
+ {"ISO-8859-11", 8},
+ {"ISO-8859-12", 8},
+ {"ISO-8859-13", 8},
+ {"ISO-8859-14", 8},
+ {"ISO-8859-15", 8},
+ {"ISO-8859-16", 8},
+ {"CP1252", 8},
+ {0, 0}
+};
+
+/* Variables used in signal handlers */
+/* """"""""""""""""""""""""""""""""" */
+volatile sig_atomic_t got_winch = 0;
+volatile sig_atomic_t got_winch_alrm = 0;
+volatile sig_atomic_t got_search_alrm = 0;
+volatile sig_atomic_t got_help_alrm = 0;
+
+/* Terminal setting variables */
+/* """""""""""""""""""""""""" */
+struct termios new_in_attrs;
+struct termios old_in_attrs;
+
+/* Interval timers used */
+/* """""""""""""""""""" */
+struct itimerval search_itv;
+struct itimerval hlp_itv;
+struct itimerval winch_itv;
+
+/* Structure containing some terminal characteristics */
+/* """""""""""""""""""""""""""""""""""""""""""""""""" */
+struct term_s
+{
+ int ncolumns; /* number of columns */
+ int nlines; /* number of lines */
+ int curs_column; /* current cursor column */
+ int curs_line; /* current cursor line */
+ int colors; /* number of available colors */
+
+ int has_setf; /* has set_foreground terminfo capability */
+ int has_setb; /* has set_background terminfo capability */
+ int has_hpa; /* has column_address terminfo capability */
+};
+
+/* structure describing a word */
+/* """"""""""""""""""""""""""" */
+struct word_s
+{
+ char *str; /* display string */
+ char *orig; /* NULL or original string if is had been *
+ * shortenend to being displayed or altered *
+ * by is expansion. */
+ int is_last; /* 1 if the word it the last of a line */
+ int start, end, mbytes; /* start/end absolute horizontal word position *
+ * on the screen *
+ * mbytes number of multibytes to display */
+};
+
+/* structure describing the window in which we will be able to select a word */
+/* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
+struct win_s
+{
+ int start, end;
+ int count;
+ int first_column;
+ size_t cur_line;
+ int asked_max_lines, max_lines, max_cols;
+ int col_sep;
+ int tab_mode;
+ int col_mode;
+ int wide_columns;
+};
+
+/* *************************************** */
+/* Ternary Search Tree specific structures */
+/* *************************************** */
+
+/* Ternary node structure */
+/* """""""""""""""""""""" */
+struct tst_node_s
+{
+ wchar_t splitchar;
+
+ tst_node_t *lokid, *eqkid, *hikid;
+ void *data;
+};
+
+tst_node_t *root;
+
+/* ******************************* */
+/* Linked list specific structures */
+/* ******************************* */
+
+/* linked list node structure */
+/* """""""""""""""""""""""""" */
+struct ll_node_s
+{
+ void *data;
+ struct ll_node_s *next;
+};
+
+/* Linked List structure */
+/* """"""""""""""""""""" */
+struct ll_s
+{
+ ll_node_t *head;
+ ll_node_t *tail;
+ size_t len;
+};
+
+/* ************** */
+/* Help functions */
+/* ************** */
+
+/* ====================== */
+/* Usage display and exit */
+/* ====================== */
+void
+usage(char *prog)
+{
+ fprintf(stderr, "Usage: %s [-h] [-n lines] [-c cols] [-s pattern] ", prog);
+ fprintf(stderr, "[-m message] \\\n [-w] [-d] [-t] [-e] [-b] [-g]\n");
+ fprintf(stderr, "\nThis is a filter that gets words from stdin ");
+ fprintf(stderr, "and outputs the\n");
+ fprintf(stderr, "selected word (or nothing) on stdout.\n");
+ fprintf(stderr, "A selection window is displayed on /dev/tty ");
+ fprintf(stderr, "to ease that.\n");
+ fprintf(stderr, "\n-h displays this help.\n");
+ fprintf(stderr, "-n sets the number of lines in the selection window.\n");
+ fprintf(stderr, "-c sets the number of columns in the selection window. ");
+ fprintf(stderr, "implies -t.\n");
+ fprintf(stderr, "-s sets the initial cursor position (read the manual for ");
+ fprintf(stderr, "details).\n");
+ fprintf(stderr, "-m displays a one-line message above the window\n");
+ fprintf(stderr, "-w uses all the terminal width for the columns.\n");
+ fprintf(stderr, "-d deletes the selection window on exit.\n");
+ fprintf(stderr, "-t tabulates the items in the selection window.\n");
+ fprintf(stderr,
+ "-e enables ENTER to validate the selection even "
+ "in search mode.\n");
+ fprintf(stderr, "-b displays the non printable characters as space.\n");
+ fprintf(stderr, "-g separates columns with '|' in tabulate mode.\n");
+ fprintf(stderr, "-q prevents the scrollbar display.\n");
+ fprintf(stderr, "-V displays the current version of this program.\n");
+ fprintf(stderr, "\nNavigation keys are:\n");
+ fprintf(stderr, " Left/Down/Up/Right arrows or h/j/k/l\n");
+ fprintf(stderr, " Home/End\n");
+ fprintf(stderr, " SPACE to search for the next match of a previously\n");
+ fprintf(stderr, " entered search prefix, if any, see below\n");
+ fprintf(stderr, "\nExit key without output: q\n");
+ fprintf(stderr, "Selection key : ENTER\n");
+ fprintf(stderr, "Cancel key : ESC\n");
+ fprintf(stderr, "Search key : / or CTRL-F\n");
+ fprintf(stderr, "\nThe search key activates a timed search mode in which\n");
+ fprintf(stderr, "you can enter the first letters of the searched word.\n");
+ fprintf(stderr, "When entering this mode you have 7s to start typing\n");
+ fprintf(stderr, "and each entered letter gives you 5 more seconds before\n");
+ fprintf(stderr, "the timeout. After that the search mode is ended.\n");
+ fprintf(stderr, "Notes:\n");
+ fprintf(stderr, "- the timer can be cancelled by pressing ESC.\n");
+ fprintf(stderr, "- a bad search letter can be removed with ");
+ fprintf(stderr, "CTRL-H or Backspace\n");
+ fprintf(stderr, "- ENTER and SPACE/n can be used even in search mode.\n");
+ fprintf(stderr, "\n(C) Pierre Gentile (2015).\n");
+
+ exit(EXIT_FAILURE);
+}
+
+/* ==================== */
+/* Help message display */
+/* ==================== */
+void
+help(void)
+{
+ tputs(save_cursor, 1, outch);
+ tputs(enter_reverse_mode, 1, outch);
+ fputs("HLP", stdout);
+ tputs(exit_attribute_mode, 1, outch);
+ fputs(" ", stdout);
+
+ fputs("Move:", stdout);
+ tputs(enter_bold_mode, 1, outch);
+ fputs("Arrows", stdout);
+ tputs(exit_attribute_mode, 1, outch);
+ fputs("|", stdout);
+ tputs(enter_bold_mode, 1, outch);
+ fputs("h", stdout);
+ tputs(exit_attribute_mode, 1, outch);
+ fputs("/", stdout);
+ tputs(enter_bold_mode, 1, outch);
+ fputs("j", stdout);
+ tputs(exit_attribute_mode, 1, outch);
+ fputs("/", stdout);
+ tputs(enter_bold_mode, 1, outch);
+ fputs("k", stdout);
+ tputs(exit_attribute_mode, 1, outch);
+ fputs("/", stdout);
+ tputs(enter_bold_mode, 1, outch);
+ fputs("l", stdout);
+ tputs(exit_attribute_mode, 1, outch);
+ fputs(",", stdout);
+ tputs(enter_bold_mode, 1, outch);
+ fputs("PgUp", stdout);
+ tputs(exit_attribute_mode, 1, outch);
+ fputs("/", stdout);
+ tputs(enter_bold_mode, 1, outch);
+ fputs("PgDn", stdout);
+ tputs(exit_attribute_mode, 1, outch);
+ fputs("/", stdout);
+ tputs(enter_bold_mode, 1, outch);
+ fputs("Home", stdout);
+ tputs(exit_attribute_mode, 1, outch);
+ fputs("/", stdout);
+ tputs(enter_bold_mode, 1, outch);
+ fputs("End", stdout);
+ tputs(exit_attribute_mode, 1, outch);
+
+ fputs(" Cancel:", stdout);
+ tputs(enter_bold_mode, 1, outch);
+ fputs("q", stdout);
+ tputs(exit_attribute_mode, 1, outch);
+
+ fputs(" Confirm:", stdout);
+ tputs(enter_bold_mode, 1, outch);
+ fputs("CR", stdout);
+ tputs(exit_attribute_mode, 1, outch);
+
+ fputs(" Search:", stdout);
+ tputs(enter_bold_mode, 1, outch);
+ fputs("/", stdout);
+ tputs(exit_attribute_mode, 1, outch);
+ fputs("|", stdout);
+ tputs(enter_bold_mode, 1, outch);
+ fputs("^F", stdout);
+ tputs(exit_attribute_mode, 1, outch);
+ fputs("|", stdout);
+ tputs(enter_bold_mode, 1, outch);
+ fputs("SP", stdout);
+ tputs(exit_attribute_mode, 1, outch);
+ fputs("|", stdout);
+ tputs(enter_bold_mode, 1, outch);
+ fputs("n", stdout);
+ tputs(exit_attribute_mode, 1, outch);
+
+ tputs(clr_eol, 1, outch);
+ tputs(exit_attribute_mode, 1, outch);
+ tputs(restore_cursor, 1, outch);
+}
+
+/* ********************* */
+/* Linked List functions */
+/* ********************* */
+
+/* ==================================== */
+/* Function to create a new linked list */
+/* ==================================== */
+ll_t *
+ll_new(void)
+{
+ ll_t *ret = malloc(sizeof(ll_t));
+
+ if (ret)
+ ll_init(ret);
+ else
+ errno = ENOMEM;
+
+ return ret;
+}
+
+/* ======================== */
+/* initialize a linked list */
+/* ======================== */
+void
+ll_init(ll_t * list)
+{
+ list->head = NULL;
+ list->tail = NULL;
+ list->len = 0;
+}
+
+/* ==================================================== */
+/* allocate the space for a new node in the linked list */
+/* ==================================================== */
+ll_node_t *
+ll_new_node(void)
+{
+ ll_node_t *ret = malloc(sizeof(ll_node_t));
+
+ if (!ret)
+ errno = ENOMEM;
+
+ return ret;
+}
+
+/* ==================================================================== */
+/* Append a new node filled with its data at the end of the linked list */
+/* The user is responsible for the memory management of the data */
+/* ==================================================================== */
+int
+ll_append(ll_t * const list, void *const data)
+{
+ int ret = 1;
+ ll_node_t *node;
+
+ if (list)
+ {
+ node = ll_new_node();
+ if (node)
+ {
+ node->data = data;
+ node->next = NULL;
+
+ if (list->tail)
+ list->tail->next = node;
+ else
+ list->head = node;
+ list->tail = node;
+
+ ++list->len;
+ ret = 0;
+ }
+ }
+
+ return ret;
+}
+
+/* =========================================================================*/
+/* Find a node in the list containing data. Return the node pointer or NULL */
+/* if not found. */
+/* A comparison function must be provided to compare a and b (strcmp like). */
+/* =========================================================================*/
+ll_node_t *
+ll_find(ll_t * const list, void *const data,
+ int (*cmpfunc) (const void *a, const void *b))
+{
+ ll_node_t *node;
+
+ if (NULL == (node = list->head))
+ return NULL;
+
+ do
+ {
+ if (0 == cmpfunc(node->data, data))
+ return node;
+ }
+ while (NULL != (node = node->next));
+
+ return NULL;
+}
+
+/* ***************** */
+/* Utility functions */
+/* ***************** */
+
+/* =============================== */
+/* 7 bits aware version of isprint */
+/* =============================== */
+int
+isprint7(int i)
+{
+ return (i >= 0x20 && i <= 0x7e);
+}
+
+/* =============================== */
+/* 8 bits aware version of isprint */
+/* =============================== */
+int
+isprint8(int i)
+{
+ unsigned char c = i & (unsigned char) 0xff;
+
+ return (c >= 0x20 && c < 0x7f) || (c >= (unsigned char) 0xa0);
+}
+
+/* ************************* */
+/* Terminal utilty functions */
+/* ************************* */
+
+/* ===================================================================== */
+/* outch is a function version of putchar that can be passed to tputs as */
+/* a routine to call. */
+/* ===================================================================== */
+int
+outch(int c)
+{
+ putchar(c);
+ return (1);
+}
+
+/* ================================================ */
+/* Sets the terminal in non echo/non canonical mode */
+/* wait for at least one byte, no timeout. */
+/* ================================================ */
+void
+setup_term(int const fd)
+{
+ /* Save the terminal parameters and configure it in raw mode */
+ /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""" */
+ tcgetattr(fd, &old_in_attrs);
+
+ new_in_attrs.c_iflag = 0;
+ new_in_attrs.c_oflag = old_in_attrs.c_oflag;
+ new_in_attrs.c_cflag = old_in_attrs.c_cflag;
+ new_in_attrs.c_lflag = old_in_attrs.c_lflag & ~(ICANON | ECHO | ISIG);
+
+ new_in_attrs.c_cc[VMIN] = 1; /* wait for at lear 1 byte */
+ new_in_attrs.c_cc[VTIME] = 0; /* no timeout */
+
+ tcsetattr(fd, TCSANOW, &new_in_attrs);
+}
+
+/* ====================================== */
+/* Sets the terminal in its previous mode */
+/* ====================================== */
+void
+restore_term(int const fd)
+{
+ tcsetattr(fd, TCSANOW, &old_in_attrs);
+}
+
+/* =============================================== */
+/* Gets the terminal numbers of lines and columns */
+/* Assume that the TIOCGWINSZ, ioctl is available */
+/* Defaults to 80x24 */
+/* =============================================== */
+void
+get_terminal_size(int *const r, int *const c)
+{
+ struct winsize ws;
+
+ if (ioctl(0, TIOCGWINSZ, &ws) == 0)
+ {
+ *r = ws.ws_row;
+ *c = ws.ws_col;
+ }
+ else
+ {
+ *r = tigetnum("lines");
+ *c = tigetnum("cols");
+
+ if (*r < 0 || *c < 0)
+ {
+ *r = 80;
+ *c = 24;
+ }
+ }
+}
+
+/* ======================================================= */
+/* Gets cursor position the terminal */
+/* Assumes that the Escape sequence ESC [ 6 n is available */
+/* ======================================================= */
+int
+get_cursor_position(int *const r, int *const c)
+{
+ char buf[32];
+ int i = 0;
+
+ /* Report cursor location */
+ /* """""""""""""""""""""" */
+ if (write(1, "\x1b[6n", 4) != 4)
+ return -1;
+
+ /* Read the response: ESC [ rows ; cols R */
+ /* """""""""""""""""""""""""""""""""""""" */
+ while ((size_t) i < sizeof(buf) - 1)
+ {
+ if (read(0, buf + i, 1) != 1)
+ break;
+
+ if (buf[i] == 'R')
+ break;
+
+ i++;
+ }
+ buf[i] = '\0';
+
+ /* Parse it. */
+ /* """"""""" */
+ if (buf[0] != 0x1b || buf[1] != '[')
+ return -1;
+
+ if (sscanf(buf + 2, "%d;%d", r, c) != 2)
+ return -1;
+
+ return 1;
+}
+
+/* *********************************************** */
+/* Strings and multibyte strings utility functions */
+/* *********************************************** */
+
+/* ============================================================= */
+/* Removes all ANSI color codes from s and puts the result in d. */
+/* Memory space for d must have been allocated before. */
+/* ============================================================= */
+void
+strip_ansi_color(char *s, toggle_t * toggle)
+{
+ char *p = s;
+ long len = strlen(s);
+
+ while (*s != '\0')
+ {
+ /* Remove a sequence of \x1b[...m from s */
+ /* """"""""""""""""""""""""""""""""""""" */
+ if ((*s == 0x1b) && (*(s + 1) == '['))
+ {
+ while ((*s != '\0') && (*s++ != 'm'))
+ {
+ ; /* do nothing */
+ }
+ }
+ /* Convert a single \x1b in '.' */
+ /* """""""""""""""""""""""""""" */
+ else if (*s == 0x1b)
+ {
+ if (toggle->blank_nonprintable && len > 1)
+ *s++ = ' ';
+ else
+ *s++ = '.';
+ p++;
+ }
+ /* No ESC char, we can move on */
+ /* """"""""""""""""""""""""""" */
+ else
+ *p++ = *s++;
+ }
+ /* If the whole string has been stripped, leave a signe dot */
+ /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
+ if (s - p == len)
+ *p++ = '.';
+
+ *p = '\0';
+}
+
+/* ========================================================= */
+/* Decode the number of bytes taken by a character (UTF-8) */
+/* It is the length of the eading sequence of bits set to 1 */
+/* (Count Leading Ones) */
+/* ========================================================= */
+int
+count_leading_set_bits(unsigned char c)
+{
+ if (c >= 0xf0)
+ return 4;
+ else if (c >= 0xe0)
+ return 3;
+ else if (c >= 0xc2)
+ return 2;
+ else
+ return 1;
+}
+
+/* =============================================== */
+/* Is the string str2 a prefix of the string str1? */
+/* =============================================== */
+int
+strprefix(char *str1, char *str2)
+{
+ while (*str1 != '\0' && *str1 == *str2)
+ {
+ str1++;
+ str2++;
+ }
+
+ return *str2 == '\0';
+}
+
+/* ====================================================================== */
+/* Multibytes extraction of the prefix of n multibyte chars from a string */
+/* The destination string d must have been allocated before. */
+/* pos is updated to reflect the postion AFTER the prefix. */
+/* ====================================================================== */
+char *
+mb_strprefix(char *d, char *s, int n, int *pos)
+{
+ int i = 0;
+ int j = 0;
+
+ *pos = 0;
+
+ while (s[i] && j < n)
+ {
+ d[i] = s[i];
+ i++;
+ j++;
+ while (s[i] && (s[i] & 0xC0) == 0x80)
+ {
+ d[i] = s[i];
+ i++;
+ }
+ }
+
+ *pos = i;
+
+ d[i] = '\0';
+
+ return d;
+}
+
+/* ============================================================ */
+/* Converts a multibyte (UTF-8) char string to a wchar_t string */
+/* ============================================================ */
+wchar_t *
+mb_strtowcs(char *s)
+{
+ int converted = 0;
+ unsigned char *ch;
+ wchar_t *wptr, *w;
+ int size;
+
+ size = strlen(s);
+ w = malloc((size + 1) * sizeof(wchar_t));
+ w[0] = L'\0';
+
+ wptr = w;
+ for (ch = (unsigned char *) s; *ch; ch += converted)
+ {
+ if ((converted = mbtowc(wptr, (char *) ch, 4)) > 0)
+ wptr++;
+ else
+ {
+ *wptr++ = (wchar_t) * ch;
+ converted = 1;
+ }
+ }
+
+ *wptr = L'\0';
+
+ return w;
+}
+
+/* *************************************************************** */
+/* Ternary Search Tree functions */
+/* Inspired by: https://www.cs.princeton.edu/~rs/strings/tstdemo.c */
+/* *************************************************************** */
+
+/* ====================================== */
+/* Ternary search tree insertion function */
+/* ====================================== */
+tst_node_t *
+tst_insert(tst_node_t * p, wchar_t * w, void *data)
+{
+ if (p == NULL)
+ {
+ p = (tst_node_t *) malloc(sizeof(struct tst_node_s));
+ p->splitchar = *w;
+ p->lokid = p->eqkid = p->hikid = NULL;
+ p->data = NULL;
+ }
+
+ if (*w < p->splitchar)
+ p->lokid = tst_insert(p->lokid, w, data);
+ else if (*w == p->splitchar)
+ {
+ if (*w == L'\0')
+ {
+ p->data = data;
+ p->eqkid = NULL;
+ }
+ else
+ p->eqkid = tst_insert(p->eqkid, ++w, data);
+ }
+ else
+ p->hikid = tst_insert(p->hikid, w, data);
+
+ return (p);
+}
+
+/* ====================================== */
+/* Ternary search tree insertion function */
+/* user data aree not cleaned */
+/* ====================================== */
+void
+tst_cleanup(tst_node_t * p)
+{
+ if (p)
+ {
+ tst_cleanup(p->lokid);
+ if (p->splitchar)
+ tst_cleanup(p->eqkid);
+ tst_cleanup(p->hikid);
+ free(p);
+ }
+}
+
+/* ========================================================== */
+/* Recurvive traversal of a ternary tree. A callback function */
+/* is also called when a complete string is found */
+/* returns 1 if the callback function succeed (returned 1) at */
+/* least once */
+/* the first_call argument is for initializing the static */
+/* variable */
+/* ========================================================== */
+int
+tst_traverse(tst_node_t * p, int (*callback) (void *), int first_call)
+{
+ static int rc;
+
+ if (first_call)
+ rc = 0;
+
+ if (!p)
+ return 0;
+ tst_traverse(p->lokid, callback, 0);
+ if (p->splitchar != L'\0')
+ tst_traverse(p->eqkid, callback, 0);
+ else
+ rc += (*callback) (p->data);
+ tst_traverse(p->hikid, callback, 0);
+
+ return !!rc;
+}
+
+/* ============================================ */
+/* search a complete string in the Ternary tree */
+/* ============================================ */
+void *
+tst_search(tst_node_t * root, wchar_t * w)
+{
+ tst_node_t *p;
+
+ p = root;
+
+ while (p)
+ {
+ if (*w < p->splitchar)
+ p = p->lokid;
+ else if (*w == p->splitchar)
+ {
+ if (*w++ == L'\0')
+ return p->data;
+ p = p->eqkid;
+ }
+ else
+ p = p->hikid;
+ }
+
+ return NULL;
+}
+
+/* =============================================================== */
+/* search all srings begining with the same prefix */
+/* the callback function will be applied to each of theses strings */
+/* returns NULL if no string matched the prefix */
+/* =============================================================== */
+void *
+tst_prefix_search(tst_node_t * root, wchar_t * w, int (*callback) (void *))
+{
+ tst_node_t *p = root;
+ int len = wcslen(w);
+ int rc;
+
+ while (p)
+ {
+ if (*w < p->splitchar)
+ p = p->lokid;
+ else if (*w == p->splitchar)
+ {
+ len--;
+ if (*w++ == L'\0')
+ return p->data;
+ if (len == 0)
+ {
+ rc = tst_traverse(p->eqkid, callback, 1);
+ return (void *) (long) rc;
+ }
+ p = p->eqkid;
+ }
+ else
+ p = p->hikid;
+ }
+
+ return NULL;
+}
+
+/* ======================================================================= */
+/* callback function used by tst_traverse */
+/* iterates the linked list attached to the string containing the index of */
+/* the words in the input flow. each page number is then used to detemine */
+/* the lower page greater than the cursor position */
+/* ----------------------------------------------------------------------- */
+/* Requires new_current to be set to count - 1 at start */
+/* Updates new_current to the smallest greater position than current */
+/* ======================================================================= */
+int
+tst_cb(void *elem)
+{
+ size_t n = 0;
+ int rc = 0;
+
+ /* The data attached to the string in the tst is a linked list of position */
+ /* of the string in the the input flow, This list is naturally sorted */
+ /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
+ ll_t *list = (ll_t *) elem;
+
+ ll_node_t *node = list->head;
+
+ while (n++ < list->len)
+ {
+ int pos;
+
+ pos = *(int *) (node->data);
+
+ /* We already are at the last word, report the finding */
+ /* """"""""""""""""""""""""""""""""""""""""""""""""""" */
+ if (pos == count - 1)
+ return 1;
+
+ /* Only consider the indexes above the current cursor position */
+ /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
+ if (pos > current)
+ {
+ /* As the future new current index has been set to the hihgest possible */
+ /* value, each new possible position can only improve the estimation */
+ /* we se rc to 1 to mark that */
+ /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
+ if (pos < new_current)
+ {
+ new_current = pos;
+ rc = 1;
+ }
+ }
+
+ node = node->next;
+ }
+ return rc;
+}
+
+/* ======================================================================= */
+/* callback function used by tst_traverse */
+/* iterates the linked list attached to the string containing the index of */
+/* the words in the input flow. each page number is then used to detemine */
+/* the lower page greater than the cursor position */
+/* ----------------------------------------------------------------------- */
+/* This is a special version of tst_cb wich permit to find the first word. */
+/* ----------------------------------------------------------------------- */
+/* Requires new_current to be set to count - 1 at start */
+/* Updates new_current to the smallest greater position than current */
+/* ======================================================================= */
+int
+tst_cb_cli(void *elem)
+{
+ size_t n = 0;
+ int rc = 0;
+
+ /* The data attached to the string in the tst is a linked list of position */
+ /* of the string in the the input flow, This list is naturally sorted */
+ /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
+ ll_t *list = (ll_t *) elem;
+
+ ll_node_t *node = list->head;
+
+ while (n++ < list->len)
+ {
+ int pos;
+
+ pos = *(int *) (node->data);
+
+ /* We already are at the last word, report the finding */
+ /* """"""""""""""""""""""""""""""""""""""""""""""""""" */
+ if (pos == count - 1)
+ return 1;
+
+ /* Only consider the indexes above the current cursor position */
+ /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
+ if (pos >= current) /* Enable the search of the current word */
+ {
+ /* As the future new current index has been set to the hihgest possible */
+ /* value, each new possible position can only improve the estimation */
+ /* we se rc to 1 to mark that */
+ /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
+ if (pos < new_current)
+ {
+ new_current = pos;
+ rc = 1;
+ }
+ }
+
+ node = node->next;
+ }
+ return rc;
+}
+
+/* *************** */
+/* Input functions */
+/* *************** */
+
+/* ================================================= */
+/* Non delay reading of a scancode */
+/* updates a scancodes buffer aand return its length */
+/* in bytes */
+/* ================================================= */
+int
+get_scancode(unsigned char *s, int max)
+{
+ int c;
+ int i = 1;
+ struct termios original_ts, nowait_ts;
+
+ if ((c = getchar()) == EOF)
+ return 0;
+
+ /* initialize the string withe the first byte */
+ /* """""""""""""""""""""""""""""""""""""""""" */
+ memset(s, '\0', max);
+ s[0] = c;
+
+ /* Code 0x1b (ESC) found, proceed to check if additional codes */
+ /* are available */
+ /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
+ if (c == 0x1b || c > 0x80)
+ {
+ /* Save the terminal parameters and configure getchar() */
+ /* to return immediately */
+ /* """""""""""""""""""""""""""""""""""""""""""""""""""" */
+ tcgetattr(0, &original_ts);
+ nowait_ts = original_ts;
+ nowait_ts.c_lflag &= ~ISIG;
+ nowait_ts.c_cc[VMIN] = 0;
+ nowait_ts.c_cc[VTIME] = 0;
+ tcsetattr(0, TCSADRAIN, &nowait_ts);
+
+ /* Check if additional code is available after 0x1b */
+ /* """""""""""""""""""""""""""""""""""""""""""""""" */
+ if ((c = getchar()) != EOF)
+ {
+ s[1] = c;
+
+ i = 2;
+ while (i < max && (c = getchar()) != EOF)
+ s[i++] = c;
+ }
+ else
+ {
+ /* There isn't new code, this mean 0x1b came from ESC key pression */
+ /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
+ }
+
+ /* Restore the save terminal parameters */
+ /* """""""""""""""""""""""""""""""""""" */
+ tcsetattr(0, TCSADRAIN, &original_ts);
+ }
+
+ return i;
+}
+
+/* ===================================================================== */
+/* Get bytes from stdin. If the first byte is the leading character of a */
+/* multibyte one, the following ones a also read */
+/* Th count_leading_set_bits function is used to obten the number of */
+/* bytes of the character */
+/* ===================================================================== */
+int
+get_bytes(FILE * input, char *mb_buffer, ll_t * word_delims_list,
+ toggle_t * toggle, langinfo_t * langinfo)
+{
+ int byte;
+ int last = 0;
+ int n;
+
+ /* read the first byte */
+ /* """"""""""""""""""" */
+ byte = mb_buffer[last++] = fgetc(input);
+
+ if (byte == EOF)
+ return EOF;
+
+ /* Check if we need to read more bytes to form a sequence */
+ /* and put the number of bytes of the sequence in last. */
+ /* """""""""""""""""""""""""""""""""""""""""""""""""""""" */
+ if (langinfo->utf8 && ((n = count_leading_set_bits(byte)) > 1))
+ {
+ while (last < n && (byte = fgetc(input)) != EOF && (byte & 0xc0) == 0x80)
+ mb_buffer[last++] = byte;
+
+ if (byte == EOF)
+ return EOF;
+ }
+
+ /* Convert a well known byte (if alone in) into */
+ /* its canonical representation or into a dot */
+ /* when not printable. */
+ /* """""""""""""""""""""""""""""""""""""""""""" */
+ if (last == 1 && toggle->blank_nonprintable && byte != 0x1b && byte != EOF
+ && !my_isprint(byte)
+ && ll_find(word_delims_list, mb_buffer, delims_cmp) == NULL)
+ {
+ mb_buffer[0] = ' ';
+ mb_buffer[1] = '\0';
+ }
+ else
+ mb_buffer[last] = '\0';
+
+ return byte;
+}
+
+/* ==========================================================================*/
+/* expand the string str by replacing all its embedded special characters by */
+/* their corresponding escape sequence */
+/* dest must be long enough to contain the expanded string */
+/* ==========================================================================*/
+int
+expand(char *src, char *dest, langinfo_t * langinfo)
+{
+ char c;
+ int n;
+ int all_spaces = 1;
+ char *ptr = dest;
+ size_t len = 0;
+
+ while ((c = *(src++)))
+ {
+ if (c != ' ')
+ all_spaces = 0;
+
+ /* UTF-8 codepoints take more than on character */
+ /* """""""""""""""""""""""""""""""""""""""""""" */
+ if ((n = count_leading_set_bits(c)) > 1)
+ {
+ if (langinfo->utf8)
+ /* If the locale is UTF-8 aware, copy src into ptr. */
+ /* """""""""""""""""""""""""""""""""""""""""""""""" */
+ do
+ {