diff options
Diffstat (limited to 'smenu.c')
-rw-r--r-- | smenu.c | 3737 |
1 files changed, 3737 insertions, 0 deletions
@@ -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 + { |