fuck! don't perform ssl handshake for blocked hosts!
[mediator.git] / src / cmdline.c
blobcdf92200c579d57d82073241362c2b5ad7821aff
1 static int cmdline_active = 0;
2 static char cmdline_input[1024];
3 static int cmdline_input_len = 0;
4 static int cmdline_curpos = 2;
6 static char *cmdline_argv[64] = {0};
7 static int cmdline_argc = 0;
10 static void cmdline_beep (void) {
11 if (cmdline_active > 0) write(STDOUT_FILENO, "\x07", 1);
15 __attribute__((destructor)) static void dtor_cmline_ (void) {
16 for (int f = 0; f < ARRAYLEN(cmdline_argv); ++f) {
17 if (cmdline_argv[f] != NULL) free(cmdline_argv[f]);
18 cmdline_argv[f] = NULL;
23 static void cmdline_parse_command_line (void) {
24 int pos = 0;
25 cmdline_argc = 0;
26 cmdline_input[cmdline_input_len] = 0;
27 for (int f = 0; f < ARRAYLEN(cmdline_argv); ++f) {
28 if (cmdline_argv[f] != NULL) free(cmdline_argv[f]);
29 cmdline_argv[f] = NULL;
31 while (pos < cmdline_input_len) {
32 // skip spaces
33 const char *sp;
34 while (pos < cmdline_input_len && isspace(cmdline_input[pos])) ++pos;
35 if (pos >= cmdline_input_len) break;
36 if (cmdline_argc >= ARRAYLEN(cmdline_argv)) break;
37 // save starting pos
38 sp = cmdline_input+pos;
39 // skip non-spaces
40 while (pos < cmdline_input_len && !isspace(cmdline_input[pos])) ++pos;
41 // mark end and skip it
42 cmdline_input[pos++] = 0;
43 cmdline_argv[cmdline_argc++] = strdup(sp);
45 cmdline_input[0] = 0;
46 cmdline_input_len = 0;
50 __attribute__((format(printf,1,2))) static void cmdline_addf (const char *fmt, ...) {
51 static char str[65536];
52 va_list ap;
53 int cnt;
54 va_start(ap, fmt);
55 cnt = vsnprintf(str, sizeof(str), fmt, ap);
56 va_end(ap);
57 if (cmdline_input_len+cnt >= sizeof(cmdline_input)) {
58 cmdline_beep();
59 } else {
60 cmdline_input[cmdline_input_len] = 0;
61 strcat(cmdline_input, str);
62 cmdline_input_len = strlen(cmdline_input);
67 typedef void (*cmdline_cb) (int argc, char *argv[], int do_autocomplete);
70 typedef struct {
71 char *name;
72 char *desc;
73 cmdline_cb handler;
74 } cmdline_command_t;
76 static cmdline_command_t *cmdline_command_list = NULL;
77 static int cmdline_command_count = 0;
80 #define CMDLINE_CMD(name,desc) \
81 static void cmdline_cmd_##name##_ (int argc, char *argv[], int do_autocomplete); \
82 __attribute__((constructor)) static void ctor_cmd_##name##_ (void) { \
83 cmdline_add_command(#name, cmdline_cmd_##name##_, desc); \
84 } \
85 static void cmdline_cmd_##name##_ (int argc, char *argv[], int do_autocomplete)
88 #define CMDLINE_DEFAULT_AC() \
89 if (do_autocomplete) { \
90 for (int f = 0; f < argc; ++f) cmdline_addf("%s ", argv[f]); \
91 return; \
95 static int cmdline_find_command (const char *name, int namelen) {
96 if (name != NULL) {
97 if (namelen < 0) namelen = strlen(name);
98 for (int f = 0; f < cmdline_command_count; ++f) {
99 if (strncmp(cmdline_command_list[f].name, name, namelen) == 0 && strlen(cmdline_command_list[f].name) == namelen) return f;
102 return -1;
106 static int cmdline_sort_cmp (const void *ip0, const void *ip1) {
107 const cmdline_command_t *i0 = (const cmdline_command_t *)ip0;
108 const cmdline_command_t *i1 = (const cmdline_command_t *)ip1;
109 return strcmp(i0->name, i1->name);
113 static void cmdline_add_command (const char *name, cmdline_cb handler, const char *desc) {
114 if (name != NULL) {
115 int idx = cmdline_find_command(name, -1);
116 if (idx < 0 && handler == NULL) return; // nothing to do
117 if (desc == NULL) desc = "";
118 if (idx >= 0) {
119 if (handler == NULL) {
120 // remove command
121 abort(); // not yet
122 } else {
123 // replace command
124 cmdline_command_list[idx].handler = handler;
125 free(cmdline_command_list[idx].desc);
126 cmdline_command_list[idx].desc = strdup(desc);
128 } else {
129 // new command
130 void *np = realloc(cmdline_command_list, sizeof(cmdline_command_list[0])*(cmdline_command_count+1));
131 if (np == NULL) abort();
132 cmdline_command_list = np;
133 idx = cmdline_command_count++;
134 cmdline_command_list[idx].name = strdup(name);
135 cmdline_command_list[idx].desc = strdup(desc);
136 cmdline_command_list[idx].handler = handler;
137 qsort(cmdline_command_list, cmdline_command_count, sizeof(cmdline_command_list[0]), cmdline_sort_cmp);
143 static void cmdline_process_command (void) {
144 cmdline_parse_command_line();
145 if (cmdline_argc > 0) {
146 int idx;
147 if (strcmp(cmdline_argv[0], "help") == 0 && cmdline_argc == 2) {
148 idx = cmdline_find_command(cmdline_argv[1], -1);
149 if (idx >= 0) {
150 if (cmdline_command_list[idx].desc[0]) {
151 conlogf("%s: %s\n", cmdline_command_list[idx].name, cmdline_command_list[idx].desc);
152 } else {
153 conlogf("%s: no help\n", cmdline_command_list[idx].name);
155 return;
158 idx = cmdline_find_command(cmdline_argv[0], -1);
159 if (idx >= 0) {
160 cmdline_command_list[idx].handler(cmdline_argc, cmdline_argv, 0);
161 } else {
162 conlogf("unknown command: '%s'\n", cmdline_argv[0]);
168 static void cmdline_autocomplete (void) {
169 int pos = 0;
170 if (cmdline_input_len == 0 && cmdline_active > 0) {
171 // ÐÅÞÁÔÁÅÍ ×ÓÅ
172 for (int idx = 0; idx < cmdline_command_count; ++idx) conlogf(" %s\n", cmdline_command_list[idx].name);
173 return;
175 if (cmdline_input_len >= ARRAYLEN(cmdline_input)-4) return;
176 if (cmdline_input_len == 0 && isspace(cmdline_input[0])) return;
177 while (pos < cmdline_input_len && !isspace(cmdline_input[pos])) ++pos;
178 if (pos < cmdline_input_len) {
179 // this is autocomplete query for command
180 int idx = cmdline_find_command(cmdline_input, pos);
181 if (idx >= 0) {
182 cmdline_parse_command_line();
183 cmdline_command_list[idx].handler(cmdline_argc, cmdline_argv, 1);
184 } else {
185 cmdline_beep();
187 return;
189 cmdline_input[pos] = 0;
191 const char *found = NULL;
192 int foundlen = 0;
193 int pfxcount = 0;
194 int slen = pos;
195 // ÐÅÒ×ÙÊ ÐÒÏÈÏÄ: ÓÞÉÔÁÅÍ ÐÒÅÆÉËÓÙ, ÚÁÐÏÍÉÎÁÅÍ ÓÁÍÙÊ ÄÌÉÎÎÙÊ ÎÁÊÄÅÎÙÊ
196 for (int idx = 0; idx < cmdline_command_count; ++idx) {
197 const char *s = cmdline_command_list[idx].name;
198 int l0 = strlen(s);
199 if (slen <= l0 && strncmp(s, cmdline_input, slen) == 0) {
200 // ÎÁÛÌÉ
201 if (l0 > foundlen) {
202 found = s;
203 foundlen = l0;
205 ++pfxcount;
208 if (pfxcount == 0) { cmdline_beep(); return; } // ÎÅ ÎÁÛÌÉ ×ÏÏÂÝÅ ÎÉÈÅÒÁ, ×ÁÌÉÍ ÏÔÓÀÄÁ, ÐÁÃÁÎÙ!
209 if (pfxcount == 1) {
210 // ÎÁÛÌÉ ÏÄÉÎ, ÚÁÅÂÉÓØ!
211 cmdline_input_len = snprintf(cmdline_input, sizeof(cmdline_input), "%s ", found);
212 return;
214 // ÎÁÛÌÉ ÄÏÈÅÒÁ, ÜÔÏ ÐÒÉÓËÏÒÂÎÏ
215 // ÉÝÅÍ ÓÁÍÙÊ ÄÌÉÎÎÙÊ ÐÒÅÆÉËÓ, ÚÁÏÄÎÏ ÐÅÞÁÔÁÅÍ ×Ó£, ÞÔÏ ÍÏÖÎÏ
216 snprintf(cmdline_input, sizeof(cmdline_input), "%s", found); // ÄÌÉÎÎÅÅ ÎÅ ÂÙ×ÁÅÔ, Ñ ÇÁÒÁÎÔÉÒÕÀ ÜÔÏ
217 for (int idx = 0; idx < cmdline_command_count; ++idx) {
218 const char *s = cmdline_command_list[idx].name;
219 if (strncmp(s, cmdline_input, slen) == 0) {
220 // ÎÁÛÅ, ÐÅÞÁÔÁÅÍ É ÐÒÁ×ÉÍ ÐÒÅÆÉËÓ
221 char *t = cmdline_input;
222 conlogf("%s\n", s);
223 while (*t && *s && *t == *s) { ++t; ++s; }
224 *t = '\0'; // ÄÁ ÐÏÈÅÒÕ, ÔÕÔ É ÏÂÒÅÖÅÍ
225 int l = strlen(cmdline_input);
226 if (l < slen) slen = l;
229 cmdline_input_len = strlen(cmdline_input);
230 // ×Ó£
235 static void cmdline_init (void) {
236 if (!cmdline_active) {
237 if (isatty(STDIN_FILENO) && isatty(STDOUT_FILENO)) {
238 cmdline_active = 1;
239 rawtty_on();
240 cmdline_input_len = 0;
241 cmdline_curpos = 2;
242 } else {
243 cmdline_active = -11;
249 static void cmdline_stop (void) {
250 if (cmdline_active > 0) {
251 rawtty_off();
252 cmdline_active = 0;
254 for (int f = cmdline_command_count-1; f >= 0; --f) {
255 free(cmdline_command_list[f].desc);
256 free(cmdline_command_list[f].name);
258 if (cmdline_command_list != NULL) free(cmdline_command_list);
259 cmdline_command_list = NULL;
260 cmdline_command_count = 0;
264 static int cmdline_fd (void) {
265 return (cmdline_active > 0 ? STDIN_FILENO : -1);
269 static void cmdline_setcur (void) {
270 if (cmdline_active > 0) {
271 char buf[64];
272 int len = snprintf(buf, sizeof(buf), "\x1b[1;%dH", cmdline_curpos);
273 write(STDOUT_FILENO, buf, len);
278 static void cmdline_draw (void) {
279 if (cmdline_active > 0) {
280 int stpos = 0, len = cmdline_input_len;
281 int wdt = rawtty_width()-2;
282 //write(STDOUT_FILENO, "\x1b[1;1H\x1b[0;44;33;2m>", 19);
283 write(STDOUT_FILENO, "\x1b[1;1H\x1b[0m>", 11);
284 if (len > wdt) {
285 stpos += len-wdt;
286 len = wdt;
288 if (len > 0) write(STDOUT_FILENO, cmdline_input+stpos, len);
289 write(STDOUT_FILENO, "\x1b[K\x1b[0m", 7);
294 static void cmdline_fix_curpos (void) {
295 if (cmdline_active > 0) {
296 int wdt = rawtty_width();
297 int ll = cmdline_input_len+2;
298 if (ll > wdt) ll = wdt;
299 cmdline_curpos = ll;
304 // no need to check for cmdline_active here
305 static void cmdline_process_keys (void) {
306 if (cmdline_active <= 0) return; // just in case
307 while (rawtty_is_keyhit()) {
308 uint8_t ch;
309 if (read(STDIN_FILENO, &ch, 1) != 1) break;
310 if (ch == 3) {
311 // ^C
312 raise(SIGINT);
313 } else if (ch == 8 || ch == 127) {
314 // BS
315 if (cmdline_input_len > 0) --cmdline_input_len;
316 } else if (ch == 9) {
317 // tab
318 cmdline_autocomplete();
319 } else if (ch == 10) {
320 // enter
321 //cmdline_input[cmdline_input_len] = 0;
322 //cmdline_input_len = 0;
323 cmdline_process_command();
324 } else if (ch == 25) {
325 // ^Y
326 cmdline_input_len = 0;
327 } else if (ch >= 32 && ch < 127) {
328 if (cmdline_input_len+2 < sizeof(cmdline_input)) {
329 cmdline_input[cmdline_input_len++] = ch;
330 } else {
331 cmdline_beep();
335 cmdline_fix_curpos();
336 cmdline_draw();
341 * ÐÒÏÈÏÄÉÍÓÑ ÐÏ ÓÐÉÓËÕ, ÓÞÉÔÁÅÍ ÐÒÅÆÉËÓÙ
342 * ÅÓÌÉ × ÉÔÏÇÅ 0: ÓÒÁËÁ, ÐÉÚÄÅÃ, ÉÚÒÁÉÌØ, ÖÉÄÙ, ÓÍÅÒÔØ, ÒÁÚÒÕÛÅÎÉÑ, ×ÏÚ×ÒÁÝÁÅÍ ""
343 * ÅÓÌÉ × ÉÔÏÇÅ 1: ÚÁÅÂÉÓØ, ×ÏÚ×ÒÁÝÁÅÍ ÜÔÏÔ 1
344 * ÅÓÌÉ × ÉÔÏÇÅ ÄÏÈÕÑ: ×ÙÂÉÒÁÅÍ ÎÁÉÂÏÌÅÅ ÄÌÉÎÎÙÊ ÐÒÅÆÉËÓ, ×ÏÚ×ÒÁÝÁÅÍ; ÚÁÏÄÎÏ ÐÅÞÁÔÁÅÍ ×ÓÅ ×ÏÚÍÏÖÎÙÅ ËÏÍÁÎÄÙ
345 * ÒÅÚÕÌØÔÁÔ ÎÅ ÂÙ×ÁÅÔ NULL, É ÅÇÏ ÎÁÄÏ free()
346 * õþôé! `cmds` ÄÏÌÖÎÏ ÂÙÔØ ÎÅÐÕÓÔÏÊ ÓÔÒÏËÏÊ, ÂÅÚ ÌÉÛÎÉÈ ÐÒÏÂÅÌÏ×
347 * ÅÓÌÉ `cmds` -- NULL, ÔÏ ÎÁÐÅÞÁÔÁÔØ ×Ó£
348 * `matches` ÕÓÔÁÎÁ×ÌÉ×ÁÅÔÓÑ × ËÏÌÉÞÅÓÔ×Ï ÎÁÊÄÅÎÙÈ ÐÏÐÁÄÁÎÉÊ (Ô.Å. 0 -- ÎÅÔ, 1 -- ÔÏÞÎÏ, É ÔÙ ÐÙ)
350 __attribute__((sentinel)) static char *cmdline_complete_abbrev (const char *cmds, int doprint, int *matches, ...) {
351 if (matches != NULL) *matches = 0;
352 if (cmds != NULL && cmds[0]) {
353 va_list va;
354 const char *found = NULL, *s;
355 int foundlen = 0;
356 int pfxcount = 0;
357 int slen = strlen(cmds);
358 char *res;
359 // ÐÅÒ×ÙÊ ÐÒÏÈÏÄ: ÓÞÉÔÁÅÍ ÐÒÅÆÉËÓÙ, ÚÁÐÏÍÉÎÁÅÍ ÓÁÍÙÊ ÄÌÉÎÎÙÊ ÎÁÊÄÅÎÙÊ
360 va_start(va, matches);
361 while ((s = va_arg(va, const char *)) != NULL) {
362 int l0 = strlen(s);
363 if (slen <= l0 && strncmp(s, cmds, slen) == 0) {
364 // ÎÁÛÌÉ
365 if (l0 > foundlen) {
366 found = s;
367 foundlen = l0;
369 ++pfxcount;
372 va_end(va);
373 if (matches != NULL) *matches = pfxcount;
374 if (pfxcount == 0) return strdup(cmds); // ÎÅ ÎÁÛÌÉ ×ÏÏÂÝÅ ÎÉÈÅÒÁ, ×ÁÌÉÍ ÏÔÓÀÄÁ, ÐÁÃÁÎÙ!
375 if (pfxcount == 1) return strdup(found); // ÎÁÛÌÉ ÒÏ×ÎÏ ÏÄÉÎ, ÜÔÏ ÎÁÛ, ÚÁÅÂÉÓØ!
376 // ÎÁÛÌÉ ÄÏÈÅÒÁ, ÜÔÏ ÐÒÉÓËÏÒÂÎÏ
377 // ÉÝÅÍ ÓÁÍÙÊ ÄÌÉÎÎÙÊ ÐÒÅÆÉËÓ, ÚÁÏÄÎÏ ÐÅÞÁÔÁÅÍ ×Ó£, ÞÔÏ ÍÏÖÎÏ
378 res = strdup(found); // ÄÌÉÎÎÅÅ ÎÅ ÂÙ×ÁÅÔ, Ñ ÇÁÒÁÎÔÉÒÕÀ ÜÔÏ
379 va_start(va, matches);
380 while ((s = va_arg(va, const char *)) != NULL) {
381 if (strncmp(s, res, slen) == 0) {
382 // ÎÁÛÅ, ÐÅÞÁÔÁÅÍ É ÐÒÁ×ÉÍ ÐÒÅÆÉËÓ
383 char *t = res;
384 if (doprint) conlogf(" %s\n", s);
385 while (*t && *s && *t == *s) { ++t; ++s; }
386 *t = '\0'; // ÄÁ ÐÏÈÅÒÕ, ÔÕÔ É ÏÂÒÅÖÅÍ
389 va_end(va);
390 // ×ÓÅ, ×ÏÚ×ÒÁÝÁÅÍ ÎÁÊÄÅÎÏÅ
391 return res;
392 } else if (cmds == NULL && doprint) {
393 // ÔÕÐÏ ÎÁÐÅÞÁÔÁÅÍ ×ÓÅ ×ÏÚÍÏÖÎÙÅ
394 va_list va;
395 const char *s;
396 va_start(va, matches);
397 while ((s = va_arg(va, const char *)) != NULL) conlogf(" %s\n", s);
398 va_end(va);
400 return strdup("");