add casts to zune macros to silence some warnings
[tangerine.git] / workbench / libs / locale / formatstring.c
blobe4c965d82bfe501a5b2af0539105e797c3f69f43
1 /*
2 Copyright © 1995-2007, The AROS Development Team. All rights reserved.
3 $Id$
5 Desc:
6 Lang: english
7 */
9 #include <stdlib.h>
10 #include <string.h>
11 #include <exec/types.h>
12 #include <utility/hooks.h>
13 #include <proto/utility.h>
14 #include <libraries/locale.h>
15 #include <aros/asmcall.h>
16 #include "locale_intern.h"
18 #include <aros/debug.h>
20 #if USE_QUADFMT
21 typedef QUAD FMTLARGESTTYPE;
22 typedef UQUAD UFMTLARGESTTYPE;
23 #else /* USE_QUADFMT */
24 typedef LONG FMTLARGESTTYPE;
25 typedef ULONG UFMTLARGESTTYPE;
26 #endif /* USE_QUADFMT */
28 static const UBYTE hexarray [] = "0123456789abcdef";
29 static const UBYTE HEXarray [] = "0123456789ABCDEF";
31 /*****************************************************************************
33 NAME */
34 #include <proto/locale.h>
36 AROS_LH4(APTR, FormatString,
38 /* SYNOPSIS */
39 AROS_LHA(struct Locale *, locale, A0),
40 AROS_LHA(STRPTR , fmtTemplate, A1),
41 AROS_LHA(APTR , dataStream, A2),
42 AROS_LHA(struct Hook *, putCharFunc, A3),
44 /* LOCATION */
45 struct LocaleBase *, LocaleBase, 11, Locale)
47 /* FUNCTION
49 INPUTS
51 RESULT
53 NOTES
55 EXAMPLE
57 BUGS
59 SEE ALSO
61 INTERNALS
63 HISTORY
64 27-11-96 digulla automatically created from
65 locale_lib.fd and clib/locale_protos.h
67 *****************************************************************************/
69 AROS_LIBFUNC_INIT
71 enum {OUTPUT = 0,
72 FOUND_FORMAT} state;
74 ULONG template_pos;
75 BOOL end;
76 ULONG max_argpos;
77 ULONG arg_counter;
78 ULONG *stream;
79 BOOL scanning;
81 #define INDICES 256
82 UWORD indices[INDICES];
84 if (!fmtTemplate)
85 return dataStream;
87 template_pos = 0; /* Current position in the template string */
88 state = OUTPUT; /* current state of parsing */
89 end = FALSE;
90 max_argpos = 0;
91 arg_counter = 0;
92 stream = (ULONG *) dataStream;
93 scanning = TRUE; /* The first time I will go through
94 and determine the width of the data in the dataStream */
96 #ifdef __MORPHOS__
97 memclr(indices, sizeof(indices));
98 #else
99 memset(indices, 0, sizeof(indices));
100 #endif
102 while (!end)
105 ** A format description starts here?
107 if (fmtTemplate[template_pos] == '%')
109 arg_counter++;
110 state = FOUND_FORMAT;
113 switch (state)
115 case OUTPUT:
117 ** Call the hook for this character
119 if (!scanning)
121 AROS_UFC3(VOID, putCharFunc->h_Entry,
122 AROS_UFCA(struct Hook *, putCharFunc, A0),
123 AROS_UFCA(struct Locale *, locale, A2),
124 AROS_UFCA(UBYTE, fmtTemplate[template_pos], A1));
128 ** End of template string? -> End of this function.
130 if (fmtTemplate[template_pos] == '\0')
132 if (scanning)
135 ** The scanning phase is over. Next time we do the output.
137 int i, sum;
138 scanning = FALSE;
139 template_pos = 0;
140 arg_counter = 0;
142 ** prepare the indices array
144 sum = indices[0];
145 indices[0] = 0;
147 i = 1;
149 while (i <= max_argpos)
151 int _sum;
152 if (indices[i] != 0)
153 _sum = sum + indices[i];
154 else
155 _sum = sum + 4;
157 indices[i] = sum;
158 sum = _sum;
159 i++;
162 else
165 ** We already went through the output phase. So this is
166 ** the end of it.
168 end = TRUE;
171 else
172 template_pos++;
174 //kprintf("OUTPUT: template_pos: %d\n",template_pos);
176 break;
178 case FOUND_FORMAT:
180 ** The '%' was found in the template string
182 template_pos++;
184 //kprintf("FOUND_FORMAT: template_pos: %d\n",template_pos);
186 ** Does the user want the '%' to be printed?
188 if (fmtTemplate[template_pos] == '%')
190 if (!scanning)
192 AROS_UFC3(VOID, putCharFunc->h_Entry,
193 AROS_UFCA(struct Hook * , putCharFunc, A0),
194 AROS_UFCA(struct Locale *, locale, A2),
195 AROS_UFCA(UBYTE, fmtTemplate[template_pos], A1));
197 template_pos++;
198 arg_counter--; //stegerg
200 else
203 ** Now parsing...
204 ** Template format: %[arg_pos$][flags][width][.limit][length]type
206 ** arg_pos specifies the position of the argument in the dataStream
207 ** flags only '-' is allowd
208 ** width
209 ** .limit
210 ** datasize size of the datatype
211 ** type b,d,D,u,U,x,X,s,c
213 ULONG arg_pos = 1;
214 BOOL left = FALSE; // no flag was found
215 UBYTE fill = ' ';
216 ULONG minus;
217 ULONG width = 0;
218 ULONG limit = ~0;
219 ULONG buflen = 0;
220 ULONG datasize;
221 UFMTLARGESTTYPE tmp= 0;
222 #define BUFFERSIZE 128
223 UBYTE buf[BUFFERSIZE];
224 UBYTE *buffer = buf;
227 ** arg_pos
230 //kprintf("next char: %c\n",fmtTemplate[template_pos]);
232 if (fmtTemplate[template_pos] >= '0' &&
233 fmtTemplate[template_pos] <= '9')
235 ULONG old_template_pos = template_pos;
237 for (arg_pos = 0; (fmtTemplate[template_pos] >= '0' &&
238 fmtTemplate[template_pos] <= '9'); template_pos++)
240 arg_pos = arg_pos * 10 + fmtTemplate[template_pos] - '0';
243 if (fmtTemplate[template_pos] == '$')
244 template_pos++;
245 else
247 arg_pos = arg_counter;
248 template_pos = old_template_pos;
251 else
252 arg_pos = arg_counter;
255 ** flags
257 if (fmtTemplate[template_pos] == '-')
259 template_pos++;
260 left = TRUE;
264 ** fill character a '0'?
266 if (fmtTemplate[template_pos] == '0')
268 template_pos++;
269 fill = '0';
273 ** width
275 if (fmtTemplate[template_pos] >= '0' &&
276 fmtTemplate[template_pos] <= '9')
278 for (width = 0; (fmtTemplate[template_pos] >= '0' &&
279 fmtTemplate[template_pos] <= '9'); template_pos++)
281 width = width * 10 + fmtTemplate[template_pos] - '0';
286 ** limit
288 if (fmtTemplate[template_pos] == '.')
290 template_pos++;
292 if (fmtTemplate[template_pos] >= '0' &&
293 fmtTemplate[template_pos] <= '9')
295 for (limit = 0; (fmtTemplate[template_pos] >= '0' &&
296 fmtTemplate[template_pos] <= '9'); template_pos++)
298 limit = limit * 10 + fmtTemplate[template_pos] - '0';
304 ** Length
306 switch (fmtTemplate[template_pos])
308 #if USE_QUADFMT
309 case 'L':
310 datasize = 8;
311 template_pos++;
312 break;
313 #endif /* USE_QUADFMT */
315 case 'l':
316 template_pos++;
317 #if USE_QUADFMT
318 if (fmtTemplate[template_pos] == 'l')
320 datasize = 8;
321 template_pos++;
323 else
324 #endif /* USE_QUADFMT */
325 datasize = 4;
326 break;
328 default:
329 datasize = 2;
330 break;
334 ** Print it according to the given type info.
336 switch (fmtTemplate[template_pos])
338 case 'b': /* BSTR, see autodocs */
340 ** Important parameters:
341 ** arg_pos, left, buflen, limit
343 if (!scanning)
345 BSTR s = (BSTR)*(UBYTE **)(((IPTR)stream)+indices[arg_pos-1]);
347 buffer = AROS_BSTR_ADDR(s);
348 buflen = AROS_BSTR_strlen(s);
350 #if !USE_GLOBALLIMIT
351 if (buflen > limit)
352 buflen = limit;
353 #endif /* !USE_GLOBALLIMIT */
355 else
356 indices[arg_pos-1] = sizeof(BPTR);
357 break;
359 case 'd': /* signed decimal */
360 case 'u': /* unsigned decimal */
362 minus = fmtTemplate[template_pos] == 'd';
364 if (!scanning)
366 switch (datasize)
368 #if USE_QUADFMT
369 case 8:
370 tmp = *(UQUAD *)(((IPTR)stream)+indices[arg_pos-1]);
371 //buffer = &buf[16+1];
372 minus *= (FMTLARGESTTYPE) tmp < 0;
373 if (minus)
374 tmp = -tmp;
375 break;
376 #endif /* USE_QUADFMT */
378 case 4:
379 tmp = *(ULONG *)(((IPTR)stream)+indices[arg_pos-1]);
380 //buffer = &buf[8+1];
381 minus *= (LONG) tmp < 0;
382 if (minus)
383 tmp = (ULONG) -tmp;
384 break;
386 default: /* 2 */
387 tmp = *(UWORD *)(((IPTR)stream)+indices[arg_pos-1]);
388 //buffer = &buf[4+1];
389 minus *= (WORD) tmp < 0;
390 if (minus)
391 tmp = (UWORD) -tmp;
392 break;
395 buffer = &buf[BUFFERSIZE];
398 *--buffer = (tmp % 10) + '0';
399 tmp /= 10;
400 buflen++;
402 while (tmp);
404 if (minus)
406 *--buffer = '-';
407 buflen++;
411 else
412 indices[arg_pos-1] = datasize;
413 break;
415 case 'D': /* signed decimal with locale's formatting conventions */
416 case 'U': /* unsigned decimal with locale's formatting conventions */
417 if (!scanning)
419 UBYTE groupsize;
420 ULONG group_index = 0;
422 minus = fmtTemplate[template_pos] == 'D';
424 switch (datasize)
426 #if USE_QUADFMT
427 case 8:
428 tmp = *(UQUAD *)(((IPTR)stream)+indices[arg_pos-1]);
429 minus *= (FMTLARGESTTYPE) tmp < 0;
430 if (minus)
431 tmp = -tmp;
432 break;
433 #endif /* USE_QUADFMT */
435 case 4:
436 tmp = *(ULONG *)(((IPTR)stream)+indices[arg_pos-1]);
437 minus *= (LONG) tmp < 0;
438 if (minus)
439 tmp = (ULONG) -tmp;
440 break;
442 default: /* 2 */
443 tmp = *(UWORD *)(((IPTR)stream)+indices[arg_pos-1]);
444 minus *= (WORD) tmp < 0;
445 if (minus)
446 tmp = (UWORD) -tmp;
447 break;
450 /* BUFFERSIZE should be big enough to format a string
451 ** according to locale's formatting conventions
453 buffer = &buf[BUFFERSIZE];
454 groupsize = locale ? locale->loc_Grouping[group_index] : 255;
458 *--buffer = (tmp % 10) + '0';
459 tmp /= 10;
460 buflen++;
462 groupsize--;
464 if (groupsize == 0 && tmp != 0)
467 ** Write the separator
470 *--buffer = locale->loc_GroupSeparator[group_index];
472 groupsize = locale->loc_Grouping[group_index+1];
474 if (groupsize == 0)
477 ** Supposed to use the previous element
479 groupsize = locale->loc_Grouping[group_index];
481 else
482 group_index++;
484 buflen++;
487 while (tmp);
489 if (minus)
491 *--buffer = '-';
492 buflen++;
495 else
496 indices[arg_pos-1] = datasize;
497 break;
499 case 'x': /* upper case hexadecimal string */
500 case 'X': /* lower case hexadecimal string */
501 case 'p': /* lower case pointer string */
502 case 'P': /* upper case pointer string */
504 if (!scanning)
506 const UBYTE *hexa;
508 /* %p is always at least natural pointer size (32bit) */
509 if (datasize < sizeof(void *) && (fmtTemplate[template_pos] == 'p' ||
510 fmtTemplate[template_pos] == 'P'))
512 datasize = sizeof(void *);
515 switch (datasize)
517 #if USE_QUADFMT
518 case 8:
519 tmp = *(UQUAD *)(((IPTR)stream)+indices[arg_pos-1]);
520 //buffer = &buf[16+1];
521 break;
522 #endif /* USE_QUADFMT */
524 case 4:
525 tmp = *(ULONG *)(((IPTR)stream)+indices[arg_pos-1]);
526 //buffer = &buf[8+1];
527 break;
529 default: /* 2 */
530 tmp = *(UWORD *)(((IPTR)stream)+indices[arg_pos-1]);
531 //buffer = &buf[4+1];
532 break;
535 buffer = &buf[BUFFERSIZE];
537 /* NOTE: x/X is reverse to printf, coz orig RawDoFmt %lx for uppercase. */
538 hexa = (fmtTemplate[template_pos] == 'X' ||
539 fmtTemplate[template_pos] == 'p') ? hexarray : HEXarray;
542 *--buffer = hexa[tmp&0x0f];
543 tmp >>= 4;
544 buflen++;
546 while (tmp);
548 else
549 indices[arg_pos-1] = datasize;
550 break;
552 case 's': /* NULL terminated string */
554 if (!scanning)
556 buffer = *(UBYTE **)(((IPTR)stream)+indices[arg_pos-1]);
557 if (!buffer)
559 buffer = "(null)";
560 buflen = 7;
562 else
563 buflen = strlen(buffer);
565 #if !USE_GLOBALLIMIT
566 if (buflen > limit)
567 buflen = limit;
568 #endif /* !USE_GLOBALLIMIT */
570 else
571 indices[arg_pos-1] = sizeof(UBYTE *); /* the pointer has 4 bytes */
573 break;
575 case 'c': /* Character */
576 if (!scanning)
578 switch (datasize)
580 #if USE_QUADFMT
581 case 8:
582 buf[0] = (UBYTE)*(UQUAD *)(((IPTR)stream)+indices[arg_pos-1]);
583 break;
584 #endif /* USE_QUADFMT */
586 case 4:
587 buf[0] = (UBYTE)*(ULONG *)(((IPTR)stream)+indices[arg_pos-1]);
588 break;
590 default: /* 2 */
591 buf[0] = (UBYTE)*(WORD *)(((IPTR)stream)+indices[arg_pos-1]);
592 break;
595 buflen = 1;
597 else
598 indices[arg_pos-1] = datasize;
599 break;
601 default:
602 /* Ignore the faulty '%' */
604 if (!scanning)
606 buf[0] = fmtTemplate[template_pos];
607 width = 1;
608 buflen = 1;
611 arg_pos = --arg_counter;
612 break;
616 if (!scanning)
618 int i;
621 Now everything I need is known:
622 buffer - contains the string to be printed
623 buflen - size of the string
624 fill - the pad character
625 left - is 1 if the string should be left aligned
626 width - is the minimal width of the field
627 limit - maximum number of characters to output from a string, default ~0
630 #if USE_GLOBALLIMIT
631 if (buflen > limit)
632 buflen = limit;
633 #endif /* USE_GLOBALLIMIT */
635 /* Print padding if right aligned */
636 if (!left)
637 for (i = buflen; i < width; i++)
638 AROS_UFC3(VOID, putCharFunc->h_Entry,
639 AROS_UFCA(struct Hook *, putCharFunc , A0),
640 AROS_UFCA(struct Locale *, locale , A2),
641 AROS_UFCA(UBYTE, fill , A1)
644 /* Print body up to buflen */
645 for (i = 0; i < buflen; i++)
647 AROS_UFC3(VOID, putCharFunc->h_Entry,
648 AROS_UFCA(struct Hook *, putCharFunc , A0),
649 AROS_UFCA(struct Locale *, locale , A2),
650 AROS_UFCA(UBYTE, *buffer++ , A1)
654 /* Pad right if left aligned */
655 if (left)
656 for (i = buflen; i < width; i++)
657 AROS_UFC3(VOID, putCharFunc->h_Entry,
658 AROS_UFCA(struct Hook *, putCharFunc , A0),
659 AROS_UFCA(struct Locale *, locale , A2),
660 AROS_UFCA(UBYTE, fill , A1)
664 template_pos++;
666 if (arg_pos > max_argpos)
667 max_argpos = arg_pos;
670 state = OUTPUT;
671 break;
675 return (APTR) (((IPTR)stream) + indices[max_argpos]);
677 AROS_LIBFUNC_EXIT
678 } /* FormatString */