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 f2ea910c7b1263b0a306558bc69e6d25769e8a46

TABs > SPACEs
Author: Martin Dvorak
Author date (UTC): 2014-10-29 18:28
Committer name: Martin Dvorak
Committer date (UTC): 2014-10-29 18:28
Parent(s): 9e7a900fad85f96d221de048aa15cd5df1fcc3d2
Signing key:
Tree: 1ea322be5d5621e4d60288856f5ca35892619d27
File Lines added Lines deleted
src/hstr.c 830 830
File src/hstr.c changed (mode: 100644) (index 821b1f0..e106d86)
67 67 #define HH_COLOR_PROMPT 3 #define HH_COLOR_PROMPT 3
68 68 #define HH_COLOR_DELETE 4 #define HH_COLOR_DELETE 4
69 69
70 #define HH_ENV_VAR_CONFIG "HH_CONFIG"
71
72 #define HH_CONFIG_MONO "monochromatic"
73 #define HH_CONFIG_HICOLOR "hicolor"
74 #define HH_CONFIG_CASE "casesensitive"
75 #define HH_CONFIG_REGEXP "regexp"
76 #define HH_CONFIG_SUBSTRING "substring"
77 #define HH_CONFIG_KEYWORDS "keywords"
78 #define HH_CONFIG_SORTING "rawhistory"
79 #define HH_CONFIG_FAVORITES "favorites"
80 #define HH_CONFIG_DEBUG "debug"
81 #define HH_CONFIG_WARN "warning"
70 #define HH_ENV_VAR_CONFIG "HH_CONFIG"
71
72 #define HH_CONFIG_MONO "monochromatic"
73 #define HH_CONFIG_HICOLOR "hicolor"
74 #define HH_CONFIG_CASE "casesensitive"
75 #define HH_CONFIG_REGEXP "regexp"
76 #define HH_CONFIG_SUBSTRING "substring"
77 #define HH_CONFIG_KEYWORDS "keywords"
78 #define HH_CONFIG_SORTING "rawhistory"
79 #define HH_CONFIG_FAVORITES "favorites"
80 #define HH_CONFIG_DEBUG "debug"
81 #define HH_CONFIG_WARN "warning"
82 82
83 83 #define HH_DEBUG_LEVEL_NONE 0 #define HH_DEBUG_LEVEL_NONE 0
84 84 #define HH_DEBUG_LEVEL_WARN 1 #define HH_DEBUG_LEVEL_WARN 1
85 85 #define HH_DEBUG_LEVEL_DEBUG 2 #define HH_DEBUG_LEVEL_DEBUG 2
86 86
87 #define HH_VIEW_RANKING 0
88 #define HH_VIEW_HISTORY 1
89 #define HH_VIEW_FAVORITES 2
87 #define HH_VIEW_RANKING 0
88 #define HH_VIEW_HISTORY 1
89 #define HH_VIEW_FAVORITES 2
90 90
91 #define HH_MATCH_SUBSTRING 0
92 #define HH_MATCH_REGEXP 1
93 #define HH_MATCH_KEYWORDS 2
91 #define HH_MATCH_SUBSTRING 0
92 #define HH_MATCH_REGEXP 1
93 #define HH_MATCH_KEYWORDS 2
94 94
95 #define HH_NUM_HISTORY_MATCH 3
95 #define HH_NUM_HISTORY_MATCH 3
96 96
97 #define HH_CASE_INSENSITIVE 0
98 #define HH_CASE_SENSITIVE 1
97 #define HH_CASE_INSENSITIVE 0
98 #define HH_CASE_SENSITIVE 1
99 99
100 100 #define SPACE_PADDING " " #define SPACE_PADDING " "
101 101
 
118 118 #endif #endif
119 119
120 120 void logstring(char* str){ void logstring(char* str){
121 FILE *f = fopen("/home/tbabej/hh.log", "a");
122 if (f == NULL)
123 {
121 FILE *f = fopen("/home/tbabej/hh.log", "a");
122 if (f == NULL)
123 {
124 124 perror("fopen"); perror("fopen");
125 printf("Error opening file!\n");
126 exit(1);
127 }
128 fprintf(f, "Debug: %s \n", str);
125 printf("Error opening file!\n");
126 exit(1);
127 }
128 fprintf(f, "Debug: %s \n", str);
129 129 fclose(f); fclose(f);
130 130 } }
131 131
132 132 static const char *HH_VIEW_LABELS[]={ static const char *HH_VIEW_LABELS[]={
133 "ranking",
134 "history",
135 "favorites"
133 "ranking",
134 "history",
135 "favorites"
136 136 }; };
137 137
138 138 static const char *HH_MATCH_LABELS[]={ static const char *HH_MATCH_LABELS[]={
139 "exact",
140 "regexp",
141 "keywords"
139 "exact",
140 "regexp",
141 "keywords"
142 142 }; };
143 143
144 144 static const char *HH_CASE_LABELS[]={ static const char *HH_CASE_LABELS[]={
145 "insensitive",
146 "sensitive"
145 "insensitive",
146 "sensitive"
147 147 }; };
148 148
149 149 static const char *INSTALL_STRING= static const char *INSTALL_STRING=
150 "\n# add this configuration to ~/.bashrc"
151 "\nexport HH_CONFIG=hicolor # get more colors"
152 "\nshopt -s histappend # append new history items to .bash_history"
153 "\nexport HISTCONTROL=ignorespace # leading space hides commands from history"
154 "\nexport HISTFILESIZE=10000 # increase history file size (default is 500)"
155 "\nexport HISTSIZE=${HISTFILESIZE} # increase history size (default is 500)"
156 "\nexport PROMPT_COMMAND=\"history -a; history -n; ${PROMPT_COMMAND}\" # mem/file sync"
157 "\n# if this is interactive shell, then bind hh to Ctrl-r"
158 "\nif [[ $- =~ .*i.* ]]; then bind '\"\\C-r\": \"\\C-a hh \\C-j\"'; fi"
159 "\n\n";
150 "\n# add this configuration to ~/.bashrc"
151 "\nexport HH_CONFIG=hicolor # get more colors"
152 "\nshopt -s histappend # append new history items to .bash_history"
153 "\nexport HISTCONTROL=ignorespace # leading space hides commands from history"
154 "\nexport HISTFILESIZE=10000 # increase history file size (default is 500)"
155 "\nexport HISTSIZE=${HISTFILESIZE} # increase history size (default is 500)"
156 "\nexport PROMPT_COMMAND=\"history -a; history -n; ${PROMPT_COMMAND}\" # mem/file sync"
157 "\n# if this is interactive shell, then bind hh to Ctrl-r"
158 "\nif [[ $- =~ .*i.* ]]; then bind '\"\\C-r\": \"\\C-a hh \\C-j\"'; fi"
159 "\n\n";
160 160
161 161 static const char *HELP_STRING= static const char *HELP_STRING=
162 "Usage: hh [option] [arg1] [arg2]..."
163 "\nShell history suggest box:"
164 "\n"
165 "\n --favorites -f ... show favorites view"
166 "\n --non-interactive -n ... print filtered history and exit"
167 "\n --show-configuration -s ... show configuration to be added to ~/.bashrc"
168 "\n --version -V ... show version details"
169 "\n --help -h ... help"
170 "\n"
171 "\nReport bugs to martin.dvorak@mindforger.com"
172 "\nHome page: https://github.com/dvorka/hstr"
173 "\n";
162 "Usage: hh [option] [arg1] [arg2]..."
163 "\nShell history suggest box:"
164 "\n"
165 "\n --favorites -f ... show favorites view"
166 "\n --non-interactive -n ... print filtered history and exit"
167 "\n --show-configuration -s ... show configuration to be added to ~/.bashrc"
168 "\n --version -V ... show version details"
169 "\n --help -h ... help"
170 "\n"
171 "\nReport bugs to martin.dvorak@mindforger.com"
172 "\nHome page: https://github.com/dvorka/hstr"
173 "\n";
174 174
175 175 static const char *VERSION_STRING= static const char *VERSION_STRING=
176 "hh version \"1.13\""
177 "\n build \""__DATE__" " __TIME__"\""
178 "\n";
176 "hh version \"1.14\""
177 "\n build \""__DATE__" " __TIME__"\""
178 "\n";
179 179
180 180 // TODO help screen - curses window (tig) // TODO help screen - curses window (tig)
181 181 static const char *LABEL_HELP= static const char *LABEL_HELP=
182 "Type to filter, UP/DOWN move, DEL remove, TAB select, C-f add favorite, C-g cancel";
182 "Type to filter, UP/DOWN move, DEL remove, TAB select, C-f add favorite, C-g cancel";
183 183
184 #define GETOPT_NO_ARGUMENT 0
185 #define GETOPT_REQUIRED_ARGUMENT 1
186 #define GETOPT_OPTIONAL_ARGUMENT 2
184 #define GETOPT_NO_ARGUMENT 0
185 #define GETOPT_REQUIRED_ARGUMENT 1
186 #define GETOPT_OPTIONAL_ARGUMENT 2
187 187
188 188 static const struct option long_options[] = { static const struct option long_options[] = {
189 {"favorites", GETOPT_NO_ARGUMENT, NULL, 'f'},
190 {"version", GETOPT_NO_ARGUMENT, NULL, 'V'},
191 {"help", GETOPT_NO_ARGUMENT, NULL, 'h'},
192 {"non-interactive", GETOPT_NO_ARGUMENT, NULL, 'n'},
193 {"show-configuration", GETOPT_NO_ARGUMENT, NULL, 's'},
194 {0, 0, NULL, 0 }
189 {"favorites", GETOPT_NO_ARGUMENT, NULL, 'f'},
190 {"version", GETOPT_NO_ARGUMENT, NULL, 'V'},
191 {"help", GETOPT_NO_ARGUMENT, NULL, 'h'},
192 {"non-interactive", GETOPT_NO_ARGUMENT, NULL, 'n'},
193 {"show-configuration", GETOPT_NO_ARGUMENT, NULL, 's'},
194 {0, 0, NULL, 0 }
195 195 }; };
196 196
197 197 typedef struct { typedef struct {
198 HistoryItems *history;
199 FavoriteItems *favorites;
198 HistoryItems *history;
199 FavoriteItems *favorites;
200 200
201 char **selection;
202 regmatch_t *selectionRegexpMatch;
203 unsigned selectionSize;
201 char **selection;
202 regmatch_t *selectionRegexpMatch;
203 unsigned selectionSize;
204 204
205 int historyMatch; // TODO patternMatching: exact, regexp
206 int historyView; // TODO view: favs, ...
207 int caseSensitive;
205 int historyMatch; // TODO patternMatching: exact, regexp
206 int historyView; // TODO view: favs, ...
207 int caseSensitive;
208 208
209 bool interactive;
209 bool interactive;
210 210
211 bool hicolor;
212 int debugLevel;
211 bool hicolor;
212 int debugLevel;
213 213
214 HstrRegexp regexp;
214 HstrRegexp regexp;
215 215
216 char cmdline[CMDLINE_LNG];
216 char cmdline[CMDLINE_LNG];
217 217 } Hstr; } Hstr;
218 218
219 219 static Hstr *hstr; static Hstr *hstr;
220 220
221 221 void hstr_init(Hstr *hstr) void hstr_init(Hstr *hstr)
222 222 { {
223 hstr->selection=NULL;
224 hstr->selectionRegexpMatch=NULL;
225 hstr->selectionSize=0;
223 hstr->selection=NULL;
224 hstr->selectionRegexpMatch=NULL;
225 hstr->selectionSize=0;
226 226
227 hstr->historyMatch=HH_MATCH_SUBSTRING;
228 hstr->historyView=HH_VIEW_RANKING;
229 hstr->caseSensitive=HH_CASE_INSENSITIVE;
227 hstr->historyMatch=HH_MATCH_SUBSTRING;
228 hstr->historyView=HH_VIEW_RANKING;
229 hstr->caseSensitive=HH_CASE_INSENSITIVE;
230 230
231 hstr->interactive=true;
231 hstr->interactive=true;
232 232
233 hstr->hicolor=FALSE;
233 hstr->hicolor=FALSE;
234 234
235 hstr->debugLevel=HH_DEBUG_LEVEL_NONE;
236 hstr->cmdline[0]=0;
237 hstr_regexp_init(&hstr->regexp);
235 hstr->debugLevel=HH_DEBUG_LEVEL_NONE;
236 hstr->cmdline[0]=0;
237 hstr_regexp_init(&hstr->regexp);
238 238 } }
239 239
240 240 void hstr_get_env_configuration(Hstr *hstr) void hstr_get_env_configuration(Hstr *hstr)
241 241 { {
242 char *hstr_config=getenv(HH_ENV_VAR_CONFIG);
243 if(hstr_config && strlen(hstr_config)>0) {
244 if(strstr(hstr_config,HH_CONFIG_HICOLOR)) {
245 hstr->hicolor=TRUE;
246 }
247 if(strstr(hstr_config,HH_CONFIG_CASE)) {
248 hstr->caseSensitive=HH_CASE_SENSITIVE;
249 }
250 if(strstr(hstr_config,HH_CONFIG_REGEXP)) {
251 hstr->historyMatch=HH_MATCH_REGEXP;
252 } else {
253 if(strstr(hstr_config,HH_CONFIG_SUBSTRING)) {
254 hstr->historyMatch=HH_MATCH_SUBSTRING;
255 } else {
256 if(strstr(hstr_config, HH_CONFIG_KEYWORDS)) {
257 hstr->historyMatch=HH_MATCH_KEYWORDS;
258 }
259 }
260 }
261 if(strstr(hstr_config,HH_CONFIG_SORTING)) {
262 hstr->historyView=HH_VIEW_HISTORY;
263 } else {
264 if(strstr(hstr_config,HH_CONFIG_FAVORITES)) {
265 hstr->historyView=HH_VIEW_FAVORITES;
266 }
267 }
268
269 if(strstr(hstr_config,HH_CONFIG_DEBUG)) {
270 hstr->debugLevel=HH_DEBUG_LEVEL_DEBUG;
271 } else {
272 if(strstr(hstr_config,HH_CONFIG_WARN)) {
273 hstr->debugLevel=HH_DEBUG_LEVEL_WARN;
274 }
275 }
276 }
242 char *hstr_config=getenv(HH_ENV_VAR_CONFIG);
243 if(hstr_config && strlen(hstr_config)>0) {
244 if(strstr(hstr_config,HH_CONFIG_HICOLOR)) {
245 hstr->hicolor=TRUE;
246 }
247 if(strstr(hstr_config,HH_CONFIG_CASE)) {
248 hstr->caseSensitive=HH_CASE_SENSITIVE;
249 }
250 if(strstr(hstr_config,HH_CONFIG_REGEXP)) {
251 hstr->historyMatch=HH_MATCH_REGEXP;
252 } else {
253 if(strstr(hstr_config,HH_CONFIG_SUBSTRING)) {
254 hstr->historyMatch=HH_MATCH_SUBSTRING;
255 } else {
256 if(strstr(hstr_config, HH_CONFIG_KEYWORDS)) {
257 hstr->historyMatch=HH_MATCH_KEYWORDS;
258 }
259 }
260 }
261 if(strstr(hstr_config,HH_CONFIG_SORTING)) {
262 hstr->historyView=HH_VIEW_HISTORY;
263 } else {
264 if(strstr(hstr_config,HH_CONFIG_FAVORITES)) {
265 hstr->historyView=HH_VIEW_FAVORITES;
266 }
267 }
268
269 if(strstr(hstr_config,HH_CONFIG_DEBUG)) {
270 hstr->debugLevel=HH_DEBUG_LEVEL_DEBUG;
271 } else {
272 if(strstr(hstr_config,HH_CONFIG_WARN)) {
273 hstr->debugLevel=HH_DEBUG_LEVEL_WARN;
274 }
275 }
276 }
277 277 } }
278 278
279 279 int print_prompt(Hstr *hstr) int print_prompt(Hstr *hstr)
280 280 { {
281 char *user = getenv(ENV_VAR_USER);
282 char *hostname=malloc(HOSTNAME_BUFFER);
283 int xoffset = 0, promptLength;
284
285 if(hstr->hicolor) {
286 color_attr_on(COLOR_PAIR(HH_COLOR_PROMPT));
287 color_attr_on(A_BOLD);
288 }
289 user=(user?user:"me");
290 get_hostname(HOSTNAME_BUFFER, hostname);
291 mvprintw(Y_OFFSET_PROMPT, xoffset, "%s@%s$ ", user, hostname);
292 promptLength=strlen(user)+1+strlen(hostname)+1+1;
293 free(hostname);
294 if(hstr->hicolor) {
295 color_attr_off(A_BOLD);
296 color_attr_off(COLOR_PAIR(HH_COLOR_PROMPT));
297 }
298 refresh();
299
300 return promptLength;
281 char *user = getenv(ENV_VAR_USER);
282 char *hostname=malloc(HOSTNAME_BUFFER);
283 int xoffset = 0, promptLength;
284
285 if(hstr->hicolor) {
286 color_attr_on(COLOR_PAIR(HH_COLOR_PROMPT));
287 color_attr_on(A_BOLD);
288 }
289 user=(user?user:"me");
290 get_hostname(HOSTNAME_BUFFER, hostname);
291 mvprintw(Y_OFFSET_PROMPT, xoffset, "%s@%s$ ", user, hostname);
292 promptLength=strlen(user)+1+strlen(hostname)+1+1;
293 free(hostname);
294 if(hstr->hicolor) {
295 color_attr_off(A_BOLD);
296 color_attr_off(COLOR_PAIR(HH_COLOR_PROMPT));
297 }
298 refresh();
299
300 return promptLength;
301 301 } }
302 302
303 303 void print_help_label() void print_help_label()
304 304 { {
305 char screenLine[CMDLINE_LNG];
306 snprintf(screenLine, getmaxx(stdscr), "%s", LABEL_HELP);
307 mvprintw(Y_OFFSET_HELP, 0, "%s", screenLine); clrtoeol();
308 refresh();
305 char screenLine[CMDLINE_LNG];
306 snprintf(screenLine, getmaxx(stdscr), "%s", LABEL_HELP);
307 mvprintw(Y_OFFSET_HELP, 0, "%s", screenLine); clrtoeol();
308 refresh();
309 309 } }
310 310
311 311 void print_cmd_deleted_label(const char *cmd, int occurences, Hstr *hstr) void print_cmd_deleted_label(const char *cmd, int occurences, Hstr *hstr)
312 312 { {
313 char screenLine[CMDLINE_LNG];
314 snprintf(screenLine, getmaxx(stdscr), "History item '%s' deleted (%d occurrence%s)", cmd, occurences, (occurences==1?"":"s"));
315 // TODO make this function
316 if(hstr->hicolor) {
317 color_attr_on(COLOR_PAIR(HH_COLOR_DELETE));
318 color_attr_on(A_BOLD);
319 }
320 mvprintw(Y_OFFSET_HELP, 0, "%s", screenLine);
321 if(hstr->hicolor) {
322 color_attr_off(A_BOLD);
323 color_attr_on(COLOR_PAIR(1));
324 }
325 clrtoeol();
326 refresh();
313 char screenLine[CMDLINE_LNG];
314 snprintf(screenLine, getmaxx(stdscr), "History item '%s' deleted (%d occurrence%s)", cmd, occurences, (occurences==1?"":"s"));
315 // TODO make this function
316 if(hstr->hicolor) {
317 color_attr_on(COLOR_PAIR(HH_COLOR_DELETE));
318 color_attr_on(A_BOLD);
319 }
320 mvprintw(Y_OFFSET_HELP, 0, "%s", screenLine);
321 if(hstr->hicolor) {
322 color_attr_off(A_BOLD);
323 color_attr_on(COLOR_PAIR(1));
324 }
325 clrtoeol();
326 refresh();
327 327 } }
328 328
329 329 void print_regexp_error(const char *errorMessage) void print_regexp_error(const char *errorMessage)
330 330 { {
331 char screenLine[CMDLINE_LNG];
332 snprintf(screenLine, getmaxx(stdscr), "%s", errorMessage);
333 if(hstr->hicolor) {
334 color_attr_on(COLOR_PAIR(HH_COLOR_DELETE));
335 color_attr_on(A_BOLD);
336 }
337 mvprintw(Y_OFFSET_HELP, 0, "%s", screenLine);
338 if(hstr->hicolor) {
339 color_attr_off(A_BOLD);
340 color_attr_on(COLOR_PAIR(1));
341 }
342 clrtoeol();
343 refresh();
331 char screenLine[CMDLINE_LNG];
332 snprintf(screenLine, getmaxx(stdscr), "%s", errorMessage);
333 if(hstr->hicolor) {
334 color_attr_on(COLOR_PAIR(HH_COLOR_DELETE));
335 color_attr_on(A_BOLD);
336 }
337 mvprintw(Y_OFFSET_HELP, 0, "%s", screenLine);
338 if(hstr->hicolor) {
339 color_attr_off(A_BOLD);
340 color_attr_on(COLOR_PAIR(1));
341 }
342 clrtoeol();
343 refresh();
344 344 } }
345 345
346 346 void print_cmd_added_favorite_label(const char *cmd, Hstr *hstr) void print_cmd_added_favorite_label(const char *cmd, Hstr *hstr)
347 347 { {
348 char screenLine[CMDLINE_LNG];
349 snprintf(screenLine, getmaxx(stdscr), "Command '%s' added to favorites (C-/ to show favorites)", cmd);
350 if(hstr->hicolor) {
351 color_attr_on(COLOR_PAIR(HH_COLOR_INFO));
352 color_attr_on(A_BOLD);
353 }
354 mvprintw(Y_OFFSET_HELP, 0, screenLine);
355 if(hstr->hicolor) {
356 color_attr_off(A_BOLD);
357 color_attr_on(COLOR_PAIR(1));
358 }
359 clrtoeol();
360 refresh();
348 char screenLine[CMDLINE_LNG];
349 snprintf(screenLine, getmaxx(stdscr), "Command '%s' added to favorites (C-/ to show favorites)", cmd);
350 if(hstr->hicolor) {
351 color_attr_on(COLOR_PAIR(HH_COLOR_INFO));
352 color_attr_on(A_BOLD);
353 }
354 mvprintw(Y_OFFSET_HELP, 0, screenLine);
355 if(hstr->hicolor) {
356 color_attr_off(A_BOLD);
357 color_attr_on(COLOR_PAIR(1));
358 }
359 clrtoeol();
360 refresh();
361 361 } }
362 362
363 363 void print_history_label(Hstr *hstr) void print_history_label(Hstr *hstr)
364 364 { {
365 int width=getmaxx(stdscr);
366
367 char screenLine[CMDLINE_LNG];
368 snprintf(screenLine, width, "- HISTORY - view:%s (C-/) - match:%s (C-e) - case:%s (C-t) - %d/%d/%d ",
369 HH_VIEW_LABELS[hstr->historyView],
370 HH_MATCH_LABELS[hstr->historyMatch],
371 HH_CASE_LABELS[hstr->caseSensitive],
372 hstr->history->count,
373 hstr->history->rawCount,
374 hstr->favorites->count);
375 width -= strlen(screenLine);
376 unsigned i;
377 for (i=0; i < width; i++) {
378 strcat(screenLine, "-");
379 }
380 if(hstr->hicolor) {
381 color_attr_on(A_BOLD);
382 }
383 color_attr_on(A_REVERSE);
384 mvprintw(Y_OFFSET_HISTORY, 0, "%s", screenLine);
385 color_attr_off(A_REVERSE);
386 if(hstr->hicolor) {
387 color_attr_off(A_BOLD);
388 }
389 refresh();
365 int width=getmaxx(stdscr);
366
367 char screenLine[CMDLINE_LNG];
368 snprintf(screenLine, width, "- HISTORY - view:%s (C-/) - match:%s (C-e) - case:%s (C-t) - %d/%d/%d ",
369 HH_VIEW_LABELS[hstr->historyView],
370 HH_MATCH_LABELS[hstr->historyMatch],
371 HH_CASE_LABELS[hstr->caseSensitive],
372 hstr->history->count,
373 hstr->history->rawCount,
374 hstr->favorites->count);
375 width -= strlen(screenLine);
376 unsigned i;
377 for (i=0; i < width; i++) {
378 strcat(screenLine, "-");
379 }
380 if(hstr->hicolor) {
381 color_attr_on(A_BOLD);
382 }
383 color_attr_on(A_REVERSE);
384 mvprintw(Y_OFFSET_HISTORY, 0, "%s", screenLine);
385 color_attr_off(A_REVERSE);
386 if(hstr->hicolor) {
387 color_attr_off(A_BOLD);
388 }
389 refresh();
390 390 } }
391 391
392 392 void print_pattern(char *pattern, int y, int x) void print_pattern(char *pattern, int y, int x)
393 393 { {
394 color_attr_on(A_BOLD);
395 mvprintw(y, x, "%s", pattern);
396 color_attr_off(A_BOLD);
397 clrtoeol();
394 color_attr_on(A_BOLD);
395 mvprintw(y, x, "%s", pattern);
396 color_attr_off(A_BOLD);
397 clrtoeol();
398 398 } }
399 399
400 400 unsigned get_max_history_items() unsigned get_max_history_items()
401 401 { {
402 return (getmaxy(stdscr)-Y_OFFSET_ITEMS);
402 return (getmaxy(stdscr)-Y_OFFSET_ITEMS);
403 403 } }
404 404
405 405 // TODO don't realloc if size doesn't change // TODO don't realloc if size doesn't change
406 406 void hstr_realloc_selection(unsigned size, Hstr *hstr) void hstr_realloc_selection(unsigned size, Hstr *hstr)
407 407 { {
408 if(hstr->selection) {
409 if(size) {
410 hstr->selection
411 =realloc(hstr->selection, sizeof(char*) * size);
412 hstr->selectionRegexpMatch
413 =realloc(hstr->selectionRegexpMatch, sizeof(regmatch_t) * size);
414 } else {
415 free(hstr->selection);
416 free(hstr->selectionRegexpMatch);
417 hstr->selection=NULL;
418 hstr->selectionRegexpMatch=NULL;
419 }
420 } else {
421 if(size) {
422 hstr->selection = malloc(sizeof(char*) * size);
423 hstr->selectionRegexpMatch = malloc(sizeof(regmatch_t) * size);
424 }
425 }
408 if(hstr->selection) {
409 if(size) {
410 hstr->selection
411 =realloc(hstr->selection, sizeof(char*) * size);
412 hstr->selectionRegexpMatch
413 =realloc(hstr->selectionRegexpMatch, sizeof(regmatch_t) * size);
414 } else {
415 free(hstr->selection);
416 free(hstr->selectionRegexpMatch);
417 hstr->selection=NULL;
418 hstr->selectionRegexpMatch=NULL;
419 }
420 } else {
421 if(size) {
422 hstr->selection = malloc(sizeof(char*) * size);
423 hstr->selectionRegexpMatch = malloc(sizeof(regmatch_t) * size);
424 }
425 }
426 426 } }
427 427
428 428 unsigned hstr_make_selection(char *prefix, HistoryItems *history, int maxSelectionCount, Hstr *hstr) unsigned hstr_make_selection(char *prefix, HistoryItems *history, int maxSelectionCount, Hstr *hstr)
429 429 { {
430 hstr_realloc_selection(maxSelectionCount, hstr);
431
432 unsigned i, selectionCount=0;
433 char **source;
434 unsigned count;
435
436 switch(hstr->historyView) {
437 case HH_VIEW_HISTORY:
438 source=history->raw;
439 count=history->rawCount;
440 break;
441 case HH_VIEW_FAVORITES:
442 source=hstr->favorites->items;
443 count=hstr->favorites->count;
444 break;
445 case HH_VIEW_RANKING:
446 default:
447 source=history->items;
448 count=history->count;
449 break;
450 }
451
452 regmatch_t regexpMatch;
453 char regexpErrorMessage[CMDLINE_LNG];
454 bool regexpCompilationError=false;
430 hstr_realloc_selection(maxSelectionCount, hstr);
431
432 unsigned i, selectionCount=0;
433 char **source;
434 unsigned count;
435
436 switch(hstr->historyView) {
437 case HH_VIEW_HISTORY:
438 source=history->raw;
439 count=history->rawCount;
440 break;
441 case HH_VIEW_FAVORITES:
442 source=hstr->favorites->items;
443 count=hstr->favorites->count;
444 break;
445 case HH_VIEW_RANKING:
446 default:
447 source=history->items;
448 count=history->count;
449 break;
450 }
451
452 regmatch_t regexpMatch;
453 char regexpErrorMessage[CMDLINE_LNG];
454 bool regexpCompilationError=false;
455 455 bool keywordsAllMatch; bool keywordsAllMatch;
456 456 char *keywordsSavePtr; char *keywordsSavePtr;
457 457 char *keywordsToken; char *keywordsToken;
458 458 char *keywordsParsedLine; char *keywordsParsedLine;
459 char *keywordsPointerToDelete;
460 for(i=0; i<count && selectionCount<maxSelectionCount; i++) {
461 if(source[i]) {
462 if(!prefix || !strlen(prefix)) {
463 hstr->selection[selectionCount++]=source[i];
464 } else {
465 switch(hstr->historyMatch) {
466 case HH_MATCH_SUBSTRING:
467 switch(hstr->caseSensitive) {
468 case HH_CASE_SENSITIVE:
469 if(source[i]==strstr(source[i], prefix)) {
470 hstr->selection[selectionCount++]=source[i];
471 }
472 break;
473 case HH_CASE_INSENSITIVE:
474 if(source[i]==strcasestr(source[i], prefix)) {
475 hstr->selection[selectionCount++]=source[i];
476 }
477 break;
478 }
479 break;
480 case HH_MATCH_REGEXP:
481 if(hstr_regexp_match(&(hstr->regexp), prefix, source[i], &regexpMatch, regexpErrorMessage, CMDLINE_LNG)) {
482 hstr->selection[selectionCount]=source[i];
483 hstr->selectionRegexpMatch[selectionCount].rm_so=regexpMatch.rm_so;
484 hstr->selectionRegexpMatch[selectionCount].rm_eo=regexpMatch.rm_eo;
485 selectionCount++;
486 } else {
487 if(!regexpCompilationError) {
488 // TODO fix broken messages - getting just escape sequences
489 // print_regexp_error(regexpErrorMessage);
490 regexpCompilationError=true;
491 }
492 }
493 break;
494 case HH_MATCH_KEYWORDS:
495 // TODO: differentiate between case-sensitive and insensitive
496 keywordsParsedLine = strdup(prefix);
459 char *keywordsPointerToDelete;
460 for(i=0; i<count && selectionCount<maxSelectionCount; i++) {
461 if(source[i]) {
462 if(!prefix || !strlen(prefix)) {
463 hstr->selection[selectionCount++]=source[i];
464 } else {
465 switch(hstr->historyMatch) {
466 case HH_MATCH_SUBSTRING:
467 switch(hstr->caseSensitive) {
468 case HH_CASE_SENSITIVE:
469 if(source[i]==strstr(source[i], prefix)) {
470 hstr->selection[selectionCount++]=source[i];
471 }
472 break;
473 case HH_CASE_INSENSITIVE:
474 if(source[i]==strcasestr(source[i], prefix)) {
475 hstr->selection[selectionCount++]=source[i];
476 }
477 break;
478 }
479 break;
480 case HH_MATCH_REGEXP:
481 if(hstr_regexp_match(&(hstr->regexp), prefix, source[i], &regexpMatch, regexpErrorMessage, CMDLINE_LNG)) {
482 hstr->selection[selectionCount]=source[i];
483 hstr->selectionRegexpMatch[selectionCount].rm_so=regexpMatch.rm_so;
484 hstr->selectionRegexpMatch[selectionCount].rm_eo=regexpMatch.rm_eo;
485 selectionCount++;
486 } else {
487 if(!regexpCompilationError) {
488 // TODO fix broken messages - getting just escape sequences
489 // print_regexp_error(regexpErrorMessage);
490 regexpCompilationError=true;
491 }
492 }
493 break;
494 case HH_MATCH_KEYWORDS:
495 // TODO: differentiate between case-sensitive and insensitive
496 keywordsParsedLine = strdup(prefix);
497 497 keywordsAllMatch = true; keywordsAllMatch = true;
498 keywordsPointerToDelete = keywordsParsedLine;
499 while (true) {
500 keywordsToken = strtok_r(keywordsParsedLine, " ", &keywordsSavePtr);
498 keywordsPointerToDelete = keywordsParsedLine;
499 while (true) {
500 keywordsToken = strtok_r(keywordsParsedLine, " ", &keywordsSavePtr);
501 501 keywordsParsedLine = NULL; keywordsParsedLine = NULL;
502 if (keywordsToken == NULL) {
503 break;
504 }
505
506 if (strcasestr(source[i], keywordsToken) == NULL) {
507 keywordsAllMatch = false;
508 }
509 }
502 if (keywordsToken == NULL) {
503 break;
504 }
505
506 if (strcasestr(source[i], keywordsToken) == NULL) {
507 keywordsAllMatch = false;
508 }
509 }
510 510 if (keywordsAllMatch) { if (keywordsAllMatch) {
511 hstr->selection[selectionCount++]=source[i];
512 }
513 free(keywordsPointerToDelete);
514 break;
515 }
516 }
517 }
518 }
519
520 if(prefix && selectionCount<maxSelectionCount) {
521 char *substring;
522 for(i=0; i<count && selectionCount<maxSelectionCount; i++) {
523 switch(hstr->historyMatch) {
524 case HH_MATCH_SUBSTRING:
525 switch(hstr->caseSensitive) {
526 case HH_CASE_SENSITIVE:
527 substring = strstr(source[i], prefix);
528 if (substring != NULL && substring!=source[i]) {
529 hstr->selection[selectionCount++]=source[i];
530 }
531 break;
532 case HH_CASE_INSENSITIVE:
533 substring = strcasestr(source[i], prefix);
534 if (substring != NULL && substring!=source[i]) {
535 hstr->selection[selectionCount++]=source[i];
536 }
537 break;
538 }
539 break;
540 case HH_MATCH_REGEXP:
541 // all regexps matched previously - user decides whether match ^ or infix
542 break;
543 // case HH_MATCH_KEYWORDS:
544 // keywordsParsedLine =
545 //
546 // break;
547 }
548 }
549 }
550
551 hstr->selectionSize=selectionCount;
552 return selectionCount;
511 hstr->selection[selectionCount++]=source[i];
512 }
513 free(keywordsPointerToDelete);
514 break;
515 }
516 }
517 }
518 }
519
520 if(prefix && selectionCount<maxSelectionCount) {
521 char *substring;
522 for(i=0; i<count && selectionCount<maxSelectionCount; i++) {
523 switch(hstr->historyMatch) {
524 case HH_MATCH_SUBSTRING:
525 switch(hstr->caseSensitive) {
526 case HH_CASE_SENSITIVE:
527 substring = strstr(source[i], prefix);
528 if (substring != NULL && substring!=source[i]) {
529 hstr->selection[selectionCount++]=source[i];
530 }
531 break;
532 case HH_CASE_INSENSITIVE:
533 substring = strcasestr(source[i], prefix);
534 if (substring != NULL && substring!=source[i]) {
535 hstr->selection[selectionCount++]=source[i];
536 }
537 break;
538 }
539 break;
540 case HH_MATCH_REGEXP:
541 // all regexps matched previously - user decides whether match ^ or infix
542 break;
543 // case HH_MATCH_KEYWORDS:
544 // keywordsParsedLine =
545 //
546 // break;
547 }
548 }
549 }
550
551 hstr->selectionSize=selectionCount;
552 return selectionCount;
553 553 } }
554 554
555 555 void print_selection_row(char *text, int y, int width, char *pattern) void print_selection_row(char *text, int y, int width, char *pattern)
556 556 { {
557 char screenLine[CMDLINE_LNG];
558 snprintf(screenLine, width, " %s", text);
559 mvprintw(y, 0, "%s", screenLine); clrtoeol();
560
561 if(pattern && strlen(pattern)) {
562 color_attr_on(A_BOLD);
563 char *p;
564
565 switch(hstr->historyMatch) {
566 case HH_MATCH_SUBSTRING:
567 switch(hstr->caseSensitive) {
568 case HH_CASE_INSENSITIVE:
569 p=strcasestr(text, pattern);
570 snprintf(screenLine, strlen(pattern)+1, "%s", p);
571 mvprintw(y, 1+(p-text), "%s", screenLine);
572 break;
573 case HH_CASE_SENSITIVE:
574 p=strstr(text, pattern);
575 mvprintw(y, 1+(p-text), "%s", pattern);
576 break;
577 }
578 break;
579 case HH_MATCH_REGEXP:
580 p=strstr(text, pattern);
581 mvprintw(y, 1+(p-text), "%s", pattern);
582 break;
583 case HH_MATCH_KEYWORDS:
584 p=strstr(text, pattern);
585 mvprintw(y, 1+(p-text), "%s", pattern);
586 break;
587 }
588 color_attr_off(A_BOLD);
589 }
557 char screenLine[CMDLINE_LNG];
558 snprintf(screenLine, width, " %s", text);
559 mvprintw(y, 0, "%s", screenLine); clrtoeol();
560
561 if(pattern && strlen(pattern)) {
562 color_attr_on(A_BOLD);
563 char *p;
564
565 switch(hstr->historyMatch) {
566 case HH_MATCH_SUBSTRING:
567 switch(hstr->caseSensitive) {
568 case HH_CASE_INSENSITIVE:
569 p=strcasestr(text, pattern);
570 snprintf(screenLine, strlen(pattern)+1, "%s", p);
571 mvprintw(y, 1+(p-text), "%s", screenLine);
572 break;
573 case HH_CASE_SENSITIVE:
574 p=strstr(text, pattern);
575 mvprintw(y, 1+(p-text), "%s", pattern);
576 break;
577 }
578 break;
579 case HH_MATCH_REGEXP:
580 p=strstr(text, pattern);
581 mvprintw(y, 1+(p-text), "%s", pattern);
582 break;
583 case HH_MATCH_KEYWORDS:
584 p=strstr(text, pattern);
585 mvprintw(y, 1+(p-text), "%s", pattern);
586 break;
587 }
588 color_attr_off(A_BOLD);
589 }
590 590 } }
591 591
592 592 void hstr_print_highlighted_selection_row(char *text, int y, int width, Hstr *hstr) void hstr_print_highlighted_selection_row(char *text, int y, int width, Hstr *hstr)
593 593 { {
594 color_attr_on(A_BOLD);
595 if(hstr->hicolor) {
596 color_attr_on(COLOR_PAIR(2));
597 } else {
598 color_attr_on(A_REVERSE);
599 }
600 char screenLine[CMDLINE_LNG];
601 snprintf(screenLine, getmaxx(stdscr),
602 "%s%s" SPACE_PADDING SPACE_PADDING SPACE_PADDING,
603 (terminal_has_colors()?" ":">"), text);
604 mvprintw(y, 0, "%s", screenLine);
605 if(hstr->hicolor) {
606 color_attr_on(COLOR_PAIR(1));
607 } else {
608 color_attr_off(A_REVERSE);
609 }
610 color_attr_off(A_BOLD);
594 color_attr_on(A_BOLD);
595 if(hstr->hicolor) {
596 color_attr_on(COLOR_PAIR(2));
597 } else {
598 color_attr_on(A_REVERSE);
599 }
600 char screenLine[CMDLINE_LNG];
601 snprintf(screenLine, getmaxx(stdscr),
602 "%s%s" SPACE_PADDING SPACE_PADDING SPACE_PADDING,
603 (terminal_has_colors()?" ":">"), text);
604 mvprintw(y, 0, "%s", screenLine);
605 if(hstr->hicolor) {
606 color_attr_on(COLOR_PAIR(1));
607 } else {
608 color_attr_off(A_REVERSE);
609 }
610 color_attr_off(A_BOLD);
611 611 } }
612 612
613 613 char *hstr_print_selection(unsigned maxHistoryItems, char *pattern, Hstr *hstr) char *hstr_print_selection(unsigned maxHistoryItems, char *pattern, Hstr *hstr)
614 614 { {
615 char *result=NULL;
616 unsigned selectionCount=hstr_make_selection(pattern, hstr->history, maxHistoryItems, hstr);
617 if (selectionCount > 0) {
618 result=hstr->selection[0];
619 }
620
621 int height=get_max_history_items();
622 int width=getmaxx(stdscr);
623 unsigned i;
624 int y=Y_OFFSET_ITEMS;
625
626 move(Y_OFFSET_ITEMS, 0);
627 clrtobot();
628
629 int start, end;
630 char screenLine[CMDLINE_LNG];
631 for (i = 0; i<height; ++i) {
632 if(i<hstr->selectionSize) {
633 // TODO make this function
634 if(pattern && strlen(pattern)) {
635 if(hstr->historyMatch==HH_MATCH_REGEXP) {
636 start=hstr->selectionRegexpMatch[i].rm_so;
637 end=hstr->selectionRegexpMatch[i].rm_eo-start;
638 strncpy(screenLine,
639 hstr->selection[i]+start,
640 end);
641 screenLine[end]=0;
642 } else {
643 strcpy(screenLine, pattern);
644 }
645 print_selection_row(hstr->selection[i], y++, width, screenLine);
646 } else {
647 print_selection_row(hstr->selection[i], y++, width, pattern);
648 }
649 } else {
650 mvprintw(y++, 0, " ");
651 }
652 }
653 refresh();
654
655 return result;
615 char *result=NULL;
616 unsigned selectionCount=hstr_make_selection(pattern, hstr->history, maxHistoryItems, hstr);
617 if (selectionCount > 0) {
618 result=hstr->selection[0];
619 }
620
621 int height=get_max_history_items();
622 int width=getmaxx(stdscr);
623 unsigned i;
624 int y=Y_OFFSET_ITEMS;
625
626 move(Y_OFFSET_ITEMS, 0);
627 clrtobot();
628
629 int start, end;
630 char screenLine[CMDLINE_LNG];
631 for (i = 0; i<height; ++i) {
632 if(i<hstr->selectionSize) {
633 // TODO make this function
634 if(pattern && strlen(pattern)) {
635 if(hstr->historyMatch==HH_MATCH_REGEXP) {
636 start=hstr->selectionRegexpMatch[i].rm_so;
637 end=hstr->selectionRegexpMatch[i].rm_eo-start;
638 strncpy(screenLine,
639 hstr->selection[i]+start,
640 end);
641 screenLine[end]=0;
642 } else {
643 strcpy(screenLine, pattern);
644 }
645 print_selection_row(hstr->selection[i], y++, width, screenLine);
646 } else {
647 print_selection_row(hstr->selection[i], y++, width, pattern);
648 }
649 } else {
650 mvprintw(y++, 0, " ");
651 }
652 }
653 refresh();
654
655 return result;
656 656 } }
657 657
658 658 void highlight_selection(int selectionCursorPosition, int previousSelectionCursorPosition, char *pattern, Hstr *hstr) void highlight_selection(int selectionCursorPosition, int previousSelectionCursorPosition, char *pattern, Hstr *hstr)
659 659 { {
660 if(previousSelectionCursorPosition!=SELECTION_CURSOR_IN_PROMPT) {
661 // TODO make this function
662 char buffer[CMDLINE_LNG];
663 if(pattern && strlen(pattern) && hstr->historyMatch==HH_MATCH_REGEXP) {
664 int start=hstr->selectionRegexpMatch[previousSelectionCursorPosition].rm_so;
665 int end=hstr->selectionRegexpMatch[previousSelectionCursorPosition].rm_eo-start;
666 strncpy(buffer,
667 hstr->selection[previousSelectionCursorPosition]+start,
668 end);
669 buffer[end]=0;
670 } else {
671 strcpy(buffer, pattern);
672 }
673 print_selection_row(
674 hstr->selection[previousSelectionCursorPosition],
675 Y_OFFSET_ITEMS+previousSelectionCursorPosition,
676 getmaxx(stdscr),
677 buffer);
678 }
679 if(selectionCursorPosition!=SELECTION_CURSOR_IN_PROMPT) {
680 hstr_print_highlighted_selection_row(
681 hstr->selection[selectionCursorPosition],
682 Y_OFFSET_ITEMS+selectionCursorPosition,
683 getmaxx(stdscr),
684 hstr);
685 }
660 if(previousSelectionCursorPosition!=SELECTION_CURSOR_IN_PROMPT) {
661 // TODO make this function
662 char buffer[CMDLINE_LNG];
663 if(pattern && strlen(pattern) && hstr->historyMatch==HH_MATCH_REGEXP) {
664 int start=hstr->selectionRegexpMatch[previousSelectionCursorPosition].rm_so;
665 int end=hstr->selectionRegexpMatch[previousSelectionCursorPosition].rm_eo-start;
666 strncpy(buffer,
667 hstr->selection[previousSelectionCursorPosition]+start,
668 end);
669 buffer[end]=0;
670 } else {
671 strcpy(buffer, pattern);
672 }
673 print_selection_row(
674 hstr->selection[previousSelectionCursorPosition],
675 Y_OFFSET_ITEMS+previousSelectionCursorPosition,
676 getmaxx(stdscr),
677 buffer);
678 }
679 if(selectionCursorPosition!=SELECTION_CURSOR_IN_PROMPT) {
680 hstr_print_highlighted_selection_row(
681 hstr->selection[selectionCursorPosition],
682 Y_OFFSET_ITEMS+selectionCursorPosition,
683 getmaxx(stdscr),
684 hstr);
685 }
686 686 } }
687 687
688 688 void selection_remove(char *cmd, Hstr *hstr) void selection_remove(char *cmd, Hstr *hstr)
689 689 { {
690 if(hstr->historyView==HH_VIEW_FAVORITES) {
691 favorites_remove(hstr->favorites, cmd);
692 } else {
693 if(hstr->history->count) {
694 int i, w;
695 for(i=0, w=0; i<hstr->history->count; i++) {
696 if(strcmp(hstr->history->items[i], cmd)) {
697 hstr->history->items[w]=hstr->history->items[i];
698 w++;
699 }
700 }
701 hstr->history->count=w;
702 }
703 }
690 if(hstr->historyView==HH_VIEW_FAVORITES) {
691 favorites_remove(hstr->favorites, cmd);
692 } else {
693 if(hstr->history->count) {
694 int i, w;
695 for(i=0, w=0; i<hstr->history->count; i++) {
696 if(strcmp(hstr->history->items[i], cmd)) {
697 hstr->history->items[w]=hstr->history->items[i];
698 w++;
699 }
700 }
701 hstr->history->count=w;
702 }
703 }
704 704 } }
705 705
706 706 void hstr_on_exit(Hstr *hstr) void hstr_on_exit(Hstr *hstr)
707 707 { {
708 history_mgmt_flush();
709 free_prioritized_history();
708 history_mgmt_flush();
709 free_prioritized_history();
710 710 } }
711 711
712 712 void signal_callback_handler_ctrl_c(int signum) void signal_callback_handler_ctrl_c(int signum)
713 713 { {
714 if(signum==SIGINT) {
715 hstr_curses_stop();
716 hstr_on_exit(hstr);
717 exit(signum);
718 }
714 if(signum==SIGINT) {
715 hstr_curses_stop();
716 hstr_on_exit(hstr);
717 exit(signum);
718 }
719 719 } }
720 720
721 721 int seletion_source_remove(char *delete, Hstr *hstr) int seletion_source_remove(char *delete, Hstr *hstr)
722 722 { {
723 if(hstr->historyView!=HH_VIEW_FAVORITES) {
724 return history_mgmt_remove(delete);
725 } else {
726 return favorites_remove(hstr->favorites, delete);
727 }
723 if(hstr->historyView!=HH_VIEW_FAVORITES) {
724 return history_mgmt_remove(delete);
725 } else {
726 return favorites_remove(hstr->favorites, delete);
727 }
728 728 } }
729 729
730 730 void hstr_next_view(Hstr *hstr) void hstr_next_view(Hstr *hstr)
731 731 { {
732 hstr->historyView++;
733 hstr->historyView=hstr->historyView%3;
732 hstr->historyView++;
733 hstr->historyView=hstr->historyView%3;
734 734 } }
735 735
736 736 void stdout_history_and_return(Hstr *hstr) { void stdout_history_and_return(Hstr *hstr) {
737 unsigned selectionCount=hstr_make_selection(hstr->cmdline, hstr->history, hstr->history->rawCount, hstr);
738 if (selectionCount > 0) {
739 int i;
740 for(i=0; i<selectionCount; i++) {
741 printf("%s\n",hstr->selection[i]);
742 }
743 }
737 unsigned selectionCount=hstr_make_selection(hstr->cmdline, hstr->history, hstr->history->rawCount, hstr);
738 if (selectionCount > 0) {
739 int i;
740 for(i=0; i<selectionCount; i++) {
741 printf("%s\n",hstr->selection[i]);
742 }
743 }
744 744 } }
745 745
746 746 void loop_to_select(Hstr *hstr) void loop_to_select(Hstr *hstr)
747 747 { {
748 signal(SIGINT, signal_callback_handler_ctrl_c);
749
750 hstr_curses_start();
751 // TODO move the code below to hstr_curses
752 color_init_pair(HH_COLOR_NORMAL, -1, -1);
753 if(hstr->hicolor) {
754 color_init_pair(HH_COLOR_HIROW, COLOR_WHITE, COLOR_GREEN);
755 color_init_pair(HH_COLOR_PROMPT, COLOR_BLUE, -1);
756 color_init_pair(HH_COLOR_DELETE, COLOR_WHITE, COLOR_RED);
757 }
758
759 color_attr_on(COLOR_PAIR(HH_COLOR_NORMAL));
760 print_help_label();
761 print_history_label(hstr);
762 // TODO why do I print non-filtered selection when on command line there is a pattern?
763 hstr_print_selection(get_max_history_items(), NULL, hstr);
764 color_attr_off(COLOR_PAIR(HH_COLOR_NORMAL));
765
766 bool done=FALSE, skip=TRUE, executeResult=FALSE, lowercase=TRUE, printDefaultLabel=FALSE;
767 int basex=print_prompt(hstr);
768 int x=basex, y=0, c, cursorX=0, cursorY=0, maxHistoryItems, deleteOccurences;
769 int width=getmaxx(stdscr);
770 int selectionCursorPosition=SELECTION_CURSOR_IN_PROMPT;
771 int previousSelectionCursorPosition=SELECTION_CURSOR_IN_PROMPT;
772 char *result="", *msg, *delete;
773 char pattern[SELECTION_PREFIX_MAX_LNG];
774 pattern[0]=0;
775 // TODO this is too late! > don't render twice
776 // TODO overflow
777 strcpy(pattern, hstr->cmdline);
778
779 while (!done) {
780 maxHistoryItems=get_max_history_items();
781
782 if(!skip) {
783 c = wgetch(stdscr);
784 } else {
785 if(strlen(pattern)) {
786 color_attr_on(A_BOLD);
787 mvprintw(y, basex, "%s", pattern);
788 color_attr_off(A_BOLD);
789 cursorX=getcurx(stdscr);
790 cursorY=getcury(stdscr);
791 result=hstr_print_selection(maxHistoryItems, pattern, hstr);
792 move(cursorY, cursorX);
793 }
794 skip=FALSE;
795 continue;
796 }
797
798 if(printDefaultLabel) {
799 print_help_label();
800 printDefaultLabel=FALSE;
801 }
802
803 switch (c) {
804 case KEY_DC: // DEL
805 if(selectionCursorPosition!=SELECTION_CURSOR_IN_PROMPT) {
806 delete=hstr->selection[selectionCursorPosition];
807 msg=malloc(strlen(delete)+1);
808 strcpy(msg,delete);
809 selection_remove(delete, hstr);
810 deleteOccurences=seletion_source_remove(delete, hstr);
811 result=hstr_print_selection(maxHistoryItems, pattern, hstr);
812 print_cmd_deleted_label(msg, deleteOccurences, hstr);
813 move(y, basex+strlen(pattern));
814 printDefaultLabel=TRUE;
815 }
816 print_history_label(hstr);
817 break;
818 case K_CTRL_E:
819 hstr->historyMatch++;
820 hstr->historyMatch=hstr->historyMatch%HH_NUM_HISTORY_MATCH;
821 // TODO make this a function
822 result=hstr_print_selection(maxHistoryItems, pattern, hstr);
823 print_history_label(hstr);
824 selectionCursorPosition=SELECTION_CURSOR_IN_PROMPT;
825 if(strlen(pattern)<(width-basex-1)) {
826 print_pattern(pattern, y, basex);
827 cursorX=getcurx(stdscr);
828 cursorY=getcury(stdscr);
829 }
830 break;
831 case K_CTRL_T:
832 hstr->caseSensitive=!hstr->caseSensitive;
833 hstr->regexp.caseSensitive=hstr->caseSensitive;
834 result=hstr_print_selection(maxHistoryItems, pattern, hstr);
835 print_history_label(hstr);
836 selectionCursorPosition=SELECTION_CURSOR_IN_PROMPT;
837 if(strlen(pattern)<(width-basex-1)) {
838 print_pattern(pattern, y, basex);
839 cursorX=getcurx(stdscr);
840 cursorY=getcury(stdscr);
841 }
842 break;
843 case K_CTRL_SLASH:
844 hstr_next_view(hstr);
845 result=hstr_print_selection(maxHistoryItems, pattern, hstr);
846 print_history_label(hstr);
847 // TODO function
848 selectionCursorPosition=SELECTION_CURSOR_IN_PROMPT;
849 if(strlen(pattern)<(width-basex-1)) {
850 print_pattern(pattern, y, basex);
851 cursorX=getcurx(stdscr);
852 cursorY=getcury(stdscr);
853 }
854 break;
855 case K_CTRL_F:
856 if(selectionCursorPosition!=SELECTION_CURSOR_IN_PROMPT) {
857 result=hstr->selection[selectionCursorPosition];
858
859 if(hstr->historyView==HH_VIEW_FAVORITES) {
860 favorites_choose(hstr->favorites, result);
861 } else {
862 favorites_add(hstr->favorites, result);
863 print_cmd_added_favorite_label(result, hstr);
864 printDefaultLabel=TRUE;
865 }
866 result=hstr_print_selection(maxHistoryItems, pattern, hstr);
867 selectionCursorPosition=SELECTION_CURSOR_IN_PROMPT;
868 // TODO code review
869 if(strlen(pattern)<(width-basex-1)) {
870 print_pattern(pattern, y, basex);
871 cursorX=getcurx(stdscr);
872 cursorY=getcury(stdscr);
873 }
874 }
875 break;
876 case KEY_RESIZE:
877 print_history_label(hstr);
878 result=hstr_print_selection(maxHistoryItems, pattern, hstr);
879 print_history_label(hstr);
880 selectionCursorPosition=SELECTION_CURSOR_IN_PROMPT;
881 move(y, basex+strlen(pattern));
882 break;
883 case K_CTRL_U:
884 case K_CTRL_W: // TODO supposed to delete just one word backward
885 pattern[0]=0;
886 print_pattern(pattern, y, basex);
887 break;
888 case K_CTRL_L:
889 toggle_case(pattern, lowercase);
890 lowercase=!lowercase;
891 print_pattern(pattern, y, basex);
892 selectionCursorPosition=SELECTION_CURSOR_IN_PROMPT;
893 break;
894 case K_CTRL_H:
895 case K_BACKSPACE:
896 case KEY_BACKSPACE:
897 if(hstr_strlen(pattern)>0) {
898 hstr_chop(pattern);
899 x--;
900 print_pattern(pattern, y, basex);
901 }
902
903 // TODO why I make selection if it's done in print_selection?
904 if(strlen(pattern)>0) {
905 hstr_make_selection(pattern, hstr->history, maxHistoryItems, hstr);
906 } else {
907 hstr_make_selection(NULL, hstr->history, maxHistoryItems, hstr);
908 }
909 result=hstr_print_selection(maxHistoryItems, pattern, hstr);
910
911 move(y, basex+hstr_strlen(pattern));
912 break;
913 case KEY_UP:
914 previousSelectionCursorPosition=selectionCursorPosition;
915 if(selectionCursorPosition>0) {
916 selectionCursorPosition--;
917 } else {
918 selectionCursorPosition=hstr->selectionSize-1;
919 }
920 highlight_selection(selectionCursorPosition, previousSelectionCursorPosition, pattern, hstr);
921 move(y, basex+strlen(pattern));
922 break;
923 case K_CTRL_R:
924 case KEY_DOWN:
925 if(selectionCursorPosition==SELECTION_CURSOR_IN_PROMPT) {
926 selectionCursorPosition=previousSelectionCursorPosition=0;
927 } else {
928 previousSelectionCursorPosition=selectionCursorPosition;
929 if((selectionCursorPosition+1)<hstr->selectionSize) {
930 selectionCursorPosition++;
931 } else {
932 selectionCursorPosition=0;
933 }
934 }
935 if(hstr->selectionSize) {
936 highlight_selection(selectionCursorPosition, previousSelectionCursorPosition, pattern, hstr);
937 }
938 move(y, basex+strlen(pattern));
939 break;
940 case K_ENTER:
941 case KEY_ENTER:
942 executeResult=TRUE;
943 case K_TAB:
944 case KEY_LEFT:
945 case KEY_RIGHT:
946 if(selectionCursorPosition!=SELECTION_CURSOR_IN_PROMPT) {
947 result=hstr->selection[selectionCursorPosition];
948 if(hstr->historyView==HH_VIEW_FAVORITES) {
949 favorites_choose(hstr->favorites,result);
950 }
951 } else {
952 result=(pattern==NULL?"":pattern);
953 }
954 done=TRUE;
955 break;
956 case K_CTRL_G:
957 case K_ESC:
958 result=NULL;
959 history_clear_dirty();
960 done=TRUE;
961 break;
962 case K_CTRL_X:
963 result=NULL;
964 done=TRUE;
965 break;
966 default:
967 LOGKEYS(Y_OFFSET_HELP, c);
968 LOGCURSOR(Y_OFFSET_HELP);
969 LOGUTF8(Y_OFFSET_HELP,pattern);
970
971 if(c>K_CTRL_Z) {
972 selectionCursorPosition=SELECTION_CURSOR_IN_PROMPT;
973
974 if(strlen(pattern)<(width-basex-1)) {
975 strcat(pattern, (char*)(&c));
976 print_pattern(pattern, y, basex);
977 cursorX=getcurx(stdscr);
978 cursorY=getcury(stdscr);
979 }
980
981 result = hstr_print_selection(maxHistoryItems, pattern, hstr);
982 move(cursorY, cursorX);
983 refresh();
984 }
985 break;
986 }
987 }
988 hstr_curses_stop();
989
990 if(result!=NULL) {
991 fill_terminal_input(result, TRUE);
992 if(executeResult) {
993 fill_terminal_input("\n", FALSE);
994 }
995 }
748 signal(SIGINT, signal_callback_handler_ctrl_c);
749
750 hstr_curses_start();
751 // TODO move the code below to hstr_curses
752 color_init_pair(HH_COLOR_NORMAL, -1, -1);
753 if(hstr->hicolor) {
754 color_init_pair(HH_COLOR_HIROW, COLOR_WHITE, COLOR_GREEN);
755 color_init_pair(HH_COLOR_PROMPT, COLOR_BLUE, -1);
756 color_init_pair(HH_COLOR_DELETE, COLOR_WHITE, COLOR_RED);
757 }
758
759 color_attr_on(COLOR_PAIR(HH_COLOR_NORMAL));
760 print_help_label();
761 print_history_label(hstr);
762 // TODO why do I print non-filtered selection when on command line there is a pattern?
763 hstr_print_selection(get_max_history_items(), NULL, hstr);
764 color_attr_off(COLOR_PAIR(HH_COLOR_NORMAL));
765
766 bool done=FALSE, skip=TRUE, executeResult=FALSE, lowercase=TRUE, printDefaultLabel=FALSE;
767 int basex=print_prompt(hstr);
768 int x=basex, y=0, c, cursorX=0, cursorY=0, maxHistoryItems, deleteOccurences;
769 int width=getmaxx(stdscr);
770 int selectionCursorPosition=SELECTION_CURSOR_IN_PROMPT;
771 int previousSelectionCursorPosition=SELECTION_CURSOR_IN_PROMPT;
772 char *result="", *msg, *delete;
773 char pattern[SELECTION_PREFIX_MAX_LNG];
774 pattern[0]=0;
775 // TODO this is too late! > don't render twice
776 // TODO overflow
777 strcpy(pattern, hstr->cmdline);
778
779 while (!done) {
780 maxHistoryItems=get_max_history_items();
781
782 if(!skip) {
783 c = wgetch(stdscr);
784 } else {
785 if(strlen(pattern)) {
786 color_attr_on(A_BOLD);
787 mvprintw(y, basex, "%s", pattern);
788 color_attr_off(A_BOLD);
789 cursorX=getcurx(stdscr);
790 cursorY=getcury(stdscr);
791 result=hstr_print_selection(maxHistoryItems, pattern, hstr);
792 move(cursorY, cursorX);
793 }
794 skip=FALSE;
795 continue;
796 }
797
798 if(printDefaultLabel) {
799 print_help_label();
800 printDefaultLabel=FALSE;
801 }
802
803 switch (c) {
804 case KEY_DC: // DEL
805 if(selectionCursorPosition!=SELECTION_CURSOR_IN_PROMPT) {
806 delete=hstr->selection[selectionCursorPosition];
807 msg=malloc(strlen(delete)+1);
808 strcpy(msg,delete);
809 selection_remove(delete, hstr);
810 deleteOccurences=seletion_source_remove(delete, hstr);
811 result=hstr_print_selection(maxHistoryItems, pattern, hstr);
812 print_cmd_deleted_label(msg, deleteOccurences, hstr);
813 move(y, basex+strlen(pattern));
814 printDefaultLabel=TRUE;
815 }
816 print_history_label(hstr);
817 break;
818 case K_CTRL_E:
819 hstr->historyMatch++;
820 hstr->historyMatch=hstr->historyMatch%HH_NUM_HISTORY_MATCH;
821 // TODO make this a function
822 result=hstr_print_selection(maxHistoryItems, pattern, hstr);
823 print_history_label(hstr);
824 selectionCursorPosition=SELECTION_CURSOR_IN_PROMPT;
825 if(strlen(pattern)<(width-basex-1)) {
826 print_pattern(pattern, y, basex);
827 cursorX=getcurx(stdscr);
828 cursorY=getcury(stdscr);
829 }
830 break;
831 case K_CTRL_T:
832 hstr->caseSensitive=!hstr->caseSensitive;
833 hstr->regexp.caseSensitive=hstr->caseSensitive;
834 result=hstr_print_selection(maxHistoryItems, pattern, hstr);
835 print_history_label(hstr);
836 selectionCursorPosition=SELECTION_CURSOR_IN_PROMPT;
837 if(strlen(pattern)<(width-basex-1)) {
838 print_pattern(pattern, y, basex);
839 cursorX=getcurx(stdscr);
840 cursorY=getcury(stdscr);
841 }
842 break;
843 case K_CTRL_SLASH:
844 hstr_next_view(hstr);
845 result=hstr_print_selection(maxHistoryItems, pattern, hstr);
846 print_history_label(hstr);
847 // TODO function
848 selectionCursorPosition=SELECTION_CURSOR_IN_PROMPT;
849 if(strlen(pattern)<(width-basex-1)) {
850 print_pattern(pattern, y, basex);
851 cursorX=getcurx(stdscr);
852 cursorY=getcury(stdscr);
853 }
854 break;
855 case K_CTRL_F:
856 if(selectionCursorPosition!=SELECTION_CURSOR_IN_PROMPT) {
857 result=hstr->selection[selectionCursorPosition];
858
859 if(hstr->historyView==HH_VIEW_FAVORITES) {
860 favorites_choose(hstr->favorites, result);
861 } else {
862 favorites_add(hstr->favorites, result);
863 print_cmd_added_favorite_label(result, hstr);
864 printDefaultLabel=TRUE;
865 }
866 result=hstr_print_selection(maxHistoryItems, pattern, hstr);
867 selectionCursorPosition=SELECTION_CURSOR_IN_PROMPT;
868 // TODO code review
869 if(strlen(pattern)<(width-basex-1)) {
870 print_pattern(pattern, y, basex);
871 cursorX=getcurx(stdscr);
872 cursorY=getcury(stdscr);
873 }
874 }
875 break;
876 case KEY_RESIZE:
877 print_history_label(hstr);
878 result=hstr_print_selection(maxHistoryItems, pattern, hstr);
879 print_history_label(hstr);
880 selectionCursorPosition=SELECTION_CURSOR_IN_PROMPT;
881 move(y, basex+strlen(pattern));
882 break;
883 case K_CTRL_U:
884 case K_CTRL_W: // TODO supposed to delete just one word backward
885 pattern[0]=0;
886 print_pattern(pattern, y, basex);
887 break;
888 case K_CTRL_L:
889 toggle_case(pattern, lowercase);
890 lowercase=!lowercase;
891 print_pattern(pattern, y, basex);
892 selectionCursorPosition=SELECTION_CURSOR_IN_PROMPT;
893 break;
894 case K_CTRL_H:
895 case K_BACKSPACE:
896 case KEY_BACKSPACE:
897 if(hstr_strlen(pattern)>0) {
898 hstr_chop(pattern);
899 x--;
900 print_pattern(pattern, y, basex);
901 }
902
903 // TODO why I make selection if it's done in print_selection?
904 if(strlen(pattern)>0) {
905 hstr_make_selection(pattern, hstr->history, maxHistoryItems, hstr);
906 } else {
907 hstr_make_selection(NULL, hstr->history, maxHistoryItems, hstr);
908 }
909 result=hstr_print_selection(maxHistoryItems, pattern, hstr);
910
911 move(y, basex+hstr_strlen(pattern));
912 break;
913 case KEY_UP:
914 previousSelectionCursorPosition=selectionCursorPosition;
915 if(selectionCursorPosition>0) {
916 selectionCursorPosition--;
917 } else {
918 selectionCursorPosition=hstr->selectionSize-1;
919 }
920 highlight_selection(selectionCursorPosition, previousSelectionCursorPosition, pattern, hstr);
921 move(y, basex+strlen(pattern));
922 break;
923 case K_CTRL_R:
924 case KEY_DOWN:
925 if(selectionCursorPosition==SELECTION_CURSOR_IN_PROMPT) {
926 selectionCursorPosition=previousSelectionCursorPosition=0;
927 } else {
928 previousSelectionCursorPosition=selectionCursorPosition;
929 if((selectionCursorPosition+1)<hstr->selectionSize) {
930 selectionCursorPosition++;
931 } else {
932 selectionCursorPosition=0;
933 }
934 }
935 if(hstr->selectionSize) {
936 highlight_selection(selectionCursorPosition, previousSelectionCursorPosition, pattern, hstr);
937 }
938 move(y, basex+strlen(pattern));
939 break;
940 case K_ENTER:
941 case KEY_ENTER:
942 executeResult=TRUE;
943 case K_TAB:
944 case KEY_LEFT:
945 case KEY_RIGHT:
946 if(selectionCursorPosition!=SELECTION_CURSOR_IN_PROMPT) {
947 result=hstr->selection[selectionCursorPosition];
948 if(hstr->historyView==HH_VIEW_FAVORITES) {
949 favorites_choose(hstr->favorites,result);
950 }
951 } else {
952 result=(pattern==NULL?"":pattern);
953 }
954 done=TRUE;
955 break;
956 case K_CTRL_G:
957 case K_ESC:
958 result=NULL;
959 history_clear_dirty();
960 done=TRUE;
961 break;
962 case K_CTRL_X:
963 result=NULL;
964 done=TRUE;
965 break;
966 default:
967 LOGKEYS(Y_OFFSET_HELP, c);
968 LOGCURSOR(Y_OFFSET_HELP);
969 LOGUTF8(Y_OFFSET_HELP,pattern);
970
971 if(c>K_CTRL_Z) {
972 selectionCursorPosition=SELECTION_CURSOR_IN_PROMPT;
973
974 if(strlen(pattern)<(width-basex-1)) {
975 strcat(pattern, (char*)(&c));
976 print_pattern(pattern, y, basex);
977 cursorX=getcurx(stdscr);
978 cursorY=getcury(stdscr);
979 }
980
981 result = hstr_print_selection(maxHistoryItems, pattern, hstr);
982 move(cursorY, cursorX);
983 refresh();
984 }
985 break;
986 }
987 }
988 hstr_curses_stop();
989
990 if(result!=NULL) {
991 fill_terminal_input(result, TRUE);
992 if(executeResult) {
993 fill_terminal_input("\n", FALSE);
994 }
995 }
996 996 } }
997 997
998 998 // TODO protection from line overflow (snprinf) // TODO protection from line overflow (snprinf)
999 999 void hstr_assemble_cmdline_pattern(int argc, char* argv[], Hstr* hstr, int startIndex) void hstr_assemble_cmdline_pattern(int argc, char* argv[], Hstr* hstr, int startIndex)
1000 1000 { {
1001 if(argc>0) {
1002 int i;
1003 for(i=startIndex; i<argc; i++) {
1004 if((strlen(hstr->cmdline)+strlen(argv[i])*2)>CMDLINE_LNG) break;
1005 if(strstr(argv[i], " ")) {
1006 strcat(hstr->cmdline, "\"");
1007 }
1008 strcat(hstr->cmdline, argv[i]);
1009 if(strstr(argv[i], " ")) {
1010 strcat(hstr->cmdline, "\"");
1011 }
1012 if((i+1<argc)) {
1013 strcat(hstr->cmdline, " ");
1014 }
1015 }
1016 }
1001 if(argc>0) {
1002 int i;
1003 for(i=startIndex; i<argc; i++) {
1004 if((strlen(hstr->cmdline)+strlen(argv[i])*2)>CMDLINE_LNG) break;
1005 if(strstr(argv[i], " ")) {
1006 strcat(hstr->cmdline, "\"");
1007 }
1008 strcat(hstr->cmdline, argv[i]);
1009 if(strstr(argv[i], " ")) {
1010 strcat(hstr->cmdline, "\"");
1011 }
1012 if((i+1<argc)) {
1013 strcat(hstr->cmdline, " ");
1014 }
1015 }
1016 }
1017 1017 } }
1018 1018
1019 1019 // TODO make favorites loading lazy (load on the first opening of favorites view) // TODO make favorites loading lazy (load on the first opening of favorites view)
1020 1020 void hstr_init_favorites(Hstr *hstr) void hstr_init_favorites(Hstr *hstr)
1021 1021 { {
1022 hstr->favorites=malloc(sizeof(FavoriteItems));
1023 favorites_init(hstr->favorites);
1024 favorites_get(hstr->favorites);
1022 hstr->favorites=malloc(sizeof(FavoriteItems));
1023 favorites_init(hstr->favorites);
1024 favorites_get(hstr->favorites);
1025 1025 } }
1026 1026
1027 1027 void hstr_main(Hstr *hstr) void hstr_main(Hstr *hstr)
1028 1028 { {
1029 hstr->history=get_prioritized_history();
1030 if(hstr->history) {
1031 history_mgmt_open();
1032 if(hstr->interactive) {
1033 loop_to_select(hstr);
1034 } else {
1035 stdout_history_and_return(hstr);
1036 }
1037 hstr_on_exit(hstr);
1038 } else {
1039 printf("No history - nothing to suggest...\n");
1040 }
1029 hstr->history=get_prioritized_history();
1030 if(hstr->history) {
1031 history_mgmt_open();
1032 if(hstr->interactive) {
1033 loop_to_select(hstr);
1034 } else {
1035 stdout_history_and_return(hstr);
1036 }
1037 hstr_on_exit(hstr);
1038 } else {
1039 printf("No history - nothing to suggest...\n");
1040 }
1041 1041 } }
1042 1042
1043 1043 void hstr_getopt(int argc, char **argv, Hstr *hstr) void hstr_getopt(int argc, char **argv, Hstr *hstr)
1044 1044 { {
1045 int option_index = 0;
1046 int option = getopt_long(argc, argv, "fVhns", long_options, &option_index);
1047 if(option != -1) {
1048 switch(option) {
1049 case 'f':
1050 hstr->historyView=HH_VIEW_FAVORITES;
1051 break;
1052 case 'n':
1053 hstr->interactive=false;
1054 break;
1055 case 'V':
1056 printf("%s", VERSION_STRING);
1057 exit(EXIT_SUCCESS);
1058 case 'h':
1059 printf("%s", HELP_STRING);
1060 exit(EXIT_SUCCESS);
1061 case 's':
1062 printf("%s", INSTALL_STRING);
1063 exit(EXIT_SUCCESS);
1064
1065 case '?':
1066 default:
1067 printf("Unrecognized option: '%s'", HELP_STRING);
1068 printf("%s", HELP_STRING);
1069 exit(EXIT_SUCCESS);
1070 }
1071 }
1072
1073 if(optind < argc) {
1074 hstr_assemble_cmdline_pattern(argc, argv, hstr, optind);
1075 }
1045 int option_index = 0;
1046 int option = getopt_long(argc, argv, "fVhns", long_options, &option_index);
1047 if(option != -1) {
1048 switch(option) {
1049 case 'f':
1050 hstr->historyView=HH_VIEW_FAVORITES;
1051 break;
1052 case 'n':
1053 hstr->interactive=false;
1054 break;
1055 case 'V':
1056 printf("%s", VERSION_STRING);
1057 exit(EXIT_SUCCESS);
1058 case 'h':
1059 printf("%s", HELP_STRING);
1060 exit(EXIT_SUCCESS);
1061 case 's':
1062 printf("%s", INSTALL_STRING);
1063 exit(EXIT_SUCCESS);
1064
1065 case '?':
1066 default:
1067 printf("Unrecognized option: '%s'", HELP_STRING);
1068 printf("%s", HELP_STRING);
1069 exit(EXIT_SUCCESS);
1070 }
1071 }
1072
1073 if(optind < argc) {
1074 hstr_assemble_cmdline_pattern(argc, argv, hstr, optind);
1075 }
1076 1076 } }
1077 1077
1078 1078 int main(int argc, char *argv[]) int main(int argc, char *argv[])
1079 1079 { {
1080 setlocale(LC_ALL, "");
1080 setlocale(LC_ALL, "");
1081 1081
1082 hstr=malloc(sizeof(Hstr));
1082 hstr=malloc(sizeof(Hstr));
1083 1083
1084 hstr_init(hstr);
1085 hstr_get_env_configuration(hstr);
1086 hstr_getopt(argc, argv, hstr);
1087 hstr_init_favorites(hstr);
1088 hstr_main(hstr);
1084 hstr_init(hstr);
1085 hstr_get_env_configuration(hstr);
1086 hstr_getopt(argc, argv, hstr);
1087 hstr_init_favorites(hstr);
1088 hstr_main(hstr);
1089 1089
1090 favorites_destroy(hstr->favorites);
1091 free(hstr);
1090 favorites_destroy(hstr->favorites);
1091 free(hstr);
1092 1092
1093 return EXIT_SUCCESS;
1093 return EXIT_SUCCESS;
1094 1094 } }
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