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) {
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
) {
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;
38 sp
= cmdline_input
+pos
;
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
);
46 cmdline_input_len
= 0;
50 __attribute__((format(printf
,1,2))) static void cmdline_addf (const char *fmt
, ...) {
51 static char str
[65536];
55 cnt
= vsnprintf(str
, sizeof(str
), fmt
, ap
);
57 if (cmdline_input_len
+cnt
>= sizeof(cmdline_input
)) {
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
);
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); \
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]); \
95 static int cmdline_find_command (const char *name
, int namelen
) {
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
;
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
) {
115 int idx
= cmdline_find_command(name
, -1);
116 if (idx
< 0 && handler
== NULL
) return; // nothing to do
117 if (desc
== NULL
) desc
= "";
119 if (handler
== NULL
) {
124 cmdline_command_list
[idx
].handler
= handler
;
125 free(cmdline_command_list
[idx
].desc
);
126 cmdline_command_list
[idx
].desc
= strdup(desc
);
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) {
147 if (strcmp(cmdline_argv
[0], "help") == 0 && cmdline_argc
== 2) {
148 idx
= cmdline_find_command(cmdline_argv
[1], -1);
150 if (cmdline_command_list
[idx
].desc
[0]) {
151 conlogf("%s: %s\n", cmdline_command_list
[idx
].name
, cmdline_command_list
[idx
].desc
);
153 conlogf("%s: no help\n", cmdline_command_list
[idx
].name
);
158 idx
= cmdline_find_command(cmdline_argv
[0], -1);
160 cmdline_command_list
[idx
].handler(cmdline_argc
, cmdline_argv
, 0);
162 conlogf("unknown command: '%s'\n", cmdline_argv
[0]);
168 static void cmdline_autocomplete (void) {
170 if (cmdline_input_len
== 0 && cmdline_active
> 0) {
172 for (int idx
= 0; idx
< cmdline_command_count
; ++idx
) conlogf(" %s\n", cmdline_command_list
[idx
].name
);
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
);
182 cmdline_parse_command_line();
183 cmdline_command_list
[idx
].handler(cmdline_argc
, cmdline_argv
, 1);
189 cmdline_input
[pos
] = 0;
191 const char *found
= NULL
;
195 // ÐÅÒ×ÙÊ ÐÒÏÈÏÄ: ÓÞÉÔÁÅÍ ÐÒÅÆÉËÓÙ, ÚÁÐÏÍÉÎÁÅÍ ÓÁÍÙÊ ÄÌÉÎÎÙÊ ÎÁÊÄÅÎÙÊ
196 for (int idx
= 0; idx
< cmdline_command_count
; ++idx
) {
197 const char *s
= cmdline_command_list
[idx
].name
;
199 if (slen
<= l0
&& strncmp(s
, cmdline_input
, slen
) == 0) {
208 if (pfxcount
== 0) { cmdline_beep(); return; } // ÎÅ ÎÁÛÌÉ ×ÏÏÂÝÅ ÎÉÈÅÒÁ, ×ÁÌÉÍ ÏÔÓÀÄÁ, ÐÁÃÁÎÙ!
210 // ÎÁÛÌÉ ÏÄÉÎ, ÚÁÅÂÉÓØ!
211 cmdline_input_len
= snprintf(cmdline_input
, sizeof(cmdline_input
), "%s ", found
);
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
;
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
);
235 static void cmdline_init (void) {
236 if (!cmdline_active
) {
237 if (isatty(STDIN_FILENO
) && isatty(STDOUT_FILENO
)) {
240 cmdline_input_len
= 0;
243 cmdline_active
= -11;
249 static void cmdline_stop (void) {
250 if (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) {
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);
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
;
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()) {
309 if (read(STDIN_FILENO
, &ch
, 1) != 1) break;
313 } else if (ch
== 8 || ch
== 127) {
315 if (cmdline_input_len
> 0) --cmdline_input_len
;
316 } else if (ch
== 9) {
318 cmdline_autocomplete();
319 } else if (ch
== 10) {
321 //cmdline_input[cmdline_input_len] = 0;
322 //cmdline_input_len = 0;
323 cmdline_process_command();
324 } else if (ch
== 25) {
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
;
335 cmdline_fix_curpos();
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]) {
354 const char *found
= NULL
, *s
;
357 int slen
= strlen(cmds
);
359 // ÐÅÒ×ÙÊ ÐÒÏÈÏÄ: ÓÞÉÔÁÅÍ ÐÒÅÆÉËÓÙ, ÚÁÐÏÍÉÎÁÅÍ ÓÁÍÙÊ ÄÌÉÎÎÙÊ ÎÁÊÄÅÎÙÊ
360 va_start(va
, matches
);
361 while ((s
= va_arg(va
, const char *)) != NULL
) {
363 if (slen
<= l0
&& strncmp(s
, cmds
, slen
) == 0) {
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 // ÎÁÛÅ, ÐÅÞÁÔÁÅÍ É ÐÒÁ×ÉÍ ÐÒÅÆÉËÓ
384 if (doprint
) conlogf(" %s\n", s
);
385 while (*t
&& *s
&& *t
== *s
) { ++t
; ++s
; }
386 *t
= '\0'; // ÄÁ ÐÏÈÅÒÕ, ÔÕÔ É ÏÂÒÅÖÅÍ
390 // ×ÓÅ, ×ÏÚ×ÒÁÝÁÅÍ ÎÁÊÄÅÎÏÅ
392 } else if (cmds
== NULL
&& doprint
) {
393 // ÔÕÐÏ ÎÁÐÅÞÁÔÁÅÍ ×ÓÅ ×ÏÚÍÏÖÎÙÅ
396 va_start(va
, matches
);
397 while ((s
= va_arg(va
, const char *)) != NULL
) conlogf(" %s\n", s
);