From b3564338dd2a7598cb35968d5c806a9e4b744c96 Mon Sep 17 00:00:00 2001 From: pgen Date: Sat, 29 Jul 2017 16:30:06 +0200 Subject: A giant commit because a lot of things have been rewritten/enhanced/fixed - Bump to version 0.9.10 * The words acquisition is rewritten and splitted in multiple phases * Add the capacity to (de)select rows an columns containing regexes * -i, -e, -C and -R can be used more than once and have a cumulative effect * Bugfix: -s is able to jump to non selectable words * Bugfix: The attributes are not overloaded when reading configuration files * Bugfix: the right bar is too far right in some cases. * Bugfix: the right shift indicator is omitted in some cases * Add a -x|-X option to set a timeout in seconds * Add a -f option to select an alternative configuration file * Add a -a option to set the attributes of the displayed elements * Add more controls in the selection parser * smenu can now read words directly from a file given as parameter * Rewrite the replace function used by -S/-I/-E with a more sed-like one * Searches should always consider the visual aspect of words (-S/-I/-E) --- .clang-format | 1 + ChangeLog | 16 + Makefile.in | 6 +- TODO | 3 + config.h.in | 19 +- configure | 93 +- configure.ac | 7 +- smenu.1 | 281 +++-- smenu.c | 3761 ++++++++++++++++++++++++++++++++++++++------------------- smenu.spec.in | 6 +- version | 2 +- 11 files changed, 2860 insertions(+), 1335 deletions(-) diff --git a/.clang-format b/.clang-format index 77936dc..5d5f2f8 100644 --- a/.clang-format +++ b/.clang-format @@ -41,3 +41,4 @@ SpacesInParentheses: 'false' SpacesInSquareBrackets: 'false' TabWidth: '2' UseTab: Never +PenaltyBreakBeforeFirstCallParameter: '100' diff --git a/ChangeLog b/ChangeLog index 2d8a102..826e998 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,19 @@ +- Bump to version 0.9.10 +* The words acquisition is rewritten and splitted in multiple phases +* Add the capacity to (de)select rows an columns containing regexes +* -i, -e, -C and -R can be used more than once and have a cumulative effect +* Bugfix: -s is able to jump to non selectable words +* Bugfix: The attributes are not overloaded when reading configuration files +* Bugfix: the right bar is too far right in some cases. +* Bugfix: the right shift indicator is omitted in some cases +* Add a -x|-X option to set a timeout in seconds +* Add a -f option to select an alternative configuration file +* Add a -a option to set the attributes of the displayed elements +* Add more controls in the selection parser +* smenu can now read words directly from a file given as parameter +* Rewrite the replace function used by -S/-I/-E with a more sed-like one +* Searches should always consider the visual aspect of words (-S/-I/-E) + Thu Dec 22 20:48:17 UTC 2016 - p.gen.progs@gmail.com - Version 0.9.9 diff --git a/Makefile.in b/Makefile.in index aab3b5a..f315ef5 100644 --- a/Makefile.in +++ b/Makefile.in @@ -84,9 +84,9 @@ DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ $(top_srcdir)/configure $(am__configure_deps) \ $(srcdir)/config.h.in $(srcdir)/smenu.spec.in \ $(top_srcdir)/build-aux/depcomp $(dist_man_MANS) ChangeLog \ - TODO build-aux/config.guess build-aux/config.sub \ - build-aux/depcomp build-aux/install-sh build-aux/missing \ - $(top_srcdir)/build-aux/config.guess \ + TODO build-aux/compile build-aux/config.guess \ + build-aux/config.sub build-aux/depcomp build-aux/install-sh \ + build-aux/missing $(top_srcdir)/build-aux/config.guess \ $(top_srcdir)/build-aux/config.sub \ $(top_srcdir)/build-aux/install-sh \ $(top_srcdir)/build-aux/missing diff --git a/TODO b/TODO index 4c9538d..356dac7 100644 --- a/TODO +++ b/TODO @@ -1,2 +1,5 @@ # B: Bugs to fix, I: possible Improvements # ========================================. +I: Improve configure.ac +I: Improve the checking of system/library calls returns +I: Add an automatic test suite diff --git a/config.h.in b/config.h.in index c6dc169..a9956b5 100644 --- a/config.h.in +++ b/config.h.in @@ -1,11 +1,17 @@ /* config.h.in. Generated from configure.ac by autoheader. */ +/* Define to 1 if you have the header file. */ +#undef HAVE_FCNTL_H + /* Define to 1 if you have the header file. */ #undef HAVE_INTTYPES_H /* Define to 1 if you have the header file. */ #undef HAVE_LANGINFO_H +/* Define to 1 if you have the header file. */ +#undef HAVE_LIMITS_H + /* Define to 1 if you have the header file. */ #undef HAVE_LOCALE_H @@ -28,6 +34,10 @@ /* Define to 1 if you have the `pathconf' function. */ #undef HAVE_PATHCONF +/* Define to 1 if your system has a GNU libc compatible `realloc' function, + and to 0 otherwise. */ +#undef HAVE_REALLOC + /* Define to 1 if you have the `regcomp' function. */ #undef HAVE_REGCOMP @@ -43,9 +53,6 @@ /* Define to 1 if you have the `strchr' function. */ #undef HAVE_STRCHR -/* Define to 1 if you have the `strdup' function. */ -#undef HAVE_STRDUP - /* Define to 1 if you have the header file. */ #undef HAVE_STRINGS_H @@ -55,6 +62,9 @@ /* Define to 1 if you have the `strrchr' function. */ #undef HAVE_STRRCHR +/* Define to 1 if you have the `strspn' function. */ +#undef HAVE_STRSPN + /* Define to 1 if you have the header file. */ #undef HAVE_SYS_IOCTL_H @@ -138,5 +148,8 @@ /* Define to rpl_malloc if the replacement function should be used. */ #undef malloc +/* Define to rpl_realloc if the replacement function should be used. */ +#undef realloc + /* Define to `unsigned int' if does not define. */ #undef size_t diff --git a/configure b/configure index 340f1a0..da125e1 100755 --- a/configure +++ b/configure @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69 for smenu 0.9.9. +# Generated by GNU Autoconf 2.69 for smenu 0.9.10. # # Report bugs to . # @@ -580,8 +580,8 @@ MAKEFLAGS= # Identity of this package. PACKAGE_NAME='smenu' PACKAGE_TARNAME='smenu' -PACKAGE_VERSION='0.9.9' -PACKAGE_STRING='smenu 0.9.9' +PACKAGE_VERSION='0.9.10' +PACKAGE_STRING='smenu 0.9.10' PACKAGE_BUGREPORT='p.gen.progs@gmail.com' PACKAGE_URL='' @@ -1275,7 +1275,7 @@ if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures smenu 0.9.9 to adapt to many kinds of systems. +\`configure' configures smenu 0.9.10 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1345,7 +1345,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of smenu 0.9.9:";; + short | recursive ) echo "Configuration of smenu 0.9.10:";; esac cat <<\_ACEOF @@ -1436,7 +1436,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -smenu configure 0.9.9 +smenu configure 0.9.10 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. @@ -1859,7 +1859,7 @@ cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by smenu $as_me 0.9.9, which was +It was created by smenu $as_me 0.9.10, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -2725,7 +2725,7 @@ fi # Define the identity of the package. PACKAGE='smenu' - VERSION='0.9.9' + VERSION='0.9.10' cat >>confdefs.h <<_ACEOF @@ -4806,7 +4806,8 @@ $as_echo "#define STDC_HEADERS 1" >>confdefs.h fi -for ac_header in langinfo.h locale.h stdlib.h string.h sys/ioctl.h sys/time.h termios.h unistd.h wchar.h +for ac_header in fcntl.h limits.h langinfo.h locale.h stdint.h stdlib.h \ + string.h sys/ioctl.h sys/time.h termios.h unistd.h wchar.h do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" @@ -4901,7 +4902,75 @@ $as_echo "#define malloc rpl_malloc" >>confdefs.h fi -for ac_func in mblen memset nl_langinfo pathconf regcomp setlocale strchr strdup strrchr +for ac_header in stdlib.h +do : + ac_fn_c_check_header_mongrel "$LINENO" "stdlib.h" "ac_cv_header_stdlib_h" "$ac_includes_default" +if test "x$ac_cv_header_stdlib_h" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_STDLIB_H 1 +_ACEOF + +fi + +done + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for GNU libc compatible realloc" >&5 +$as_echo_n "checking for GNU libc compatible realloc... " >&6; } +if ${ac_cv_func_realloc_0_nonnull+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "$cross_compiling" = yes; then : + ac_cv_func_realloc_0_nonnull=no +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#if defined STDC_HEADERS || defined HAVE_STDLIB_H +# include +#else +char *realloc (); +#endif + +int +main () +{ +return ! realloc (0, 0); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + ac_cv_func_realloc_0_nonnull=yes +else + ac_cv_func_realloc_0_nonnull=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_realloc_0_nonnull" >&5 +$as_echo "$ac_cv_func_realloc_0_nonnull" >&6; } +if test $ac_cv_func_realloc_0_nonnull = yes; then : + +$as_echo "#define HAVE_REALLOC 1" >>confdefs.h + +else + $as_echo "#define HAVE_REALLOC 0" >>confdefs.h + + case " $LIBOBJS " in + *" realloc.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS realloc.$ac_objext" + ;; +esac + + +$as_echo "#define realloc rpl_realloc" >>confdefs.h + +fi + + +for ac_func in mblen memset nl_langinfo pathconf regcomp \ + setlocale strchr strrchr strspn do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" @@ -5538,7 +5607,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by smenu $as_me 0.9.9, which was +This file was extended by smenu $as_me 0.9.10, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -5604,7 +5673,7 @@ _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -smenu config.status 0.9.9 +smenu config.status 0.9.10 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" diff --git a/configure.ac b/configure.ac index 8e9f135..de48dec 100644 --- a/configure.ac +++ b/configure.ac @@ -25,14 +25,17 @@ AC_SEARCH_LIBS([tgetent], [tinfo curses ncursesw ncurses], [HAVE_CURSES=True]) # Checks for header files. AC_HEADER_STDC -AC_CHECK_HEADERS([langinfo.h locale.h stdlib.h string.h sys/ioctl.h sys/time.h termios.h unistd.h wchar.h]) +AC_CHECK_HEADERS([fcntl.h limits.h langinfo.h locale.h stdint.h stdlib.h \ + string.h sys/ioctl.h sys/time.h termios.h unistd.h wchar.h]) # Checks for typedefs, structures, and compiler characteristics. AC_TYPE_SIZE_T # Checks for library functions. AC_FUNC_MALLOC -AC_CHECK_FUNCS([mblen memset nl_langinfo pathconf regcomp setlocale strchr strdup strrchr]) +AC_FUNC_REALLOC +AC_CHECK_FUNCS([mblen memset nl_langinfo pathconf regcomp \ + setlocale strchr strrchr strspn]) AC_CANONICAL_HOST # OS-specific tests diff --git a/smenu.1 b/smenu.1 index df951a7..0162443 100644 --- a/smenu.1 +++ b/smenu.1 @@ -4,43 +4,51 @@ smenu - filter that allows to interactively select a word from stdin and outputs the selection to stdout. .SH SYNOPSIS .nf -\f(CRsmenu [\fB-h\fP|\fB-?\fP] [\fB-n\fP \fIlines\fP] [\fB-t\fP [\fIcols\fP]] \\ - [\fB-k\fP] [\fB-s\fP \fIpattern\fP] [\fB-m\fP \fImessage\fP] [\fB-w\fP] \\ +\f(CRsmenu [\fB-h\fP|\fB-?\fP] [\fB-f\fP \fIconfiguration_file\fP] \\ + [\fB-n\fP \fIlines\fP] [\fB-t\fP [\fIcols\fP]] [\fB-k\fP] \\ + [\fB-s\fP \fIpattern\fP] [\fB-m\fP \fImessage\fP] [\fB-w\fP] \\ [\fB-d\fP] [\fB-M\fP] [\fB-c\fP] [\fB-l\fP] [\fB-r\fP] [\fB-b\fP] \\ - [\fB-i\fP \fIregex\fP] [\fB-e\fP \fIregex\fP] \\ - [\fB-C\fP \ -[\fIa\fP|\fIA\fP|\fIs\fP|\fIS\fP|\fIr\fP|\fIR\fP|\fId\fP|\fID\fP]\ -[\fIcol1\fP[-\fIcol2\fP],[\fIcol1\fP[-\fIcol2\fP]... \\ + [\fB-a\fP] (i:|e:|c:)\fIATTR\fP [(i:|e:|c:)\fIATTR\fP]... \\ + [\fB-i\fP \fIregex\fP] [\fB-e\fP \fIregex\fP] [\fB-C\fP \ +[\fIa\fP|\fIs\fP|\fIi\fP|\fIr\fP|\fId\fP|\fIe\fP]] \\ [\fB-R\fP \ -[\fIa\fP|\fIA\fP|\fIs\fP|\fIS\fP|\fIr\fP|\fIR\fP|\fId\fP|\fID\fP]\ -[\fIrow1\fP[-\fIrow2\fP],[\fIrow1\fP[-\fIrow2\fP]... \\ - [\fB-S\fP \fI/regex/string/\fP[\fIg\fP][\fIv\fP][\fIs\fP][\fIi\fP]] \ -[\fB-I\fP \fI/regex/string/\fP[\fIg\fP][\fIv\fP][\fIs\fP][\fIi\fP]] \\ +[\fIa\fP|\fIs\fP|\fIi\fP|\fIr\fP|\fId\fP|\fIe\fP]] \\ + [\fB-S\fP \fI/regex/string/\fP[\fIg\fP][\fIv\fP][\fIs\fP][\fIi\fP]] \\ + [\fB-I\fP \fI/regex/string/\fP[\fIg\fP][\fIv\fP][\fIs\fP][\fIi\fP]] \\ [\fB-E\fP \fI/regex/string/\fP[\fIg\fP][\fIv\fP][\fIs\fP][\fIi\fP]] \\ - [\fB-A\fP \fIregex\fP] [\fB-Z\fP \fIregex\fP \\ - [\fB-1\fP \fIregex\fP [\fIATTR\fP] \ -[\fB-2\fP \fIregex\fP [\fIATTR\fP] ... \ -[\fB-5\fP \fIregex\fP [\fIATTR\fP] \\ + [\fB-A\fP \fIregex\fP] [\fB-Z\fP \fIregex\fP] \\ + [\fB-1\fP \fIregex\fP [\fIATTR\fP]] \ +[\fB-2\fP \fIregex\fP [\fIATTR\fP]] ... \ +[\fB-5\fP \fIregex\fP [\fIATTR\fP]] \\ [\fB-g\fP] [\fB-q\fP] [\fB-W\fP \fIbytes\fP] [\fB-L\fP \fIbytes\fP] \\ - [\fB-T\fP [\fIseperator\fP]]] [\fB-V\fP] + [\fB-T\fP [\fIseperator\fP]] [\fB-V\fP] \\ + [\fB-x\fP|\fB-X\fP \fItype\fP [\fIword\fP] \fIdelay\fP] [input_file] + + ::= \fIcol1\fP[-\fIcol2\fP],...|\fI\fP,... + ::= \fIcol1\fP[-\fIcol2\fP],...|\fI\fP,... + ::= [fg][/bg][,style] + ::= \fB\fIregex\fB\fP + + and can be freely mixed. .fi .SH DESCRIPTION -This small utility acts as a filter (reads from stdin and writes to -stdout). All the words taken from stdin are presented in a scrolling -window on the terminal at the current cursor position. +This small utility acts as a filter when no input file is given +(reads from stdin and writes to stdout) or takes its inputs from that file. + +All read words are presented in a scrolling window on the terminal +\fBat\fP the current cursor position without clearing the screen before. .P -A selection cursor is initially positioned on the first selectable word +The selection cursor is initially positioned on the first selectable word by default. .P -It is possible to filter the selectable words in the input stream by -using an include regular expression to match the selectable words -and an exclude regular expression to match the non-selectable words. +Options exists to explicitly or implicitly include or exclude some words by +using extended regular expressions. -Notice that when some words are excluded they can no more be re-included -after. +Notice that when some words are explicitly excluded they can no more be +re-included after. .P -Non-selectable words are skipped when moving the selection cursor and -cannot be searched for. +Excluded words are skipped when the selection cursor is moved and cannot +be searched for. .P The \fB-W\fP option can be used to set the characters (or multibyte sequences) which will be used to delimit the input words. @@ -55,32 +63,33 @@ characters \fIa\fP \fIb\fP \fIt\fP \fIn\fP \fIv\fP \fIf\fP \fIr\fP and Quotations (single and double) in the input stream enable to ignore the word separators so that a group of words are taken as a single entity. .P -Non printable characters in words that are not a delimiter are +Non printable characters in words that are not delimiters are converted to their traditional form (\fI\\n\fP for end-of-line, \fI\\t\fP for tabulation...) by default. A single dot (\fI.\fP) is also used as a placeholder otherwise. .P \fBWarning\fP, \fBUTF-8\fP encoded codepoints are quietly converted -into a dot (\fI.\fP) when the user locale is not \fBUTF-8\fP aware like +into dots (\fI.\fP) when the user locale is not \fBUTF-8\fP aware like \fBPOSIX\fP or \fBC\fP by example. .P .SS "Moving among words" -The cursor can then be moved in every direction by using the +The cursor can be moved in every direction by using the keyboard arrow keys (\fB\(<-\fP,\fB\(da\fP,\fB\(ua\fP,\fB\(->\fP) or the \fIvi\fP direction keys (\fBh\fP, \fBj\fP, \fBk\fP and \fBl\fP). -\fBHOME\fP, \fBEND\fP, \fBPgDn\fP and \fBPgUp\fP can also be used if -available and have their traditional meanings. +\fBHOME\fP, \fBEND\fP, \fBPgDn\fP and \fBPgUp\fP can also be used, if +available, and have their traditional meanings. .SS "Searching for a word" -The key \fB/\fP or the key combination \fB^F\fP (\fBCtrl-f\fP) can be -used to initiate a search by prefix among the words after the cursor. +The key \fB/\fP or the key combination \fB^F\fP (\fBCTRL+f\fP) can be +used to initiate a search by prefix among the words (possibly altered +by \fB-S\fP/\fB-I\fP/\fB-E\fP) located after the cursor. .P -After that, the cursor attribute is modified and all characters -entered 7s after this change go into a search buffer with the cursor -moving immediately to the next word matching this prefix. +After that, the cursor attributes are modified and all the characters +entered up to 7s after this change are put in a search buffer and the cursor +moves immediately to the next word matching this prefix. .P -Any character entered in the 5s after this action completes this buffer, -resets the 5s timer and advances the cursor again if another word matches -the new buffer. +Any character entered before a 5s timeout after this action completes +this buffer and resets the 5s timer and advances the cursor again if +another word matches the new buffer. .P As soon as the timer ends, the search mode is ended and the cursor regains its initial appearance. @@ -148,15 +157,15 @@ l l l l . /@v@/@v .TE .P -A \fB+\fP can also appear in the scroll bar in lieu of a \fB|\fP, +A \fB+\fP can also appear in the scroll bar in lieu of the vertical bar, giving the relative position of the cursor line in the bunch of input words. -.SS "Terminal resizing" +.SS "Terminal resizing (also see BUGS/LIMITATIONS)" The windows is redrawn if the terminal is resized. The redrawing is actually done only 1s after the end of the resizing to avoid artefacts on screen. The cursor will remain on the current selected word but may be displayed -on another place in the window. +at another place in the window. .SS Unicode support This utility is Unicode aware and should be able to display correctly any Unicode character (even double-width ones) as long as the current @@ -194,12 +203,15 @@ allowed: bar = 7/4,b ; scroll bar attributes search_field = 0/6 ; search field attributes search_text = 7,bu ; search text attributes + ; include = b ; selectable color attributes exclude = 4/0,u ; non-selectable color attributes tag = 0/5 ; tagged (selected) attributes special1 = 7/4,b ; attributes for the special level 1 special2 = bu ; attributes for the special level 2 special3 = /3,b ; attributes for the special level 3 + special4 = 7/4 ; attributes for the special level 4 + special5 = 7/2,b ; attributes for the special level 5 [window] lines = 7 ; default number of lines of the window @@ -242,7 +254,14 @@ columns@256 .TE .SH OPTIONS .IP "\fB-h\fP or \fB-?\fP" -Displays a log (\fB-h\fP) or short (\fB-?\fP) help message and exits. +Displays a long (\fB-h\fP) or short (\fB-?\fP) help message and exits. +.IP "\fB-f\fP \fIconfiguration_file\fB" +This option gives the possibility to select an alternative configuration +file. If the given file doesn't exist or is not readable then the +default values will be used. + +The \fB.smenu\fP files in the user's home directory and in the current +directory, if present, will be ignored when this option is used. .IP "\fB-n\fP \fIlines\fB" Gives the maximum number of lines in the scrolling selection window. By default five lines at most are displayed and the other ones, if @@ -269,6 +288,11 @@ Pre-Position the cursor to the first word matching the specified pattern. .IP * 2 A \fB#\fP immediately followed by a \fBnumber\fP giving the initial position of the cursor (counting from 0). + +If the word at this position is excluded, then the first previous non +excluded word is selected if it exists, otherwise the first non excluded +word is selected. + If this number if greater than the number of words, the cursor will be set on the latest selectable position. .IP * 2 @@ -279,7 +303,7 @@ A string starting with a \fB/\fP indicating that we want the cursor to be set to the first word matching the given regular expression. .IP * 2 A \fBprefix\fP string indicating that we want the cursor to be set on the -first word matching the string given (\fBCa\fP will match \fBCancel\fP +first word matching the string given (\fBa\fP will match \fBCancel\fP by example). .PP Warning, when searching for a prefix or a regular expression, smenu @@ -331,45 +355,110 @@ This mode is the same as column mode but without any column alignment. .IP \fB-r\fP Enables \fBENTER\fP to validate the selection even in search mode. .IP \fB-b\fP -Replace all non-printable characters by a blank. +Replaces all non-printable characters by a blank. +.IP "\fB-a \fIPREFIX:ATTR\fP [\fIPREFIX:ATTR\fP...]" +Sets the display attributes of the elements displayed and the cursor. + +At least one attribute prefixed attribute must be given. + +\fIPREFIX\fP can take the following values: +.RS +.IP \fIe\fP +excluded words. +.IP \fIi\fP +included words. +.IP \fIc\fP +cursor. +.IP \fIb\fP +scroll bar. +.IP \fIs\fP +shift indicator. +.IP \fIt\fP +tagged words. +.IP \fIsf\fP +search field. +.IP \fIst\fP +search buffered text. +.RE + +If more than one attribute is given, then they must be separated by +spaces. + +See the \fB-1\fP option for the \fIATTR\fP syntax. .IP "\fB-i\fP \fIregex\fP" Sets the \fBi\fPnclude filter to match the selectable words. -All the other words will become non-selectable (excluded) +All the other words will become implicitly non-selectable (excluded) + +\fB-i\fP can be used more than once with cumulative effect. .IP "\fB-e\fP \fIregex\fP" Sets the \fBe\fPxclude filter to match the non-selectable words. +All the other selectable words will become implicitly selectable (included) + +\fB-e\fP can be used more than once with cumulative effect. This filter has a higher priority than the include filter. -.P -The \fIregex\fP selections above are done before the possible word -alterations realized with \fB-I\fP or \fB-E\fP (see below). -.IP "\fB-C\fP [a|A|s|S|r|R|d|D][col1[-col2]],[col1[-col2]]..." -In column mode, restricts the previous selections or de-selections to -some columns. +.PP +.RS +The \fIregex\fP selections made using \fB-i\fP and/or \fB-e\fP are done +before the possible words alterations made by \fB-I\fP or \fB-E\fP +(see below). +.RE +.IP "\fB-C\fP [\fIa\fP|\fIs\fP|\fIi\fP|\fIr\fP|\fId\fP|\fIe\fP] \ +<\fIcol selectors\fP>" + +\fBImportant notice\fP: the letters \fIa\fP,\fIs\fP,\fIr\fP and \fId\fP +after \fB-C\fP are deprecated and will be removed in a future release. +Please only use \fIi\fP and \fIe\fP. + +These letters are case independent so \fII\fP can be used in place of +\fIi\fP per example. + +In column mode, This option allows to restrict the previous selections +or de-selections to some columns. If no selection is given via \fB-i\fP and \fB-e\fP this option gives the -possibility to select entire columns by giving their numbers (1 based). +possibility to select entire columns by giving their numbers (1 based) +of extended regular expressions. + +\fIa\fP, \fIs\fP and \fIi\fP or nothing select the specified ranges +of columns. +\fIr\fP, \fId\fP and \fIe\fP select all but the specified ranges +of columns. -\fBa\fP/\fBA\fP, \fBs\fP/\fBS\fP or nothing select the specified ranges -of columns. \fBr\fP/\fBR\fP or \fBd\fP/\fBD\fP select all but the -specified columns. +The words in the selected columns will be considered as \fBi\fPncluded +And the others \fBe\fPxcluded. -The words in the selected columns will be considered as included and the -others excluded. +A selection by regular expressions means that a column containing a word +matching one of these expression will be included or excluded according +to the letter given after the option. -Example of columns selection: \fI-a2,3,5-7\fP forces the cursor to only -navigate in columns \fB2\fP,\fB3\fP,\fB5\fP,\fB6\fP and \fB7\fP. -If \fBd\fP was used in place of \fBa\fP, all the columns would have been -selected \fBexcept\fP the columns \fB2\fP,\fB3\fP,\fB5\fP,\fB6\fP and -\fB7\fP. +Regular expressions and column numbers can be freely mixed. + +Example of columns selection: \f(CB-Ci2,3,/X./,5-7\fP forces the cursor +to only navigate in columns \fB2\fP,\fB3\fP,\fB5\fP,\fB6\fP and \fB7\fP +and those containing a two characters word starting with '\fBX\fP'. +If \fIe\fP was used in place of \fIi\fP, all the columns would have been +selected \fBexcept\fP the columns \fB2\fP,\fB3\fP,\fB5\fP,\fB6\fP,\fB7\fP +and those matching the extended regular expression '\f(CBX.\fP'. Spaces are allowed in the selection string if they are protected. The column mode is forced when this option is selected. -.IP "\fB-R\fP [a|A|s|S|r|R|d|D][col1[-col2]],[col1[-col2]]..." +.IP "\fB-R\fP [\fIa\fP|\fIs\fP|\fIi\fP|\fIr\fP|\fId\fP|\fIe\fP] \ +<\fIrow selectors\fP>" Similar to \fB-C\fP but for the rows. One difference though: this is the line mode which is forced by this option NOT the column mode. -.IP "\fB-S\fP /\fIregex\fP/replacement string/[g][v][s]" + +\fBImportant notice\fP: As with \fB-C\fP, the letters +\fIa\fP,\fIs\fP,\fIr\fP and \fId\fP after are deprecated and will be +removed in a future release. Please only use \fIi\fP and \fIe\fP. + +\fB-C\fP and \fB-R\fP can be used more than once in a cumulative manner: +The selection mode (selection or de-selection) is given by the first +occurrence of the options, the other occurrences will only update the +selected or de-selected ranges. +.IP "\fB-S\fP /\fIregex\fP/replacement string/[\fIg\fP][\fIv\fP][\fIs\fP]" Post-processes the words by applying a regular expression based substitution. The argument must be formatted as in the \fBsed\fP editor. @@ -404,24 +493,27 @@ selected meanwhile will display the same as above but \f(CBR\fP will contain the original word \fIb\fP if \fIB\fP is selected. In both cases, only the word \fIB\fP will be searchable and not \fIb\fP. -.RE +.P Notice that a substitution resulting in an empty string is equivalent to removing the word. -.IP "\fB-I\fP /\fIregex\fP/replacement string/[g][v][s]" +.RE +.IP "\fB-I\fP /\fIregex\fP/replacement string/[\fIg\fP][\fIv\fP][\fIs\fP]" Post-processes the \fBselectable\fP words by applying a regular expression based substitution (see \fB-S\fP for details). -.IP "\fB-E\fP /\fIregex\fP/replacement string/[g][v][s]" +.IP "\fB-E\fP /\fIregex\fP/replacement string/[\fIg\fP][\fIv\fP][\fIs\fP]" Post-processes the \fBexcluded\fP (or \fBnon-selectable\fP) words by applying a regular expression based substitution (see \fB-S\fP for details). -.P +.PP +.RS The \fB/\fP separator that \fB-I\fP and \fB-E\fP are using above can be substituted by any other character except \fISPACE\fP, \fI\\t\fP, \fI\\f\fP, \fI\\n\fP, \fI\\r\fP and \fI\\v\fP. -.P +.PP In the four previous options, \fIregex\fP is a \fBPOSIX\fP \fBE\fPxtended \fBR\fPegular \fBE\fPxpression. For details, please refer to the \fBregex\fP manual page. +.RE .IP "\fB-A\fP \fIregex\fP" In column mode, forces all words matching the given regular expression to be the first one in the displayed line. @@ -500,6 +592,41 @@ even contain control character as in \f(CB$'\\n'\fP. A space is used as the default separator if none is given. .IP \fB-V\fP Displays the current version and quits. +.TP +\fB-x \fItype\fP [\fIword\fP] \fIdelay\fP +.TQ +\fB-X \fItype\fP [\fIword\fP] \fIdelay\fP +Sets a timeout. +Three types of timeout are possible: +.RS +.TP 10 +current: +On timeout, the current cursor position determines the selection as +if the \fBENTER\fP has been pressed +.TP 10 +quit: +On timeout, nothing is selected as if the \fBq\fP key has been pressed +.TP 10 +word: +On timeout, the word given after the type is selected. +Note that this word doesn't need to be part of the words coming from +the standard input. +.PP +Each type can be be shortened as a prefix of the full name ("cur" for +"current" of "q" for "quit" per example). + +The delay must be set in seconds and cannot be above 99999 seconds. + +The remaining time (in seconds) is added at the end of the message +displayed above the selection window and is updated in real time each +second. + +Each key press except \fBENTER\fP, \fBq\fP, \fBQ\fP and \fB^C\fP resets +the timer to its initial value. + +The \fB-X\fP version works like \fB-x\fP but no periodic remaining +messages is displayed above the selection window. +.RE .SH NOTES If tabulators (\fI\\t\fP) are embedded in the input, there is no way to replace them with the original number of spaces. @@ -600,8 +727,16 @@ with the cursor on \fIQuit\fP and only the numbers and "Quit" selectable. The selected number or string will be available in \f(CBR\fP Try to understand it as an exercise. -.SH BUGS -None that I am aware of. -If you found one, please tell me. +.SH BUGS/LIMITATIONS +Some terminal emulators, those notably based on VTE version later than +0.35 (see https://github.com/GNOME/vte/commit/01380d), have a new feature +that gives them the possibility to wrap/unwrap already displayed lines +when resizing the window. + +As far as I known, there is no terminfo entry to disable that. + +On these types of terminals, the automatic re-display of the output of +smenu will be disturbed and some artifacts may appear on the screen if +the terminal window is resized. .SH AUTHORS \(co 2015 Pierre Gentile (p.gen.progs@gmail.com) diff --git a/smenu.c b/smenu.c index d0a3f2a..1d5ff2f 100644 --- a/smenu.c +++ b/smenu.c @@ -25,6 +25,8 @@ #if defined(__sun) && defined(__SVR4) #include #endif +#include +#include #include #include #include @@ -59,174 +61,249 @@ typedef struct txt_attr_s txt_attr_t; typedef struct limits_s limits_t; typedef struct sed_s sed_t; typedef struct interval_s interval_t; +typedef struct timeout_s timeout_t; /* ********** */ /* Prototypes */ /* ********** */ -static void help(win_t * win, term_t * term, int last_line, toggle_t * toggle); -static void short_usage(void); -static void usage(void); +static void +help(win_t * win, term_t * term, int last_line, toggle_t * toggle); + +static void +short_usage(void); + +static void +usage(void); + +static void * +xmalloc(size_t size); + +static void * +xcalloc(size_t num, size_t size); + +static void * +xrealloc(void * ptr, size_t size); -static void * xmalloc(size_t size); -static void * xcalloc(size_t num, size_t size); -static void * xrealloc(void * ptr, size_t size); +static interval_t * +interval_new(void); -static interval_t * interval_new(void); -static int interval_comp(void * a, void * b); -static void interval_swap(void * a, void * b); +static int +interval_comp(void * a, void * b); + +static void +interval_swap(void * a, void * b); + +static int +ll_append(ll_t * const list, void * const data); -static int ll_append(ll_t * const list, void * const data); #if 0 /* here for coherency but not used. */ static int ll_prepend(ll_t * const list, void *const data); -static void ll_insert_before(ll_t * const list, ll_node_t * node, - void *const data); -static void ll_insert_after(ll_t * const list, ll_node_t * node, - void *const data); + +static void +ll_insert_before(ll_t * const list, ll_node_t * node, void *const data); + +static void +ll_insert_after(ll_t * const list, ll_node_t * node, void *const data); #endif -static ll_node_t * ll_partition(ll_node_t * l, ll_node_t * h, - int (*comp)(void *, void *), - void (*swap)(void *, void *)); -static void ll_quicksort(ll_node_t * l, ll_node_t * h, - int (*comp)(void *, void *), - void (*swap)(void * a, void *)); -static void ll_sort(ll_t * list, int (*comp)(void *, void *), - void (*swap)(void * a, void *)); -static int ll_delete(ll_t * const list, ll_node_t * node); -static ll_node_t * ll_find(ll_t * const, void * const, - int (*)(const void *, const void *)); -static void ll_init(ll_t * list); -static ll_node_t * ll_new_node(void); -static ll_t * ll_new(void); - -static void ltrim(char * str, const char * trim); -static void rtrim(char * str, const char * trim, size_t min_len); -static int my_stricmp(const char * str1, const char * str2); - -static int isprint7(int i); -static int isprint8(int i); - -static int count_leading_set_bits(unsigned char c); -static int get_cursor_position(int * const r, int * const c); -static void get_terminal_size(int * const r, int * const c); -static char * mb_strprefix(char * d, char * s, int n, int * pos); -static int mb_strlen(char * str); -static wchar_t * mb_strtowcs(char * s); -static int validate_mb(const char * str, int length); -static int outch(int c); -static void restore_term(int const fd); -static void setup_term(int const fd); -static void strip_ansi_color(char * s, toggle_t * toggle); -static int strprefix(char * str1, char * str2); - -static int tst_cb(void * elem); -static int tst_cb_cli(void * elem); + +static ll_node_t * +ll_partition(ll_node_t * l, ll_node_t * h, int (*comp)(void *, void *), + void (*swap)(void *, void *)); + +static void +ll_quicksort(ll_node_t * l, ll_node_t * h, int (*comp)(void *, void *), + void (*swap)(void * a, void *)); + +static void +ll_sort(ll_t * list, int (*comp)(void *, void *), + void (*swap)(void * a, void *)); + +static int +ll_delete(ll_t * const list, ll_node_t * node); + +static ll_node_t * +ll_find(ll_t * const, void * const, int (*)(const void *, const void *)); + +static void +ll_init(ll_t * list); + +static ll_node_t * +ll_new_node(void); + +static ll_t * +ll_new(void); + +static void +ltrim(char * str, const char * trim); + +static void +rtrim(char * str, const char * trim, size_t min_len); + +static int +isempty(const char * str); + +static int +my_stricmp(const char * str1, const char * str2); + +static int +isprint7(int i); + +static int +isprint8(int i); + +static int +count_leading_set_bits(unsigned char c); + +static int +get_cursor_position(int * const r, int * const c); + +static void +get_terminal_size(int * const r, int * const c); + +static char * +mb_strprefix(char * d, char * s, int n, int * pos); + +static int +mb_strlen(char * str); + +static wchar_t * +mb_strtowcs(char * s); + +static int +validate_mb(const char * str, int length); + +static int +outch(int c); + +static void +restore_term(int const fd); + +static void +setup_term(int const fd); + +static void +strip_ansi_color(char * s, toggle_t * toggle); + +static int +strprefix(char * str1, char * str2); + +static int +tst_cb(void * elem); + +static int +tst_cb_cli(void * elem); + #if 0 /* here for coherency but not used. */ static void tst_cleanup(tst_node_t * p); #endif -static tst_node_t * tst_insert(tst_node_t * p, wchar_t * w, void * data); -static void * tst_prefix_search(tst_node_t * root, wchar_t * w, - int (*callback)(void *)); -static void * tst_search(tst_node_t * root, wchar_t * w); -static int tst_traverse(tst_node_t * p, int (*callback)(void *), - int first_call); - -static int ini_load(const char * filename, win_t * win, term_t * term, - limits_t * limits, - int (*report)(win_t * win, term_t * term, limits_t * limits, - const char * section, const char * name, - char * value)); -static int ini_cb(win_t * win, term_t * term, limits_t * limits, - const char * section, const char * name, char * value); -static char * make_ini_path(char * name, char * base); - -static void set_foreground_color(term_t * term, int color); -static void set_background_color(term_t * term, int color); - -static void set_win_start_end(win_t * win, int current, int last); -static int build_metadata(word_t * word_a, term_t * term, int count, - win_t * win); -static int disp_lines(word_t * word_a, win_t * win, toggle_t * toggle, - int current, int count, int search_mode, - char * search_buf, term_t * term, int last_line, - char * tmp_max_word, langinfo_t * langinfo); -static void get_message_lines(char * message, ll_t * message_lines_list, - int * message_max_width, int * message_max_len); -static int disp_message(ll_t * message_lines_list, int width, int max_len, - term_t * term, win_t * win); -static void disp_word(word_t * word_a, int pos, int search_mode, char * buffer, - term_t * term, win_t * win, char * tmp_max_word); -static int egetopt(int nargc, char ** nargv, char * ostr); -static int expand(char * src, char * dest, langinfo_t * langinfo); -static int get_bytes(FILE * input, char * mb_buffer, ll_t * word_delims_list, - toggle_t * toggle, langinfo_t * langinfo); -static int get_scancode(unsigned char * s, int max); -static char * get_word(FILE * input, ll_t * word_delims_list, - ll_t * record_delims_list, char * mb_buffer, - unsigned char * is_last, toggle_t * toggle, - langinfo_t * langinfo, win_t * win, limits_t * limits); - -static void left_margin_putp(char * s, term_t * term, win_t * win); -static void right_margin_putp(char * s1, char * s2, langinfo_t * langinfo, - term_t * term, win_t * win, int line, int offset); - -static int search_next(tst_node_t * tst, word_t * word_a, char * search_buf, - int after_only); -static void sig_handler(int s); - -static void set_new_first_column(win_t * win, term_t * term, word_t * word_a); - -static int parse_sed_like_string(sed_t * sed); -static int replace(char * orig, sed_t * sed, char * buf, size_t bufsiz); - -static int decode_txt_attr_toggles(char * s, txt_attr_t * attr); -static int parse_txt_attr(char * str, txt_attr_t * attr, short max_color); -static void apply_txt_attr(term_t * term, txt_attr_t attr); -static int (*my_isprint)(int); +static tst_node_t * +tst_insert(tst_node_t * p, wchar_t * w, void * data); -static int delims_cmp(const void * a, const void * b); +static void * +tst_prefix_search(tst_node_t * root, wchar_t * w, int (*callback)(void *)); -/* **************** */ -/* Global variables */ -/* **************** */ +static void * +tst_search(tst_node_t * root, wchar_t * w); -int dummy_rc; /* temporary variable to silence * - * the compiler */ +static int +tst_traverse(tst_node_t * p, int (*callback)(void *), int first_call); -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 containing 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 */ +static int +ini_load(const char * filename, win_t * win, term_t * term, limits_t * limits, + int (*report)(win_t * win, term_t * term, limits_t * limits, + const char * section, const char * name, char * value)); -/* UTF-8 useful symbols */ -/* """"""""""""""""""""" */ -char * broken_line_sym = "\xc2\xa6"; /* broken_bar */ -char * shift_left_sym = "\xe2\x86\x90"; /* leftwards_arrow */ -char * shift_right_sym = "\xe2\x86\x92"; /* rightwards_arrow */ +static int +ini_cb(win_t * win, term_t * term, limits_t * limits, const char * section, + const char * name, char * value); -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 */ +static char * +make_ini_path(char * name, char * base); -tst_node_t * root; +static void +set_foreground_color(term_t * term, int color); -/* 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; +static void +set_background_color(term_t * term, int color); + +static void +set_win_start_end(win_t * win, int current, int last); + +static int +build_metadata(word_t * word_a, term_t * term, int count, win_t * win); + +static int +disp_lines(word_t * word_a, win_t * win, toggle_t * toggle, int current, + int count, int search_mode, char * search_buf, term_t * term, + int last_line, char * tmp_word, langinfo_t * langinfo); + +static void +get_message_lines(char * message, ll_t * message_lines_list, + int * message_max_width, int * message_max_len); + +static int +disp_message(ll_t * message_lines_list, int width, int max_len, term_t * term, + win_t * win); + +static void +disp_word(word_t * word_a, int pos, int search_mode, char * buffer, + term_t * term, win_t * win, char * tmp_word); + +static int +egetopt(int nargc, char ** nargv, char * ostr); + +static int +expand(char * src, char * dest, langinfo_t * langinfo); + +static int +get_bytes(FILE * input, char * mb_buffer, ll_t * word_delims_list, + toggle_t * toggle, langinfo_t * langinfo); +static int +get_scancode(unsigned char * s, int max); + +static char * +get_word(FILE * input, ll_t * word_delims_list, ll_t * record_delims_list, + char * mb_buffer, unsigned char * is_last, toggle_t * toggle, + langinfo_t * langinfo, win_t * win, limits_t * limits); + +static void +left_margin_putp(char * s, term_t * term, win_t * win); + +static void +right_margin_putp(char * s1, char * s2, langinfo_t * langinfo, term_t * term, + win_t * win, int line, int offset); + +static int +search_nex(tst_node_t * tst, word_t * word_a, char * search_buf, + int after_only); + +static void +sig_handler(int s); + +static void +set_new_first_column(win_t * win, term_t * term, word_t * word_a); + +static int +parse_sed_like_string(sed_t * sed); + +static int +replace(char * orig, sed_t * sed); + +static int +decode_txt_attr_toggles(char * s, txt_attr_t * attr); + +static int +parse_txt_attr(char * str, txt_attr_t * attr, short max_color); + +static void +apply_txt_attr(term_t * term, txt_attr_t attr); + +static int (*my_isprint)(int); + +static int +delims_cmp(const void * a, const void * b); /* ***************** */ /* emums and structs */ @@ -241,10 +318,40 @@ enum filter_types EXCLUDE_FILTER }; +/* Used when managing the -R option */ +/* """""""""""""""""""""""""""""""" */ +enum row_regex_types +{ + ROW_REGEX_EXCLUDE = 0, + ROW_REGEX_INCLUDE = 1 +}; + +/* Used when managing the -C option */ +/* """""""""""""""""""""""""""""""" */ enum filter_infos { - INCLUDE_MARK = '1', - EXCLUDE_MARK = '0' + EXCLUDE_MARK = 0, /* must be 0 because used in various tests * + * these words cannot be re-included */ + INCLUDE_MARK = 1, /* to forcibly include a word, these words can * + * be excluded later */ + SOFT_EXCLUDE_MARK = 2, /* word with this mark are excluded by default * + * but can be included later */ + SOFT_INCLUDE_MARK = 3 /* word with this mark are included by default * + * but can be excluded later */ +}; + +enum timeout_types +{ + CURRENT, /* on timeout, outputs the selected word */ + QUIT, /* on timeout, quit without selecting anything */ + WORD /* on timeout , outputs the specified word */ +}; + +enum attribute_settings +{ + UNSET = 0, /* must be 0 for future testings */ + SET, + FORCED /* an attribute setting has been given in the command line */ }; /* Locale informations */ @@ -291,10 +398,11 @@ struct termios old_in_attrs; /* Interval timers used */ /* """""""""""""""""""" */ -struct itimerval search_itv; /* used when searching */ -struct itimerval hlp_itv; /* used to remove the help line */ -struct itimerval winch_itv; /* used to delay the redisplay when * - * the terminal is resized */ +struct itimerval periodic_itv; /* refresh rate for the timeout counter */ + +int search_timer = -1; +int help_timer = -1; +int winch_timer = -1; /* Structure containing the attributes components */ /* """""""""""""""""""""""""""""""""""""""""""""" */ @@ -322,21 +430,22 @@ struct term_s short colors; /* number of available colors */ short color_method; /* color method (0=classic (0-7), 1=ANSI) */ - char has_cursor_up; /* has cuu1 terminfo capability */ - char has_cursor_down; /* has cud1 terminfo capability */ - char has_cursor_left; /* has cub1 terminfo capability */ - char has_cursor_right; /* has cuf1 terminfo capability */ - char has_save_cursor; /* has sc terminfo capability */ - char has_restore_cursor; /* has rc terminfo capability */ - char has_setf; /* has set_foreground terminfo capability */ - char has_setb; /* has set_background terminfo capability */ - char has_setaf; /* idem for set_a_foreground (ANSI) */ - char has_setab; /* idem for set_a_background (ANSI) */ - char has_hpa; /* has column_address terminfo capability */ - char has_bold; /* has bold mode */ - char has_reverse; /* has reverse mode */ - char has_underline; /* has underline mode */ - char has_standout; /* has standout mode */ + char has_cursor_up; /* has cuu1 terminfo capability */ + char has_cursor_down; /* has cud1 terminfo capability */ + char has_cursor_left; /* has cub1 terminfo capability */ + char has_cursor_right; /* has cuf1 terminfo capability */ + char has_parm_right_cursor; /* has cuf terminfo capability */ + char has_save_cursor; /* has sc terminfo capability */ + char has_restore_cursor; /* has rc terminfo capability */ + char has_setf; /* has set_foreground terminfo capability */ + char has_setb; /* has set_background terminfo capability */ + char has_setaf; /* idem for set_a_foreground (ANSI) */ + char has_setab; /* idem for set_a_background (ANSI) */ + char has_hpa; /* has column_address terminfo capability */ + char has_bold; /* has bold mode */ + char has_reverse; /* has reverse mode */ + char has_underline; /* has underline mode */ + char has_standout; /* has standout mode */ }; /* Structure describing a word */ @@ -354,7 +463,7 @@ struct word_s * by is expansion. */ unsigned char is_tagged; /* 1 if the word is tagged, 0 if not */ unsigned char is_last; /* 1 if the word is the last of a line */ - unsigned char is_selectable; /* word is is_selectable */ + unsigned char is_selectable; /* word is selectable */ }; /* Structure describing the window in which the user */ @@ -367,7 +476,7 @@ struct win_s int cur_line; /* relative number of the cursor line (1+) */ int asked_max_lines; /* requested number of lines in the window */ int max_lines; /* effective number of lines in the window */ - int max_cols; /* max number of words in a sigle line */ + int max_cols; /* max number of words in a single line */ int max_width; /* max line length. In column, tab or line * * mode it can be greater than the * * terminal width */ @@ -387,6 +496,7 @@ struct win_s txt_attr_t shift_attr; /* shift indicator attributes */ txt_attr_t search_field_attr; /* search mode field attributes */ txt_attr_t search_text_attr; /* search mode text attributes */ + txt_attr_t include_attr; /* selectable words attributes */ txt_attr_t exclude_attr; /* non-selectable words attributes */ txt_attr_t tag_attr; /* non-selectable words attributes */ txt_attr_t special_attr[5]; /* special (-1,...) words attributes */ @@ -415,6 +525,74 @@ struct interval_s int high; }; +/* Structure used by the replace function to delimit matches */ +/* """"""""""""""""""""""""""""""""""""""""""""""""""""""""" */ +struct range_s +{ + size_t start; + size_t end; +}; + +struct timeout_s +{ + unsigned type; /* timeout type: current/quit/word */ + unsigned initial_value; /* 0: no timeout else value in sec */ + unsigned remain; /* remaining seconds */ + unsigned reached; /* 1: timeout has expired, else 0 */ +}; + +/* **************** */ +/* Global variables */ +/* **************** */ + +int dummy_rc; /* temporary variable to silence * + * the compiler */ + +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 containing 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 */ + +char * word_buffer; + +/* UTF-8 useful symbols */ +/* """"""""""""""""""""" */ +char * broken_line_sym = "\xc2\xa6"; /* broken_bar */ +char * shift_left_sym = "\xe2\x86\x90"; /* leftwards_arrow */ +char * shift_right_sym = "\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 */ + +tst_node_t * root; + +/* 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; +volatile sig_atomic_t got_timeout_tick = 0; + +/* Variables used when a timeout is set (option -x) */ +/* """""""""""""""""""""""""""""""""""""""""""""""" */ +timeout_t timeout; +char * timeout_word; /* printed word when the timeout type is WORD. */ +char * timeout_seconds; /* string containing the number of remaining * + * seconds. */ +int quiet_timeout = 0; /* 1 when we want no message to be displayed. */ + /* ************************************************************************ */ /* custom fgetc/ungetc implementation able to unget more than one character */ /* ************************************************************************ */ @@ -431,7 +609,7 @@ static size_t next_buffer_pos = 0; /* next free position in the getc buffer */ /* ====================================== */ /* get a (possibly pushed-back) character */ /* ====================================== */ -int +static int my_fgetc(FILE * input) { return (next_buffer_pos > 0) ? getc_buffer[--next_buffer_pos] : fgetc(input); @@ -440,7 +618,7 @@ my_fgetc(FILE * input) /* ============================ */ /* push character back on input */ /* ============================ */ -void +static void my_ungetc(int c) { if (next_buffer_pos >= GETC_BUFF_SIZE) @@ -493,41 +671,57 @@ struct ll_s /* =================== */ /* Short usage display */ /* =================== */ -void +static void short_usage(void) { - fprintf(stderr, "Usage: smenu [-h|-?] [-n lines] [-c] [-l] [-s pattern] "); - fprintf(stderr, "[-m message] [-w] \\\n"); - fprintf(stderr, " [-d] [-M] [-t [cols]] [-k] [-r] [-b] [-i regex] "); - fprintf(stderr, "[-e regex] \\\n"); - fprintf(stderr, " [-C [a|A|s|S|r|R|d|D]col1[-col2],[col1[-col2]]...]"); - fprintf(stderr, " \\\n"); - fprintf(stderr, " [-R [a|A|s|S|r|R|d|D]row1[-row2],[row1[-row2]]...] "); - fprintf(stderr, " \\\n"); + fprintf(stderr, "Usage: smenu [-h|-?] [-f config_file] [-n lines] "); + fprintf(stderr, "[-t [cols]] [-k] \\\n"); + fprintf(stderr, " [-s pattern] [-m message] [-w] [-d] [-M] [-c] [-l] "); + fprintf(stderr, "[-r] [-b] \\\n"); + fprintf(stderr, " [-a prefix:attr [prefix:attr]...] "); + fprintf(stderr, "[-i regex] [-e regex] \\\n"); + fprintf(stderr, " [-C [a|s|i|r|d|e]] "); + fprintf(stderr, "[-R [a|s|i|r|d|e]] \\\n"); fprintf(stderr, " [-S /regex/repl/[g][v][s][i]] "); - fprintf(stderr, "[-I /regex/repl/[g][v][s][i]] \\\n"); + fprintf(stderr, "[-I /regex/repl/[g][v][s][i]] \\\n"); fprintf(stderr, " [-E /regex/repl/[g][v][s][i]] "); - fprintf(stderr, "[-A regex] [-Z regex] \\\n"); - fprintf(stderr, " [-1 regex [attr]] [-2 regex [attr]] ... "); - fprintf(stderr, "[-5 regex [attr]] [-g] \\\n"); - fprintf(stderr, " [-W bytes] [-L bytes] [-T [separator]] [-V]\n"); + fprintf(stderr, "[-A regex] [-Z regex] \\\n"); + fprintf(stderr, " [-1 regex [attr]] [-2 regex [attr]]... "); + fprintf(stderr, "[-5 regex [attr]] \\\n"); + fprintf(stderr, " [-g] [-q] [-W bytes] [-L bytes] [-T [separator]]"); + fprintf(stderr, " \\\n"); + fprintf(stderr, " [-V] [-x|-X current|quit|word [] ] "); + fprintf(stderr, "[input_file]\n\n"); + fprintf(stderr, " ::= col1[-col2]...|...\n"); + fprintf(stderr, " ::= row1[-row2]...|...\n"); + fprintf(stderr, " ::= e|i|c|b|s|t|sf|st\n"); + fprintf(stderr, " ::= [fg][/bg][,style] \n"); + fprintf(stderr, " (ex: 7/4,bu)\n"); + fprintf(stderr, " ::= regex \n"); + fprintf(stderr, " (ex: /regex/ or :regex:)\n\n"); + fprintf(stderr, " and can be freely mixed "); + fprintf(stderr, "when used\n"); + fprintf(stderr, " with -C and -R (ex: 2,/x/,4-5).\n"); } /* ====================== */ /* Usage display and exit */ /* ====================== */ -void +static void usage(void) { short_usage(); 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\n"); + fprintf(stderr, "and outputs the selected\n"); + fprintf(stderr, "words (or nothing) on stdout in a nice selection "); + fprintf(stderr, "window\n\n"); fprintf(stderr, "The selection window appears on /dev/tty "); - fprintf(stderr, "immediately after the current line.\n\n"); + fprintf(stderr, "immediately after the current line\n"); + fprintf(stderr, "(no clear screen!).\n\n"); fprintf(stderr, "The following options are available:\n\n"); fprintf(stderr, "-h displays this help.\n"); + fprintf(stderr, "-f selects an alternative configuration file.\n"); fprintf(stderr, "-n sets the number of lines in the selection window.\n"); fprintf(stderr, "-t tabulates the items. The number of columns can be limited " @@ -551,12 +745,14 @@ usage(void) "-r enables ENTER to validate the selection even in " "search mode.\n"); fprintf(stderr, "-b displays the non printable characters as space.\n"); + fprintf(stderr, "-a sets the attributes for the various displayed "); + fprintf(stderr, "elements.\n"); fprintf(stderr, "-i sets the regex input filter to match the selectable words.\n"); fprintf(stderr, "-e sets the regex input filter to match the non-selectable " "words.\n"); - fprintf(stderr, "-C sets column restrictions for selections.\n"); + fprintf(stderr, "-C sets columns restrictions for selections.\n"); fprintf(stderr, "-R sets rows restrictions for selections.\n"); fprintf(stderr, "-S sets the post-processing action to apply to all words.\n"); @@ -585,6 +781,8 @@ usage(void) fprintf(stderr, "on the output.\n"); fprintf(stderr, " A single space is the default separator.\n"); fprintf(stderr, "-V displays the current version and quits.\n"); + fprintf(stderr, + "-x|-X sets a timeout and specifies what to do when it expires.\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"); @@ -626,7 +824,7 @@ usage(void) /* ==================== */ /* Help message display */ /* ==================== */ -void +static void help(win_t * win, term_t * term, int last_line, toggle_t * toggle) { int index; /* used to identify the objects int the help line */ @@ -640,24 +838,23 @@ help(win_t * win, term_t * term, int last_line, toggle_t * toggle) struct entry_s { char attr; /* r=reverse, n=normal, b=bold */ - char * str; /* string to be displayed for un object in the help line */ + char * str; /* string to be displayed for an object in the help line */ int len; /* length of one of these objects */ }; - struct entry_s entries[] = { - { 'r', "HLP", 3 }, { 'n', " ", 1 }, { 'n', "Move:", 5 }, - { 'b', "Arrows", 6 }, { 'n', "|", 1 }, { 'b', "h", 1 }, - { 'b', "j", 1 }, { 'b', "k", 1 }, { 'b', "l", 1 }, - { 'n', ",", 1 }, { 'b', "PgUp", 4 }, { 'n', "/", 1 }, - { 'b', "PgDn", 4 }, { 'n', "/", 1 }, { 'b', "Home", 4 }, - { 'n', "/", 1 }, { 'b', "End", 3 }, { 'n', " ", 1 }, - { 'n', "Abort:", 6 }, { 'b', "q", 1 }, { 'n', " ", 1 }, - { 'n', "Select:", 7 }, { 'b', "CR", 2 }, { 'n', " ", 1 }, - { 'n', "Find:", 5 }, { 'b', "/", 1 }, { 'n', "|", 1 }, - { 'b', "^F", 2 }, { 'n', "|", 1 }, { 'b', "SP", 2 }, - { 'n', "|", 1 }, { 'b', "n", 1 }, { 'n', " ", 1 }, - { 'n', "Tag:", 4 }, { 'b', "t", 1 } - }; + struct entry_s entries[] = + { { 'r', "HLP", 3 }, { 'n', " ", 1 }, { 'n', "Move:", 5 }, + { 'b', "Arrows", 6 }, { 'n', "|", 1 }, { 'b', "h", 1 }, + { 'b', "j", 1 }, { 'b', "k", 1 }, { 'b', "l", 1 }, + { 'n', ",", 1 }, { 'b', "PgUp", 4 }, { 'n', "/", 1 }, + { 'b', "PgDn", 4 }, { 'n', "/", 1 }, { 'b', "Home", 4 }, + { 'n', "/", 1 }, { 'b', "End", 3 }, { 'n', " ", 1 }, + { 'n', "Abort:", 6 }, { 'b', "q", 1 }, { 'n', " ", 1 }, + { 'n', "Select:", 7 }, { 'b', "CR", 2 }, { 'n', " ", 1 }, + { 'n', "Find:", 5 }, { 'b', "/", 1 }, { 'n', "|", 1 }, + { 'b', "^F", 2 }, { 'n', "|", 1 }, { 'b', "SP", 2 }, + { 'n', "|", 1 }, { 'b', "n", 1 }, { 'n', " ", 1 }, + { 'n', "Tag:", 4 }, { 'b', "t", 1 } }; entries_nb = sizeof(entries) / sizeof(struct entry_s); @@ -739,7 +936,7 @@ help(win_t * win, term_t * term, int last_line, toggle_t * toggle) /* ================= */ /* Customized malloc */ /* ================= */ -void * +static void * xmalloc(size_t size) { void * allocated; @@ -762,7 +959,7 @@ xmalloc(size_t size) /* ================= */ /* Customized calloc */ /* ================= */ -void * +static void * xcalloc(size_t n, size_t size) { void * allocated; @@ -785,7 +982,7 @@ xcalloc(size_t n, size_t size) /* ================== */ /* Customized realloc */ /* ================== */ -void * +static void * xrealloc(void * p, size_t size) { void * allocated; @@ -806,7 +1003,7 @@ xrealloc(void * p, size_t size) /* =================================== */ /* strdup implementation using xmalloc */ /* =================================== */ -char * +static char * xstrdup(const char * p) { char * allocated; @@ -833,7 +1030,7 @@ xstrdup(const char * p) /* Returns 0 if some unexpected */ /* toggle is found else 0 */ /* ================================ */ -int +static int decode_txt_attr_toggles(char * s, txt_attr_t * attr) { int rc = 1; @@ -847,27 +1044,27 @@ decode_txt_attr_toggles(char * s, txt_attr_t * attr) { case 'b': attr->bold = 1; - attr->is_set = 1; + attr->is_set = SET; break; case 'd': attr->dim = 1; - attr->is_set = 1; + attr->is_set = SET; break; case 'r': attr->reverse = 1; - attr->is_set = 1; + attr->is_set = SET; break; case 's': attr->standout = 1; - attr->is_set = 1; + attr->is_set = SET; break; case 'u': attr->underline = 1; - attr->is_set = 1; + attr->is_set = SET; break; case 'i': attr->italic = 1; - attr->is_set = 1; + attr->is_set = SET; break; default: rc = 0; @@ -886,7 +1083,7 @@ decode_txt_attr_toggles(char * s, txt_attr_t * attr) /* Returns 1 on success else 0 */ /* attr will be filled by the function */ /* ============================================================ */ -int +static int parse_txt_attr(char * str, txt_attr_t * attr, short max_color) { int n; @@ -940,16 +1137,13 @@ parse_txt_attr(char * str, txt_attr_t * attr, short max_color) if (n == 2) rc = decode_txt_attr_toggles(s2, attr); - if (!attr->is_set) - attr->is_set = (d1 >= 0 || d2 >= 0); - return rc; } /* ============================================= */ /* Set the terminal attributes according to attr */ /* ============================================= */ -void +static void apply_txt_attr(term_t * term, txt_attr_t attr) { if (attr.fg >= 0) @@ -986,7 +1180,7 @@ apply_txt_attr(term_t * term, txt_attr_t attr) /* line of the ini file. */