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