Hint added.
[AROS.git] / workbench / libs / locale / formatstring.c
blob94b6b361bbe05f2fcfae89fb3a1d4bf31b7dee07
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;
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;
54 while (!end)
57 ** A format description starts here?
59 if (fmtTemplate[template_pos] == '%')
61 arg_counter++;
62 state = FOUND_FORMAT;
65 switch (state)
67 case OUTPUT:
69 ** Call the hook for this character
71 AROS_UFC3NR(VOID, putCharFunc->h_Entry,
72 AROS_UFCA(const struct Hook *, putCharFunc, A0),
73 AROS_UFCA(const struct Locale *, locale, A2),
74 AROS_UFCA(UBYTE, fmtTemplate[template_pos], A1));
77 ** End of template string? -> End of this function.
79 if (fmtTemplate[template_pos] == '\0')
81 end = TRUE;
83 else
84 template_pos++;
86 //kprintf("OUTPUT: template_pos: %d\n",template_pos);
88 break;
90 case FOUND_FORMAT:
92 ** The '%' was found in the template string
94 template_pos++;
96 //kprintf("FOUND_FORMAT: template_pos: %d\n",template_pos);
98 ** Does the user want the '%' to be printed?
100 if (fmtTemplate[template_pos] == '%')
102 AROS_UFC3NR(VOID, putCharFunc->h_Entry,
103 AROS_UFCA(const struct Hook *, putCharFunc, A0),
104 AROS_UFCA(const struct Locale *, locale, A2),
105 AROS_UFCA(UBYTE, fmtTemplate[template_pos], A1));
106 template_pos++;
107 arg_counter--; //stegerg
109 else
112 ** Now parsing...
113 ** Template format: %[arg_pos$][flags][width][.limit][length]type
115 ** arg_pos specifies the position of the argument in the dataStream
116 ** flags only '-' is allowed
117 ** width
118 ** .limit
119 ** datasize size of the datatype
120 ** type b,d,D,u,U,x,X,s,c
122 ULONG arg_pos = 1;
123 BOOL left = FALSE; // no flag was found
124 UBYTE fill = ' ';
125 ULONG minus;
126 ULONG width = 0;
127 ULONG limit = ~0;
128 ULONG buflen = 0;
129 ULONG datasize;
130 UFMTLARGESTTYPE tmp = 0;
131 #define BUFFERSIZE 128
132 UBYTE buf[BUFFERSIZE];
133 UBYTE *buffer = buf;
136 ** arg_pos
139 //kprintf("next char: %c\n",fmtTemplate[template_pos]);
141 if (fmtTemplate[template_pos] >= '0' &&
142 fmtTemplate[template_pos] <= '9')
144 ULONG old_template_pos = template_pos;
146 for (arg_pos = 0; (fmtTemplate[template_pos] >= '0' &&
147 fmtTemplate[template_pos] <= '9');
148 template_pos++)
150 arg_pos =
151 arg_pos * 10 + fmtTemplate[template_pos] - '0';
154 if (fmtTemplate[template_pos] == '$')
155 template_pos++;
156 else
158 arg_pos = arg_counter;
159 template_pos = old_template_pos;
162 else
163 arg_pos = arg_counter;
166 ** flags
168 if (fmtTemplate[template_pos] == '-')
170 template_pos++;
171 left = TRUE;
175 ** fill character a '0'?
177 if (fmtTemplate[template_pos] == '0')
179 template_pos++;
180 fill = '0';
184 ** width
186 if (fmtTemplate[template_pos] >= '0' &&
187 fmtTemplate[template_pos] <= '9')
189 for (width = 0; (fmtTemplate[template_pos] >= '0' &&
190 fmtTemplate[template_pos] <= '9');
191 template_pos++)
193 width =
194 width * 10 + fmtTemplate[template_pos] - '0';
199 ** limit
201 if (fmtTemplate[template_pos] == '.')
203 template_pos++;
205 if (fmtTemplate[template_pos] >= '0' &&
206 fmtTemplate[template_pos] <= '9')
208 for (limit = 0; (fmtTemplate[template_pos] >= '0' &&
209 fmtTemplate[template_pos] <= '9');
210 template_pos++)
212 limit =
213 limit * 10 + fmtTemplate[template_pos] -
214 '0';
220 ** Length
222 switch (fmtTemplate[template_pos])
224 case 'i':
225 /* IPTR-sized type, can be mixed with %d, %u or %x */
226 datasize = sizeof(IPTR);
227 template_pos++;
228 break;
229 case 'L':
230 datasize = sizeof(UQUAD);
231 template_pos++;
232 break;
233 case 'l':
234 template_pos++;
235 if (fmtTemplate[template_pos] == 'l')
237 datasize = sizeof(UQUAD);
238 template_pos++;
240 else
241 datasize = sizeof(ULONG);
242 break;
244 default:
245 datasize = sizeof(UWORD);
246 break;
250 ** Print it according to the given type info.
252 switch (fmtTemplate[template_pos])
254 case 'b': /* BSTR, see autodocs */
256 ** Important parameters:
257 ** arg_pos, left, buflen, limit
260 BSTR s = (BSTR) * (UBYTE **) ARG(arg_pos);
262 if (s != (BSTR) BNULL)
264 buffer = AROS_BSTR_ADDR(s);
265 buflen = AROS_BSTR_strlen(s);
267 else
269 buffer = "";
270 buflen = 0;
273 #if !USE_GLOBALLIMIT
274 if (buflen > limit)
275 buflen = limit;
276 #endif /* !USE_GLOBALLIMIT */
278 break;
280 case 'd': /* signed decimal */
281 case 'u': /* unsigned decimal */
283 minus = fmtTemplate[template_pos] == 'd';
286 switch (datasize)
288 case 8:
289 tmp = *(UQUAD *) ARG(arg_pos);
290 //buffer = &buf[16+1];
291 minus *= (FMTLARGESTTYPE) tmp < 0;
292 if (minus)
293 tmp = -tmp;
294 break;
295 case 4:
296 tmp = *(ULONG *) ARG(arg_pos);
297 //buffer = &buf[8+1];
298 minus *= (LONG) tmp < 0;
299 if (minus)
300 tmp = (ULONG) - tmp;
301 break;
303 default: /* 2 */
304 tmp = *(UWORD *) ARG(arg_pos);
305 //buffer = &buf[4+1];
306 minus *= (WORD) tmp < 0;
307 if (minus)
308 tmp = (UWORD) - tmp;
309 break;
312 buffer = &buf[BUFFERSIZE];
315 *--buffer = (tmp % 10) + '0';
316 tmp /= 10;
317 buflen++;
319 while (tmp);
321 if (minus)
323 *--buffer = '-';
324 buflen++;
328 break;
330 case 'D': /* signed decimal with locale's formatting conventions */
331 case 'U': /* unsigned decimal with locale's formatting conventions */
333 UBYTE groupsize;
334 ULONG group_index = 0;
336 minus = fmtTemplate[template_pos] == 'D';
338 switch (datasize)
340 case 8:
341 tmp = *(UQUAD *) ARG(arg_pos);
342 minus *= (FMTLARGESTTYPE) tmp < 0;
343 if (minus)
344 tmp = -tmp;
345 break;
346 case 4:
347 tmp = *(ULONG *) ARG(arg_pos);
348 minus *= (LONG) tmp < 0;
349 if (minus)
350 tmp = (ULONG) - tmp;
351 break;
353 default: /* 2 */
354 tmp = *(UWORD *) ARG(arg_pos);
355 minus *= (WORD) tmp < 0;
356 if (minus)
357 tmp = (UWORD) - tmp;
358 break;
361 /* BUFFERSIZE should be big enough to format a string
362 ** according to locale's formatting conventions
364 buffer = &buf[BUFFERSIZE];
365 groupsize =
366 locale ? locale->
367 loc_Grouping[group_index] : 255;
371 *--buffer = (tmp % 10) + '0';
372 tmp /= 10;
373 buflen++;
375 groupsize--;
377 if (groupsize == 0 && tmp != 0)
380 ** Write the separator
383 *--buffer =
384 locale->loc_GroupSeparator[group_index];
386 groupsize =
387 locale->loc_Grouping[group_index + 1];
389 if (groupsize == 0)
392 ** Supposed to use the previous element
394 groupsize =
395 locale->loc_Grouping[group_index];
397 else
398 group_index++;
400 buflen++;
403 while (tmp);
405 if (minus)
407 *--buffer = '-';
408 buflen++;
411 break;
413 case 'p': /* lower case pointer string */
414 case 'P': /* upper case pointer string */
415 fill = '0';
416 width = sizeof(APTR) * 2;
417 /* %p is always at least natural pointer size */
418 if (datasize < sizeof(APTR))
419 datasize = sizeof(APTR);
420 case 'x': /* upper case hexadecimal string */
421 case 'X': /* lower case hexadecimal string */
424 const UBYTE *hexa;
426 switch (datasize)
428 case 8:
429 tmp = *(UQUAD *) ARG(arg_pos);
430 //buffer = &buf[16+1];
431 break;
432 case 4:
433 tmp = *(ULONG *) ARG(arg_pos);
434 //buffer = &buf[8+1];
435 break;
437 default: /* 2 */
438 tmp = *(UWORD *) ARG(arg_pos);
439 //buffer = &buf[4+1];
440 break;
443 buffer = &buf[BUFFERSIZE];
445 /* NOTE: x/X is reverse to printf, coz orig RawDoFmt %lx for uppercase. */
446 hexa = (fmtTemplate[template_pos] == 'X' ||
447 fmtTemplate[template_pos] ==
448 'p') ? hexarray : HEXarray;
451 *--buffer = hexa[tmp & 0x0f];
452 tmp >>= 4;
453 buflen++;
455 while (tmp);
457 break;
459 case 's': /* NULL terminated string */
462 buffer = *(UBYTE **) ARG(arg_pos);
465 * RawDoFmt() in original AmigaOS(tm) formats NULL pointers as empty strings,
466 * and not something like "(null)". Some software may rely on this behavior.
467 * %b is handled in similar manner.
469 if (!buffer)
470 buffer = "";
471 buflen = strlen(buffer);
473 #if !USE_GLOBALLIMIT
474 if (buflen > limit)
475 buflen = limit;
476 #endif /* !USE_GLOBALLIMIT */
479 break;
481 case 'c': /* Character */
483 switch (datasize)
485 case 8:
486 buf[0] =
487 (UBYTE) * (UQUAD *) ARG(arg_pos);
488 break;
489 case 4:
490 buf[0] =
491 (UBYTE) * (ULONG *) ARG(arg_pos);
492 break;
494 default: /* 2 */
495 buf[0] =
496 (UBYTE) * (WORD *) ARG(arg_pos);
497 break;
500 buflen = 1;
502 break;
504 default:
505 /* Ignore the faulty '%' */
507 buf[0] = fmtTemplate[template_pos];
508 width = 1;
509 buflen = 1;
510 arg_pos = --arg_counter;
511 break;
516 int i;
519 Now everything I need is known:
520 buffer - contains the string to be printed
521 buflen - size of the string
522 fill - the pad character
523 left - is 1 if the string should be left aligned
524 width - is the minimal width of the field
525 limit - maximum number of characters to output from a string, default ~0
528 #if USE_GLOBALLIMIT
529 if (buflen > limit)
530 buflen = limit;
531 #endif /* USE_GLOBALLIMIT */
533 /* Print padding if right aligned */
534 if (!left)
535 for (i = buflen; i < width; i++)
536 AROS_UFC3NR(VOID, putCharFunc->h_Entry,
537 AROS_UFCA(const struct Hook *, putCharFunc, A0),
538 AROS_UFCA(const struct Locale *, locale, A2),
539 AROS_UFCA(UBYTE, fill, A1));
541 /* Print body up to buflen */
542 for (i = 0; i < buflen; i++)
544 AROS_UFC3NR(VOID, putCharFunc->h_Entry,
545 AROS_UFCA(const struct Hook *, putCharFunc, A0),
546 AROS_UFCA(const struct Locale *, locale, A2),
547 AROS_UFCA(UBYTE, *buffer++, A1));
550 /* Pad right if left aligned */
551 if (left)
552 for (i = buflen; i < width; i++)
553 AROS_UFC3NR(VOID, putCharFunc->h_Entry,
554 AROS_UFCA(const struct Hook *, putCharFunc, A0),
555 AROS_UFCA(const struct Locale *, locale, A2),
556 AROS_UFCA(UBYTE, fill, A1));
559 template_pos++;
561 if (arg_pos > max_argpos)
562 max_argpos = arg_pos;
565 state = OUTPUT;
566 break;
570 return (APTR)ARG(max_argpos);
573 /*****************************************************************************
575 NAME */
576 #include <proto/locale.h>
578 AROS_LH4(APTR, FormatString,
580 /* SYNOPSIS */
581 AROS_LHA(const struct Locale *, locale, A0),
582 AROS_LHA(CONST_STRPTR, fmtTemplate, A1),
583 AROS_LHA(CONST_APTR, dataStream, A2),
584 AROS_LHA(const struct Hook *, putCharFunc, A3),
586 /* LOCATION */
587 struct LocaleBase *, LocaleBase, 11, Locale)
589 /* FUNCTION
591 INPUTS
593 RESULT
595 NOTES
597 EXAMPLE
599 BUGS
601 SEE ALSO
603 INTERNALS
605 *****************************************************************************/
607 AROS_LIBFUNC_INIT
608 ULONG *indices;
609 ULONG indexSize = 0;
610 #if defined(__arm__) || defined(__x86_64__)
611 va_list nullarg = {};
612 #else
613 va_list nullarg = 0;
614 #endif
616 /* Generate the indexes for the provided datastream */
617 GetDataStreamFromFormat(fmtTemplate, nullarg, NULL, NULL, NULL, &indexSize);
618 indices = alloca(indexSize);
619 GetDataStreamFromFormat(fmtTemplate, nullarg, NULL, NULL, indices, &indexSize);
621 return InternalFormatString(locale, fmtTemplate,
622 dataStream, indices, putCharFunc);
624 AROS_LIBFUNC_EXIT