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 8a102753944def6e5e0faaf26537655b2b59487e

Fixed #79 by switching to getopt for cmdline processing. Fixed #52 and fixed #44 by implementation of non-interactive mode. Fixed #42 by checking that substitution works prior passing params to hh.
Author: Martin Dvorak
Author date (UTC): 2014-05-03 06:12
Committer name: Martin Dvorak
Committer date (UTC): 2014-05-03 06:12
Parent(s): c866cc140bc9936d91f51e7596b7333e9853a876
Signing key:
Tree: 821fcbe108d745ef1eabf03d6509b6f661aad3cd
File Lines added Lines deleted
man/hh.1 7 4
src/hstr.c 80 44
File man/hh.1 changed (mode: 100644) (index 03da908..01d7bd1)
... ... hh allows removal of commands from history - for instance with a typo
15 15 or with a sensitive content. or with a sensitive content.
16 16 .SH OPTIONS .SH OPTIONS
17 17 .TP .TP
18 \fB--help\fR
18 \fB-h --help\fR
19 19 Show help Show help
20 20 .TP .TP
21 \fB--favorites\fR
21 \fB-n --non-interactive\fR
22 Filter filtered history and exit
23 .TP
24 \fB-f --favorites\fR
22 25 Show favorites view immediately Show favorites view immediately
23 26 .TP .TP
24 \fB--show-configuration\fR
27 \fB-s --show-configuration\fR
25 28 Show configuration that can be added to ~/.bashrc Show configuration that can be added to ~/.bashrc
26 29 .TP .TP
27 \fB--version\fR
30 \fB-V --version\fR
28 31 Show version information Show version information
29 32 .SH KEYS .SH KEYS
30 33 .TP .TP
File src/hstr.c changed (mode: 100644) (index 9ba350b..0d24c20)
10 10 #define _GNU_SOURCE #define _GNU_SOURCE
11 11
12 12 #include <curses.h> #include <curses.h>
13 #include <getopt.h>
13 14 #include <regex.h> #include <regex.h>
14 15 #include <signal.h> #include <signal.h>
15 16 #include <stdbool.h> #include <stdbool.h>
 
... ... static const char *HELP_STRING=
136 137 "Usage: hh [option] [arg1] [arg2]..." "Usage: hh [option] [arg1] [arg2]..."
137 138 "\nShell history suggest box:" "\nShell history suggest box:"
138 139 "\n" "\n"
139 "\n --favorites -f ... show command favorites"
140 "\n --show-configuration ... show configuration to be added to ~/.bashrc"
141 "\n --version ... show version details"
142 "\n --help ... display this help and exit"
140 "\n --favorites -f ... show favorites view"
141 "\n --non-interactive -n ... print filtered history and exit"
142 "\n --show-configuration -s ... show configuration to be added to ~/.bashrc"
143 "\n --version -V ... show version details"
144 "\n --help -h ... help"
143 145 "\n" "\n"
144 146 "\nReport bugs to martin.dvorak@mindforger.com" "\nReport bugs to martin.dvorak@mindforger.com"
145 147 "\nHome page: https://github.com/dvorka/hstr" "\nHome page: https://github.com/dvorka/hstr"
 
... ... static const char *VERSION_STRING=
154 156 static const char *LABEL_HELP= static const char *LABEL_HELP=
155 157 "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";
156 158
159 #define GETOPT_NO_ARGUMENT 0
160 #define GETOPT_REQUIRED_ARGUMENT 1
161 #define GETOPT_OPTIONAL_ARGUMENT 2
162
163 static const struct option long_options[] = {
164 {"favorites", GETOPT_NO_ARGUMENT, NULL, 'f'},
165 {"version", GETOPT_NO_ARGUMENT, NULL, 'V'},
166 {"help", GETOPT_NO_ARGUMENT, NULL, 'h'},
167 {"non-interactive", GETOPT_NO_ARGUMENT, NULL, 'n'},
168 {"show-configuration", GETOPT_NO_ARGUMENT, NULL, 's'},
169 {0, 0, NULL, 0 }
170 };
171
157 172 typedef struct { typedef struct {
158 173 HistoryItems *history; HistoryItems *history;
159 174 FavoriteItems *favorites; FavoriteItems *favorites;
 
... ... typedef struct {
166 181 int historyView; // TODO view: favs, ... int historyView; // TODO view: favs, ...
167 182 int caseSensitive; int caseSensitive;
168 183
184 bool interactive;
185
169 186 bool hicolor; bool hicolor;
170 187 int debugLevel; int debugLevel;
171 188
 
... ... void hstr_init(Hstr *hstr)
186 203 hstr->historyView=HH_VIEW_RANKING; hstr->historyView=HH_VIEW_RANKING;
187 204 hstr->caseSensitive=HH_CASE_INSENSITIVE; hstr->caseSensitive=HH_CASE_INSENSITIVE;
188 205
206 hstr->interactive=true;
207
189 208 hstr->hicolor=FALSE; hstr->hicolor=FALSE;
190 209
191 210 hstr->debugLevel=HH_DEBUG_LEVEL_NONE; hstr->debugLevel=HH_DEBUG_LEVEL_NONE;
192
193 211 hstr->cmdline[0]=0; hstr->cmdline[0]=0;
194
195 212 hstr_regexp_init(&hstr->regexp); hstr_regexp_init(&hstr->regexp);
196 213 } }
197 214
 
... ... void hstr_next_view(Hstr *hstr)
626 643 hstr->historyView=hstr->historyView%3; hstr->historyView=hstr->historyView%3;
627 644 } }
628 645
646 void stdout_history_and_return(Hstr *hstr) {
647 unsigned selectionCount=hstr_make_selection(hstr->cmdline, hstr->history, hstr->history->rawCount, hstr);
648 if (selectionCount > 0) {
649 int i;
650 for(i=0; i<selectionCount; i++) {
651 printf("%s\n",hstr->selection[i]);
652 }
653 }
654 }
655
629 656 void loop_to_select(Hstr *hstr) void loop_to_select(Hstr *hstr)
630 657 { {
631 658 signal(SIGINT, signal_callback_handler_ctrl_c); signal(SIGINT, signal_callback_handler_ctrl_c);
 
... ... void loop_to_select(Hstr *hstr)
644 671 color_attr_on(COLOR_PAIR(HH_COLOR_NORMAL)); color_attr_on(COLOR_PAIR(HH_COLOR_NORMAL));
645 672 print_help_label(); print_help_label();
646 673 print_history_label(hstr); print_history_label(hstr);
674 // TODO why do I print non-filtered selection when on command line there is a pattern?
647 675 hstr_print_selection(get_max_history_items(stdscr), NULL, hstr); hstr_print_selection(get_max_history_items(stdscr), NULL, hstr);
648 676 color_attr_off(COLOR_PAIR(HH_COLOR_NORMAL)); color_attr_off(COLOR_PAIR(HH_COLOR_NORMAL));
649 677
 
... ... void loop_to_select(Hstr *hstr)
656 684 char *result="", *msg, *delete; char *result="", *msg, *delete;
657 685 char pattern[SELECTION_PREFIX_MAX_LNG]; char pattern[SELECTION_PREFIX_MAX_LNG];
658 686 pattern[0]=0; pattern[0]=0;
687 // TODO this is too late! > don't render twice
659 688 strcpy(pattern, hstr->cmdline); strcpy(pattern, hstr->cmdline);
660 689 while (!done) { while (!done) {
661 690 maxHistoryItems=get_max_history_items(stdscr); maxHistoryItems=get_max_history_items(stdscr);
 
... ... void loop_to_select(Hstr *hstr)
869 898 } }
870 899 } }
871 900
872 // TODO support BASH substitutions: !!, !!ps, !$, !*
873 void hstr_assemble_cmdline_pattern(int argc, char* argv[], Hstr* hstr)
901 // TODO protection from line overflow (snprinf)
902 void hstr_assemble_cmdline_pattern(int argc, char* argv[], Hstr* hstr, int startIndex)
874 903 { {
875 904 if(argc>0) { if(argc>0) {
876 905 int i; int i;
877 for(i=1; i<argc; i++) {
906 for(i=startIndex; i<argc; i++) {
878 907 if((strlen(hstr->cmdline)+strlen(argv[i])*2)>CMDLINE_LNG) break; if((strlen(hstr->cmdline)+strlen(argv[i])*2)>CMDLINE_LNG) break;
879 908 if(strstr(argv[i], " ")) { if(strstr(argv[i], " ")) {
880 909 strcat(hstr->cmdline, "\""); strcat(hstr->cmdline, "\"");
 
... ... void hstr_assemble_cmdline_pattern(int argc, char* argv[], Hstr* hstr)
890 919 } }
891 920 } }
892 921
893 // TODO to be rewritten to getopt
894 void hstr_get_cmdline_options(int argc, char *argv[], Hstr *hstr)
895 {
896 if(argc>0) {
897 if(argc==2) {
898 if(strstr(argv[1], "--favorites") || strstr(argv[1], "-f")) {
899 hstr->historyView=HH_VIEW_FAVORITES;
900 return;
901 } else {
902 if(strstr(argv[1], "--show-configuration")) {
903 printf("%s", INSTALL_STRING);
904 exit(EXIT_SUCCESS);
905 } else {
906 if(strstr(argv[1], "--version")) {
907 printf("%s", VERSION_STRING);
908 exit(EXIT_SUCCESS);
909 } else {
910 if(strstr(argv[1], "--help")) {
911 printf("Unknown option: %s\n", argv[1]);
912 printf("%s", HELP_STRING);
913 exit(EXIT_SUCCESS);
914 } else {
915 hstr_assemble_cmdline_pattern(argc, argv, hstr);
916 }
917 }
918 }
919 }
920 }
921 }
922
923 }
924
925 922 // 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)
926 923 void hstr_init_favorites(Hstr *hstr) void hstr_init_favorites(Hstr *hstr)
927 924 { {
 
... ... void hstr_main(Hstr *hstr)
935 932 hstr->history=get_prioritized_history(); hstr->history=get_prioritized_history();
936 933 if(hstr->history) { if(hstr->history) {
937 934 history_mgmt_open(); history_mgmt_open();
938 loop_to_select(hstr);
935 if(hstr->interactive) {
936 loop_to_select(hstr);
937 } else {
938 stdout_history_and_return(hstr);
939 }
939 940 hstr_on_exit(hstr); hstr_on_exit(hstr);
940 941 } else { } else {
941 942 printf("No history - nothing to suggest...\n"); printf("No history - nothing to suggest...\n");
942 943 } }
943 944 } }
944 945
946 void hstr_getopt(int argc, char **argv, Hstr *hstr)
947 {
948 int option_index = 0;
949 int option = getopt_long(argc, argv, "fVhns", long_options, &option_index);
950 if(option != -1) {
951 switch(option) {
952 case 'f':
953 hstr->historyView=HH_VIEW_FAVORITES;
954 break;
955 case 'n':
956 hstr->interactive=false;
957 break;
958 case 'V':
959 printf("%s", VERSION_STRING);
960 exit(EXIT_SUCCESS);
961 case 'h':
962 printf("%s", HELP_STRING);
963 exit(EXIT_SUCCESS);
964 case 's':
965 printf("%s", INSTALL_STRING);
966 exit(EXIT_SUCCESS);
967
968 case '?':
969 default:
970 printf("Unrecognized option: '%s'", HELP_STRING);
971 printf("%s", HELP_STRING);
972 exit(EXIT_SUCCESS);
973 }
974 }
975
976 if(optind < argc) {
977 hstr_assemble_cmdline_pattern(argc, argv, hstr, optind);
978 }
979 }
980
945 981 int main(int argc, char *argv[]) int main(int argc, char *argv[])
946 982 { {
947 983 hstr=malloc(sizeof(Hstr)); hstr=malloc(sizeof(Hstr));
948 984
949 985 hstr_init(hstr); hstr_init(hstr);
950 hstr_get_env_configuration(hstr);
951 hstr_get_cmdline_options(argc, argv, hstr);
986 hstr_get_env_configuration(hstr);
987 hstr_getopt(argc, argv, hstr);
952 988 hstr_init_favorites(hstr); hstr_init_favorites(hstr);
953 989 hstr_main(hstr); hstr_main(hstr);
954 990
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