2 Copyright © 1995-2014, The AROS Development Team. All rights reserved.
5 Desc: Format a string and emit it.
10 #include <aros/libcall.h>
11 #include <aros/asmcall.h>
12 #include <exec/rawfmt.h>
13 #include <proto/exec.h>
18 #include "exec_intern.h"
19 #include "exec_util.h"
23 #define is_va_list(ap) ap.__ap
24 #define null_va_list(ap) va_list ap = {NULL}
25 #define VA_NULL {NULL}
29 #define is_va_list(ap) ap
30 #define null_va_list(ap) void *ap = NULL
34 /* Fetch the data from a va_list.
36 Variables are allocated in the va_list using the default argument
37 promotion rule of the C standard, which states that:
39 "types char and short int are promoted to int, and float is promoted
40 to double" (http://www.eskimo.com/~scs/C-faq/q15.2.html)
42 That rule directly translates into relations on types sizes, rather
43 than the types themselves, since sizeof(char) and sizeof(short) is always
44 less than or equal to sizeof(int), and sizeof(float) is always less than
45 or equal to sizeof(double). In addition, we also handle the case of
46 sizeof(long), whilst the sizeof(double) case is not handled for two reasons:
48 1) RawDoFmt() doesn't handle floating point values.
49 2) Given (1), sizeof(type) > sizeof(long) would hold true if
50 and only if type were a 64 bit pointer and long's and pointers
51 had different sizes (quite unusual). */
53 #define fetch_va_arg(type) \
57 if (sizeof(type) <= sizeof(int)) \
58 res = (type)(IPTR)va_arg(VaListStream, int); \
60 if (sizeof(type) == sizeof(long)) \
61 res = (type)(IPTR)va_arg(VaListStream, long); \
63 res = (type)(IPTR)va_arg(VaListStream, void *); \
68 /* Fetch an argument from memory.
70 We can be sure data is always aligned to a WORD boundary,
71 which is what we need.
73 However, on some architectures some kind of data needs to
74 have a certain alignment greater than 2 bytes, and this
75 code will miserably fail if DataStream is not properly
76 aligned; in such cases data should be fetched in pieces,
77 taking into account the endianess of the machine.
79 Currently it is assumed that ULONG values are stored
80 in an array of IPTRs. This results in good portability for
81 both 32- and 64-bit systems. */
83 #define fetch_mem_arg(type) \
87 if (sizeof(type) <= sizeof(UWORD)) \
89 res = *(UWORD *)DataStream ; \
90 DataStream = (UWORD *)DataStream + 1; \
92 else if (sizeof(type) <= sizeof(ULONG)) \
94 res = *(ULONG *)DataStream; \
95 DataStream = (ULONG *)DataStream + 1; \
97 else if (sizeof(type) <= sizeof(IPTR)) \
99 res = *(IPTR *)DataStream; \
100 DataStream = (IPTR *)DataStream + 1; \
105 /* Fetch the data either from memory or from the va_list, depending
106 on the value of VaListStream. */
107 #define fetch_arg(type) \
108 (is_va_list(VaListStream) ? fetch_va_arg(type) : fetch_mem_arg(type))
111 * Fetch a number from the stream.
113 * size - one of 'h', 'l', 'i'
116 * EXPERIMENTAL: 'i' is used to represent full IPTR value on 64-bit systems
118 #define fetch_number(size, sign) \
120 ? (size == 'i' ? fetch_arg(IPTR) : (size == 'l' ? fetch_arg(ULONG) : fetch_arg(UWORD))) \
121 : (size == 'i' ? fetch_arg(SIPTR) : (size == 'l' ? fetch_arg(LONG) : fetch_arg(WORD))))
123 /* Call the PutCharProc function with the given parameters. */
127 switch ((IPTR)PutChProc) \
129 case (IPTR)RAWFMTFUNC_STRING: \
130 *(PutChData++) = ch; \
132 case (IPTR)RAWFMTFUNC_SERIAL: \
135 case (IPTR)RAWFMTFUNC_COUNT: \
136 (*((ULONG *)PutChData))++; \
139 if (is_va_list(VaListStream)) \
141 APTR (*proc)(APTR, UBYTE) = (APTR)PutChProc;\
142 PutChData = proc((APTR)PutChData, ch); \
146 AROS_UFC2NR(void, PutChProc, \
147 AROS_UFCA(UBYTE, (ch), D0), \
148 AROS_UFCA(APTR , PutChData, A3)); \
154 * DataStream == NULL can't be used to select between new or old style PutChProc() because
155 * RawDoFmt(<string without parameters>, NULL, PutChProc, PutChData); is valid and used by
157 * In order to get around we use specially formed va_list with NULL value.
160 APTR
InternalRawDoFmt(CONST_STRPTR FormatString
, APTR DataStream
, VOID_FUNC PutChProc
,
161 APTR inPutChData
, va_list VaListStream
)
164 /* Frequently, AmigaOS users of RawDoFmt() rely upon the AmigaOS
165 * behaviour that A3 *in this routine* is the pointer to PutChData,
166 * *and* that it can be modified in PutChProc.
168 register volatile UBYTE
*PutChData
asm("%a3");
170 UBYTE
*PutChData
= inPutChData
;
173 /* As long as there is something to format left */
174 while (*FormatString
)
176 /* Check for '%' sign */
177 if (*FormatString
== '%')
180 left - left align flag
182 minus - 1: number is negative
183 minwidth - minimum width
184 maxwidth - maximum width
185 size - one of 'h', 'l', 'i'.
186 width - width of printable string
187 buf - pointer to printable string
198 /* Number of decimal places required to convert a unsigned long to
199 ascii. The formula is: ceil(number_of_bits*log10(2)).
200 Since I can't do this here I use .302 instead of log10(2) and
201 +1 instead of ceil() which most often leads to exactly the
202 same result (and never becomes smaller).
204 Note that when the buffer is large enough for decimal it's
205 large enough for hexadecimal as well. */
207 #define CBUFSIZE (sizeof(IPTR)*8*302/1000+1)
208 /* The buffer for converting long to ascii. */
209 UBYTE cbuf
[CBUFSIZE
];
212 /* Skip over '%' character */
215 /* '-' modifier? (left align) */
216 if (*FormatString
== '-')
217 left
= *FormatString
++;
219 /* '0' modifer? (pad with zeros) */
220 if (*FormatString
== '0')
221 fill
= *FormatString
++;
223 /* Get minimal width */
224 while (*FormatString
>= '0' && *FormatString
<= '9')
226 minwidth
= minwidth
* 10 + (*FormatString
++ - '0');
229 /* Dot following width modifier? */
230 if(*FormatString
== '.')
233 /* Get maximum width */
235 if(*FormatString
>= '0' && *FormatString
<= '9')
239 maxwidth
= maxwidth
*10 + (*FormatString
++ - '0');
240 while (*FormatString
>= '0' && *FormatString
<= '9');
245 switch (*FormatString
)
249 size
= *FormatString
++;
253 /* Switch over possible format characters. Sets minus, width and buf. */
254 switch(*FormatString
)
259 BSTR s
= fetch_arg(BSTR
);
263 buf
= AROS_BSTR_ADDR(s
);
264 width
= AROS_BSTR_strlen(s
);
277 buf
= fetch_arg(UBYTE
*);
285 IPTR number
= 0; int base
;
286 static const char digits
[] = "0123456789ABCDEF";
291 minwidth
= sizeof(APTR
)*2;
296 number
= fetch_number(size
, 1);
303 number
= fetch_number(size
, -1);
304 minus
= (SIPTR
)number
< 0;
306 if (minus
) number
= -number
;
313 number
= fetch_number(size
, 1);
317 buf
= &cbuf
[CBUFSIZE
];
320 *--buf
= digits
[number
% base
];
329 /* single character */
331 /* Some space for the result */
335 *buf
= fetch_number(size
, 1);
339 /* '%' before '\0'? */
342 This is nonsense - but do something useful:
343 Instead of reading over the '\0' reuse the '\0'.
346 /* Get compiler happy */
350 /* Convert '%unknown' to 'unknown'. This includes '%%' to '%'. */
352 buf
= (UBYTE
*)FormatString
;
357 if (width
> maxwidth
) width
= maxwidth
;
359 /* Skip the format character */
363 Now everything I need is known:
364 buf - contains the string to be printed
365 width - the size of the string
366 minus - is 1 if there is a '-' to print
367 fill - is the pad character
368 left - is 1 if the string should be left aligned
369 minwidth - is the minimal width of the field
370 (maxwidth is already part of width)
375 /* Print '-' (if there is one and the pad character is no space) */
376 if (minus
&& fill
!= ' ')
379 /* Pad left if not left aligned */
381 for (i
= width
+ minus
; i
< minwidth
; i
++)
384 /* Print '-' (if there is one and the pad character is a space) */
385 if(minus
&& fill
== ' ')
388 /* Print body upto width */
389 for(i
=0; i
<width
; i
++) {
394 /* Pad right if left aligned */
396 for(i
= width
+ minus
; i
<minwidth
; i
++)
401 /* No '%' sign? Put the formatstring out */
402 PutCh(*FormatString
);
406 /* All done. Put the terminator out. */
409 /* Return the rest of the DataStream or buffer. */
410 return is_va_list(VaListStream
) ? (APTR
)PutChData
: DataStream
;
413 /*****************************************************************************
417 AROS_LH4I(RAWARG
,RawDoFmt
,
420 AROS_LHA(CONST_STRPTR
, FormatString
, A0
),
421 AROS_LHA(RAWARG
, DataStream
, A1
),
422 AROS_LHA(VOID_FUNC
, PutChProc
, A2
),
423 AROS_LHA(APTR
, PutChData
, A3
),
426 struct ExecBase
*, SysBase
, 87, Exec
)
429 printf-style formatting function with callback hook.
432 FormatString - Pointer to the format string with any of the following
433 DataStream formatting options allowed:
435 %[leftalign][minwidth.][maxwidth][size][type]
437 leftalign - '-' means align left. Default: align right.
438 minwidth - minimum width of field. Defaults to 0.
439 maxwidth - maximum width of field (for strings only).
440 Defaults to no limit.
442 size - 'l' means LONG. 'll' or 'L' means QUAD
443 (AROS extension). Defaults to WORD, if
444 nothing is specified.
446 type - 'b' BSTR. It will use the internal representation
447 of the BSTR defined by the ABI.
448 'c' single character.
449 'd' signed decimal number.
450 's' C string. NULL terminated.
451 'u' unsigned decimal number.
452 'x' unsigned hexadecimal number.
453 'P' pointer. Size depends on the architecture.
454 'p' The same as 'P', for AmigaOS v4 compatibility.
456 DataStream - Pointer to a zone of memory containing the data. Data has to be
459 PutChProc - Callback function. Called for each character, including
460 the NULL terminator. The fuction is called as follows:
462 AROS_UFC2(void, PutChProc,
463 AROS_UFCA(UBYTE, char, D0),
464 AROS_UFCA(APTR , PutChData, A3));
466 Additionally, PutChProc can be set to one of the following
469 RAWFMTFUNC_STRING - Write output to string buffer pointed
470 to by PutChData which is incremented
472 RAWFMTFUNC_SERIAL - Write output to debug output. PutChData
473 is ignored and not touched.
474 RAWFMTFUNC_COUNT - Count number of characters in the result.
475 PutChData is a pointer to ULONG which
476 is incremented every character. Initial
477 value of the counter is kept as it is.
479 If you want to be compatible with AmigaOS you
480 should check that exec.library has at least version 45.
482 PutChData - Data propagated to each call of the callback hook.
485 Pointer to the rest of the DataStream.
488 The field size defaults to WORD which may be different from the
489 default integer size of the compiler. If you don't take care about
490 this the result will be messy.
492 There are different solutions for GCC:
493 - Define Datastream between #pragma pack(2) / #pragma pack().
494 - Use __attribute__((packed)) for Datastream.
495 - Only use type of LONG/ULONG for integer variables. Additionally only use
496 %ld/%lu in FormatString.
499 Build a sprintf style function:
501 void my_sprintf(UBYTE *buffer, UBYTE *format, ...);
503 static void callback(UBYTE chr __reg(d0), UBYTE **data __reg(a3))
508 void my_sprintf(UBYTE *buffer, UBYTE *format, ...)
510 AROS_SLOWSTACKFORMAT_PRE(format)
511 RawDoFmt(format, AROS_SLOWSTACKFORMAT_ARG(format), &callback, &buffer);
512 AROS_SLOWSTACKFORMAT_POST(format)
515 The above example uses AROS_SLOWSTACKFORMAT_* macros in the function
516 in order to make sure that arguments are all passed on
517 the stack on all architectures. The alternative is to use
518 VNewRawDoFmt() function which takes va_list instead of array
522 PutChData cannot be modified from the callback hook on non-m68k
528 In AROS this function supports also 'i' type specifier
529 standing for full IPTR argument. This makes difference on
530 64-bit machines. At the moment this addition is not stable
531 and subject to change. Consider using %P or %p to output
532 full 64-bit pointers.
534 When locale.library starts up this function is replaced
535 with advanced version, supporting extensions supported
536 by FormatString() function.
538 ******************************************************************************/
542 null_va_list(vaListStream
);
544 return InternalRawDoFmt(FormatString
, (APTR
)DataStream
, PutChProc
, PutChData
, vaListStream
);