locale.library: use system default locale if locale argument is NULL.
[AROS.git] / workbench / libs / locale / formatstring.c
blob66f3196106ebfac764d710ee84b4227fc1ab39f3
1 /*
2 Copyright © 1995-2012, The AROS Development Team. All rights reserved.
3 $Id$
5 Desc:
6 Lang: english
7 */
9 #include <stdarg.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <exec/types.h>
13 #include <utility/hooks.h>
14 #include <proto/utility.h>
15 #include <libraries/locale.h>
16 #include <aros/asmcall.h>
17 #include "locale_intern.h"
19 #include <clib/alib_protos.h>
21 #include <aros/debug.h>
23 typedef QUAD FMTLARGESTTYPE;
24 typedef UQUAD UFMTLARGESTTYPE;
26 static const UBYTE hexarray[] = "0123456789abcdef";
27 static const UBYTE HEXarray[] = "0123456789ABCDEF";
29 APTR InternalFormatString(const struct Locale * locale,
30 CONST_STRPTR fmtTemplate, CONST_APTR dataStream,
31 ULONG *indexStream, const struct Hook * putCharFunc)
33 enum
34 { OUTPUT = 0,
35 FOUND_FORMAT
36 } state;
38 #define ARG(x) ((CONST_APTR)((IPTR)dataStream + indexStream[(x) - 1]))
40 ULONG template_pos;
41 BOOL end;
42 ULONG max_argpos, max_argpos_datasize;
43 ULONG arg_counter;
45 if (!fmtTemplate)
46 return (APTR)dataStream;
48 template_pos = 0; /* Current position in the template string */
49 state = OUTPUT; /* current state of parsing */
50 end = FALSE;
51 max_argpos = 1;
52 arg_counter = 0;
53 max_argpos_datasize = 0;
55 while (!end)
58 ** A format description starts here?
60 if (fmtTemplate[template_pos] == '%')
62 arg_counter++;
63 state = FOUND_FORMAT;
66 switch (state)
68 case OUTPUT:
70 ** Call the hook for this character
72 AROS_UFC3NR(VOID, putCharFunc->h_Entry,
73 AROS_UFCA(const struct Hook *, putCharFunc, A0),
74 AROS_UFCA(const struct Locale *, locale, A2),
75 AROS_UFCA(UBYTE, fmtTemplate[template_pos], A1));
78 ** End of template string? -> End of this function.
80 if (fmtTemplate[template_pos] == '\0')
82 end = TRUE;
84 else
85 template_pos++;
87 //kprintf("OUTPUT: template_pos: %d\n",template_pos);
89 break;
91 case FOUND_FORMAT:
93 ** The '%' was found in the template string
95 template_pos++;
97 //kprintf("FOUND_FORMAT: template_pos: %d\n",template_pos);
99 ** Does the user want the '%' to be printed?
101 if (fmtTemplate[template_pos] == '%')
103 AROS_UFC3NR(VOID, putCharFunc->h_Entry,
104 AROS_UFCA(const struct Hook *, putCharFunc, A0),
105 AROS_UFCA(const struct Locale *, locale, A2),
106 AROS_UFCA(UBYTE, fmtTemplate[template_pos], A1));
107 template_pos++;
108 arg_counter--; //stegerg
110 else
113 ** Now parsing...
114 ** Template format: %[arg_pos$][flags][width][.limit][length]type
116 ** arg_pos specifies the position of the argument in the dataStream
117 ** flags only '-' is allowed
118 ** width
119 ** .limit
120 ** datasize size of the datatype
121 ** type b,d,D,u,U,x,X,s,c
123 ULONG arg_pos = 1;
124 BOOL left = FALSE; // no flag was found
125 UBYTE fill = ' ';
126 ULONG minus;
127 ULONG width = 0;
128 ULONG limit = ~0;
129 ULONG buflen = 0;
130 ULONG datasize;
131 UFMTLARGESTTYPE tmp = 0;
132 #define BUFFERSIZE 128
133 UBYTE buf[BUFFERSIZE];
134 UBYTE *buffer = buf;
137 ** arg_pos
140 //kprintf("next char: %c\n",fmtTemplate[template_pos]);
142 if (fmtTemplate[template_pos] >= '0' &&
143 fmtTemplate[template_pos] <= '9')
145 ULONG old_template_pos = template_pos;
147 for (arg_pos = 0; (fmtTemplate[template_pos] >= '0' &&
148 fmtTemplate[template_pos] <= '9');
149 template_pos++)
151 arg_pos =
152 arg_pos * 10 + fmtTemplate[template_pos] - '0';
155 if (fmtTemplate[template_pos] == '$')
156 template_pos++;
157 else
159 arg_pos = arg_counter;
160 template_pos = old_template_pos;
163 else
164 arg_pos = arg_counter;
167 ** flags
169 if (fmtTemplate[template_pos] == '-')
171 template_pos++;
172 left = TRUE;
176 ** fill character a '0'?
178 if (fmtTemplate[template_pos] == '0')
180 template_pos++;
181 fill = '0';
185 ** width
187 if (fmtTemplate[template_pos] >= '0' &&
188 fmtTemplate[template_pos] <= '9')
190 for (width = 0; (fmtTemplate[template_pos] >= '0' &&
191 fmtTemplate[template_pos] <= '9');
192 template_pos++)
194 width =
195 width * 10 + fmtTemplate[template_pos] - '0';
200 ** limit
202 if (fmtTemplate[template_pos] == '.')
204 template_pos++;
206 if (fmtTemplate[template_pos] >= '0' &&
207 fmtTemplate[template_pos] <= '9')
209 for (limit = 0; (fmtTemplate[template_pos] >= '0' &&
210 fmtTemplate[template_pos] <= '9');
211 template_pos++)
213 limit =
214 limit * 10 + fmtTemplate[template_pos] -
215 '0';
221 ** Length
223 switch (fmtTemplate[template_pos])
225 case 'i':
226 /* IPTR-sized type, can be mixed with %d, %u or %x */
227 datasize = sizeof(IPTR);
228 template_pos++;
229 break;
230 case 'L':
231 datasize = sizeof(UQUAD);
232 template_pos++;
233 break;
234 case 'l':
235 template_pos++;
236 if (fmtTemplate[template_pos] == 'l')
238 datasize = sizeof(UQUAD);
239 template_pos++;
241 else
242 datasize = sizeof(ULONG);
243 break;
245 default:
246 datasize = sizeof(UWORD);
247 break;
251 ** Print it according to the given type info.
253 switch (fmtTemplate[template_pos])
255 case 'b': /* BSTR, see autodocs */
257 ** Important parameters:
258 ** arg_pos, left, buflen, limit
261 datasize = sizeof(IPTR);
262 BSTR s = (BSTR) * (UBYTE **) ARG(arg_pos);
264 if (s != (BSTR) BNULL)
266 buffer = AROS_BSTR_ADDR(s);
267 buflen = AROS_BSTR_strlen(s);
269 else
271 buffer = "";
272 buflen = 0;
275 #if !USE_GLOBALLIMIT
276 if (buflen > limit)
277 buflen = limit;
278 #endif /* !USE_GLOBALLIMIT */
280 break;
282 case 'd': /* signed decimal */
283 case 'u': /* unsigned decimal */
285 minus = fmtTemplate[template_pos] == 'd';
288 switch (datasize)
290 case 8:
291 tmp = *(UQUAD *) ARG(arg_pos);
292 //buffer = &buf[16+1];
293 minus *= (FMTLARGESTTYPE) tmp < 0;
294 if (minus)
295 tmp = -tmp;
296 break;
297 case 4:
298 tmp = *(ULONG *) ARG(arg_pos);
299 //buffer = &buf[8+1];
300 minus *= (LONG) tmp < 0;
301 if (minus)
302 tmp = (ULONG) - tmp;
303 break;
305 default: /* 2 */
306 tmp = *(UWORD *) ARG(arg_pos);
307 //buffer = &buf[4+1];
308 minus *= (WORD) tmp < 0;
309 if (minus)
310 tmp = (UWORD) - tmp;
311 break;
314 buffer = &buf[BUFFERSIZE];
317 *--buffer = (tmp % 10) + '0';
318 tmp /= 10;
319 buflen++;
321 while (tmp);
323 if (minus)
325 *--buffer = '-';
326 buflen++;
330 break;
332 case 'D': /* signed decimal with locale's formatting conventions */
333 case 'U': /* unsigned decimal with locale's formatting conventions */
335 UBYTE groupsize;
336 ULONG group_index = 0;
338 minus = fmtTemplate[template_pos] == 'D';
340 switch (datasize)
342 case 8:
343 tmp = *(UQUAD *) ARG(arg_pos);
344 minus *= (FMTLARGESTTYPE) tmp < 0;
345 if (minus)
346 tmp = -tmp;
347 break;
348 case 4:
349 tmp = *(ULONG *) ARG(arg_pos);
350 minus *= (LONG) tmp < 0;
351 if (minus)
352 tmp = (ULONG) - tmp;
353 break;
355 default: /* 2 */
356 tmp = *(UWORD *) ARG(arg_pos);
357 minus *= (WORD) tmp < 0;
358 if (minus)
359 tmp = (UWORD) - tmp;
360 break;
363 /* BUFFERSIZE should be big enough to format a string
364 ** according to locale's formatting conventions
366 buffer = &buf[BUFFERSIZE];
367 groupsize =
368 locale ? locale->
369 loc_Grouping[group_index] : 255;
373 *--buffer = (tmp % 10) + '0';
374 tmp /= 10;
375 buflen++;
377 groupsize--;
379 if (groupsize == 0 && tmp != 0)
382 ** Write the separator
385 *--buffer =
386 locale->loc_GroupSeparator[group_index];
388 groupsize =
389 locale->loc_Grouping[group_index + 1];
391 if (groupsize == 0)
394 ** Supposed to use the previous element
396 groupsize =
397 locale->loc_Grouping[group_index];
399 else
400 group_index++;
402 buflen++;
405 while (tmp);
407 if (minus)
409 *--buffer = '-';
410 buflen++;
413 break;
415 case 'p': /* lower case pointer string */
416 case 'P': /* upper case pointer string */
417 fill = '0';
418 width = sizeof(APTR) * 2;
419 /* %p is always at least natural pointer size */
420 if (datasize < sizeof(APTR))
421 datasize = sizeof(APTR);
422 case 'x': /* upper case hexadecimal string */
423 case 'X': /* lower case hexadecimal string */
426 const UBYTE *hexa;
428 switch (datasize)
430 case 8:
431 tmp = *(UQUAD *) ARG(arg_pos);
432 //buffer = &buf[16+1];
433 break;
434 case 4:
435 tmp = *(ULONG *) ARG(arg_pos);
436 //buffer = &buf[8+1];
437 break;
439 default: /* 2 */
440 tmp = *(UWORD *) ARG(arg_pos);
441 //buffer = &buf[4+1];
442 break;
445 buffer = &buf[BUFFERSIZE];
447 /* NOTE: x/X is reverse to printf, coz orig RawDoFmt %lx for uppercase. */
448 hexa = (fmtTemplate[template_pos] == 'X' ||
449 fmtTemplate[template_pos] ==
450 'p') ? hexarray : HEXarray;
453 *--buffer = hexa[tmp & 0x0f];
454 tmp >>= 4;
455 buflen++;
457 while (tmp);
459 break;
461 case 's': /* NULL terminated string */
463 datasize = sizeof(IPTR);
465 buffer = *(UBYTE **) ARG(arg_pos);
468 * RawDoFmt() in original AmigaOS(tm) formats NULL pointers as empty strings,
469 * and not something like "(null)". Some software may rely on this behavior.
470 * %b is handled in similar manner.
472 if (!buffer)
473 buffer = "";
474 buflen = strlen(buffer);
476 #if !USE_GLOBALLIMIT
477 if (buflen > limit)
478 buflen = limit;
479 #endif /* !USE_GLOBALLIMIT */
482 break;
484 case 'c': /* Character */
486 switch (datasize)
488 case 8:
489 buf[0] =
490 (UBYTE) * (UQUAD *) ARG(arg_pos);
491 break;
492 case 4:
493 buf[0] =
494 (UBYTE) * (ULONG *) ARG(arg_pos);
495 break;
497 default: /* 2 */
498 buf[0] =
499 (UBYTE) * (WORD *) ARG(arg_pos);
500 break;
503 buflen = 1;
505 break;
507 default:
508 /* Ignore the faulty '%' */
510 buf[0] = fmtTemplate[template_pos];
511 width = 1;
512 buflen = 1;
513 arg_pos = --arg_counter;
514 break;
519 int i;
522 Now everything I need is known:
523 buffer - contains the string to be printed
524 buflen - size of the string
525 fill - the pad character
526 left - is 1 if the string should be left aligned
527 width - is the minimal width of the field
528 limit - maximum number of characters to output from a string, default ~0
531 #if USE_GLOBALLIMIT
532 if (buflen > limit)
533 buflen = limit;
534 #endif /* USE_GLOBALLIMIT */
536 /* Print padding if right aligned */
537 if (!left)
538 for (i = buflen; i < width; i++)
539 AROS_UFC3NR(VOID, putCharFunc->h_Entry,
540 AROS_UFCA(const struct Hook *, putCharFunc, A0),
541 AROS_UFCA(const struct Locale *, locale, A2),
542 AROS_UFCA(UBYTE, fill, A1));
544 /* Print body up to buflen */
545 for (i = 0; i < buflen; i++)
547 AROS_UFC3NR(VOID, putCharFunc->h_Entry,
548 AROS_UFCA(const struct Hook *, putCharFunc, A0),
549 AROS_UFCA(const struct Locale *, locale, A2),
550 AROS_UFCA(UBYTE, *buffer++, A1));
553 /* Pad right if left aligned */
554 if (left)
555 for (i = buflen; i < width; i++)
556 AROS_UFC3NR(VOID, putCharFunc->h_Entry,
557 AROS_UFCA(const struct Hook *, putCharFunc, A0),
558 AROS_UFCA(const struct Locale *, locale, A2),
559 AROS_UFCA(UBYTE, fill, A1));
562 template_pos++;
564 if (arg_pos > max_argpos)
566 max_argpos = arg_pos;
567 max_argpos_datasize = datasize;
571 state = OUTPUT;
572 break;
576 return (APTR)(ARG(max_argpos) + max_argpos_datasize);
579 /*****************************************************************************
581 NAME */
582 #include <proto/locale.h>
584 AROS_LH4(APTR, FormatString,
586 /* SYNOPSIS */
587 AROS_LHA(const struct Locale *, locale, A0),
588 AROS_LHA(CONST_STRPTR, fmtTemplate, A1),
589 AROS_LHA(RAWARG, dataStream, A2),
590 AROS_LHA(const struct Hook *, putCharFunc, A3),
592 /* LOCATION */
593 struct LocaleBase *, LocaleBase, 11, Locale)
595 /* FUNCTION
597 INPUTS
599 RESULT
601 NOTES
603 EXAMPLE
605 BUGS
607 SEE ALSO
609 INTERNALS
611 *****************************************************************************/
613 AROS_LIBFUNC_INIT
614 ULONG *indices;
615 ULONG indexSize = 0;
616 APTR retval;
617 struct Locale *def_locale = NULL;
618 #if defined(__arm__) || defined(__x86_64__)
619 va_list nullarg = {};
620 #else
621 va_list nullarg = 0;
622 #endif
624 if (locale == NULL)
626 locale = OpenLocale(NULL);
627 def_locale = (struct Locale *)locale;
630 /* Generate the indexes for the provided datastream */
631 GetDataStreamFromFormat(fmtTemplate, nullarg, NULL, NULL, NULL, &indexSize);
632 indices = alloca(indexSize);
633 GetDataStreamFromFormat(fmtTemplate, nullarg, NULL, NULL, indices, &indexSize);
635 retval = InternalFormatString(locale, fmtTemplate,
636 dataStream, indices, putCharFunc);
638 CloseLocale(def_locale);
640 return retval;
642 AROS_LIBFUNC_EXIT