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