d5eac3b42fda46f130e2e9d71473f87ed6b5b915
[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)
168 {
169         unsigned int i;
170         char tmp;
171
172         rdline_puts(rdl, vt100_clear_right);
173         if (!CIRBUF_IS_EMPTY(&rdl->right)) {
174                 CIRBUF_FOREACH(&rdl->right, i, tmp) {
175                         rdl->write_char(rdl, tmp);
176                 }
177                 rdline_miniprintf(rdl, vt100_multi_left,
178                                   CIRBUF_GET_LEN(&rdl->right));
179         }
180 }
181
182 void
183 rdline_redisplay(struct rdline *rdl)
184 {
185         unsigned int i;
186         char tmp;
187
188         rdline_puts(rdl, vt100_home);
189         for (i=0 ; i<rdl->prompt_size ; i++)
190                 rdl->write_char(rdl, rdl->prompt[i]);
191         CIRBUF_FOREACH(&rdl->left, i, tmp) {
192                 rdl->write_char(rdl, tmp);
193         }
194         display_right_buffer(rdl);
195 }
196
197 int
198 rdline_char_in(struct rdline *rdl, char c)
199 {
200         unsigned int i;
201         int cmd;
202         char tmp;
203 #ifndef NO_RDLINE_HISTORY
204         char *buf;
205 #endif
206
207         if (rdl->status == RDLINE_EXITED)
208                 return RDLINE_RES_EXITED;
209         if (rdl->status != RDLINE_RUNNING)
210                 return RDLINE_RES_NOT_RUNNING;
211
212         cmd = vt100_parser(&rdl->vt100, c);
213         if (cmd == -2)
214                 return RDLINE_RES_SUCCESS;
215
216         if (cmd >= 0) {
217                 switch (cmd) {
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                 case CMDLINE_KEY_CTRL_F:
229                 case CMDLINE_KEY_RIGHT_ARR:
230                         if (CIRBUF_IS_EMPTY(&rdl->right))
231                                 break;
232                         tmp = cirbuf_get_head(&rdl->right);
233                         cirbuf_del_head(&rdl->right);
234                         cirbuf_add_tail(&rdl->left, tmp);
235                         rdline_puts(rdl, vt100_right_arr);
236                         break;
237
238                 case CMDLINE_KEY_WLEFT:
239                         while (! CIRBUF_IS_EMPTY(&rdl->left) &&
240                                (tmp = cirbuf_get_tail(&rdl->left)) &&
241                                isblank2(tmp)) {
242                                 rdline_puts(rdl, vt100_left_arr);
243                                 cirbuf_del_tail(&rdl->left);
244                                 cirbuf_add_head(&rdl->right, tmp);
245                         }
246                         while (! CIRBUF_IS_EMPTY(&rdl->left) &&
247                                (tmp = cirbuf_get_tail(&rdl->left)) &&
248                                !isblank2(tmp)) {
249                                 rdline_puts(rdl, vt100_left_arr);
250                                 cirbuf_del_tail(&rdl->left);
251                                 cirbuf_add_head(&rdl->right, tmp);
252                         }
253                         break;
254
255                 case CMDLINE_KEY_WRIGHT:
256                         while (! CIRBUF_IS_EMPTY(&rdl->right) &&
257                                (tmp = cirbuf_get_head(&rdl->right)) &&
258                                isblank2(tmp)) {
259                                 rdline_puts(rdl, vt100_right_arr);
260                                 cirbuf_del_head(&rdl->right);
261                                 cirbuf_add_tail(&rdl->left, tmp);
262                         }
263                         while (! CIRBUF_IS_EMPTY(&rdl->right) &&
264                                (tmp = cirbuf_get_head(&rdl->right)) &&
265                                !isblank2(tmp)) {
266                                 rdline_puts(rdl, vt100_right_arr);
267                                 cirbuf_del_head(&rdl->right);
268                                 cirbuf_add_tail(&rdl->left, tmp);
269                         }
270                         break;
271
272                 case CMDLINE_KEY_BKSPACE:
273                         if(!cirbuf_del_tail_safe(&rdl->left)) {
274                                 rdline_puts(rdl, vt100_bs);
275                                 display_right_buffer(rdl);
276                         }
277                         break;
278
279                 case CMDLINE_KEY_META_BKSPACE:
280                         while (! CIRBUF_IS_EMPTY(&rdl->left) && isblank2(cirbuf_get_tail(&rdl->left))) {
281                                 rdline_puts(rdl, vt100_bs);
282                                 cirbuf_del_tail(&rdl->left);
283                         }
284                         while (! CIRBUF_IS_EMPTY(&rdl->left) && !isblank2(cirbuf_get_tail(&rdl->left))) {
285                                 rdline_puts(rdl, vt100_bs);
286                                 cirbuf_del_tail(&rdl->left);
287                         }
288                         display_right_buffer(rdl);
289                         break;
290
291                 case CMDLINE_KEY_SUPPR:
292                 case CMDLINE_KEY_CTRL_D:
293                         if (cmd == CMDLINE_KEY_CTRL_D &&
294                             CIRBUF_IS_EMPTY(&rdl->left) &&
295                             CIRBUF_IS_EMPTY(&rdl->right)) {
296                                 return RDLINE_RES_EOF;
297                         }
298                         if (!cirbuf_del_head_safe(&rdl->right)) {
299                                 display_right_buffer(rdl);
300                         }
301                         break;
302
303                 case CMDLINE_KEY_CTRL_A:
304                         if (CIRBUF_IS_EMPTY(&rdl->left))
305                                 break;
306                         rdline_miniprintf(rdl, vt100_multi_left,
307                                             CIRBUF_GET_LEN(&rdl->left));
308                         while (! CIRBUF_IS_EMPTY(&rdl->left)) {
309                                 tmp = cirbuf_get_tail(&rdl->left);
310                                 cirbuf_del_tail(&rdl->left);
311                                 cirbuf_add_head(&rdl->right, tmp);
312                         }
313                         break;
314
315                 case CMDLINE_KEY_CTRL_E:
316                         if (CIRBUF_IS_EMPTY(&rdl->right))
317                                 break;
318                         rdline_miniprintf(rdl, vt100_multi_right,
319                                             CIRBUF_GET_LEN(&rdl->right));
320                         while (! CIRBUF_IS_EMPTY(&rdl->right)) {
321                                 tmp = cirbuf_get_head(&rdl->right);
322                                 cirbuf_del_head(&rdl->right);
323                                 cirbuf_add_tail(&rdl->left, tmp);
324                         }
325                         break;
326
327 #ifndef NO_RDLINE_KILL_BUF
328                 case CMDLINE_KEY_CTRL_K:
329                         cirbuf_get_buf_head(&rdl->right, rdl->kill_buf, RDLINE_BUF_SIZE);
330                         rdl->kill_size = CIRBUF_GET_LEN(&rdl->right);
331                         cirbuf_del_buf_head(&rdl->right, rdl->kill_size);
332                         rdline_puts(rdl, vt100_clear_right);
333                         break;
334
335                 case CMDLINE_KEY_CTRL_Y:
336                         i=0;
337                         while(CIRBUF_GET_LEN(&rdl->right) + CIRBUF_GET_LEN(&rdl->left) <
338                               RDLINE_BUF_SIZE &&
339                               i < rdl->kill_size) {
340                                 cirbuf_add_tail(&rdl->left, rdl->kill_buf[i]);
341                                 rdl->write_char(rdl, rdl->kill_buf[i]);
342                                 i++;
343                         }
344                         display_right_buffer(rdl);
345                         break;
346 #endif /* !NO_RDLINE_KILL_BUF */
347
348                 case CMDLINE_KEY_CTRL_C:
349                         rdline_puts(rdl, "\r\n");
350                         rdline_newline(rdl, rdl->prompt);
351                         break;
352
353                 case CMDLINE_KEY_CTRL_L:
354                         rdline_redisplay(rdl);
355                         break;
356
357                 case CMDLINE_KEY_TAB:
358                 case CMDLINE_KEY_HELP:
359                         cirbuf_align_left(&rdl->left);
360                         rdl->left_buf[CIRBUF_GET_LEN(&rdl->left)] = '\0';
361                         if (rdl->complete) {
362                                 char tmp_buf[BUFSIZ];
363                                 int complete_state;
364                                 int ret;
365                                 unsigned int tmp_size;
366
367                                 if (cmd == CMDLINE_KEY_TAB)
368                                         complete_state = 0;
369                                 else
370                                         complete_state = -1;
371
372                                 /* see in parse.h for help on complete() */
373                                 ret = rdl->complete(rdl, rdl->left_buf,
374                                                     tmp_buf, sizeof(tmp_buf),
375                                                     &complete_state);
376                                 /* no completion or error */
377                                 if (ret <= 0) {
378                                         return RDLINE_RES_COMPLETE;
379                                 }
380
381                                 tmp_size = strlen(tmp_buf);
382                                 /* add chars */
383                                 if (ret == RDLINE_RES_COMPLETE) {
384                                         i=0;
385                                         while(CIRBUF_GET_LEN(&rdl->right) + CIRBUF_GET_LEN(&rdl->left) <
386                                               RDLINE_BUF_SIZE &&
387                                               i < tmp_size) {
388                                                 cirbuf_add_tail(&rdl->left, tmp_buf[i]);
389                                                 rdl->write_char(rdl, tmp_buf[i]);
390                                                 i++;
391                                         }
392                                         display_right_buffer(rdl);
393                                         return RDLINE_RES_COMPLETE; /* ?? */
394                                 }
395
396                                 /* choice */
397                                 rdline_puts(rdl, "\r\n");
398                                 while (ret) {
399                                         rdl->write_char(rdl, ' ');
400                                         for (i=0 ; tmp_buf[i] ; i++)
401                                                 rdl->write_char(rdl, tmp_buf[i]);
402                                         rdline_puts(rdl, "\r\n");
403                                         ret = rdl->complete(rdl, rdl->left_buf,
404                                                             tmp_buf, sizeof(tmp_buf),
405                                                             &complete_state);
406                                 }
407
408                                 rdline_redisplay(rdl);
409                         }
410                         return RDLINE_RES_COMPLETE;
411
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 #ifndef NO_RDLINE_HISTORY
418                         if (rdl->history_cur_line != -1)
419                                 rdline_remove_first_history_item(rdl);
420 #endif
421
422                         if (rdl->validate)
423                                 rdl->validate(rdl, rdl->left_buf, CIRBUF_GET_LEN(&rdl->left)+2);
424                         /* user may have stopped rdline */
425                         if (rdl->status == RDLINE_EXITED)
426                                 return RDLINE_RES_EXITED;
427                         return RDLINE_RES_VALIDATED;
428
429 #ifndef NO_RDLINE_HISTORY
430                 case CMDLINE_KEY_UP_ARR:
431                         if (rdl->history_cur_line == 0) {
432                                 rdline_remove_first_history_item(rdl);
433                         }
434                         if (rdl->history_cur_line <= 0) {
435                                 rdline_add_history(rdl, rdline_get_buffer(rdl));
436                                 rdl->history_cur_line = 0;
437                         }
438
439                         buf = rdline_get_history_item(rdl, rdl->history_cur_line + 1);
440                         if (!buf)
441                                 break;
442
443                         rdl->history_cur_line ++;
444                         vt100_init(&rdl->vt100);
445                         cirbuf_init(&rdl->left, rdl->left_buf, 0, RDLINE_BUF_SIZE);
446                         cirbuf_init(&rdl->right, rdl->right_buf, 0, RDLINE_BUF_SIZE);
447                         cirbuf_add_buf_tail(&rdl->left, buf, strlen(buf));
448                         rdline_redisplay(rdl);
449                         break;
450
451                 case CMDLINE_KEY_DOWN_ARR:
452                         if (rdl->history_cur_line - 1 < 0)
453                                 break;
454
455                         rdl->history_cur_line --;
456                         buf = rdline_get_history_item(rdl, rdl->history_cur_line);
457                         if (!buf)
458                                 break;
459                         vt100_init(&rdl->vt100);
460                         cirbuf_init(&rdl->left, rdl->left_buf, 0, RDLINE_BUF_SIZE);
461                         cirbuf_init(&rdl->right, rdl->right_buf, 0, RDLINE_BUF_SIZE);
462                         cirbuf_add_buf_tail(&rdl->left, buf, strlen(buf));
463                         rdline_redisplay(rdl);
464
465                         break;
466 #endif /* !NO_RDLINE_HISTORY */
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);
488
489         return RDLINE_RES_SUCCESS;
490 }
491
492
493 /* HISTORY */
494
495 #ifndef NO_RDLINE_HISTORY
496 static void
497 rdline_remove_old_history_item(struct rdline * rdl)
498 {
499         char tmp;
500
501         while (! CIRBUF_IS_EMPTY(&rdl->history) ) {
502                 tmp = cirbuf_get_head(&rdl->history);
503                 cirbuf_del_head(&rdl->history);
504                 if (!tmp)
505                         break;
506         }
507 }
508
509 static void
510 rdline_remove_first_history_item(struct rdline * rdl)
511 {
512         char tmp;
513
514         if ( CIRBUF_IS_EMPTY(&rdl->history) ) {
515                 return;
516         }
517         else {
518                 cirbuf_del_tail(&rdl->history);
519         }
520
521         while (! CIRBUF_IS_EMPTY(&rdl->history) ) {
522                 tmp = cirbuf_get_tail(&rdl->history);
523                 if (!tmp)
524                         break;
525                 cirbuf_del_tail(&rdl->history);
526         }
527 }
528
529 static unsigned int
530 rdline_get_history_size(struct rdline * rdl)
531 {
532         unsigned int i, tmp, ret=0;
533
534         CIRBUF_FOREACH(&rdl->history, i, tmp) {
535                 if (tmp == 0)
536                         ret ++;
537         }
538
539         return ret;
540 }
541
542 char *
543 rdline_get_history_item(struct rdline * rdl, unsigned int idx)
544 {
545         unsigned int len, i, tmp;
546
547         len = rdline_get_history_size(rdl);
548         if ( idx >= len ) {
549                 return NULL;
550         }
551
552         cirbuf_align_left(&rdl->history);
553
554         CIRBUF_FOREACH(&rdl->history, i, tmp) {
555                 if ( idx == len - 1) {
556                         return rdl->history_buf + i;
557                 }
558                 if (tmp == 0)
559                         len --;
560         }
561
562         return NULL;
563 }
564
565 int
566 rdline_add_history(struct rdline * rdl, const char * buf)
567 {
568         unsigned int len, i;
569
570         len = strlen(buf);
571         for (i=0; i<len ; i++) {
572                 if (buf[i] == '\n') {
573                         len = i;
574                         break;
575                 }
576         }
577
578         if ( len >= RDLINE_HISTORY_BUF_SIZE )
579                 return -1;
580
581         while ( len >= CIRBUF_GET_FREELEN(&rdl->history) ) {
582                 rdline_remove_old_history_item(rdl);
583         }
584
585         cirbuf_add_buf_tail(&rdl->history, buf, len);
586         cirbuf_add_tail(&rdl->history, 0);
587
588         return 0;
589 }
590
591 void
592 rdline_clear_history(struct rdline * rdl)
593 {
594         cirbuf_init(&rdl->history, rdl->history_buf, 0, RDLINE_HISTORY_BUF_SIZE);
595 }
596
597 #else /* !NO_RDLINE_HISTORY */
598
599 int rdline_add_history(struct rdline * rdl, const char * buf) {return -1;}
600 void rdline_clear_history(struct rdline * rdl) {}
601 char * rdline_get_history_item(struct rdline * rdl, unsigned int i) {return NULL;}
602
603
604 #endif /* !NO_RDLINE_HISTORY */
605
606
607 /* STATIC USEFUL FUNCS */
608
609 static void
610 rdline_puts(struct rdline * rdl, const char * buf)
611 {
612         char c;
613         while ( (c = *(buf++)) != '\0' ) {
614                 rdl->write_char(rdl, c);
615         }
616 }
617
618 /* a very very basic printf with one arg and one format 'u' */
619 static void
620 rdline_miniprintf(struct rdline *rdl, const char * buf, unsigned int val)
621 {
622         char c, started=0, div=100;
623
624         while ( (c=*(buf++)) ) {
625                 if (c != '%') {
626                         rdl->write_char(rdl, c);
627                         continue;
628                 }
629                 c = *(buf++);
630                 if (c != 'u') {
631                         rdl->write_char(rdl, '%');
632                         rdl->write_char(rdl, c);
633                         continue;
634                 }
635                 /* val is never more than 255 */
636                 while (div) {
637                         c = (char)(val / div);
638                         if (c || started) {
639                                 rdl->write_char(rdl, (char)(c+'0'));
640                                 started = 1;
641                         }
642                         val %= div;
643                         div /= 10;
644                 }
645         }
646 }
647