xaizek / hstr (License: Apachev2) (since 2018-12-07)
Bash and Zsh shell history suggest box - easily view, navigate, search and manage your command history.
Commit 51672abd4a4fee4662b4ee6226ffee5985112289

Adding wide character aware strlen() for backspace.
Author: Martin Dvorak
Author date (UTC): 2014-09-11 16:28
Committer name: Martin Dvorak
Committer date (UTC): 2014-09-11 16:33
Parent(s): c09566fd29a2ed2c3f69bbf9fd993bf12e04cbcf
Signing key:
Tree: 49f06262865f973561bf0e2aa7d20f5975b41c72
File Lines added Lines deleted
src/hstr.c 16 22
src/hstr_utils.c 40 1
src/include/hstr_utils.h 3 1
tests/src/test_utf8.c 131 5
File src/hstr.c changed (mode: 100644) (index 3512e94..a89afe0)
108 108 #endif #endif
109 109
110 110 #define DEBUG_UTF8 #define DEBUG_UTF8
111
112 111 #ifdef DEBUG_UTF8 #ifdef DEBUG_UTF8
113 #define LOGUTF8(Y,P,C) mvprintw(Y, 0, "strlen(): %d, wcslen(): %d, getch(): %d ",strlen(P),wcslen(P),C)
112 #define LOGUTF8(Y,P) mvprintw(Y, 0, "strlen() %zd, mbstowcs() %zd, hstr_strlen() %d",strlen(P),mbstowcs(NULL,P,0),hstr_strlen(P)); clrtoeol()
114 113 #else #else
115 114 #define LOGUTF8(Y,P) #define LOGUTF8(Y,P)
116 115 #endif #endif
 
... ... static const char *VERSION_STRING=
162 161 "\n build \""__DATE__" " __TIME__"\"" "\n build \""__DATE__" " __TIME__"\""
163 162 "\n"; "\n";
164 163
165 // TODO help screen - tig
164 // TODO help screen - curses window (tig)
166 165 static const char *LABEL_HELP= static const char *LABEL_HELP=
167 166 "Type to filter, UP/DOWN move, DEL remove, TAB select, C-f add favorite, C-g cancel"; "Type to filter, UP/DOWN move, DEL remove, TAB select, C-f add favorite, C-g cancel";
168 167
 
... ... void print_history_label(Hstr *hstr)
370 369 refresh(); refresh();
371 370 } }
372 371
373 void print_prefix(char *pattern, int y, int x)
372 void print_pattern(char *pattern, int y, int x)
374 373 { {
375 374 color_attr_on(A_BOLD); color_attr_on(A_BOLD);
376 375 mvprintw(y, x, "%s", pattern); mvprintw(y, x, "%s", pattern);
 
... ... void loop_to_select(Hstr *hstr)
722 721 // TODO this is too late! > don't render twice // TODO this is too late! > don't render twice
723 722 // TODO overflow // TODO overflow
724 723 strcpy(pattern, hstr->cmdline); strcpy(pattern, hstr->cmdline);
724
725 725 while (!done) { while (!done) {
726 726 maxHistoryItems=get_max_history_items(); maxHistoryItems=get_max_history_items();
727 727
 
... ... void loop_to_select(Hstr *hstr)
769 769 print_history_label(hstr); print_history_label(hstr);
770 770 selectionCursorPosition=SELECTION_CURSOR_IN_PROMPT; selectionCursorPosition=SELECTION_CURSOR_IN_PROMPT;
771 771 if(strlen(pattern)<(width-basex-1)) { if(strlen(pattern)<(width-basex-1)) {
772 print_prefix(pattern, y, basex);
772 print_pattern(pattern, y, basex);
773 773 cursorX=getcurx(stdscr); cursorX=getcurx(stdscr);
774 774 cursorY=getcury(stdscr); cursorY=getcury(stdscr);
775 775 } }
 
... ... void loop_to_select(Hstr *hstr)
781 781 print_history_label(hstr); print_history_label(hstr);
782 782 selectionCursorPosition=SELECTION_CURSOR_IN_PROMPT; selectionCursorPosition=SELECTION_CURSOR_IN_PROMPT;
783 783 if(strlen(pattern)<(width-basex-1)) { if(strlen(pattern)<(width-basex-1)) {
784 print_prefix(pattern, y, basex);
784 print_pattern(pattern, y, basex);
785 785 cursorX=getcurx(stdscr); cursorX=getcurx(stdscr);
786 786 cursorY=getcury(stdscr); cursorY=getcury(stdscr);
787 787 } }
 
... ... void loop_to_select(Hstr *hstr)
793 793 // TODO function // TODO function
794 794 selectionCursorPosition=SELECTION_CURSOR_IN_PROMPT; selectionCursorPosition=SELECTION_CURSOR_IN_PROMPT;
795 795 if(strlen(pattern)<(width-basex-1)) { if(strlen(pattern)<(width-basex-1)) {
796 print_prefix(pattern, y, basex);
796 print_pattern(pattern, y, basex);
797 797 cursorX=getcurx(stdscr); cursorX=getcurx(stdscr);
798 798 cursorY=getcury(stdscr); cursorY=getcury(stdscr);
799 799 } }
 
... ... void loop_to_select(Hstr *hstr)
813 813 selectionCursorPosition=SELECTION_CURSOR_IN_PROMPT; selectionCursorPosition=SELECTION_CURSOR_IN_PROMPT;
814 814 // TODO code review // TODO code review
815 815 if(strlen(pattern)<(width-basex-1)) { if(strlen(pattern)<(width-basex-1)) {
816 print_prefix(pattern, y, basex);
816 print_pattern(pattern, y, basex);
817 817 cursorX=getcurx(stdscr); cursorX=getcurx(stdscr);
818 818 cursorY=getcury(stdscr); cursorY=getcury(stdscr);
819 819 } }
 
... ... void loop_to_select(Hstr *hstr)
829 829 case K_CTRL_U: case K_CTRL_U:
830 830 case K_CTRL_W: // TODO supposed to delete just one word backward case K_CTRL_W: // TODO supposed to delete just one word backward
831 831 pattern[0]=0; pattern[0]=0;
832 print_prefix(pattern, y, basex);
832 print_pattern(pattern, y, basex);
833 833 break; break;
834 834 case K_CTRL_L: case K_CTRL_L:
835 835 toggle_case(pattern, lowercase); toggle_case(pattern, lowercase);
836 836 lowercase=!lowercase; lowercase=!lowercase;
837 print_prefix(pattern, y, basex);
837 print_pattern(pattern, y, basex);
838 838 selectionCursorPosition=SELECTION_CURSOR_IN_PROMPT; selectionCursorPosition=SELECTION_CURSOR_IN_PROMPT;
839 839 break; break;
840 840 case K_CTRL_H: case K_CTRL_H:
841 841 case K_BACKSPACE: case K_BACKSPACE:
842 842 case KEY_BACKSPACE: case KEY_BACKSPACE:
843 TODO count how many characters to move back in case of utf8 strings/multibyte
844
845 > google strlen(pattern) for wide characters/multibyte
846 SOME SOLUTION: iterate over pattern and use function from debug to determine size of one char
847
848 if(strlen(pattern)>0) {
849 pattern[strlen(pattern)-1]=0;
843 if(hstr_strlen(pattern)>0) {
844 hstr_chop(pattern);
850 845 x--; x--;
851 print_prefix(pattern, y, basex);
846 print_pattern(pattern, y, basex);
852 847 } }
853 848
854 849 // TODO why I make selection if it's done in print_selection? // TODO why I make selection if it's done in print_selection?
 
... ... void loop_to_select(Hstr *hstr)
859 854 } }
860 855 result=hstr_print_selection(maxHistoryItems, pattern, hstr); result=hstr_print_selection(maxHistoryItems, pattern, hstr);
861 856
862 move(y, basex+strlen(pattern));
857 move(y, basex+hstr_strlen(pattern));
863 858 break; break;
864 859 case KEY_UP: case KEY_UP:
865 860 previousSelectionCursorPosition=selectionCursorPosition; previousSelectionCursorPosition=selectionCursorPosition;
 
... ... void loop_to_select(Hstr *hstr)
917 912 default: default:
918 913 LOGKEYS(Y_OFFSET_HELP, c); LOGKEYS(Y_OFFSET_HELP, c);
919 914 LOGCURSOR(Y_OFFSET_HELP); LOGCURSOR(Y_OFFSET_HELP);
915 LOGUTF8(Y_OFFSET_HELP,pattern);
920 916
921 917 if(c>K_CTRL_Z) { if(c>K_CTRL_Z) {
922 918 selectionCursorPosition=SELECTION_CURSOR_IN_PROMPT; selectionCursorPosition=SELECTION_CURSOR_IN_PROMPT;
923 919
924 920 if(strlen(pattern)<(width-basex-1)) { if(strlen(pattern)<(width-basex-1)) {
925 LOGUTF8(Y_OFFSET_HELP,pattern,c);
926
927 921 strcat(pattern, (char*)(&c)); strcat(pattern, (char*)(&c));
928 print_prefix(pattern, y, basex);
922 print_pattern(pattern, y, basex);
929 923 cursorX=getcurx(stdscr); cursorX=getcurx(stdscr);
930 924 cursorY=getcury(stdscr); cursorY=getcury(stdscr);
931 925 } }
File src/hstr_utils.c changed (mode: 100644) (index 0589f74..720663b)
... ... char *hstr_strdup(const char * s)
19 19 { {
20 20 size_t len = 1+strlen(s); size_t len = 1+strlen(s);
21 21 char *p = malloc(len); char *p = malloc(len);
22
23 22 return p ? memcpy(p, s, len) : NULL; return p ? memcpy(p, s, len) : NULL;
24 23 } }
25 24
25 // wide char aware strlen()
26 int hstr_strlen(const char *s)
27 {
28 if(s) {
29 int result=0;
30 bool isHighBitSet=false;
31 int i=0;
32 while(s[i]) {
33 if(1<<7 & s[i]) {
34 if(isHighBitSet) {
35 isHighBitSet=false;
36 result++;
37 } else {
38 isHighBitSet=true;
39 }
40 } else {
41 result++;
42 }
43 i++;
44 }
45 return result;
46 } else {
47 return 0;
48 }
49 }
50
51 void hstr_chop(char *s)
52 {
53 if(s) {
54 int i=strlen(s);
55 if(i) {
56 if(1<<7 & s[i-1]) {
57 s[i-2]=0;
58 } else {
59 s[i-1]=0;
60 }
61 }
62 }
63 }
64
26 65 void tiocsti() void tiocsti()
27 66 { {
28 67 char buf[] = DEFAULT_COMMAND; char buf[] = DEFAULT_COMMAND;
File src/include/hstr_utils.h changed (mode: 100644) (index 2fd7a94..6315319)
20 20 #define MIN(a,b) (((a)<(b))?(a):(b)) #define MIN(a,b) (((a)<(b))?(a):(b))
21 21 #define MAX(a,b) (((a)>(b))?(a):(b)) #define MAX(a,b) (((a)>(b))?(a):(b))
22 22
23 char *hstr_strdup(const char * s);
23 char *hstr_strdup(const char *s);
24 int hstr_strlen(const char *s);
25 void hstr_chop(char *s);
24 26 void tiocsti(); void tiocsti();
25 27 void fill_terminal_input(char* cmd, bool padding); void fill_terminal_input(char* cmd, bool padding);
26 28 void reverse_char_pointer_array(char **array, unsigned length); void reverse_char_pointer_array(char **array, unsigned length);
File tests/src/test_utf8.c changed (mode: 100644) (index 7f2f269..98ba726)
10 10 #include <locale.h> #include <locale.h>
11 11 #include <wchar.h> #include <wchar.h>
12 12 #include <stdio.h> #include <stdio.h>
13 #include <stdlib.h>
13 14 #include <ncurses.h> #include <ncurses.h>
14 15 #include <readline/readline.h> #include <readline/readline.h>
15 16 #include <readline/chardefs.h> #include <readline/chardefs.h>
16 17
18 #define BITS_IN_BYTE 8
19 #define INTEGRAL_TYPE char
20 void show_bits(INTEGRAL_TYPE x) {
21 int i;
22 static int intSizeInBits = sizeof(INTEGRAL_TYPE) * BITS_IN_BYTE;
23 static char symbol[2] = {'0','1'};
24 char * binary = (char *)malloc(intSizeInBits + 1);
25
26 memset(binary, 0, intSizeInBits + 1);
27
28 for (i=0; i< intSizeInBits; i++) {
29 binary[intSizeInBits-i-1] = symbol[(x>>i) & 0x01];
30 }
31
32 printf("\n%s", binary);
33 printf("\n1234567.1234567.1234567.1234567.");
34 free(binary);
35 }
36
17 37 void console_echo_czech() void console_echo_czech()
18 38 { {
19 39 int c; int c;
20 40 while(1) { while(1) {
21 41 c = getc(stdin); c = getc(stdin);
22 42 printf("\nKey: '%3d', char: '%c'", c, c); printf("\nKey: '%3d', char: '%c'", c, c);
43 show_bits(c);
23 44 } }
24 45 } }
25 46
47 void loop_string() {
48 // char *s="a";
49 char *s="Ča";
50 // char *s="Čeština";
51 int i;
52 for(i=0; i<10; i++) {
53 printf("\n%d",s[i]);
54 show_bits(s[i]);
55 if(!s[i]) break;
56 }
57 }
58
59 void get_string_length() {
60 char *s="Čeština";
61 wchar_t *w=L"Čeština";
62
63 printf("%s (7): strlen(): %zd, mbstowcs(): %zd, wcslen(): %zd",
64 s,
65 strlen(s),
66 mbstowcs(NULL,s,0),
67 wcslen(w)); // OK
68 }
69
26 70 void console_static_wide_czech() void console_static_wide_czech()
27 71 { {
28 72 setlocale(LC_ALL, ""); setlocale(LC_ALL, "");
 
... ... void curses_wide_czech()
67 111 endwin(); endwin();
68 112 } }
69 113
114 void print_char_bits(int y, int x, char c) {
115 int i;
116 static int intSizeInBits = sizeof(char) * 8;
117 static char symbol[2] = {'0','1'};
118 char * binary = (char *)malloc(intSizeInBits + 1);
119 memset(binary, 0, intSizeInBits + 1);
120 for (i=0; i< intSizeInBits; i++) {
121 binary[intSizeInBits-i-1] = symbol[(c>>i) & 0x01];
122 }
123 mvprintw(y, x, "%s", binary);
124 free(binary);
125
126 }
127
128 void getch_with_counter_curses() {
129 // TODO implement getch with counter; getch result analysis (trip); append analysis; ...
130
131 initscr();
132 keypad(stdscr, TRUE);
133 noecho();
134 start_color();
135 use_default_colors();
136
137 char pattern[512];
138 int c;
139
140 pattern[0]=0;
141 while (1) {
142 c = wgetch(stdscr);
143 strcat(pattern, (char*)(&c));
144 mvprintw(2, 0, "Pattern '%s'", pattern);
145 mvprintw(3, 0, "Char '%d'", c);
146
147 mvprintw(6, 0, "strlen() '%d'", strlen(pattern));
148 mvprintw(7, 0, "mbstowcs() '%d'", mbstowcs(NULL,pattern,0));
149
150 int i;
151 int intSizeInBits = sizeof(int) * 8;
152 char symbol[2] = {'0','1'};
153 char * binary = (char *)malloc(intSizeInBits + 1);
154 memset(binary, 0, intSizeInBits + 1);
155 for (i=0; i< intSizeInBits; i++) {
156 binary[intSizeInBits-i-1] = symbol[(c>>i) & 0x01];
157 }
158 mvprintw(10, 0, "bits: %s", binary);
159 free(binary);
160
161 mvprintw(11, 0, "high bit: %d %d ", 1<<7, 1<<7 & c);
162
163 char cc=pattern[0];
164 i=0;
165 int myStrlen=0;
166 char isHighBitSet=0;
167 while(cc) {
168 print_char_bits(12, 9*i-8, pattern[i++]);
169 cc=pattern[i];
170
171 if(1<<7 & pattern[i]) {
172 if(isHighBitSet) {
173 isHighBitSet=0;
174 myStrlen++;
175 } else {
176 isHighBitSet=1;
177 }
178 } else {
179 myStrlen++;
180 }
181 }
182
183 mvprintw(14, 0, "mystrlen(): %d ", myStrlen);
184 }
185
186 clear();
187 refresh();
188 doupdate();
189 endwin();
190 }
191
192 void done() {
193 printf("\n\n");
194 }
195
70 196 int main(int argc, char *argv[]) int main(int argc, char *argv[])
71 197 { {
72 198 //console_check(); //console_check();
73 199 //console_static_czech(); //console_static_czech();
74 200 //console_static_wide_czech(); //console_static_wide_czech();
75 201 //console_echo_czech(); //console_echo_czech();
202 //curses_wide_czech();
203 //get_string_length();
204 //loop_string();
76 205
77 // TODO study print_selection_row()
78
79 curses_wide_czech();
80
81 printf("\n\n");
206 getch_with_counter_curses();
207 done();
82 208 } }
Hints

Before first commit, do not forget to setup your git environment:
git config --global user.name "your_name_here"
git config --global user.email "your@email_here"

Clone this repository using HTTP(S):
git clone https://code.reversed.top/user/xaizek/hstr

Clone this repository using ssh (do not forget to upload a key first):
git clone ssh://rocketgit@code.reversed.top/user/xaizek/hstr

You are allowed to anonymously push to this repository.
This means that your pushed commits will automatically be transformed into a pull request:
... clone the repository ...
... make some changes and some commits ...
git push origin master