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