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