diff options
-rw-r--r-- | smenu.1 | 26 | ||||
-rw-r--r-- | smenu.c | 161 | ||||
-rw-r--r-- | smenu.h | 8 | ||||
-rw-r--r-- | tests/ignored_input/data1 | 21 | ||||
-rw-r--r-- | tests/ignored_input/t0001.good | 19 | ||||
l--------- | tests/ignored_input/t0001.in | 1 | ||||
-rw-r--r-- | tests/ignored_input/t0001.tst | 4 | ||||
-rw-r--r-- | tests/ignored_input/t0002.good | 19 | ||||
l--------- | tests/ignored_input/t0002.in | 1 | ||||
-rw-r--r-- | tests/ignored_input/t0002.tst | 4 | ||||
-rw-r--r-- | tests/ignored_input/t0003.good | 13 | ||||
l--------- | tests/ignored_input/t0003.in | 1 | ||||
-rw-r--r-- | tests/ignored_input/t0003.tst | 4 | ||||
-rw-r--r-- | tests/ignored_input/t0004.good | 11 | ||||
l--------- | tests/ignored_input/t0004.in | 1 | ||||
-rw-r--r-- | tests/ignored_input/t0004.tst | 4 | ||||
-rw-r--r-- | tests/ignored_input/t0005.good | 13 | ||||
l--------- | tests/ignored_input/t0005.in | 1 | ||||
-rw-r--r-- | tests/ignored_input/t0005.tst | 4 | ||||
-rw-r--r-- | usage.c | 2 |
20 files changed, 259 insertions, 59 deletions
@@ -20,6 +20,7 @@ and outputs the selection to stdout. [\fB-4\fP|\fB-l4\fP|\fB-level4\fP \fIregex\fP [\fIattr\fP]] [\fB-5\fP|\fB-l5\fP|\fB-level5\fP \fIregex\fP [\fIattr\fP]] [\fB-T\fP|\fB-tm\fP|\fB-tag\fP|\fB-tag_mode\fP [\fIdelim\fP]] + [\fB-z\fP|\fB-zap\fP|\fB-zap_glyphs\fP \fIbytes\fP] [\fB-P\fP|\fB-pm\fP|\fB-pin\fP|\fB-pin_mode\fP [\fIdelim\fP]] [\fB-p\fP|\fB-at\fP|\fB-auto_tag\fP] [\fB-N\fP|\fB-number\fP... [\fIregex\fP]] @@ -115,6 +116,10 @@ Example: \fI\\uc3a9\fP means latin small letter e with acute. Note that with most shells, the \fI\\\fP before the \fIu\fP need to be protected or escaped. .PP +Note also that a dot, which is the consequence of an invalid UTF-8 +sequence, will not be distinguished from a dot normally present in the +input stream. +.PP Quotations (single and double) in the input stream can be used to ignore the word separators so that a group of words are taken as a single entity. .PP @@ -132,6 +137,16 @@ These words are also kept in column mode, selectable or not. into dots (\fI.\fP) when the user locale is not \fBUTF-8\fP aware like \fBPOSIX\fP or \fBC\fP by example. .PP +smenu has an option to define a set of characters or UTF-8 sequences +wich should be ignored when reading words from the input. +This can be very useful when dealing with inputs where the EOL sequence +consists in more than one character. +.PP +A typical example is DOS or Windows files with lines ending with +\fICRLF\fI. +In such a case one might decide to ignore all \fICR\fP characters from +the input. +.PP .SS "Moving among words" The cursor can be moved in every direction by using the keyboard arrow keys (\fB\(<-\fP,\fB\(da\fP,\fB\(ua\fP,\fB\(->\fP) @@ -745,6 +760,17 @@ Examples of possible attributes are: .fi \fI\\u\fP sequences can be used in the pattern. +.IP "\fB-z\fP|\fB-zap\fP|\fB-zap_glyphs\fP \fIbytes\fP" +(Allowed in all contexts) + +Initializes a set of UTF-8 characters to be ignored when reading words from +stdin or a file. + +Example: The argument \f(CW'\\u0d\\ue282ac,'\fP means: ignore all commas, +Euro signs and carriage return characters when reading from stdin or +a file. + +As shown above \fI\\u\fP sequences can be used in the bytes set. .IP "\fB-T\fP|\fB-tm\fP|\fB-tag\fP|\fB-tag_mode\fP [\fIdelim\fP]" (Allowed in Main, Columns, Lines, Direct_access, Tabulations contexts, leads to Tagging context) @@ -2284,6 +2284,15 @@ get_scancode(unsigned char * s, size_t max) return i; } +/* =========================================================== */ +/* Helper function to compare to delimiters for use by ll_find */ +/* =========================================================== */ +int +buffer_cmp(const void * a, const void * b) +{ + return strcmp((const char *)a, (const char *)b); +} + /* ===================================================================== */ /* Get bytes from stdin. If the first byte is the leading character of a */ /* UTF-8 glyph, the following ones are also read. */ @@ -2291,44 +2300,51 @@ get_scancode(unsigned char * s, size_t max) /* the character. */ /* ===================================================================== */ int -get_bytes(FILE * input, char * utf8_buffer, langinfo_t * langinfo) +get_bytes(FILE * input, char * utf8_buffer, ll_t * zapped_glyphs_list, + langinfo_t * langinfo) { int byte; - int last = 0; + int last; int n; - /* Read the first byte */ - /* """"""""""""""""""" */ - byte = my_fgetc(input); - - if (byte == EOF) - return EOF; - - utf8_buffer[last++] = byte; - - /* 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 = utf8_get_length(byte)) > 1)) + do { - while (last < n && (byte = my_fgetc(input)) != EOF && (byte & 0xc0) == 0x80) - utf8_buffer[last++] = byte; + last = 0; + + /* Read the first byte */ + /* """"""""""""""""""" */ + byte = my_fgetc(input); if (byte == EOF) return EOF; - } - utf8_buffer[last] = '\0'; + utf8_buffer[last++] = byte; - /* Replace an invalid UTF-8 byte sequence by a single dot. */ - /* In this case the original sequence is lost (unsupported */ - /* encoding). */ - /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */ - if (langinfo->utf8 && !utf8_validate(utf8_buffer, last)) - { - byte = utf8_buffer[0] = '.'; - utf8_buffer[1] = '\0'; - } + /* 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 = utf8_get_length(byte)) > 1)) + { + while (last < n && (byte = my_fgetc(input)) != EOF + && (byte & 0xc0) == 0x80) + utf8_buffer[last++] = byte; + + if (byte == EOF) + return EOF; + } + + utf8_buffer[last] = '\0'; + + /* Replace an invalid UTF-8 byte sequence by a single dot. */ + /* In this case the original sequence is lost (unsupported */ + /* encoding). */ + /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */ + if (langinfo->utf8 && !utf8_validate(utf8_buffer, last)) + { + byte = utf8_buffer[0] = '.'; + utf8_buffer[1] = '\0'; + } + } while (ll_find(zapped_glyphs_list, utf8_buffer, buffer_cmp) != NULL); return byte; } @@ -2471,8 +2487,9 @@ expand(char * src, char * dest, langinfo_t * langinfo, toggle_t * toggle) /* ===================================================================== */ char * get_word(FILE * input, ll_t * word_delims_list, ll_t * record_delims_list, - char * utf8_buffer, unsigned char * is_last, toggle_t * toggle, - langinfo_t * langinfo, win_t * win, limits_t * limits) + ll_t * zapped_glyphs_list, char * utf8_buffer, unsigned char * is_last, + toggle_t * toggle, langinfo_t * langinfo, win_t * win, + limits_t * limits) { char * temp = NULL; int byte; @@ -2484,15 +2501,15 @@ get_word(FILE * input, ll_t * word_delims_list, ll_t * record_delims_list, /* Skip leading delimiters */ /* """"""""""""""""""""""" */ - byte = get_bytes(input, utf8_buffer, langinfo); + byte = get_bytes(input, utf8_buffer, zapped_glyphs_list, langinfo); while (byte == EOF - || ll_find(word_delims_list, utf8_buffer, delims_cmp) != NULL) + || ll_find(word_delims_list, utf8_buffer, buffer_cmp) != NULL) { if (byte == EOF) return NULL; - byte = get_bytes(input, utf8_buffer, langinfo); + byte = get_bytes(input, utf8_buffer, zapped_glyphs_list, langinfo); } /* Allocate initial word storage space */ @@ -2593,7 +2610,7 @@ get_word(FILE * input, ll_t * word_delims_list, ll_t * record_delims_list, /* Only consider delimiters when outside quotations */ /* """""""""""""""""""""""""""""""""""""""""""""""" */ if ((!is_dquote && !is_squote) - && ll_find(word_delims_list, utf8_buffer, delims_cmp) != NULL) + && ll_find(word_delims_list, utf8_buffer, buffer_cmp) != NULL) break; /* We no dot count the significant quotes */ @@ -2620,7 +2637,7 @@ get_word(FILE * input, ll_t * word_delims_list, ll_t * record_delims_list, is_special = 0; next: - byte = get_bytes(input, utf8_buffer, langinfo); + byte = get_bytes(input, utf8_buffer, zapped_glyphs_list, langinfo); } /* Nul-terminate the word to make it a string */ @@ -2634,13 +2651,14 @@ get_word(FILE * input, ll_t * word_delims_list, ll_t * record_delims_list, /* Skip all field delimiters before a record delimiter */ /* """"""""""""""""""""""""""""""""""""""""""""""""""" */ - if (ll_find(record_delims_list, utf8_buffer, delims_cmp) == NULL) + if (ll_find(record_delims_list, utf8_buffer, buffer_cmp) == NULL) { - byte = get_bytes(input, utf8_buffer, langinfo); + byte = get_bytes(input, utf8_buffer, zapped_glyphs_list, langinfo); + while (byte != EOF - && ll_find(word_delims_list, utf8_buffer, delims_cmp) != NULL - && ll_find(record_delims_list, utf8_buffer, delims_cmp) == NULL) - byte = get_bytes(input, utf8_buffer, langinfo); + && ll_find(word_delims_list, utf8_buffer, buffer_cmp) != NULL + && ll_find(record_delims_list, utf8_buffer, buffer_cmp) == NULL) + byte = get_bytes(input, utf8_buffer, zapped_glyphs_list, langinfo); if (langinfo->utf8 && utf8_get_length(utf8_buffer[0]) > 1) { @@ -2659,7 +2677,7 @@ get_word(FILE * input, ll_t * word_delims_list, ll_t * record_delims_list, /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */ if (byte == EOF || ((win->col_mode || win->line_mode || win->tab_mode) - && ll_find(record_delims_list, utf8_buffer, delims_cmp) != NULL)) + && ll_find(record_delims_list, utf8_buffer, buffer_cmp) != NULL)) *is_last = 1; else *is_last = 0; @@ -3761,15 +3779,6 @@ sig_handler(int s) } } -/* =========================================================== */ -/* Helper function to compare to delimiters for use by ll_find */ -/* =========================================================== */ -int -delims_cmp(const void * a, const void * b) -{ - return strcmp((const char *)a, (const char *)b); -} - /* ========================================================= */ /* Set new first column to display when horizontal scrolling */ /* Alter win->first_column */ @@ -5281,6 +5290,18 @@ force_last_column_action(char * ctx_name, char * opt_name, char * param, } void +zapped_glyphs_action(char * ctx_name, char * opt_name, char * param, + int nb_values, char ** values, int nb_opt_data, + void ** opt_data, int nb_ctx_data, void ** ctx_data) +{ + char ** glyph = opt_data[0]; + langinfo_t * langinfo = opt_data[1]; + + *glyph = xstrdup(values[0]); + utf8_interpret(*glyph, langinfo); +} + +void separators_action(char * ctx_name, char * opt_name, char * param, int nb_values, char ** values, int nb_opt_data, void ** opt_data, int nb_ctx_data, void ** ctx_data) @@ -5935,8 +5956,9 @@ main(int argc, char * argv[]) struct sigaction sa; /* Signal structure */ - char * iws = NULL, *ils = NULL; + char * iws = NULL, *ils = NULL, *zg = NULL; ll_t * word_delims_list = NULL; + ll_t * zapped_glyphs_list = NULL; ll_t * record_delims_list = NULL; char utf8_buffer[5]; /* buffer to store the bytes of a UTF-8 glyph * @@ -6186,6 +6208,7 @@ main(int argc, char * argv[]) "[special_level_3 #...<3] " "[special_level_4 #...<3] " "[special_level_5 #...<3] " + "[zapped_glyphs #bytes] " "[lines [#height]] " "[blank_nonprintable] " "[center_mode] " @@ -6335,6 +6358,7 @@ main(int argc, char * argv[]) "-W -ws -wd -word_delimiters -word_separators"); ctxopt_add_opt_settings(parameters, "line_separators", "-L -ls -ld -line-delimiters -line_separators"); + ctxopt_add_opt_settings(parameters, "zapped_glyphs", "-z -zap -zap-glyphs"); ctxopt_add_opt_settings(parameters, "no_scoll_bar", "-q -no_bar -no-scroll_bar"); ctxopt_add_opt_settings(parameters, "post_subst_all", "-S -subst"); @@ -6438,6 +6462,8 @@ main(int argc, char * argv[]) &langinfo, NULL); ctxopt_add_opt_settings(actions, "line_separators", separators_action, &ils, &langinfo, NULL); + ctxopt_add_opt_settings(actions, "zapped_glyphs", zapped_glyphs_action, &zg, + &langinfo, NULL); ctxopt_add_opt_settings(actions, "tag_mode", tag_mode_action, &toggle, &win, &langinfo, NULL); ctxopt_add_opt_settings(actions, "pin_mode", pin_mode_action, &toggle, &win, @@ -7004,6 +7030,29 @@ main(int argc, char * argv[]) tab_max_size = 0; min_size = 0; + /* Parse the list of glyphs to be zapped (option -z). */ + /* """""""""""""""""""""""""""""""""""""""""""""""""" */ + zapped_glyphs_list = ll_new(); + if (zg != NULL) + { + int utf8_len; + char * zg_ptr = zg; + char * tmp; + + utf8_len = mblen(zg_ptr, 4); + + while (utf8_len != 0) + { + tmp = xmalloc(utf8_len + 1); + memcpy(tmp, zg_ptr, utf8_len); + tmp[utf8_len] = '\0'; + ll_append(zapped_glyphs_list, tmp); + + zg_ptr += utf8_len; + utf8_len = mblen(zg_ptr, 4); + } + } + /* Parse the word separators string (option -W). If it is empty then */ /* the standard delimiters (space, tab and EOL) are used. Each of its */ /* UTF-8 sequences are stored in a linked list. */ @@ -7064,7 +7113,7 @@ main(int argc, char * argv[]) /* Add this record delimiter as a word delimiter */ /* """"""""""""""""""""""""""""""""""""""""""""" */ - if (ll_find(word_delims_list, tmp, delims_cmp) == NULL) + if (ll_find(word_delims_list, tmp, buffer_cmp) == NULL) ll_append(word_delims_list, tmp); ils_ptr += utf8_len; @@ -7383,10 +7432,10 @@ main(int argc, char * argv[]) /* - The -R is taken into account */ /* - The first part of the -C option is done */ /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */ - while ( - (word = get_word(input_file, word_delims_list, record_delims_list, - utf8_buffer, &is_last, &toggle, &langinfo, &win, &limits)) - != NULL) + while ((word = get_word(input_file, word_delims_list, record_delims_list, + zapped_glyphs_list, utf8_buffer, &is_last, &toggle, + &langinfo, &win, &limits)) + != NULL) { int selectable; int is_first = 0; @@ -519,15 +519,17 @@ size_t expand(char * src, char * dest, langinfo_t * langinfo, toggle_t * toggle); int -get_bytes(FILE * input, char * utf8_buffer, langinfo_t * langinfo); +get_bytes(FILE * input, char * utf8_buffer, ll_t * ignored_glyphs_list, + langinfo_t * langinfo); int get_scancode(unsigned char * s, size_t max); char * get_word(FILE * input, ll_t * word_delims_list, ll_t * record_delims_list, - char * utf8_buffer, unsigned char * is_last, toggle_t * toggle, - langinfo_t * langinfo, win_t * win, limits_t * limits); + ll_t * ignored_glyphs_list, char * utf8_buffer, + unsigned char * is_last, toggle_t * toggle, langinfo_t * langinfo, + win_t * win, limits_t * limits); void left_margin_putp(char * s, term_t * term, win_t * win); diff --git a/tests/ignored_input/data1 b/tests/ignored_input/data1 new file mode 100644 index 0000000..dfc89e4 --- /dev/null +++ b/tests/ignored_input/data1 @@ -0,0 +1,21 @@ +0
+1
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+2
+20
+3
+4
+5
+6
+7
+8
+9
diff --git a/tests/ignored_input/t0001.good b/tests/ignored_input/t0001.good new file mode 100644 index 0000000..dce09d9 --- /dev/null +++ b/tests/ignored_input/t0001.good @@ -0,0 +1,19 @@ +$ OUT=$(smenu -c -z '\u0d' t0001.in) + +0 ┐ +4:20 +1 ║ +4:20 +10 │ +4:20 +11 │ +0:07 1:07 4:20 +12 ▼ +4:20 +$ + +$ echo ":$OUT:" + +:11: + +$ exit 0 diff --git a/tests/ignored_input/t0001.in b/tests/ignored_input/t0001.in new file mode 120000 index 0000000..0abc8f1 --- /dev/null +++ b/tests/ignored_input/t0001.in @@ -0,0 +1 @@ +data1
\ No newline at end of file diff --git a/tests/ignored_input/t0001.tst b/tests/ignored_input/t0001.tst new file mode 100644 index 0000000..0ee4876 --- /dev/null +++ b/tests/ignored_input/t0001.tst @@ -0,0 +1,4 @@ +\S[150]\s[10]OUT=$(smenu -c -z '\\u0d' t0001.in) +\S[150]\s[150]lll\r +\S[150]\s[10]echo ":$\s[10]OUT:" +exit 0 diff --git a/tests/ignored_input/t0002.good b/tests/ignored_input/t0002.good new file mode 100644 index 0000000..d33f86e --- /dev/null +++ b/tests/ignored_input/t0002.good @@ -0,0 +1,19 @@ +$ OUT=$(smenu -c -z '1\u0d' t0002.in) + +0 ┐ +4:20 +0 ║ +4:20 +2 │ +4:20 +3 │ +0:07 1:07 4:20 +4 ▼ +4:20 +$ + +$ echo ":$OUT:" + +:3: + +$ exit 0 diff --git a/tests/ignored_input/t0002.in b/tests/ignored_input/t0002.in new file mode 120000 index 0000000..0abc8f1 --- /dev/null +++ b/tests/ignored_input/t0002.in @@ -0,0 +1 @@ +data1
\ No newline at end of file diff --git a/tests/ignored_input/t0002.tst b/tests/ignored_input/t0002.tst new file mode 100644 index 0000000..af671e6 --- /dev/null +++ b/tests/ignored_input/t0002.tst @@ -0,0 +1,4 @@ +\S[150]\s[10]OUT=$(smenu -c -z '1\\u0d' t0002.in) +\S[150]\s[150]lll\r +\S[150]\s[10]echo ":$\s[10]OUT:" +exit 0 diff --git a/tests/ignored_input/t0003.good b/tests/ignored_input/t0003.good new file mode 100644 index 0000000..1b65fe8 --- /dev/null +++ b/tests/ignored_input/t0003.good @@ -0,0 +1,13 @@ +$ OUT=$(smenu -zap 02 t0003.in) + +\r 1\r 1\r 11\r 1\r 13\r 14\r 15\r 16\r 17\r 18\r 19\r \r \r 3\r 4\r 5\r 6\r +11:07 12:07 13:07 14:07 +7\r 8\r 9\r + +$ + +$ echo ":$OUT:" + +:11\r: + +$ exit 0 diff --git a/tests/ignored_input/t0003.in b/tests/ignored_input/t0003.in new file mode 120000 index 0000000..0abc8f1 --- /dev/null +++ b/tests/ignored_input/t0003.in @@ -0,0 +1 @@ +data1
\ No newline at end of file diff --git a/tests/ignored_input/t0003.tst b/tests/ignored_input/t0003.tst new file mode 100644 index 0000000..a5a548e --- /dev/null +++ b/tests/ignored_input/t0003.tst @@ -0,0 +1,4 @@ +\S[150]\s[10]OUT=$(smenu -zap 02 t0003.in) +\S[150]\s[150]lll\r +\S[150]\s[10]echo ":$\s[10]OUT:" +exit 0 diff --git a/tests/ignored_input/t0004.good b/tests/ignored_input/t0004.good new file mode 100644 index 0000000..1971c93 --- /dev/null +++ b/tests/ignored_input/t0004.good @@ -0,0 +1,11 @@ +$ OUT=$(smenu -zap '×\uc2bf«»' t0004.in) + +a±bc a x +7:07 +$ + +$ echo ":$OUT:" + +:x: + +$ exit 0 diff --git a/tests/ignored_input/t0004.in b/tests/ignored_input/t0004.in new file mode 120000 index 0000000..fee93d1 --- /dev/null +++ b/tests/ignored_input/t0004.in @@ -0,0 +1 @@ +data2
\ No newline at end of file diff --git a/tests/ignored_input/t0004.tst b/tests/ignored_input/t0004.tst new file mode 100644 index 0000000..0c8405b --- /dev/null +++ b/tests/ignored_input/t0004.tst @@ -0,0 +1,4 @@ +\S[150]\s[10]OUT=$(smenu -zap '×\\uc2bf«»' t0004.in) +\S[150]\s[150]lll\r +\S[150]\s[10]echo ":$\s[10]OUT:" +exit 0 diff --git a/tests/ignored_input/t0005.good b/tests/ignored_input/t0005.good new file mode 100644 index 0000000..c7c838e --- /dev/null +++ b/tests/ignored_input/t0005.good @@ -0,0 +1,13 @@ +$ OUT=$(smenu -c -L, -z '\u0a\u0d ' t0005.in) + +abcde + +fghi +0:07 1:07 2:07 3:07 4:07 +$ + +$ echo ":$OUT:" + +:fghi: + +$ exit 0 diff --git a/tests/ignored_input/t0005.in b/tests/ignored_input/t0005.in new file mode 120000 index 0000000..cdca2c1 --- /dev/null +++ b/tests/ignored_input/t0005.in @@ -0,0 +1 @@ +data3
\ No newline at end of file diff --git a/tests/ignored_input/t0005.tst b/tests/ignored_input/t0005.tst new file mode 100644 index 0000000..bac782d --- /dev/null +++ b/tests/ignored_input/t0005.tst @@ -0,0 +1,4 @@ +\S[150]\s[10]OUT=$(smenu -c -L, -z '\\u0a\\u0d ' t0005.in) +\S[150]\s[150]j\r +\S[150]\s[10]echo ":$\s[10]OUT:" +exit 0 @@ -34,6 +34,8 @@ common_help(void) printf("-1|-l1|-level1,-2|-l2|-level2,...,-5|-l5|-level5\n"); printf(" gives specific colors to up to 5 classes of " "selectable words.\n"); + printf("-z|-zap|-zap_glyphs bytes\n"); + printf(" defines a set of glyphs to ignore as input.\n"); printf("-n|-lines|-height\n"); printf(" sets the number of lines in the selection window.\n"); printf("-b|-blank\n"); |