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