]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | /*- |
2 | * BSD LICENSE | |
3 | * | |
4 | * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. | |
5 | * All rights reserved. | |
6 | * | |
7 | * Redistribution and use in source and binary forms, with or without | |
8 | * modification, are permitted provided that the following conditions | |
9 | * are met: | |
10 | * | |
11 | * * Redistributions of source code must retain the above copyright | |
12 | * notice, this list of conditions and the following disclaimer. | |
13 | * * Redistributions in binary form must reproduce the above copyright | |
14 | * notice, this list of conditions and the following disclaimer in | |
15 | * the documentation and/or other materials provided with the | |
16 | * distribution. | |
17 | * * Neither the name of Intel Corporation nor the names of its | |
18 | * contributors may be used to endorse or promote products derived | |
19 | * from this software without specific prior written permission. | |
20 | * | |
21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
22 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
23 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
24 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
25 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
26 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
27 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
28 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
29 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
30 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
31 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
32 | */ | |
33 | ||
34 | /* | |
35 | * Copyright (c) 2009, Olivier MATZ <zer0@droids-corp.org> | |
36 | * All rights reserved. | |
37 | * Redistribution and use in source and binary forms, with or without | |
38 | * modification, are permitted provided that the following conditions are met: | |
39 | * | |
40 | * * Redistributions of source code must retain the above copyright | |
41 | * notice, this list of conditions and the following disclaimer. | |
42 | * * Redistributions in binary form must reproduce the above copyright | |
43 | * notice, this list of conditions and the following disclaimer in the | |
44 | * documentation and/or other materials provided with the distribution. | |
45 | * * Neither the name of the University of California, Berkeley nor the | |
46 | * names of its contributors may be used to endorse or promote products | |
47 | * derived from this software without specific prior written permission. | |
48 | * | |
49 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY | |
50 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |
51 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
52 | * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY | |
53 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |
54 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |
55 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |
56 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
57 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |
58 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
59 | */ | |
60 | ||
61 | #include <stdlib.h> | |
62 | #include <stdio.h> | |
63 | #include <stdint.h> | |
64 | #include <string.h> | |
65 | #include <stdarg.h> | |
66 | #include <errno.h> | |
67 | #include <ctype.h> | |
68 | ||
69 | #include "cmdline_cirbuf.h" | |
70 | #include "cmdline_rdline.h" | |
71 | ||
72 | static void rdline_puts(struct rdline *rdl, const char *buf); | |
73 | static void rdline_miniprintf(struct rdline *rdl, | |
74 | const char *buf, unsigned int val); | |
75 | ||
76 | static void rdline_remove_old_history_item(struct rdline *rdl); | |
77 | static void rdline_remove_first_history_item(struct rdline *rdl); | |
78 | static unsigned int rdline_get_history_size(struct rdline *rdl); | |
79 | ||
80 | ||
81 | /* isblank() needs _XOPEN_SOURCE >= 600 || _ISOC99_SOURCE, so use our | |
82 | * own. */ | |
83 | static int | |
84 | isblank2(char c) | |
85 | { | |
86 | if (c == ' ' || | |
87 | c == '\t' ) | |
88 | return 1; | |
89 | return 0; | |
90 | } | |
91 | ||
92 | int | |
93 | rdline_init(struct rdline *rdl, | |
94 | rdline_write_char_t *write_char, | |
95 | rdline_validate_t *validate, | |
96 | rdline_complete_t *complete) | |
97 | { | |
98 | if (!rdl || !write_char || !validate || !complete) | |
99 | return -EINVAL; | |
100 | memset(rdl, 0, sizeof(*rdl)); | |
101 | rdl->validate = validate; | |
102 | rdl->complete = complete; | |
103 | rdl->write_char = write_char; | |
104 | rdl->status = RDLINE_INIT; | |
105 | return cirbuf_init(&rdl->history, rdl->history_buf, 0, RDLINE_HISTORY_BUF_SIZE); | |
106 | } | |
107 | ||
108 | void | |
109 | rdline_newline(struct rdline *rdl, const char *prompt) | |
110 | { | |
111 | unsigned int i; | |
112 | ||
113 | if (!rdl || !prompt) | |
114 | return; | |
115 | ||
116 | vt100_init(&rdl->vt100); | |
117 | cirbuf_init(&rdl->left, rdl->left_buf, 0, RDLINE_BUF_SIZE); | |
118 | cirbuf_init(&rdl->right, rdl->right_buf, 0, RDLINE_BUF_SIZE); | |
119 | ||
120 | rdl->prompt_size = strnlen(prompt, RDLINE_PROMPT_SIZE-1); | |
121 | if (prompt != rdl->prompt) | |
122 | memcpy(rdl->prompt, prompt, rdl->prompt_size); | |
123 | rdl->prompt[RDLINE_PROMPT_SIZE-1] = '\0'; | |
124 | ||
125 | for (i=0 ; i<rdl->prompt_size ; i++) | |
126 | rdl->write_char(rdl, rdl->prompt[i]); | |
127 | rdl->status = RDLINE_RUNNING; | |
128 | ||
129 | rdl->history_cur_line = -1; | |
130 | } | |
131 | ||
132 | void | |
133 | rdline_stop(struct rdline *rdl) | |
134 | { | |
135 | if (!rdl) | |
136 | return; | |
137 | rdl->status = RDLINE_INIT; | |
138 | } | |
139 | ||
140 | void | |
141 | rdline_quit(struct rdline *rdl) | |
142 | { | |
143 | if (!rdl) | |
144 | return; | |
145 | rdl->status = RDLINE_EXITED; | |
146 | } | |
147 | ||
148 | void | |
149 | rdline_restart(struct rdline *rdl) | |
150 | { | |
151 | if (!rdl) | |
152 | return; | |
153 | rdl->status = RDLINE_RUNNING; | |
154 | } | |
155 | ||
156 | void | |
157 | rdline_reset(struct rdline *rdl) | |
158 | { | |
159 | if (!rdl) | |
160 | return; | |
161 | vt100_init(&rdl->vt100); | |
162 | cirbuf_init(&rdl->left, rdl->left_buf, 0, RDLINE_BUF_SIZE); | |
163 | cirbuf_init(&rdl->right, rdl->right_buf, 0, RDLINE_BUF_SIZE); | |
164 | ||
165 | rdl->status = RDLINE_RUNNING; | |
166 | ||
167 | rdl->history_cur_line = -1; | |
168 | } | |
169 | ||
170 | const char * | |
171 | rdline_get_buffer(struct rdline *rdl) | |
172 | { | |
173 | if (!rdl) | |
174 | return NULL; | |
175 | unsigned int len_l, len_r; | |
176 | cirbuf_align_left(&rdl->left); | |
177 | cirbuf_align_left(&rdl->right); | |
178 | ||
179 | len_l = CIRBUF_GET_LEN(&rdl->left); | |
180 | len_r = CIRBUF_GET_LEN(&rdl->right); | |
181 | memcpy(rdl->left_buf+len_l, rdl->right_buf, len_r); | |
182 | ||
183 | rdl->left_buf[len_l + len_r] = '\n'; | |
184 | rdl->left_buf[len_l + len_r + 1] = '\0'; | |
185 | return rdl->left_buf; | |
186 | } | |
187 | ||
188 | static void | |
189 | display_right_buffer(struct rdline *rdl, int force) | |
190 | { | |
191 | unsigned int i; | |
192 | char tmp; | |
193 | ||
194 | if (!force && CIRBUF_IS_EMPTY(&rdl->right)) | |
195 | return; | |
196 | ||
197 | rdline_puts(rdl, vt100_clear_right); | |
198 | CIRBUF_FOREACH(&rdl->right, i, tmp) { | |
199 | rdl->write_char(rdl, tmp); | |
200 | } | |
201 | if (!CIRBUF_IS_EMPTY(&rdl->right)) | |
202 | rdline_miniprintf(rdl, vt100_multi_left, | |
203 | CIRBUF_GET_LEN(&rdl->right)); | |
204 | } | |
205 | ||
206 | void | |
207 | rdline_redisplay(struct rdline *rdl) | |
208 | { | |
209 | unsigned int i; | |
210 | char tmp; | |
211 | ||
212 | if (!rdl) | |
213 | return; | |
214 | ||
215 | rdline_puts(rdl, vt100_home); | |
216 | for (i=0 ; i<rdl->prompt_size ; i++) | |
217 | rdl->write_char(rdl, rdl->prompt[i]); | |
218 | CIRBUF_FOREACH(&rdl->left, i, tmp) { | |
219 | rdl->write_char(rdl, tmp); | |
220 | } | |
221 | display_right_buffer(rdl, 1); | |
222 | } | |
223 | ||
224 | int | |
225 | rdline_char_in(struct rdline *rdl, char c) | |
226 | { | |
227 | unsigned int i; | |
228 | int cmd; | |
229 | char tmp; | |
230 | char *buf; | |
231 | ||
232 | if (!rdl) | |
233 | return -EINVAL; | |
234 | ||
235 | if (rdl->status == RDLINE_EXITED) | |
236 | return RDLINE_RES_EXITED; | |
237 | if (rdl->status != RDLINE_RUNNING) | |
238 | return RDLINE_RES_NOT_RUNNING; | |
239 | ||
240 | cmd = vt100_parser(&rdl->vt100, c); | |
241 | if (cmd == -2) | |
242 | return RDLINE_RES_SUCCESS; | |
243 | ||
244 | if (cmd >= 0) { | |
245 | switch (cmd) { | |
246 | /* move caret 1 char to the left */ | |
247 | case CMDLINE_KEY_CTRL_B: | |
248 | case CMDLINE_KEY_LEFT_ARR: | |
249 | if (CIRBUF_IS_EMPTY(&rdl->left)) | |
250 | break; | |
251 | tmp = cirbuf_get_tail(&rdl->left); | |
252 | cirbuf_del_tail(&rdl->left); | |
253 | cirbuf_add_head(&rdl->right, tmp); | |
254 | rdline_puts(rdl, vt100_left_arr); | |
255 | break; | |
256 | ||
257 | /* move caret 1 char to the right */ | |
258 | case CMDLINE_KEY_CTRL_F: | |
259 | case CMDLINE_KEY_RIGHT_ARR: | |
260 | if (CIRBUF_IS_EMPTY(&rdl->right)) | |
261 | break; | |
262 | tmp = cirbuf_get_head(&rdl->right); | |
263 | cirbuf_del_head(&rdl->right); | |
264 | cirbuf_add_tail(&rdl->left, tmp); | |
265 | rdline_puts(rdl, vt100_right_arr); | |
266 | break; | |
267 | ||
268 | /* move caret 1 word to the left */ | |
269 | /* keyboard equivalent: Alt+B */ | |
270 | case CMDLINE_KEY_WLEFT: | |
271 | while (! CIRBUF_IS_EMPTY(&rdl->left) && | |
272 | (tmp = cirbuf_get_tail(&rdl->left)) && | |
273 | isblank2(tmp)) { | |
274 | rdline_puts(rdl, vt100_left_arr); | |
275 | cirbuf_del_tail(&rdl->left); | |
276 | cirbuf_add_head(&rdl->right, tmp); | |
277 | } | |
278 | while (! CIRBUF_IS_EMPTY(&rdl->left) && | |
279 | (tmp = cirbuf_get_tail(&rdl->left)) && | |
280 | !isblank2(tmp)) { | |
281 | rdline_puts(rdl, vt100_left_arr); | |
282 | cirbuf_del_tail(&rdl->left); | |
283 | cirbuf_add_head(&rdl->right, tmp); | |
284 | } | |
285 | break; | |
286 | ||
287 | /* move caret 1 word to the right */ | |
288 | /* keyboard equivalent: Alt+F */ | |
289 | case CMDLINE_KEY_WRIGHT: | |
290 | while (! CIRBUF_IS_EMPTY(&rdl->right) && | |
291 | (tmp = cirbuf_get_head(&rdl->right)) && | |
292 | isblank2(tmp)) { | |
293 | rdline_puts(rdl, vt100_right_arr); | |
294 | cirbuf_del_head(&rdl->right); | |
295 | cirbuf_add_tail(&rdl->left, tmp); | |
296 | } | |
297 | while (! CIRBUF_IS_EMPTY(&rdl->right) && | |
298 | (tmp = cirbuf_get_head(&rdl->right)) && | |
299 | !isblank2(tmp)) { | |
300 | rdline_puts(rdl, vt100_right_arr); | |
301 | cirbuf_del_head(&rdl->right); | |
302 | cirbuf_add_tail(&rdl->left, tmp); | |
303 | } | |
304 | break; | |
305 | ||
306 | /* move caret to the left */ | |
307 | case CMDLINE_KEY_CTRL_A: | |
308 | if (CIRBUF_IS_EMPTY(&rdl->left)) | |
309 | break; | |
310 | rdline_miniprintf(rdl, vt100_multi_left, | |
311 | CIRBUF_GET_LEN(&rdl->left)); | |
312 | while (! CIRBUF_IS_EMPTY(&rdl->left)) { | |
313 | tmp = cirbuf_get_tail(&rdl->left); | |
314 | cirbuf_del_tail(&rdl->left); | |
315 | cirbuf_add_head(&rdl->right, tmp); | |
316 | } | |
317 | break; | |
318 | ||
319 | /* move caret to the right */ | |
320 | case CMDLINE_KEY_CTRL_E: | |
321 | if (CIRBUF_IS_EMPTY(&rdl->right)) | |
322 | break; | |
323 | rdline_miniprintf(rdl, vt100_multi_right, | |
324 | CIRBUF_GET_LEN(&rdl->right)); | |
325 | while (! CIRBUF_IS_EMPTY(&rdl->right)) { | |
326 | tmp = cirbuf_get_head(&rdl->right); | |
327 | cirbuf_del_head(&rdl->right); | |
328 | cirbuf_add_tail(&rdl->left, tmp); | |
329 | } | |
330 | break; | |
331 | ||
332 | /* delete 1 char from the left */ | |
333 | case CMDLINE_KEY_BKSPACE: | |
334 | if(!cirbuf_del_tail_safe(&rdl->left)) { | |
335 | rdline_puts(rdl, vt100_bs); | |
336 | display_right_buffer(rdl, 1); | |
337 | } | |
338 | break; | |
339 | ||
340 | /* delete 1 char from the right */ | |
341 | case CMDLINE_KEY_SUPPR: | |
342 | case CMDLINE_KEY_CTRL_D: | |
343 | if (cmd == CMDLINE_KEY_CTRL_D && | |
344 | CIRBUF_IS_EMPTY(&rdl->left) && | |
345 | CIRBUF_IS_EMPTY(&rdl->right)) { | |
346 | return RDLINE_RES_EOF; | |
347 | } | |
348 | if (!cirbuf_del_head_safe(&rdl->right)) { | |
349 | display_right_buffer(rdl, 1); | |
350 | } | |
351 | break; | |
352 | ||
353 | /* delete 1 word from the left */ | |
354 | case CMDLINE_KEY_META_BKSPACE: | |
355 | case CMDLINE_KEY_CTRL_W: | |
356 | while (! CIRBUF_IS_EMPTY(&rdl->left) && isblank2(cirbuf_get_tail(&rdl->left))) { | |
357 | rdline_puts(rdl, vt100_bs); | |
358 | cirbuf_del_tail(&rdl->left); | |
359 | } | |
360 | while (! CIRBUF_IS_EMPTY(&rdl->left) && !isblank2(cirbuf_get_tail(&rdl->left))) { | |
361 | rdline_puts(rdl, vt100_bs); | |
362 | cirbuf_del_tail(&rdl->left); | |
363 | } | |
364 | display_right_buffer(rdl, 1); | |
365 | break; | |
366 | ||
367 | /* delete 1 word from the right */ | |
368 | case CMDLINE_KEY_META_D: | |
369 | while (! CIRBUF_IS_EMPTY(&rdl->right) && isblank2(cirbuf_get_head(&rdl->right))) | |
370 | cirbuf_del_head(&rdl->right); | |
371 | while (! CIRBUF_IS_EMPTY(&rdl->right) && !isblank2(cirbuf_get_head(&rdl->right))) | |
372 | cirbuf_del_head(&rdl->right); | |
373 | display_right_buffer(rdl, 1); | |
374 | break; | |
375 | ||
376 | /* set kill buffer to contents on the right side of caret */ | |
377 | case CMDLINE_KEY_CTRL_K: | |
378 | cirbuf_get_buf_head(&rdl->right, rdl->kill_buf, RDLINE_BUF_SIZE); | |
379 | rdl->kill_size = CIRBUF_GET_LEN(&rdl->right); | |
380 | cirbuf_del_buf_head(&rdl->right, rdl->kill_size); | |
381 | rdline_puts(rdl, vt100_clear_right); | |
382 | break; | |
383 | ||
384 | /* paste contents of kill buffer to the left side of caret */ | |
385 | case CMDLINE_KEY_CTRL_Y: | |
386 | i=0; | |
387 | while(CIRBUF_GET_LEN(&rdl->right) + CIRBUF_GET_LEN(&rdl->left) < | |
388 | RDLINE_BUF_SIZE && | |
389 | i < rdl->kill_size) { | |
390 | cirbuf_add_tail(&rdl->left, rdl->kill_buf[i]); | |
391 | rdl->write_char(rdl, rdl->kill_buf[i]); | |
392 | i++; | |
393 | } | |
394 | display_right_buffer(rdl, 0); | |
395 | break; | |
396 | ||
397 | /* clear and newline */ | |
398 | case CMDLINE_KEY_CTRL_C: | |
399 | rdline_puts(rdl, "\r\n"); | |
400 | rdline_newline(rdl, rdl->prompt); | |
401 | break; | |
402 | ||
403 | /* redisplay (helps when prompt is lost in other output) */ | |
404 | case CMDLINE_KEY_CTRL_L: | |
405 | rdline_redisplay(rdl); | |
406 | break; | |
407 | ||
408 | /* autocomplete */ | |
409 | case CMDLINE_KEY_TAB: | |
410 | case CMDLINE_KEY_HELP: | |
411 | cirbuf_align_left(&rdl->left); | |
412 | rdl->left_buf[CIRBUF_GET_LEN(&rdl->left)] = '\0'; | |
413 | if (rdl->complete) { | |
414 | char tmp_buf[BUFSIZ]; | |
415 | int complete_state; | |
416 | int ret; | |
417 | unsigned int tmp_size; | |
418 | ||
419 | if (cmd == CMDLINE_KEY_TAB) | |
420 | complete_state = 0; | |
421 | else | |
422 | complete_state = -1; | |
423 | ||
424 | /* see in parse.h for help on complete() */ | |
425 | ret = rdl->complete(rdl, rdl->left_buf, | |
426 | tmp_buf, sizeof(tmp_buf), | |
427 | &complete_state); | |
428 | /* no completion or error */ | |
429 | if (ret <= 0) { | |
430 | return RDLINE_RES_COMPLETE; | |
431 | } | |
432 | ||
433 | tmp_size = strnlen(tmp_buf, sizeof(tmp_buf)); | |
434 | /* add chars */ | |
435 | if (ret == RDLINE_RES_COMPLETE) { | |
436 | i=0; | |
437 | while(CIRBUF_GET_LEN(&rdl->right) + CIRBUF_GET_LEN(&rdl->left) < | |
438 | RDLINE_BUF_SIZE && | |
439 | i < tmp_size) { | |
440 | cirbuf_add_tail(&rdl->left, tmp_buf[i]); | |
441 | rdl->write_char(rdl, tmp_buf[i]); | |
442 | i++; | |
443 | } | |
444 | display_right_buffer(rdl, 1); | |
445 | return RDLINE_RES_COMPLETE; /* ?? */ | |
446 | } | |
447 | ||
448 | /* choice */ | |
449 | rdline_puts(rdl, "\r\n"); | |
450 | while (ret) { | |
451 | rdl->write_char(rdl, ' '); | |
452 | for (i=0 ; tmp_buf[i] ; i++) | |
453 | rdl->write_char(rdl, tmp_buf[i]); | |
454 | rdline_puts(rdl, "\r\n"); | |
455 | ret = rdl->complete(rdl, rdl->left_buf, | |
456 | tmp_buf, sizeof(tmp_buf), | |
457 | &complete_state); | |
458 | } | |
459 | ||
460 | rdline_redisplay(rdl); | |
461 | } | |
462 | return RDLINE_RES_COMPLETE; | |
463 | ||
464 | /* complete buffer */ | |
465 | case CMDLINE_KEY_RETURN: | |
466 | case CMDLINE_KEY_RETURN2: | |
467 | rdline_get_buffer(rdl); | |
468 | rdl->status = RDLINE_INIT; | |
469 | rdline_puts(rdl, "\r\n"); | |
470 | if (rdl->history_cur_line != -1) | |
471 | rdline_remove_first_history_item(rdl); | |
472 | ||
473 | if (rdl->validate) | |
474 | rdl->validate(rdl, rdl->left_buf, CIRBUF_GET_LEN(&rdl->left)+2); | |
475 | /* user may have stopped rdline */ | |
476 | if (rdl->status == RDLINE_EXITED) | |
477 | return RDLINE_RES_EXITED; | |
478 | return RDLINE_RES_VALIDATED; | |
479 | ||
480 | /* previous element in history */ | |
481 | case CMDLINE_KEY_UP_ARR: | |
482 | case CMDLINE_KEY_CTRL_P: | |
483 | if (rdl->history_cur_line == 0) { | |
484 | rdline_remove_first_history_item(rdl); | |
485 | } | |
486 | if (rdl->history_cur_line <= 0) { | |
487 | rdline_add_history(rdl, rdline_get_buffer(rdl)); | |
488 | rdl->history_cur_line = 0; | |
489 | } | |
490 | ||
491 | buf = rdline_get_history_item(rdl, rdl->history_cur_line + 1); | |
492 | if (!buf) | |
493 | break; | |
494 | ||
495 | rdl->history_cur_line ++; | |
496 | vt100_init(&rdl->vt100); | |
497 | cirbuf_init(&rdl->left, rdl->left_buf, 0, RDLINE_BUF_SIZE); | |
498 | cirbuf_init(&rdl->right, rdl->right_buf, 0, RDLINE_BUF_SIZE); | |
499 | cirbuf_add_buf_tail(&rdl->left, buf, strnlen(buf, RDLINE_BUF_SIZE)); | |
500 | rdline_redisplay(rdl); | |
501 | break; | |
502 | ||
503 | /* next element in history */ | |
504 | case CMDLINE_KEY_DOWN_ARR: | |
505 | case CMDLINE_KEY_CTRL_N: | |
506 | if (rdl->history_cur_line - 1 < 0) | |
507 | break; | |
508 | ||
509 | rdl->history_cur_line --; | |
510 | buf = rdline_get_history_item(rdl, rdl->history_cur_line); | |
511 | if (!buf) | |
512 | break; | |
513 | vt100_init(&rdl->vt100); | |
514 | cirbuf_init(&rdl->left, rdl->left_buf, 0, RDLINE_BUF_SIZE); | |
515 | cirbuf_init(&rdl->right, rdl->right_buf, 0, RDLINE_BUF_SIZE); | |
516 | cirbuf_add_buf_tail(&rdl->left, buf, strnlen(buf, RDLINE_BUF_SIZE)); | |
517 | rdline_redisplay(rdl); | |
518 | ||
519 | break; | |
520 | ||
521 | ||
522 | default: | |
523 | break; | |
524 | } | |
525 | ||
526 | return RDLINE_RES_SUCCESS; | |
527 | } | |
528 | ||
529 | if (!isprint((int)c)) | |
530 | return RDLINE_RES_SUCCESS; | |
531 | ||
532 | /* standard chars */ | |
533 | if (CIRBUF_GET_LEN(&rdl->left) + CIRBUF_GET_LEN(&rdl->right) >= RDLINE_BUF_SIZE) | |
534 | return RDLINE_RES_SUCCESS; | |
535 | ||
536 | if (cirbuf_add_tail_safe(&rdl->left, c)) | |
537 | return RDLINE_RES_SUCCESS; | |
538 | ||
539 | rdl->write_char(rdl, c); | |
540 | display_right_buffer(rdl, 0); | |
541 | ||
542 | return RDLINE_RES_SUCCESS; | |
543 | } | |
544 | ||
545 | ||
546 | /* HISTORY */ | |
547 | ||
548 | static void | |
549 | rdline_remove_old_history_item(struct rdline * rdl) | |
550 | { | |
551 | char tmp; | |
552 | ||
553 | while (! CIRBUF_IS_EMPTY(&rdl->history) ) { | |
554 | tmp = cirbuf_get_head(&rdl->history); | |
555 | cirbuf_del_head(&rdl->history); | |
556 | if (!tmp) | |
557 | break; | |
558 | } | |
559 | } | |
560 | ||
561 | static void | |
562 | rdline_remove_first_history_item(struct rdline * rdl) | |
563 | { | |
564 | char tmp; | |
565 | ||
566 | if ( CIRBUF_IS_EMPTY(&rdl->history) ) { | |
567 | return; | |
568 | } | |
569 | else { | |
570 | cirbuf_del_tail(&rdl->history); | |
571 | } | |
572 | ||
573 | while (! CIRBUF_IS_EMPTY(&rdl->history) ) { | |
574 | tmp = cirbuf_get_tail(&rdl->history); | |
575 | if (!tmp) | |
576 | break; | |
577 | cirbuf_del_tail(&rdl->history); | |
578 | } | |
579 | } | |
580 | ||
581 | static unsigned int | |
582 | rdline_get_history_size(struct rdline * rdl) | |
583 | { | |
584 | unsigned int i, tmp, ret=0; | |
585 | ||
586 | CIRBUF_FOREACH(&rdl->history, i, tmp) { | |
587 | if (tmp == 0) | |
588 | ret ++; | |
589 | } | |
590 | ||
591 | return ret; | |
592 | } | |
593 | ||
594 | char * | |
595 | rdline_get_history_item(struct rdline * rdl, unsigned int idx) | |
596 | { | |
597 | unsigned int len, i, tmp; | |
598 | ||
599 | if (!rdl) | |
600 | return NULL; | |
601 | ||
602 | len = rdline_get_history_size(rdl); | |
603 | if ( idx >= len ) { | |
604 | return NULL; | |
605 | } | |
606 | ||
607 | cirbuf_align_left(&rdl->history); | |
608 | ||
609 | CIRBUF_FOREACH(&rdl->history, i, tmp) { | |
610 | if ( idx == len - 1) { | |
611 | return rdl->history_buf + i; | |
612 | } | |
613 | if (tmp == 0) | |
614 | len --; | |
615 | } | |
616 | ||
617 | return NULL; | |
618 | } | |
619 | ||
620 | int | |
621 | rdline_add_history(struct rdline * rdl, const char * buf) | |
622 | { | |
623 | unsigned int len, i; | |
624 | ||
625 | if (!rdl || !buf) | |
626 | return -EINVAL; | |
627 | ||
628 | len = strnlen(buf, RDLINE_BUF_SIZE); | |
629 | for (i=0; i<len ; i++) { | |
630 | if (buf[i] == '\n') { | |
631 | len = i; | |
632 | break; | |
633 | } | |
634 | } | |
635 | ||
636 | if ( len >= RDLINE_HISTORY_BUF_SIZE ) | |
637 | return -1; | |
638 | ||
639 | while ( len >= CIRBUF_GET_FREELEN(&rdl->history) ) { | |
640 | rdline_remove_old_history_item(rdl); | |
641 | } | |
642 | ||
643 | cirbuf_add_buf_tail(&rdl->history, buf, len); | |
644 | cirbuf_add_tail(&rdl->history, 0); | |
645 | ||
646 | return 0; | |
647 | } | |
648 | ||
649 | void | |
650 | rdline_clear_history(struct rdline * rdl) | |
651 | { | |
652 | if (!rdl) | |
653 | return; | |
654 | cirbuf_init(&rdl->history, rdl->history_buf, 0, RDLINE_HISTORY_BUF_SIZE); | |
655 | } | |
656 | ||
657 | ||
658 | /* STATIC USEFUL FUNCS */ | |
659 | ||
660 | static void | |
661 | rdline_puts(struct rdline * rdl, const char * buf) | |
662 | { | |
663 | char c; | |
664 | while ( (c = *(buf++)) != '\0' ) { | |
665 | rdl->write_char(rdl, c); | |
666 | } | |
667 | } | |
668 | ||
669 | /* a very very basic printf with one arg and one format 'u' */ | |
670 | static void | |
671 | rdline_miniprintf(struct rdline *rdl, const char * buf, unsigned int val) | |
672 | { | |
673 | char c, started=0, div=100; | |
674 | ||
675 | while ( (c=*(buf++)) ) { | |
676 | if (c != '%') { | |
677 | rdl->write_char(rdl, c); | |
678 | continue; | |
679 | } | |
680 | c = *(buf++); | |
681 | if (c != 'u') { | |
682 | rdl->write_char(rdl, '%'); | |
683 | rdl->write_char(rdl, c); | |
684 | continue; | |
685 | } | |
686 | /* val is never more than 255 */ | |
687 | while (div) { | |
688 | c = (char)(val / div); | |
689 | if (c || started) { | |
690 | rdl->write_char(rdl, (char)(c+'0')); | |
691 | started = 1; | |
692 | } | |
693 | val %= div; | |
694 | div /= 10; | |
695 | } | |
696 | } | |
697 | } |