2 Copyright © 1995-2011, The AROS Development Team. All rights reserved.
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 <aros/debug.h>
22 typedef QUAD FMTLARGESTTYPE
;
23 typedef UQUAD UFMTLARGESTTYPE
;
24 #else /* USE_QUADFMT */
25 typedef LONG FMTLARGESTTYPE
;
26 typedef ULONG UFMTLARGESTTYPE
;
27 #endif /* USE_QUADFMT */
29 static const UBYTE hexarray
[] = "0123456789abcdef";
30 static const UBYTE HEXarray
[] = "0123456789ABCDEF";
32 static inline APTR
stream_addr(APTR
*args
, ULONG len
)
36 /* LONG data are actually IPTR-aligned */
37 *args
+= (len
> sizeof(UWORD
)) ? sizeof(IPTR
) : len
;
41 /* The stuff below is based on old gcc includes (where you can actually read how
42 varargs work) and ABI documentation.
43 It works in the same way as traditional va_arg() except it fetches
44 pointers to arguments, not arguments themselves (since on 32-bit machines
45 argument values may be larger than pointers). */
49 static inline APTR
va_addr(va_list args
, ULONG len
)
53 if (len
== sizeof(UQUAD
))
55 /* On PPC UQUAD is aligned. and occupies 2 registers (plus may waste one more for alignment) */
58 ULONG
*regsave
= (ULONG
*)args
->reg_save_area
;
60 args
->gpr
+= args
->gpr
& 1;
61 ret
= ®save
[args
->gpr
];
67 ret
= (APTR
)(((IPTR
)(args
->overflow_arg_area
+ 7)) & ~7);
68 args
->overflow_arg_area
= ret
+ sizeof(UQUAD
);
75 ULONG
*regsave
= (ULONG
*)args
->reg_save_area
;
77 ret
= ®save
[args
->gpr
++];
81 ret
= args
->overflow_arg_area
;
82 args
->overflow_arg_area
+= sizeof(ULONG
);
88 #elif defined(__x86_64__)
90 static inline APTR
va_addr(va_list args
, ULONG len
)
94 if (args
->gp_offset
< 48)
96 ret
= args
->reg_save_area
+ args
->gp_offset
;
97 args
->gp_offset
+= sizeof(IPTR
);
101 ret
= args
->overflow_arg_area
;
102 args
->overflow_arg_area
+= sizeof(IPTR
);
107 #elif defined(__arm__)
109 #define va_addr(args, len) stream_addr(&args.__ap, len)
110 #define is_va_list(ap) ap.__ap
111 #define null_va_list(ap) va_list ap = {NULL}
115 /* This works for i386 and m68k */
116 static inline APTR
_va_addr(va_list *args
)
120 *args
+= sizeof(IPTR
);
124 #define va_addr(args, len) _va_addr(&args)
130 #define is_va_list(ap) ap
131 #define null_va_list(ap) void *ap = NULL
135 APTR
InternalFormatString(const struct Locale
* locale
,
136 CONST_STRPTR fmtTemplate
, CONST_APTR dataStream
,
137 const struct Hook
* putCharFunc
, va_list VaListStream
)
151 IPTR indices
[INDICES
];
154 return (APTR
) dataStream
;
156 template_pos
= 0; /* Current position in the template string */
157 state
= OUTPUT
; /* current state of parsing */
161 scanning
= TRUE
; /* The first time I will go through
162 and determine the width of the data in the dataStream */
164 memset(indices
, sizeof(APTR
), sizeof(indices
));
169 ** A format description starts here?
171 if (fmtTemplate
[template_pos
] == '%')
174 state
= FOUND_FORMAT
;
181 ** Call the hook for this character
185 AROS_UFC3(VOID
, putCharFunc
->h_Entry
,
186 AROS_UFCA(const struct Hook
*, putCharFunc
, A0
),
187 AROS_UFCA(const struct Locale
*, locale
, A2
),
188 AROS_UFCA(UBYTE
, fmtTemplate
[template_pos
], A1
));
192 ** End of template string? -> End of this function.
194 if (fmtTemplate
[template_pos
] == '\0')
199 ** The scanning phase is over. Next time we do the output.
207 ** prepare the indices array
209 if (is_va_list(VaListStream
))
211 for (i
= 0; i
<= max_argpos
; i
++)
213 (IPTR
) va_addr(VaListStream
, indices
[i
]);
217 for (i
= 0; i
<= max_argpos
; i
++)
219 (IPTR
) stream_addr((APTR
*) & dataStream
,
227 ** We already went through the output phase. So this is
236 //kprintf("OUTPUT: template_pos: %d\n",template_pos);
242 ** The '%' was found in the template string
246 //kprintf("FOUND_FORMAT: template_pos: %d\n",template_pos);
248 ** Does the user want the '%' to be printed?
250 if (fmtTemplate
[template_pos
] == '%')
254 AROS_UFC3(VOID
, putCharFunc
->h_Entry
,
255 AROS_UFCA(const struct Hook
*, putCharFunc
, A0
),
256 AROS_UFCA(const struct Locale
*, locale
, A2
),
257 AROS_UFCA(UBYTE
, fmtTemplate
[template_pos
], A1
));
260 arg_counter
--; //stegerg
266 ** Template format: %[arg_pos$][flags][width][.limit][length]type
268 ** arg_pos specifies the position of the argument in the dataStream
269 ** flags only '-' is allowed
272 ** datasize size of the datatype
273 ** type b,d,D,u,U,x,X,s,c
276 BOOL left
= FALSE
; // no flag was found
283 UFMTLARGESTTYPE tmp
= 0;
284 #define BUFFERSIZE 128
285 UBYTE buf
[BUFFERSIZE
];
292 //kprintf("next char: %c\n",fmtTemplate[template_pos]);
294 if (fmtTemplate
[template_pos
] >= '0' &&
295 fmtTemplate
[template_pos
] <= '9')
297 ULONG old_template_pos
= template_pos
;
299 for (arg_pos
= 0; (fmtTemplate
[template_pos
] >= '0' &&
300 fmtTemplate
[template_pos
] <= '9');
304 arg_pos
* 10 + fmtTemplate
[template_pos
] - '0';
307 if (fmtTemplate
[template_pos
] == '$')
311 arg_pos
= arg_counter
;
312 template_pos
= old_template_pos
;
316 arg_pos
= arg_counter
;
321 if (fmtTemplate
[template_pos
] == '-')
328 ** fill character a '0'?
330 if (fmtTemplate
[template_pos
] == '0')
339 if (fmtTemplate
[template_pos
] >= '0' &&
340 fmtTemplate
[template_pos
] <= '9')
342 for (width
= 0; (fmtTemplate
[template_pos
] >= '0' &&
343 fmtTemplate
[template_pos
] <= '9');
347 width
* 10 + fmtTemplate
[template_pos
] - '0';
354 if (fmtTemplate
[template_pos
] == '.')
358 if (fmtTemplate
[template_pos
] >= '0' &&
359 fmtTemplate
[template_pos
] <= '9')
361 for (limit
= 0; (fmtTemplate
[template_pos
] >= '0' &&
362 fmtTemplate
[template_pos
] <= '9');
366 limit
* 10 + fmtTemplate
[template_pos
] -
375 switch (fmtTemplate
[template_pos
])
379 datasize
= sizeof(UQUAD
);
382 #endif /* USE_QUADFMT */
387 if (fmtTemplate
[template_pos
] == 'l')
389 datasize
= sizeof(UQUAD
);
393 #endif /* USE_QUADFMT */
394 datasize
= sizeof(ULONG
);
398 /* For C-style varargs default size is ULONG, single 'l' is effectively ignored */
400 is_va_list(VaListStream
) ? sizeof(ULONG
) :
406 ** Print it according to the given type info.
408 switch (fmtTemplate
[template_pos
])
410 case 'b': /* BSTR, see autodocs */
412 ** Important parameters:
413 ** arg_pos, left, buflen, limit
417 BSTR s
= (BSTR
) * (UBYTE
**) indices
[arg_pos
- 1];
419 if (s
!= (BSTR
) BNULL
)
421 buffer
= AROS_BSTR_ADDR(s
);
422 buflen
= AROS_BSTR_strlen(s
);
433 #endif /* !USE_GLOBALLIMIT */
436 indices
[arg_pos
- 1] = sizeof(BPTR
);
439 case 'd': /* signed decimal */
440 case 'u': /* unsigned decimal */
442 minus
= fmtTemplate
[template_pos
] == 'd';
450 tmp
= *(UQUAD
*) indices
[arg_pos
- 1];
451 //buffer = &buf[16+1];
452 minus
*= (FMTLARGESTTYPE
) tmp
< 0;
456 #endif /* USE_QUADFMT */
459 tmp
= *(ULONG
*) indices
[arg_pos
- 1];
460 //buffer = &buf[8+1];
461 minus
*= (LONG
) tmp
< 0;
467 tmp
= *(UWORD
*) indices
[arg_pos
- 1];
468 //buffer = &buf[4+1];
469 minus
*= (WORD
) tmp
< 0;
475 buffer
= &buf
[BUFFERSIZE
];
478 *--buffer
= (tmp
% 10) + '0';
492 indices
[arg_pos
- 1] = datasize
;
495 case 'D': /* signed decimal with locale's formatting conventions */
496 case 'U': /* unsigned decimal with locale's formatting conventions */
500 ULONG group_index
= 0;
502 minus
= fmtTemplate
[template_pos
] == 'D';
508 tmp
= *(UQUAD
*) indices
[arg_pos
- 1];
509 minus
*= (FMTLARGESTTYPE
) tmp
< 0;
513 #endif /* USE_QUADFMT */
516 tmp
= *(ULONG
*) indices
[arg_pos
- 1];
517 minus
*= (LONG
) tmp
< 0;
523 tmp
= *(UWORD
*) indices
[arg_pos
- 1];
524 minus
*= (WORD
) tmp
< 0;
530 /* BUFFERSIZE should be big enough to format a string
531 ** according to locale's formatting conventions
533 buffer
= &buf
[BUFFERSIZE
];
536 loc_Grouping
[group_index
] : 255;
540 *--buffer
= (tmp
% 10) + '0';
546 if (groupsize
== 0 && tmp
!= 0)
549 ** Write the separator
553 locale
->loc_GroupSeparator
[group_index
];
556 locale
->loc_Grouping
[group_index
+ 1];
561 ** Supposed to use the previous element
564 locale
->loc_Grouping
[group_index
];
581 indices
[arg_pos
- 1] = datasize
;
584 case 'p': /* lower case pointer string */
585 case 'P': /* upper case pointer string */
587 width
= sizeof(APTR
) * 2;
588 /* %p is always at least natural pointer size */
589 if (datasize
< sizeof(APTR
))
590 datasize
= sizeof(APTR
);
591 case 'x': /* upper case hexadecimal string */
592 case 'X': /* lower case hexadecimal string */
602 tmp
= *(UQUAD
*) indices
[arg_pos
- 1];
603 //buffer = &buf[16+1];
605 #endif /* USE_QUADFMT */
608 tmp
= *(ULONG
*) indices
[arg_pos
- 1];
609 //buffer = &buf[8+1];
613 tmp
= *(UWORD
*) indices
[arg_pos
- 1];
614 //buffer = &buf[4+1];
618 buffer
= &buf
[BUFFERSIZE
];
620 /* NOTE: x/X is reverse to printf, coz orig RawDoFmt %lx for uppercase. */
621 hexa
= (fmtTemplate
[template_pos
] == 'X' ||
622 fmtTemplate
[template_pos
] ==
623 'p') ? hexarray
: HEXarray
;
626 *--buffer
= hexa
[tmp
& 0x0f];
633 indices
[arg_pos
- 1] = datasize
;
636 case 's': /* NULL terminated string */
640 buffer
= *(UBYTE
**) indices
[arg_pos
- 1];
643 * RawDoFmt() in original AmigaOS(tm) formats NULL pointers as empty strings,
644 * and not something like "(null)". Some software may rely on this behavior.
645 * %b is handled in similar manner.
649 buflen
= strlen(buffer
);
654 #endif /* !USE_GLOBALLIMIT */
657 indices
[arg_pos
- 1] = sizeof(UBYTE
*); /* the pointer has 4 bytes */
661 case 'c': /* Character */
669 (UBYTE
) * (UQUAD
*) indices
[arg_pos
- 1];
671 #endif /* USE_QUADFMT */
675 (UBYTE
) * (ULONG
*) indices
[arg_pos
- 1];
680 (UBYTE
) * (WORD
*) indices
[arg_pos
- 1];
687 indices
[arg_pos
- 1] = datasize
;
691 /* Ignore the faulty '%' */
695 buf
[0] = fmtTemplate
[template_pos
];
700 arg_pos
= --arg_counter
;
710 Now everything I need is known:
711 buffer - contains the string to be printed
712 buflen - size of the string
713 fill - the pad character
714 left - is 1 if the string should be left aligned
715 width - is the minimal width of the field
716 limit - maximum number of characters to output from a string, default ~0
722 #endif /* USE_GLOBALLIMIT */
724 /* Print padding if right aligned */
726 for (i
= buflen
; i
< width
; i
++)
727 AROS_UFC3(VOID
, putCharFunc
->h_Entry
,
728 AROS_UFCA(const struct Hook
*, putCharFunc
, A0
),
729 AROS_UFCA(const struct Locale
*, locale
, A2
),
730 AROS_UFCA(UBYTE
, fill
, A1
));
732 /* Print body up to buflen */
733 for (i
= 0; i
< buflen
; i
++)
735 AROS_UFC3(VOID
, putCharFunc
->h_Entry
,
736 AROS_UFCA(const struct Hook
*, putCharFunc
, A0
),
737 AROS_UFCA(const struct Locale
*, locale
, A2
),
738 AROS_UFCA(UBYTE
, *buffer
++, A1
));
741 /* Pad right if left aligned */
743 for (i
= buflen
; i
< width
; i
++)
744 AROS_UFC3(VOID
, putCharFunc
->h_Entry
,
745 AROS_UFCA(const struct Hook
*, putCharFunc
, A0
),
746 AROS_UFCA(const struct Locale
*, locale
, A2
),
747 AROS_UFCA(UBYTE
, fill
, A1
));
752 if (arg_pos
> max_argpos
)
753 max_argpos
= arg_pos
;
761 return (APTR
) indices
[max_argpos
];
764 /*****************************************************************************
767 #include <proto/locale.h>
769 AROS_LH4(APTR
, FormatString
,
772 AROS_LHA(const struct Locale
*, locale
, A0
),
773 AROS_LHA(CONST_STRPTR
, fmtTemplate
, A1
),
774 AROS_LHA(CONST_APTR
, dataStream
, A2
),
775 AROS_LHA(const struct Hook
*, putCharFunc
, A3
),
778 struct LocaleBase
*, LocaleBase
, 11, Locale
)
796 *****************************************************************************/
800 null_va_list(vaListStream
);
802 return InternalFormatString(locale
, fmtTemplate
, dataStream
,
803 putCharFunc
, vaListStream
);