-
Notifications
You must be signed in to change notification settings - Fork 10
/
Copy pathjsh-completion.c
243 lines (217 loc) · 9.29 KB
/
jsh-completion.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
/* This file is part of jsh.
*
* jsh: A basic UNIX shell implementation in C
* Copyright (C) 2014 Jo Van Bulck <[email protected]>
*
* jsh is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* jsh is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with jsh. If not, see <http://www.gnu.org/licenses/>.
*/
#include "jsh-completion.h"
// #################### helper generator function definitions ####################
char *jsh_cmd_generator(const char*, int);
char *jsh_built_in_generator(const char*, int);
char *jsh_alias_generator(const char*, int);
char *jsh_external_cmd_generator(const char*, int);
char *git_completion_generator(const char*, int);
char *apt_compl_generator(const char*, int);
char *jsh_options_generator(const char*, int);
char *debug_completion_generator(const char*, int);
char *make_options_generator(const char*, int);
/*
* The function that is called by readline; returns a list of matches or NULL iff no matches
* found.
* @note: All custom autocompletion generators should be called from withing this function.
*/
char** jsh_command_completion(const char *text, int start, int end) {
char **matches = NULL;
// true iff user entered 'cmd text<TAB>'
#define USR_ENTERED(cmd) \
(start >= strlen(cmd)+1 && (strncmp(rl_line_buffer + start - strlen(cmd) - 1, \
cmd, strlen(cmd)) == 0)) // +1 for space
if (is_valid_cmd(text, rl_line_buffer, start)) {
// try custom cmd autocompletion iff this is a valid 'comd' context
matches = rl_completion_matches(text, &jsh_cmd_generator);
}
else {
// else try custom autocompletion for specific commands
if (USR_ENTERED("git")) {
matches = rl_completion_matches(text, &git_completion_generator);
}
else if (USR_ENTERED("jsh")) {
matches = rl_completion_matches(text, &jsh_options_generator);
}
else if (USR_ENTERED("make")) {
matches = rl_completion_matches(text, &make_options_generator);
}
else if (USR_ENTERED("debug")) {
matches = rl_completion_matches(text, &debug_completion_generator);
}
else if (USR_ENTERED("apt")) {
matches = rl_completion_matches(text, &apt_compl_generator);
}
}
return matches;
}
// #################### helper generator function implementations ####################
/*
* RL COMPLETION GENERATOR FUNCTION JSH DEVELOPER INFO :
*
* 1. a GNU readline generator function is a function that is passed a partially entered
* command several times and returns a single possible match each time. The first time
* it is called for a new partially entered command, @param(int state) is zero. The
* generator should then initialize static state and return the matches one by one.
* When no more matches, return NULL.
* 2. a jsh generator function is called from the "jsh_command_completion()" function above,
* using the "rl_completion_matches()" readline helper function
* 3. For a straightforward (static string array) completion generator, one should use the
* COMPLETION_SKELETON macro, see examples below.
*
* In other words (from the readline doc):
* "text is the partial word to be completed. state is zero the first time the function
* is called. The generator function returns (char *) NULL to inform rl_completion_matches()
* that there are no more possibilities left. Usually the generator function computes the
* list of possible completions when state is zero, and returns them one at a time on
* subsequent calls. Each string the generator function returns as a match must be
* allocated with malloc(); Readline frees the strings when it has finished with them."
*/
/*
* COMPLETION_SKELETON: a sketleton to facilitate the implementation of a custom completion
* generator for GNU readline.
* @arg array : a char* array with possible commands
* @arg nb_elements : the length of @param(array)
*/
#define COMPLETION_SKELETON(array, nb_elements) \
do { \
static int len; \
static int index; \
\
if (!state) { \
index = 0; \
len = strlen(text); \
} \
\
while (index < nb_elements) \
if (strncmp(array[index], text, len) == 0) \
return strclone(array[index++]); \
else \
index++; \
\
return NULL; \
} \
while (0)
/*
* jsh_cmd_generator: a readline generator that combines several helper generators so that
* results from these generators are combined in a single generator.
*/
char *jsh_cmd_generator(const char *text, int state) {
// a separate state for each helper generator, so that it starts from zero
static int alias_state = 0;
static int built_in_state = 0;
static int unix_state = 0;
if (!state) {
alias_state = 0;
built_in_state = 0;
unix_state = 0;
}
char *rv;
// jsh grammar priority: alias > built_in > external_cmd
if (!(rv = jsh_alias_generator(text, alias_state++)))
if (!(rv = jsh_built_in_generator(text, built_in_state++)))
rv = jsh_external_cmd_generator(text, unix_state++);
return rv;
}
/*
* jsh_alias_generator: a readline generator that returns matches with jsh aliases.
*/
char *jsh_alias_generator(const char *text, int state) {
static char **alias_keys = NULL;
static unsigned int nb_alias_keys;
if (!state) {
char **ret;
if ((ret = get_all_alias_keys(&nb_alias_keys, true))) {
if (alias_keys) {
int i;
for (i = 0; i < nb_alias_keys; i++)
free(alias_keys[i]);
free(alias_keys);
}
alias_keys = ret;
}
}
COMPLETION_SKELETON(alias_keys, nb_alias_keys);
}
/*
* jsh_built_in_generator: a readline generator that returns matches with jsh built_ins.
*/
char *jsh_built_in_generator(const char *text, int state) {
COMPLETION_SKELETON(built_ins, nb_built_ins);
}
/*
* jsh_external_cmd_generator: a readline generator that returns matches for non-jsh commands.
* TODO search $PATH at boot-time and save the directory content in an array...
*/
char *jsh_external_cmd_generator(const char *text, int state) {
// hackhackhack: an array with some usefull commands
const char *widely_used_cmds[] = {"git", "cat", "grep", "ls", "exit", "sudo", "kill", \
"killall", "links", "find", "clear", "chmod", "echo", "make", "poweroff", "reboot", \
"pacman", "aptitude", "apt-cache", "apt-get", "man", "nano", "vi", "gcc", "jsh", "zsh", \
"bash"};
#define nb_widely_used_cmds (sizeof(widely_used_cmds)/sizeof(widely_used_cmds[0]))
COMPLETION_SKELETON(widely_used_cmds, nb_widely_used_cmds);
}
/*
* git_completion_generator: a readline completor for git commands
*/
char *git_completion_generator(const char *text, int state) {
static const char *git_cmds[] = {"add", "bisect", "branch", "checkout", "clone", \
"commit", "diff", "fetch", "grep", "init", "log", "merge", "mv", "pull", "push", \
"rebase", "reset", "rm", "show", "status", "tag"};
static const int nb_elements = (sizeof(git_cmds)/sizeof(git_cmds[0]));
COMPLETION_SKELETON(git_cmds, nb_elements);
}
/*
* debug_completion_generator: a proof of concept readline completor for "jsh debug on/off"
*/
char *debug_completion_generator(const char *text, int state) {
static const char *options[] = {"on", "off"};
static const int nb_options = (sizeof(options)/sizeof(options[0]));
COMPLETION_SKELETON(options, nb_options);
}
/*
* make_completion_generator: a readline completion generator for GNU make
*/
char *make_options_generator(const char *text, int state) {
static const char *options[] = { "--always-make", "--environment-overrides", \
"--ignore-errors", "--keep-going", "install", "--no-keep-going", "--stop", "install", \
"clean", "help"};
static const int nb_elements = (sizeof(options)/sizeof(options[0]));
COMPLETION_SKELETON(options, nb_elements);
}
/*
* jsh_options_generator: a readline completion generator for "jsh --options"
*/
char *jsh_options_generator(const char *text, int state) {
static const char *options[] = {"--nodebug", "--debug", "--color", "--nocolor", \
"--norc", "--license", "--version", "--help"}; //TODO dont hardcode here --> put enum in jsh.c?
static const int nb_options = (sizeof(options)/sizeof(options[0]));
COMPLETION_SKELETON(options, nb_options);
}
/*
* apt_compl_generator: a readline completion generator for "apt-get"
*/
char *apt_compl_generator(const char *text, int state) {
static const char *options[] = { "list", "search", "show", "install", "remove", \
"edit-sources", "update", "upgrade", "full-upgrade"};
static const int nb_elements = (sizeof(options)/sizeof(options[0]));
COMPLETION_SKELETON(options, nb_elements);
}