New build system.
[osmocom-bb.git] / src / target / firmware / lib / vsprintf.c
1 /*
2  *  linux/lib/vsprintf.c
3  *
4  *  Copyright (C) 1991, 1992  Linus Torvalds
5  */
6
7 /* vsprintf.c -- Lars Wirzenius & Linus Torvalds. */
8 /*
9  * Wirzenius wrote this portably, Torvalds fucked it up :-)
10  */
11
12 /* 
13  * Fri Jul 13 2001 Crutcher Dunnavant <crutcher+kernel@datastacks.com>
14  * - changed to provide snprintf and vsnprintf functions
15  * So Feb  1 16:51:32 CET 2004 Juergen Quade <quade@hsnr.de>
16  * - scnprintf and vscnprintf
17  */
18
19 #include <stdio.h>
20 #include <limits.h>
21 #include <stdarg.h>
22 #include <sys/types.h>
23 #include <string.h>
24 #include <asm/ctype.h>
25
26 #include <asm/div64.h>
27
28 /**
29  * simple_strtoul - convert a string to an unsigned long
30  * @cp: The start of the string
31  * @endp: A pointer to the end of the parsed string will be placed here
32  * @base: The number base to use
33  */
34 unsigned long simple_strtoul(const char *cp,char **endp,unsigned int base)
35 {
36         unsigned long result = 0,value;
37
38         if (!base) {
39                 base = 10;
40                 if (*cp == '0') {
41                         base = 8;
42                         cp++;
43                         if ((toupper(*cp) == 'X') && isxdigit(cp[1])) {
44                                 cp++;
45                                 base = 16;
46                         }
47                 }
48         } else if (base == 16) {
49                 if (cp[0] == '0' && toupper(cp[1]) == 'X')
50                         cp += 2;
51         }
52         while (isxdigit(*cp) &&
53                (value = isdigit(*cp) ? *cp-'0' : toupper(*cp)-'A'+10) < base) {
54                 result = result*base + value;
55                 cp++;
56         }
57         if (endp)
58                 *endp = (char *)cp;
59         return result;
60 }
61
62
63 /**
64  * simple_strtol - convert a string to a signed long
65  * @cp: The start of the string
66  * @endp: A pointer to the end of the parsed string will be placed here
67  * @base: The number base to use
68  */
69 long simple_strtol(const char *cp,char **endp,unsigned int base)
70 {
71         if(*cp=='-')
72                 return -simple_strtoul(cp+1,endp,base);
73         return simple_strtoul(cp,endp,base);
74 }
75
76
77 /**
78  * simple_strtoull - convert a string to an unsigned long long
79  * @cp: The start of the string
80  * @endp: A pointer to the end of the parsed string will be placed here
81  * @base: The number base to use
82  */
83 unsigned long long simple_strtoull(const char *cp,char **endp,unsigned int base)
84 {
85         unsigned long long result = 0,value;
86
87         if (!base) {
88                 base = 10;
89                 if (*cp == '0') {
90                         base = 8;
91                         cp++;
92                         if ((toupper(*cp) == 'X') && isxdigit(cp[1])) {
93                                 cp++;
94                                 base = 16;
95                         }
96                 }
97         } else if (base == 16) {
98                 if (cp[0] == '0' && toupper(cp[1]) == 'X')
99                         cp += 2;
100         }
101         while (isxdigit(*cp) && (value = isdigit(*cp) ? *cp-'0' : (islower(*cp)
102             ? toupper(*cp) : *cp)-'A'+10) < base) {
103                 result = result*base + value;
104                 cp++;
105         }
106         if (endp)
107                 *endp = (char *)cp;
108         return result;
109 }
110
111
112 /**
113  * simple_strtoll - convert a string to a signed long long
114  * @cp: The start of the string
115  * @endp: A pointer to the end of the parsed string will be placed here
116  * @base: The number base to use
117  */
118 long long simple_strtoll(const char *cp,char **endp,unsigned int base)
119 {
120         if(*cp=='-')
121                 return -simple_strtoull(cp+1,endp,base);
122         return simple_strtoull(cp,endp,base);
123 }
124
125 static int skip_atoi(const char **s)
126 {
127         int i=0;
128
129         while (isdigit(**s))
130                 i = i*10 + *((*s)++) - '0';
131         return i;
132 }
133
134 #define ZEROPAD 1               /* pad with zero */
135 #define SIGN    2               /* unsigned/signed long */
136 #define PLUS    4               /* show plus */
137 #define SPACE   8               /* space if plus */
138 #define LEFT    16              /* left justified */
139 #define SPECIAL 32              /* 0x */
140 #define LARGE   64              /* use 'ABCDEF' instead of 'abcdef' */
141
142 static char * number(char * buf, char * end, unsigned long long num, int base, int size, int precision, int type)
143 {
144         char c,sign,tmp[66];
145         const char *digits;
146         static const char small_digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
147         static const char large_digits[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
148         int i;
149
150         digits = (type & LARGE) ? large_digits : small_digits;
151         if (type & LEFT)
152                 type &= ~ZEROPAD;
153         if (base < 2 || base > 36)
154                 return NULL;
155         c = (type & ZEROPAD) ? '0' : ' ';
156         sign = 0;
157         if (type & SIGN) {
158                 if ((signed long long) num < 0) {
159                         sign = '-';
160                         num = - (signed long long) num;
161                         size--;
162                 } else if (type & PLUS) {
163                         sign = '+';
164                         size--;
165                 } else if (type & SPACE) {
166                         sign = ' ';
167                         size--;
168                 }
169         }
170         if (type & SPECIAL) {
171                 if (base == 16)
172                         size -= 2;
173                 else if (base == 8)
174                         size--;
175         }
176         i = 0;
177         if (num == 0)
178                 tmp[i++]='0';
179         else while (num != 0)
180                 tmp[i++] = digits[do_div(num,base)];
181         if (i > precision)
182                 precision = i;
183         size -= precision;
184         if (!(type&(ZEROPAD+LEFT))) {
185                 while(size-->0) {
186                         if (buf <= end)
187                                 *buf = ' ';
188                         ++buf;
189                 }
190         }
191         if (sign) {
192                 if (buf <= end)
193                         *buf = sign;
194                 ++buf;
195         }
196         if (type & SPECIAL) {
197                 if (base==8) {
198                         if (buf <= end)
199                                 *buf = '0';
200                         ++buf;
201                 } else if (base==16) {
202                         if (buf <= end)
203                                 *buf = '0';
204                         ++buf;
205                         if (buf <= end)
206                                 *buf = digits[33];
207                         ++buf;
208                 }
209         }
210         if (!(type & LEFT)) {
211                 while (size-- > 0) {
212                         if (buf <= end)
213                                 *buf = c;
214                         ++buf;
215                 }
216         }
217         while (i < precision--) {
218                 if (buf <= end)
219                         *buf = '0';
220                 ++buf;
221         }
222         while (i-- > 0) {
223                 if (buf <= end)
224                         *buf = tmp[i];
225                 ++buf;
226         }
227         while (size-- > 0) {
228                 if (buf <= end)
229                         *buf = ' ';
230                 ++buf;
231         }
232         return buf;
233 }
234
235 /**
236  * vsnprintf - Format a string and place it in a buffer
237  * @buf: The buffer to place the result into
238  * @size: The size of the buffer, including the trailing null space
239  * @fmt: The format string to use
240  * @args: Arguments for the format string
241  *
242  * The return value is the number of characters which would
243  * be generated for the given input, excluding the trailing
244  * '\0', as per ISO C99. If you want to have the exact
245  * number of characters written into @buf as return value
246  * (not including the trailing '\0'), use vscnprintf. If the
247  * return is greater than or equal to @size, the resulting
248  * string is truncated.
249  *
250  * Call this function if you are already dealing with a va_list.
251  * You probably want snprintf instead.
252  */
253 int vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
254 {
255         int len;
256         unsigned long long num;
257         int i, base;
258         char *str, *end, c;
259         const char *s;
260
261         int flags;              /* flags to number() */
262
263         int field_width;        /* width of output field */
264         int precision;          /* min. # of digits for integers; max
265                                    number of chars for from string */
266         int qualifier;          /* 'h', 'l', or 'L' for integer fields */
267                                 /* 'z' support added 23/7/1999 S.H.    */
268                                 /* 'z' changed to 'Z' --davidm 1/25/99 */
269                                 /* 't' added for ptrdiff_t */
270
271         /* Reject out-of-range values early */
272         if ((int) size < 0) {
273                 return 0;
274         }
275
276         str = buf;
277         end = buf + size - 1;
278
279         if (end < buf - 1) {
280                 end = ((void *) -1);
281                 size = end - buf + 1;
282         }
283
284         for (; *fmt ; ++fmt) {
285                 if (*fmt != '%') {
286                         if (str <= end)
287                                 *str = *fmt;
288                         ++str;
289                         continue;
290                 }
291
292                 /* process flags */
293                 flags = 0;
294                 repeat:
295                         ++fmt;          /* this also skips first '%' */
296                         switch (*fmt) {
297                                 case '-': flags |= LEFT; goto repeat;
298                                 case '+': flags |= PLUS; goto repeat;
299                                 case ' ': flags |= SPACE; goto repeat;
300                                 case '#': flags |= SPECIAL; goto repeat;
301                                 case '0': flags |= ZEROPAD; goto repeat;
302                         }
303
304                 /* get field width */
305                 field_width = -1;
306                 if (isdigit(*fmt))
307                         field_width = skip_atoi(&fmt);
308                 else if (*fmt == '*') {
309                         ++fmt;
310                         /* it's the next argument */
311                         field_width = va_arg(args, int);
312                         if (field_width < 0) {
313                                 field_width = -field_width;
314                                 flags |= LEFT;
315                         }
316                 }
317
318                 /* get the precision */
319                 precision = -1;
320                 if (*fmt == '.') {
321                         ++fmt;  
322                         if (isdigit(*fmt))
323                                 precision = skip_atoi(&fmt);
324                         else if (*fmt == '*') {
325                                 ++fmt;
326                                 /* it's the next argument */
327                                 precision = va_arg(args, int);
328                         }
329                         if (precision < 0)
330                                 precision = 0;
331                 }
332
333                 /* get the conversion qualifier */
334                 qualifier = -1;
335                 if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L' ||
336                     *fmt =='Z' || *fmt == 'z' || *fmt == 't') {
337                         qualifier = *fmt;
338                         ++fmt;
339                         if (qualifier == 'l' && *fmt == 'l') {
340                                 qualifier = 'L';
341                                 ++fmt;
342                         }
343                 }
344
345                 /* default base */
346                 base = 10;
347
348                 switch (*fmt) {
349                         case 'c':
350                                 if (!(flags & LEFT)) {
351                                         while (--field_width > 0) {
352                                                 if (str <= end)
353                                                         *str = ' ';
354                                                 ++str;
355                                         }
356                                 }
357                                 c = (unsigned char) va_arg(args, int);
358                                 if (str <= end)
359                                         *str = c;
360                                 ++str;
361                                 while (--field_width > 0) {
362                                         if (str <= end)
363                                                 *str = ' ';
364                                         ++str;
365                                 }
366                                 continue;
367
368                         case 's':
369                                 s = va_arg(args, char *);
370
371                                 len = strnlen(s, precision);
372
373                                 if (!(flags & LEFT)) {
374                                         while (len < field_width--) {
375                                                 if (str <= end)
376                                                         *str = ' ';
377                                                 ++str;
378                                         }
379                                 }
380                                 for (i = 0; i < len; ++i) {
381                                         if (str <= end)
382                                                 *str = *s;
383                                         ++str; ++s;
384                                 }
385                                 while (len < field_width--) {
386                                         if (str <= end)
387                                                 *str = ' ';
388                                         ++str;
389                                 }
390                                 continue;
391
392                         case 'p':
393                                 if (field_width == -1) {
394                                         field_width = 2*sizeof(void *);
395                                         flags |= ZEROPAD;
396                                 }
397                                 str = number(str, end,
398                                                 (unsigned long) va_arg(args, void *),
399                                                 16, field_width, precision, flags);
400                                 continue;
401
402
403                         case 'n':
404                                 /* FIXME:
405                                 * What does C99 say about the overflow case here? */
406                                 if (qualifier == 'l') {
407                                         long * ip = va_arg(args, long *);
408                                         *ip = (str - buf);
409                                 } else if (qualifier == 'Z' || qualifier == 'z') {
410                                         size_t * ip = va_arg(args, size_t *);
411                                         *ip = (str - buf);
412                                 } else {
413                                         int * ip = va_arg(args, int *);
414                                         *ip = (str - buf);
415                                 }
416                                 continue;
417
418                         case '%':
419                                 if (str <= end)
420                                         *str = '%';
421                                 ++str;
422                                 continue;
423
424                                 /* integer number formats - set up the flags and "break" */
425                         case 'o':
426                                 base = 8;
427                                 break;
428
429                         case 'X':
430                                 flags |= LARGE;
431                         case 'x':
432                                 base = 16;
433                                 break;
434
435                         case 'd':
436                         case 'i':
437                                 flags |= SIGN;
438                         case 'u':
439                                 break;
440
441                         default:
442                                 if (str <= end)
443                                         *str = '%';
444                                 ++str;
445                                 if (*fmt) {
446                                         if (str <= end)
447                                                 *str = *fmt;
448                                         ++str;
449                                 } else {
450                                         --fmt;
451                                 }
452                                 continue;
453                 }
454                 if (qualifier == 'L')
455                         num = va_arg(args, long long);
456                 else if (qualifier == 'l') {
457                         num = va_arg(args, unsigned long);
458                         if (flags & SIGN)
459                                 num = (signed long) num;
460                 } else if (qualifier == 'Z' || qualifier == 'z') {
461                         num = va_arg(args, size_t);
462                 } else if (qualifier == 't') {
463                         num = va_arg(args, long);
464                 } else if (qualifier == 'h') {
465                         num = (unsigned short) va_arg(args, int);
466                         if (flags & SIGN)
467                                 num = (signed short) num;
468                 } else {
469                         num = va_arg(args, unsigned int);
470                         if (flags & SIGN)
471                                 num = (signed int) num;
472                 }
473                 str = number(str, end, num, base,
474                                 field_width, precision, flags);
475         }
476         if (str <= end)
477                 *str = '\0';
478         else if (size > 0)
479                 /* don't write out a null byte if the buf size is zero */
480                 *end = '\0';
481         /* the trailing null byte doesn't count towards the total
482         * ++str;
483         */
484         return str-buf;
485 }
486
487
488 /**
489  * vscnprintf - Format a string and place it in a buffer
490  * @buf: The buffer to place the result into
491  * @size: The size of the buffer, including the trailing null space
492  * @fmt: The format string to use
493  * @args: Arguments for the format string
494  *
495  * The return value is the number of characters which have been written into
496  * the @buf not including the trailing '\0'. If @size is <= 0 the function
497  * returns 0.
498  *
499  * Call this function if you are already dealing with a va_list.
500  * You probably want scnprintf instead.
501  */
502 int vscnprintf(char *buf, size_t size, const char *fmt, va_list args)
503 {
504         unsigned int i;
505
506         i=vsnprintf(buf,size,fmt,args);
507         return (i >= size) ? (size - 1) : i;
508 }
509
510
511 /**
512  * snprintf - Format a string and place it in a buffer
513  * @buf: The buffer to place the result into
514  * @size: The size of the buffer, including the trailing null space
515  * @fmt: The format string to use
516  * @...: Arguments for the format string
517  *
518  * The return value is the number of characters which would be
519  * generated for the given input, excluding the trailing null,
520  * as per ISO C99.  If the return is greater than or equal to
521  * @size, the resulting string is truncated.
522  */
523 int snprintf(char * buf, size_t size, const char *fmt, ...)
524 {
525         va_list args;
526         int i;
527
528         va_start(args, fmt);
529         i=vsnprintf(buf,size,fmt,args);
530         va_end(args);
531         return i;
532 }
533
534
535 /**
536  * scnprintf - Format a string and place it in a buffer
537  * @buf: The buffer to place the result into
538  * @size: The size of the buffer, including the trailing null space
539  * @fmt: The format string to use
540  * @...: Arguments for the format string
541  *
542  * The return value is the number of characters written into @buf not including
543  * the trailing '\0'. If @size is <= 0 the function returns 0. If the return is
544  * greater than or equal to @size, the resulting string is truncated.
545  */
546
547 int scnprintf(char * buf, size_t size, const char *fmt, ...)
548 {
549         va_list args;
550         unsigned int i;
551
552         va_start(args, fmt);
553         i = vsnprintf(buf, size, fmt, args);
554         va_end(args);
555         return (i >= size) ? (size - 1) : i;
556 }
557
558 /**
559  * vsprintf - Format a string and place it in a buffer
560  * @buf: The buffer to place the result into
561  * @fmt: The format string to use
562  * @args: Arguments for the format string
563  *
564  * The function returns the number of characters written
565  * into @buf. Use vsnprintf or vscnprintf in order to avoid
566  * buffer overflows.
567  *
568  * Call this function if you are already dealing with a va_list.
569  * You probably want sprintf instead.
570  */
571 int vsprintf(char *buf, const char *fmt, va_list args)
572 {
573         return vsnprintf(buf, INT_MAX, fmt, args);
574 }
575
576
577 /**
578  * sprintf - Format a string and place it in a buffer
579  * @buf: The buffer to place the result into
580  * @fmt: The format string to use
581  * @...: Arguments for the format string
582  *
583  * The function returns the number of characters written
584  * into @buf. Use snprintf or scnprintf in order to avoid
585  * buffer overflows.
586  */
587 int sprintf(char * buf, const char *fmt, ...)
588 {
589         va_list args;
590         int i;
591
592         va_start(args, fmt);
593         i=vsnprintf(buf, INT_MAX, fmt, args);
594         va_end(args);
595         return i;
596 }
597
598
599 /**
600  * vsscanf - Unformat a buffer into a list of arguments
601  * @buf:        input buffer
602  * @fmt:        format of buffer
603  * @args:       arguments
604  */
605 int vsscanf(const char * buf, const char * fmt, va_list args)
606 {
607         const char *str = buf;
608         char *next;
609         char digit;
610         int num = 0;
611         int qualifier;
612         int base;
613         int field_width;
614         int is_sign = 0;
615
616         while(*fmt && *str) {
617                 /* skip any white space in format */
618                 /* white space in format matchs any amount of
619                  * white space, including none, in the input.
620                  */
621                 if (isspace(*fmt)) {
622                         while (isspace(*fmt))
623                                 ++fmt;
624                         while (isspace(*str))
625                                 ++str;
626                 }
627
628                 /* anything that is not a conversion must match exactly */
629                 if (*fmt != '%' && *fmt) {
630                         if (*fmt++ != *str++)
631                                 break;
632                         continue;
633                 }
634
635                 if (!*fmt)
636                         break;
637                 ++fmt;
638                 
639                 /* skip this conversion.
640                  * advance both strings to next white space
641                  */
642                 if (*fmt == '*') {
643                         while (!isspace(*fmt) && *fmt)
644                                 fmt++;
645                         while (!isspace(*str) && *str)
646                                 str++;
647                         continue;
648                 }
649
650                 /* get field width */
651                 field_width = -1;
652                 if (isdigit(*fmt))
653                         field_width = skip_atoi(&fmt);
654
655                 /* get conversion qualifier */
656                 qualifier = -1;
657                 if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L' ||
658                     *fmt == 'Z' || *fmt == 'z') {
659                         qualifier = *fmt++;
660                         if (qualifier == *fmt) {
661                                 if (qualifier == 'h') {
662                                         qualifier = 'H';
663                                         fmt++;
664                                 } else if (qualifier == 'l') {
665                                         qualifier = 'L';
666                                         fmt++;
667                                 }
668                         }
669                 }
670                 base = 10;
671                 is_sign = 0;
672
673                 if (!*fmt || !*str)
674                         break;
675
676                 switch(*fmt++) {
677                 case 'c':
678                 {
679                         char *s = (char *) va_arg(args,char*);
680                         if (field_width == -1)
681                                 field_width = 1;
682                         do {
683                                 *s++ = *str++;
684                         } while (--field_width > 0 && *str);
685                         num++;
686                 }
687                 continue;
688                 case 's':
689                 {
690                         char *s = (char *) va_arg(args, char *);
691                         if(field_width == -1)
692                                 field_width = INT_MAX;
693                         /* first, skip leading white space in buffer */
694                         while (isspace(*str))
695                                 str++;
696
697                         /* now copy until next white space */
698                         while (*str && !isspace(*str) && field_width--) {
699                                 *s++ = *str++;
700                         }
701                         *s = '\0';
702                         num++;
703                 }
704                 continue;
705                 case 'n':
706                         /* return number of characters read so far */
707                 {
708                         int *i = (int *)va_arg(args,int*);
709                         *i = str - buf;
710                 }
711                 continue;
712                 case 'o':
713                         base = 8;
714                         break;
715                 case 'x':
716                 case 'X':
717                         base = 16;
718                         break;
719                 case 'i':
720                         base = 0;
721                 case 'd':
722                         is_sign = 1;
723                 case 'u':
724                         break;
725                 case '%':
726                         /* looking for '%' in str */
727                         if (*str++ != '%') 
728                                 return num;
729                         continue;
730                 default:
731                         /* invalid format; stop here */
732                         return num;
733                 }
734
735                 /* have some sort of integer conversion.
736                  * first, skip white space in buffer.
737                  */
738                 while (isspace(*str))
739                         str++;
740
741                 digit = *str;
742                 if (is_sign && digit == '-')
743                         digit = *(str + 1);
744
745                 if (!digit
746                     || (base == 16 && !isxdigit(digit))
747                     || (base == 10 && !isdigit(digit))
748                     || (base == 8 && (!isdigit(digit) || digit > '7'))
749                     || (base == 0 && !isdigit(digit)))
750                                 break;
751
752                 switch(qualifier) {
753                 case 'H':       /* that's 'hh' in format */
754                         if (is_sign) {
755                                 signed char *s = (signed char *) va_arg(args,signed char *);
756                                 *s = (signed char) simple_strtol(str,&next,base);
757                         } else {
758                                 unsigned char *s = (unsigned char *) va_arg(args, unsigned char *);
759                                 *s = (unsigned char) simple_strtoul(str, &next, base);
760                         }
761                         break;
762                 case 'h':
763                         if (is_sign) {
764                                 short *s = (short *) va_arg(args,short *);
765                                 *s = (short) simple_strtol(str,&next,base);
766                         } else {
767                                 unsigned short *s = (unsigned short *) va_arg(args, unsigned short *);
768                                 *s = (unsigned short) simple_strtoul(str, &next, base);
769                         }
770                         break;
771                 case 'l':
772                         if (is_sign) {
773                                 long *l = (long *) va_arg(args,long *);
774                                 *l = simple_strtol(str,&next,base);
775                         } else {
776                                 unsigned long *l = (unsigned long*) va_arg(args,unsigned long*);
777                                 *l = simple_strtoul(str,&next,base);
778                         }
779                         break;
780                 case 'L':
781                         if (is_sign) {
782                                 long long *l = (long long*) va_arg(args,long long *);
783                                 *l = simple_strtoll(str,&next,base);
784                         } else {
785                                 unsigned long long *l = (unsigned long long*) va_arg(args,unsigned long long*);
786                                 *l = simple_strtoull(str,&next,base);
787                         }
788                         break;
789                 case 'Z':
790                 case 'z':
791                 {
792                         size_t *s = (size_t*) va_arg(args,size_t*);
793                         *s = (size_t) simple_strtoul(str,&next,base);
794                 }
795                 break;
796                 default:
797                         if (is_sign) {
798                                 int *i = (int *) va_arg(args, int*);
799                                 *i = (int) simple_strtol(str,&next,base);
800                         } else {
801                                 unsigned int *i = (unsigned int*) va_arg(args, unsigned int*);
802                                 *i = (unsigned int) simple_strtoul(str,&next,base);
803                         }
804                         break;
805                 }
806                 num++;
807
808                 if (!next)
809                         break;
810                 str = next;
811         }
812         return num;
813 }
814
815
816 /**
817  * sscanf - Unformat a buffer into a list of arguments
818  * @buf:        input buffer
819  * @fmt:        formatting of buffer
820  * @...:        resulting arguments
821  */
822 int sscanf(const char * buf, const char * fmt, ...)
823 {
824         va_list args;
825         int i;
826
827         va_start(args,fmt);
828         i = vsscanf(buf,fmt,args);
829         va_end(args);
830         return i;
831 }
832
833 /* generic puts() implementation independent of who provides putchar() */
834 int puts(const char *s)
835 {
836 #ifdef ARCH_HAS_CONSOLE
837         return _puts(s);
838 #else
839         while (1) {
840                 char c = *s++;
841                 if (c == 0)
842                         return;
843                 putchar(c);
844         }
845         return 0;
846 #endif
847 }