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