1 /*---------------------------------------------------------------------------*
2   Project:  TwlSDK - libraries - STD
3   File:     std_sprintf.c
4 
5   Copyright 2007-2008 Nintendo. All rights reserved.
6 
7   These coded instructions, statements, and computer programs contain
8   proprietary information of Nintendo of America Inc. and/or Nintendo
9   Company Ltd., and are protected by Federal copyright law. They may
10   not be disclosed to third parties or copied or duplicated in any form,
11   in whole or in part, without the prior written consent of Nintendo.
12 
13   $Date:: 2008-11-07#$
14   $Rev: 9262 $
15   $Author: yosizaki $
16  *---------------------------------------------------------------------------*/
17 
18 
19 #include <nitro.h>
20 
21 
22 /*---------------------------------------------------------------------------*/
23 /* Functions */
24 
25 /* Internal functions for sized-buffer output */
26 typedef struct dst_string_tag
27 {
28     size_t  len;
29     char   *cur;
30     char   *base;
31 }
32 dst_string;
33 
string_put_char(dst_string * p,char c)34 static void string_put_char(dst_string * p, char c)
35 {
36     if (p->len > 0)
37         *p->cur = c, --p->len;
38     ++p->cur;
39 }
40 
string_fill_char(dst_string * p,char c,int n)41 static void string_fill_char(dst_string * p, char c, int n)
42 {
43     if (n > 0)
44     {
45         size_t  i, k = p->len;
46         if (k > (size_t) n)
47             k = (size_t) n;
48         for (i = 0; i < k; ++i)
49             p->cur[i] = c;
50         p->len -= k;
51         p->cur += n;
52     }
53 }
54 
string_put_string(dst_string * p,const char * s,int n)55 static void string_put_string(dst_string * p, const char *s, int n)
56 {
57     if (n > 0)
58     {
59         size_t  i, k = p->len;
60         if (k > (size_t) n)
61             k = (size_t) n;
62         for (i = 0; i < k; ++i)
63             p->cur[i] = s[i];
64         p->len -= k;
65         p->cur += n;
66     }
67 }
68 
69 /*---------------------------------------------------------------------------*
70   Name:         STD_TSPrintf
71 
72   Description:  With the exception of the format of the arguments, identical to STD_TVSNPrintf.
73 
74   Arguments:    dst: The buffer that will store the result
75                 fmt: Format control string
76 
77   Returns:      Identical to STD_VSNPrintf.
78  *---------------------------------------------------------------------------*/
STD_TSPrintf(char * dst,const char * fmt,...)79 SDK_WEAK_SYMBOL int STD_TSPrintf(char *dst, const char *fmt, ...)
80 {
81     int     ret;
82     va_list vlist;
83     va_start(vlist, fmt);
84     ret = STD_TVSNPrintf(dst, 0x7FFFFFFF, fmt, vlist);
85     va_end(vlist);
86     return ret;
87 }
88 
89 /*---------------------------------------------------------------------------*
90   Name:         STD_TVSPrintf
91 
92   Description:  With the exception of the format of the arguments, identical to STD_TVSNPrintf.
93 
94   Arguments:    dst: The buffer that will store the result
95                 fmt: Format control string
96                 vlist: Parameters
97 
98   Returns:      Identical to STD_VSNPrintf.
99  *---------------------------------------------------------------------------*/
STD_TVSPrintf(char * dst,const char * fmt,va_list vlist)100 SDK_WEAK_SYMBOL int STD_TVSPrintf(char *dst, const char *fmt, va_list vlist)
101 {
102     return STD_TVSNPrintf(dst, 0x7FFFFFFF, fmt, vlist);
103 }
104 
105 /*---------------------------------------------------------------------------*
106   Name:         STD_TSNPrintf
107 
108   Description:  With the exception of the format of the arguments, identical to STD_TVSNPrintf.
109 
110   Arguments:    dst: The buffer that will store the result
111                 len: Buffer length
112                 fmt: Format control string
113 
114   Returns:      Identical to STD_VSNPrintf.
115  *---------------------------------------------------------------------------*/
STD_TSNPrintf(char * dst,size_t len,const char * fmt,...)116 SDK_WEAK_SYMBOL int STD_TSNPrintf(char *dst, size_t len, const char *fmt, ...)
117 {
118     int     ret;
119     va_list vlist;
120     va_start(vlist, fmt);
121     ret = STD_TVSNPrintf(dst, len, fmt, vlist);
122     va_end(vlist);
123     return ret;
124 }
125 
126 /*---------------------------------------------------------------------------*
127   Name:         STD_TVSNPrintf
128 
129   Description:  sprintf redone to save on size.
130                 Supports basic format specifications.
131                 %([-+# ]?)([0-9]*)(\.?)([0-9]*)([l|ll|h||hh]?)([diouxXpncs%])
132 
133   Note:         To match the behavior of CodeWarrior's MSL sprintf(), '+' and '#' are disabled.
134 
135                 { // example
136                   char buf[5];
137                   sprintf(buf, "%-i\n", 45);  // "45"  (OK)
138                   sprintf(buf, "%0i\n", 45);  // "45"  (OK)
139                   sprintf(buf, "% i\n", 45);  // " 45" (OK)
140                   sprintf(buf, "%+i\n", 45);  // "%+i" ("+45" expected)
141                   sprintf(buf, "%#x\n", 45);  // "%#x" ("0x2d" expected)
142                   // but, this works correctly!
143                   sprintf(buf, "% +i\n", 45); // "+45" (OK)
144                 }
145 
146   Arguments:    dst: The buffer that will store the result
147                 len: Buffer length
148                 fmt: Format control string
149                 vlist: Parameters
150 
151   Returns:      Returns the number of characters (exclusive of '\0') when the format string is output correctly.
152                 When the buffer size is sufficient, all text is output and a terminator is provided.
153                 When the buffer size is not enough, termination occurs at dst[len-1].
154                 Nothing happens when len is 0.
155 
156  *---------------------------------------------------------------------------*/
STD_TVSNPrintf(char * dst,size_t len,const char * fmt,va_list vlist)157 SDK_WEAK_SYMBOL int STD_TVSNPrintf(char *dst, size_t len, const char *fmt, va_list vlist)
158 {
159     char    buf[24];
160     int     n_buf;
161     char    prefix[2];
162     int     n_prefix;
163 
164     const char *s = fmt;
165 
166     dst_string str;
167     str.len = len, str.cur = str.base = dst;
168 
169     while (*s)
170     {
171         if ((unsigned int)(((unsigned char)*s ^ 0x20) - 0xA1) < 0x3C)
172         {
173             /* Shift JIS character */
174             string_put_char(&str, *s++);
175             if (*s)
176                 string_put_char(&str, *s++);
177         }
178         else if (*s != '%')
179         {
180             /* normal ASCII character */
181             string_put_char(&str, *s++);
182         }
183         else
184         {
185             /* output with format */
186             enum
187             {
188                 flag_blank = 000001,   /* ' ' */
189                 flag_plus = 000002,    /* '+' */
190                 flag_sharp = 000004,   /* '#' */
191                 flag_minus = 000010,   /* '-' */
192                 flag_zero = 000020,    /* '0' */
193                 flag_l1 = 000040,      /* "l" */
194                 flag_h1 = 000100,      /* "h" */
195                 flag_l2 = 000200,      /* "ll" */
196                 flag_h2 = 000400,      /* "hh" */
197                 flag_unsigned = 010000, /* 'o', 'u', ... */
198                 flag_end
199             };
200             int     flag = 0, width = 0, precision = -1, radix = 10;
201             char    hex_char = 'a' - 10;
202             const char *p_start = s;
203             /* flags */
204             for (;;)
205             {
206                 switch (*++s)
207                 {
208                 case '+':
209                     if (s[-1] != ' ')
210                         break;
211                     flag |= flag_plus;
212                     continue;
213                 case ' ':
214                     flag |= flag_blank;
215                     continue;
216                 case '-':
217                     flag |= flag_minus;
218                     continue;
219                 case '0':
220                     flag |= flag_zero;
221                     continue;
222                 }
223                 break;
224             }
225             /* width */
226             if (*s == '*')
227             {
228                 ++s, width = va_arg(vlist, int);
229                 if (width < 0)
230                     width = -width, flag |= flag_minus;
231             }
232             else
233             {
234                 while ((*s >= '0') && (*s <= '9'))
235                     width = (width * 10) + *s++ - '0';
236             }
237             /* precision */
238             if (*s == '.')
239             {
240                 ++s, precision = 0;
241                 if (*s == '*')
242                 {
243                     ++s, precision = va_arg(vlist, int);
244                     if (precision < 0)
245                         precision = -1;
246                 }
247                 else
248                 {
249                     while ((*s >= '0') && (*s <= '9'))
250                         precision = (precision * 10) + *s++ - '0';
251                 }
252             }
253             /* Options */
254             switch (*s)
255             {
256             case 'h':
257                 if (*++s != 'h')
258                     flag |= flag_h1;
259                 else
260                     ++s, flag |= flag_h2;
261                 break;
262             case 'l':
263                 if (*++s != 'l')
264                     flag |= flag_l1;
265                 else
266                     ++s, flag |= flag_l2;
267                 break;
268             }
269 
270             /* type */
271             switch (*s)
272             {
273             case 'd':                 /* signed decimal */
274             case 'i':                 /* signed decimal */
275                 goto put_integer;
276             case 'o':                 /* unsigned octal */
277                 radix = 8;
278                 flag |= flag_unsigned;
279                 goto put_integer;
280             case 'u':                 /* unsigned decimal */
281                 flag |= flag_unsigned;
282                 goto put_integer;
283             case 'X':                 /* unsigned hexadecimal */
284                 hex_char = 'A' - 10;
285                 goto put_hexadecimal;
286             case 'x':                 /* unsigned hexadecimal */
287                 goto put_hexadecimal;
288             case 'p':                 /* pointer */
289                 /* equal to code warrior */
290                 flag |= flag_sharp;
291                 precision = 8;
292                 goto put_hexadecimal;
293 
294             case 'c':                 /* character */
295                 if (precision >= 0)
296                     goto put_invalid;
297                 {
298                     int     c = va_arg(vlist, int);
299                     width -= 1;
300                     if (flag & flag_minus)
301                     {
302                         string_put_char(&str, (char)c);
303                         string_fill_char(&str, ' ', width);
304                     }
305                     else
306                     {
307                         char    pad = (char)((flag & flag_zero) ? '0' : ' ');
308                         string_fill_char(&str, pad, width);
309                         string_put_char(&str, (char)c);
310                     }
311                     ++s;
312                 }
313                 break;
314 
315             case 's':                 /* string */
316                 {
317                     int     n_buf = 0;
318                     const char *p_buf = va_arg(vlist, const char *);
319                     if (precision < 0)
320                     {
321                         while (p_buf[n_buf])
322                             ++n_buf;
323                     }
324                     else
325                     {
326                         while ((n_buf < precision) && p_buf[n_buf])
327                             ++n_buf;
328                     }
329                     width -= n_buf;
330                     if (flag & flag_minus)
331                     {
332                         string_put_string(&str, p_buf, n_buf);
333                         string_fill_char(&str, ' ', width);
334                     }
335                     else
336                     {
337                         char    pad = (char)((flag & flag_zero) ? '0' : ' ');
338                         string_fill_char(&str, pad, width);
339                         string_put_string(&str, p_buf, n_buf);
340                     }
341                     ++s;
342                 }
343                 break;
344 
345             /* store the number of output */
346             // %n can cause security problems, so disable it
347 /*            case 'n':
348                 {
349                     int     pos = str.cur - str.base;
350                     if (flag & flag_h2)
351                         ;
352                     else if (flag & flag_h1)
353                         *va_arg(vlist, signed short *) = (signed short)pos;
354                     else if (flag & flag_l2)
355                         *va_arg(vlist, u64 *) = (u64)pos;
356                     else
357                         *va_arg(vlist, signed int *) = (signed int)pos;
358                 }
359                 ++s;
360                 break;*/
361 
362             case '%':                 /* output '%' */
363                 if (p_start + 1 != s)
364                     goto put_invalid;
365                 string_put_char(&str, *s++);
366                 break;
367 
368             default:                  /* invalid type */
369                 goto put_invalid;
370 
371               put_invalid:
372                 string_put_string(&str, p_start, s - p_start);
373                 break;
374 
375               put_hexadecimal:
376                 radix = 16;
377                 flag |= flag_unsigned;
378               put_integer:
379                 {
380                     u64     val = 0;
381                     n_prefix = 0;
382 
383                     if (flag & flag_minus)
384                         flag &= ~flag_zero;
385                     if (precision < 0)
386                         precision = 1;
387                     else
388                         flag &= ~flag_zero;
389 
390                     if (flag & flag_unsigned)
391                     {
392                         if (flag & flag_h2)
393                             val = (unsigned char)va_arg(vlist, int);
394                         else if (flag & flag_h1)
395                             val = (unsigned short)va_arg(vlist, int);
396                         else if (flag & flag_l2)
397                             val = va_arg(vlist, u64);
398                         else
399                             val = va_arg(vlist, unsigned long);
400                         flag &= ~(flag_plus | flag_blank);
401                         if (flag & flag_sharp)
402                         {
403                             if (radix == 16)
404                             {
405                                 if (val != 0)
406                                 {
407                                     prefix[0] = (char)(hex_char + (10 + 'x' - 'a'));
408                                     prefix[1] = '0';
409                                     n_prefix = 2;
410                                 }
411                             }
412                             else if (radix == 8)
413                             {
414                                 prefix[0] = '0';
415                                 n_prefix = 1;
416                             }
417                         }
418                     }
419                     else
420                     {
421                         if (flag & flag_h2)
422                             val = (char)va_arg(vlist, int);
423                         else if (flag & flag_h1)
424                             val = (short)va_arg(vlist, int);
425                         else if (flag & flag_l2)
426                             val = va_arg(vlist, u64);
427                         else
428                             val = va_arg(vlist, long);
429                         if ((val >> 32) & 0x80000000)
430                         {
431                             val = ~val + 1;
432                             prefix[0] = '-';
433                             n_prefix = 1;
434                         }
435                         else
436                         {
437                             if (val || precision)
438                             {
439                                 if (flag & flag_plus)
440                                 {
441                                     prefix[0] = '+';
442                                     n_prefix = 1;
443                                 }
444                                 else if (flag & flag_blank)
445                                 {
446                                     prefix[0] = ' ';
447                                     n_prefix = 1;
448                                 }
449                             }
450                         }
451                     }
452                     n_buf = 0;
453                     switch (radix)
454                     {
455                     case 8:
456                         while (val != 0)
457                         {
458                             int     d = (int)(val & 0x07);
459                             val >>= 3;
460                             buf[n_buf++] = (char)(d + '0');
461                         }
462                         break;
463                     case 10:
464                         if ((val >> 32) == 0)
465                         {
466 #if defined(SDK_CW) || defined(SDK_RX) || defined(__MWERKS__)
467 #pragma optimize_for_size off
468 #endif
469                             u32     v = (u32)val;
470                             while (v != 0)
471                             {
472                                 // If the operation is division of a u32 and a constant, the compiler automatically converts it to addition using magic numbers
473                                 //
474                                 u32     r = v / 10;
475                                 int     d = (int)(v - (r * 10));
476                                 v = r;
477                                 buf[n_buf++] = (char)(d + '0');
478                             }
479                         }
480                         else
481                         {
482                             while (val != 0)
483                             {
484                                 u64     r = val / 10;
485                                 int     d = (int)(val - (r * 10));
486                                 val = r;
487                                 buf[n_buf++] = (char)(d + '0');
488                             }
489                         }
490                         break;
491                     case 16:
492                         while (val != 0)
493                         {
494                             int     d = (int)(val & 0x0f);
495                             val >>= 4;
496                             buf[n_buf++] = (char)((d < 10) ? (d + '0') : (d + hex_char));
497                         }
498                         break;
499                     }
500                     if ((n_prefix > 0) && (prefix[0] == '0'))
501                     {
502                         n_prefix = 0;
503                         buf[n_buf++] = '0';
504                     }
505                 }
506                 goto put_to_stream;
507 
508               put_to_stream:
509                 {
510                     int     n_pad = precision - n_buf;
511                     if (flag & flag_zero)
512                     {
513                         if (n_pad < width - n_buf - n_prefix)
514                             n_pad = width - n_buf - n_prefix;
515                     }
516                     if (n_pad > 0)
517                         width -= n_pad;
518                     width -= n_prefix + n_buf;
519                     if (!(flag & flag_minus))
520                         string_fill_char(&str, ' ', width);
521                     while (n_prefix > 0)
522                         string_put_char(&str, prefix[--n_prefix]);
523                     string_fill_char(&str, '0', n_pad);
524                     while (n_buf > 0)
525                         string_put_char(&str, buf[--n_buf]);
526                     if (flag & flag_minus)
527                         string_fill_char(&str, ' ', width);
528                     ++s;
529                 }
530                 break;
531             }
532         }
533     }
534 
535     if (str.len > 0)
536     {
537         *str.cur = '\0';
538     }
539     else if (len > 0)
540     {
541         str.base[len - 1] = '\0';
542     }
543     return str.cur - str.base;
544 }
545