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