Fixed binary search: no more infinite loops when vendor is unknown.
[tangerine.git] / workbench / libs / locale / formatstring.c
bloba93249b712a0a5dc9cd3c73ebe1998f256ddd381
1 /*
2 Copyright © 1995-2008, 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(const struct Locale *, locale, A0),
40 AROS_LHA(CONST_STRPTR, fmtTemplate, A1),
41 AROS_LHA(CONST_APTR , dataStream, A2),
42 AROS_LHA(const 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 *****************************************************************************/
65 AROS_LIBFUNC_INIT
67 enum {OUTPUT = 0,
68 FOUND_FORMAT} state;
70 ULONG template_pos;
71 BOOL end;
72 ULONG max_argpos;
73 ULONG arg_counter;
74 ULONG *stream;
75 BOOL scanning;
77 #define INDICES 256
78 UWORD indices[INDICES];
80 if (!fmtTemplate)
81 return (APTR)dataStream;
83 template_pos = 0; /* Current position in the template string */
84 state = OUTPUT; /* current state of parsing */
85 end = FALSE;
86 max_argpos = 0;
87 arg_counter = 0;
88 stream = (ULONG *) dataStream;
89 scanning = TRUE; /* The first time I will go through
90 and determine the width of the data in the dataStream */
92 #ifdef __MORPHOS__
93 memclr(indices, sizeof(indices));
94 #else
95 memset(indices, 0, sizeof(indices));
96 #endif
98 while (!end)
101 ** A format description starts here?
103 if (fmtTemplate[template_pos] == '%')
105 arg_counter++;
106 state = FOUND_FORMAT;
109 switch (state)
111 case OUTPUT:
113 ** Call the hook for this character
115 if (!scanning)
117 AROS_UFC3(VOID, putCharFunc->h_Entry,
118 AROS_UFCA(const struct Hook *, putCharFunc, A0),
119 AROS_UFCA(const struct Locale *, locale, A2),
120 AROS_UFCA(UBYTE, fmtTemplate[template_pos], A1));
124 ** End of template string? -> End of this function.
126 if (fmtTemplate[template_pos] == '\0')
128 if (scanning)
131 ** The scanning phase is over. Next time we do the output.
133 int i, sum;
134 scanning = FALSE;
135 template_pos = 0;
136 arg_counter = 0;
138 ** prepare the indices array
140 sum = indices[0];
141 indices[0] = 0;
143 i = 1;
145 while (i <= max_argpos)
147 int _sum;
148 if (indices[i] != 0)
149 _sum = sum + indices[i];
150 else
151 _sum = sum + 4;
153 indices[i] = sum;
154 sum = _sum;
155 i++;
158 else
161 ** We already went through the output phase. So this is
162 ** the end of it.
164 end = TRUE;
167 else
168 template_pos++;
170 //kprintf("OUTPUT: template_pos: %d\n",template_pos);
172 break;
174 case FOUND_FORMAT:
176 ** The '%' was found in the template string
178 template_pos++;
180 //kprintf("FOUND_FORMAT: template_pos: %d\n",template_pos);
182 ** Does the user want the '%' to be printed?
184 if (fmtTemplate[template_pos] == '%')
186 if (!scanning)
188 AROS_UFC3(VOID, putCharFunc->h_Entry,
189 AROS_UFCA(const struct Hook * , putCharFunc, A0),
190 AROS_UFCA(const struct Locale *, locale, A2),
191 AROS_UFCA(UBYTE, fmtTemplate[template_pos], A1));
193 template_pos++;
194 arg_counter--; //stegerg
196 else
199 ** Now parsing...
200 ** Template format: %[arg_pos$][flags][width][.limit][length]type
202 ** arg_pos specifies the position of the argument in the dataStream
203 ** flags only '-' is allowd
204 ** width
205 ** .limit
206 ** datasize size of the datatype
207 ** type b,d,D,u,U,x,X,s,c
209 ULONG arg_pos = 1;
210 BOOL left = FALSE; // no flag was found
211 UBYTE fill = ' ';
212 ULONG minus;
213 ULONG width = 0;
214 ULONG limit = ~0;
215 ULONG buflen = 0;
216 ULONG datasize;
217 UFMTLARGESTTYPE tmp= 0;
218 #define BUFFERSIZE 128
219 UBYTE buf[BUFFERSIZE];
220 UBYTE *buffer = buf;
223 ** arg_pos
226 //kprintf("next char: %c\n",fmtTemplate[template_pos]);
228 if (fmtTemplate[template_pos] >= '0' &&
229 fmtTemplate[template_pos] <= '9')
231 ULONG old_template_pos = template_pos;
233 for (arg_pos = 0; (fmtTemplate[template_pos] >= '0' &&
234 fmtTemplate[template_pos] <= '9'); template_pos++)
236 arg_pos = arg_pos * 10 + fmtTemplate[template_pos] - '0';
239 if (fmtTemplate[template_pos] == '$')
240 template_pos++;
241 else
243 arg_pos = arg_counter;
244 template_pos = old_template_pos;
247 else
248 arg_pos = arg_counter;
251 ** flags
253 if (fmtTemplate[template_pos] == '-')
255 template_pos++;
256 left = TRUE;
260 ** fill character a '0'?
262 if (fmtTemplate[template_pos] == '0')
264 template_pos++;
265 fill = '0';
269 ** width
271 if (fmtTemplate[template_pos] >= '0' &&
272 fmtTemplate[template_pos] <= '9')
274 for (width = 0; (fmtTemplate[template_pos] >= '0' &&
275 fmtTemplate[template_pos] <= '9'); template_pos++)
277 width = width * 10 + fmtTemplate[template_pos] - '0';
282 ** limit
284 if (fmtTemplate[template_pos] == '.')
286 template_pos++;
288 if (fmtTemplate[template_pos] >= '0' &&
289 fmtTemplate[template_pos] <= '9')
291 for (limit = 0; (fmtTemplate[template_pos] >= '0' &&
292 fmtTemplate[template_pos] <= '9'); template_pos++)
294 limit = limit * 10 + fmtTemplate[template_pos] - '0';
300 ** Length
302 switch (fmtTemplate[template_pos])
304 #if USE_QUADFMT
305 case 'L':
306 datasize = 8;
307 template_pos++;
308 break;
309 #endif /* USE_QUADFMT */
311 case 'l':
312 template_pos++;
313 #if USE_QUADFMT
314 if (fmtTemplate[template_pos] == 'l')
316 datasize = 8;
317 template_pos++;
319 else
320 #endif /* USE_QUADFMT */
321 datasize = 4;
322 break;
324 default:
325 datasize = 2;
326 break;
330 ** Print it according to the given type info.
332 switch (fmtTemplate[template_pos])
334 case 'b': /* BSTR, see autodocs */
336 ** Important parameters:
337 ** arg_pos, left, buflen, limit
339 if (!scanning)
341 BSTR s = (BSTR)*(UBYTE **)(((IPTR)stream)+indices[arg_pos-1]);
343 buffer = AROS_BSTR_ADDR(s);
344 buflen = AROS_BSTR_strlen(s);
346 #if !USE_GLOBALLIMIT
347 if (buflen > limit)
348 buflen = limit;
349 #endif /* !USE_GLOBALLIMIT */
351 else
352 indices[arg_pos-1] = sizeof(BPTR);
353 break;
355 case 'd': /* signed decimal */
356 case 'u': /* unsigned decimal */
358 minus = fmtTemplate[template_pos] == 'd';
360 if (!scanning)
362 switch (datasize)
364 #if USE_QUADFMT
365 case 8:
366 tmp = *(UQUAD *)(((IPTR)stream)+indices[arg_pos-1]);
367 //buffer = &buf[16+1];
368 minus *= (FMTLARGESTTYPE) tmp < 0;
369 if (minus)
370 tmp = -tmp;
371 break;
372 #endif /* USE_QUADFMT */
374 case 4:
375 tmp = *(ULONG *)(((IPTR)stream)+indices[arg_pos-1]);
376 //buffer = &buf[8+1];
377 minus *= (LONG) tmp < 0;
378 if (minus)
379 tmp = (ULONG) -tmp;
380 break;
382 default: /* 2 */
383 tmp = *(UWORD *)(((IPTR)stream)+indices[arg_pos-1]);
384 //buffer = &buf[4+1];
385 minus *= (WORD) tmp < 0;
386 if (minus)
387 tmp = (UWORD) -tmp;
388 break;
391 buffer = &buf[BUFFERSIZE];
394 *--buffer = (tmp % 10) + '0';
395 tmp /= 10;
396 buflen++;
398 while (tmp);
400 if (minus)
402 *--buffer = '-';
403 buflen++;
407 else
408 indices[arg_pos-1] = datasize;
409 break;
411 case 'D': /* signed decimal with locale's formatting conventions */
412 case 'U': /* unsigned decimal with locale's formatting conventions */
413 if (!scanning)
415 UBYTE groupsize;
416 ULONG group_index = 0;
418 minus = fmtTemplate[template_pos] == 'D';
420 switch (datasize)
422 #if USE_QUADFMT
423 case 8:
424 tmp = *(UQUAD *)(((IPTR)stream)+indices[arg_pos-1]);
425 minus *= (FMTLARGESTTYPE) tmp < 0;
426 if (minus)
427 tmp = -tmp;
428 break;
429 #endif /* USE_QUADFMT */
431 case 4:
432 tmp = *(ULONG *)(((IPTR)stream)+indices[arg_pos-1]);
433 minus *= (LONG) tmp < 0;
434 if (minus)
435 tmp = (ULONG) -tmp;
436 break;
438 default: /* 2 */
439 tmp = *(UWORD *)(((IPTR)stream)+indices[arg_pos-1]);
440 minus *= (WORD) tmp < 0;
441 if (minus)
442 tmp = (UWORD) -tmp;
443 break;
446 /* BUFFERSIZE should be big enough to format a string
447 ** according to locale's formatting conventions
449 buffer = &buf[BUFFERSIZE];
450 groupsize = locale ? locale->loc_Grouping[group_index] : 255;
454 *--buffer = (tmp % 10) + '0';
455 tmp /= 10;
456 buflen++;
458 groupsize--;
460 if (groupsize == 0 && tmp != 0)
463 ** Write the separator
466 *--buffer = locale->loc_GroupSeparator[group_index];
468 groupsize = locale->loc_Grouping[group_index+1];
470 if (groupsize == 0)
473 ** Supposed to use the previous element
475 groupsize = locale->loc_Grouping[group_index];
477 else
478 group_index++;
480 buflen++;
483 while (tmp);
485 if (minus)
487 *--buffer = '-';
488 buflen++;
491 else
492 indices[arg_pos-1] = datasize;
493 break;
495 case 'x': /* upper case hexadecimal string */
496 case 'X': /* lower case hexadecimal string */
497 case 'p': /* lower case pointer string */
498 case 'P': /* upper case pointer string */
500 if (!scanning)
502 const UBYTE *hexa;
504 /* %p is always at least natural pointer size (32bit) */
505 if (datasize < sizeof(void *) && (fmtTemplate[template_pos] == 'p' ||
506 fmtTemplate[template_pos] == 'P'))
508 datasize = sizeof(void *);
511 switch (datasize)
513 #if USE_QUADFMT
514 case 8:
515 tmp = *(UQUAD *)(((IPTR)stream)+indices[arg_pos-1]);
516 //buffer = &buf[16+1];
517 break;
518 #endif /* USE_QUADFMT */
520 case 4:
521 tmp = *(ULONG *)(((IPTR)stream)+indices[arg_pos-1]);
522 //buffer = &buf[8+1];
523 break;
525 default: /* 2 */
526 tmp = *(UWORD *)(((IPTR)stream)+indices[arg_pos-1]);
527 //buffer = &buf[4+1];
528 break;
531 buffer = &buf[BUFFERSIZE];
533 /* NOTE: x/X is reverse to printf, coz orig RawDoFmt %lx for uppercase. */
534 hexa = (fmtTemplate[template_pos] == 'X' ||
535 fmtTemplate[template_pos] == 'p') ? hexarray : HEXarray;
538 *--buffer = hexa[tmp&0x0f];
539 tmp >>= 4;
540 buflen++;
542 while (tmp);
544 else
545 indices[arg_pos-1] = datasize;
546 break;
548 case 's': /* NULL terminated string */
550 if (!scanning)
552 buffer = *(UBYTE **)(((IPTR)stream)+indices[arg_pos-1]);
553 if (!buffer)
555 buffer = "(null)";
556 buflen = 7;
558 else
559 buflen = strlen(buffer);
561 #if !USE_GLOBALLIMIT
562 if (buflen > limit)
563 buflen = limit;
564 #endif /* !USE_GLOBALLIMIT */
566 else
567 indices[arg_pos-1] = sizeof(UBYTE *); /* the pointer has 4 bytes */
569 break;
571 case 'c': /* Character */
572 if (!scanning)
574 switch (datasize)
576 #if USE_QUADFMT
577 case 8:
578 buf[0] = (UBYTE)*(UQUAD *)(((IPTR)stream)+indices[arg_pos-1]);
579 break;
580 #endif /* USE_QUADFMT */
582 case 4:
583 buf[0] = (UBYTE)*(ULONG *)(((IPTR)stream)+indices[arg_pos-1]);
584 break;
586 default: /* 2 */
587 buf[0] = (UBYTE)*(WORD *)(((IPTR)stream)+indices[arg_pos-1]);
588 break;
591 buflen = 1;
593 else
594 indices[arg_pos-1] = datasize;
595 break;
597 default:
598 /* Ignore the faulty '%' */
600 if (!scanning)
602 buf[0] = fmtTemplate[template_pos];
603 width = 1;
604 buflen = 1;
607 arg_pos = --arg_counter;
608 break;
612 if (!scanning)
614 int i;
617 Now everything I need is known:
618 buffer - contains the string to be printed
619 buflen - size of the string
620 fill - the pad character
621 left - is 1 if the string should be left aligned
622 width - is the minimal width of the field
623 limit - maximum number of characters to output from a string, default ~0
626 #if USE_GLOBALLIMIT
627 if (buflen > limit)
628 buflen = limit;
629 #endif /* USE_GLOBALLIMIT */
631 /* Print padding if right aligned */
632 if (!left)
633 for (i = buflen; i < width; i++)
634 AROS_UFC3(VOID, putCharFunc->h_Entry,
635 AROS_UFCA(const struct Hook *, putCharFunc , A0),
636 AROS_UFCA(const struct Locale *, locale , A2),
637 AROS_UFCA(UBYTE, fill , A1)
640 /* Print body up to buflen */
641 for (i = 0; i < buflen; i++)
643 AROS_UFC3(VOID, putCharFunc->h_Entry,
644 AROS_UFCA(const struct Hook *, putCharFunc , A0),
645 AROS_UFCA(const struct Locale *, locale , A2),
646 AROS_UFCA(UBYTE, *buffer++ , A1)
650 /* Pad right if left aligned */
651 if (left)
652 for (i = buflen; i < width; i++)
653 AROS_UFC3(VOID, putCharFunc->h_Entry,
654 AROS_UFCA(const struct Hook *, putCharFunc , A0),
655 AROS_UFCA(const struct Locale *, locale , A2),
656 AROS_UFCA(UBYTE, fill , A1)
660 template_pos++;
662 if (arg_pos > max_argpos)
663 max_argpos = arg_pos;
666 state = OUTPUT;
667 break;
671 return (APTR) (((IPTR)stream) + indices[max_argpos]);
673 AROS_LIBFUNC_EXIT
674 } /* FormatString */