2 Copyright © 1995-2011, 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(short)) \
89 res = *(short *)DataStream ; \
90 DataStream = (short *)DataStream + 1; \
92 else if (sizeof(type) <= sizeof(long)) \
94 res = *(long *)DataStream; \
95 DataStream = (long *)DataStream + 1; \
100 /* Fetch the data either from memory or from the va_list, depending
101 on the value of VaListStream. */
102 #define fetch_arg(type) \
103 (is_va_list(VaListStream) ? fetch_va_arg(type) : fetch_mem_arg(type))
106 * Fetch a number from the stream.
108 * size - one of 'h', 'l', 'i'
111 * EXPERIMENTAL: 'i' is used to represent full IPTR value on 64-bit systems
113 #define fetch_number(size, sign) \
115 ? (size == 'i' ? fetch_arg(IPTR) : (size == 'l' ? fetch_arg(ULONG) : fetch_arg(UWORD))) \
116 : (size == 'i' ? fetch_arg(SIPTR) : (size == 'l' ? fetch_arg(LONG) : fetch_arg(WORD))))
118 /* Call the PutCharProc function with the given parameters. */
122 switch ((IPTR)PutChProc) \
124 case (IPTR)RAWFMTFUNC_STRING: \
125 *(PutChData++) = ch; \
127 case (IPTR)RAWFMTFUNC_SERIAL: \
130 case (IPTR)RAWFMTFUNC_COUNT: \
131 (*((ULONG *)PutChData))++; \
134 if (is_va_list(VaListStream)) \
136 APTR (*proc)(APTR, UBYTE) = (APTR)PutChProc;\
137 PutChData = proc((APTR)PutChData, ch); \
141 AROS_UFC2NR(void, PutChProc, \
142 AROS_UFCA(UBYTE, (ch), D0), \
143 AROS_UFCA(APTR , PutChData, A3)); \
149 * DataStream == NULL can't be used to select between new or old style PutChProc() because
150 * RawDoFmt(<string without parameters>, NULL, PutChProc, PutChData); is valid and used by
152 * In order to get around we use specially formed va_list with NULL value.
155 APTR
InternalRawDoFmt(CONST_STRPTR FormatString
, APTR DataStream
, VOID_FUNC PutChProc
,
156 APTR inPutChData
, va_list VaListStream
)
159 /* Frequently, AmigaOS users of RawDoFmt() rely upon the AmigaOS
160 * behaviour that A3 *in this routine* is the pointer to PutChData,
161 * *and* that it can be modified in PutChProc.
163 register volatile UBYTE
*PutChData
asm("%a3");
165 UBYTE
*PutChData
= inPutChData
;
168 /* As long as there is something to format left */
169 while (*FormatString
)
171 /* Check for '%' sign */
172 if (*FormatString
== '%')
175 left - left align flag
177 minus - 1: number is negative
178 minwidth - minimum width
179 maxwidth - maximum width
180 size - one of 'h', 'l', 'i'.
181 width - width of printable string
182 buf - pointer to printable string
193 /* Number of decimal places required to convert a unsigned long to
194 ascii. The formula is: ceil(number_of_bits*log10(2)).
195 Since I can't do this here I use .302 instead of log10(2) and
196 +1 instead of ceil() which most often leads to exactly the
197 same result (and never becomes smaller).
199 Note that when the buffer is large enough for decimal it's
200 large enough for hexadecimal as well. */
202 #define CBUFSIZE (sizeof(IPTR)*8*302/1000+1)
203 /* The buffer for converting long to ascii. */
204 UBYTE cbuf
[CBUFSIZE
];
207 /* Skip over '%' character */
210 /* '-' modifier? (left align) */
211 if (*FormatString
== '-')
212 left
= *FormatString
++;
214 /* '0' modifer? (pad with zeros) */
215 if (*FormatString
== '0')
216 fill
= *FormatString
++;
218 /* Get minimal width */
219 while (*FormatString
>= '0' && *FormatString
<= '9')
221 minwidth
= minwidth
* 10 + (*FormatString
++ - '0');
224 /* Dot following width modifier? */
225 if(*FormatString
== '.')
228 /* Get maximum width */
230 if(*FormatString
>= '0' && *FormatString
<= '9')
234 maxwidth
= maxwidth
*10 + (*FormatString
++ - '0');
235 while (*FormatString
>= '0' && *FormatString
<= '9');
240 switch (*FormatString
)
244 size
= *FormatString
++;
248 /* Switch over possible format characters. Sets minus, width and buf. */
249 switch(*FormatString
)
254 BSTR s
= fetch_arg(BSTR
);
258 buf
= AROS_BSTR_ADDR(s
);
259 width
= AROS_BSTR_strlen(s
);
272 buf
= fetch_arg(UBYTE
*);
280 IPTR number
= 0; int base
;
281 static const char digits
[] = "0123456789ABCDEF";
286 minwidth
= sizeof(APTR
)*2;
291 number
= fetch_number(size
, 1);
298 number
= fetch_number(size
, -1);
299 minus
= (SIPTR
)number
< 0;
301 if (minus
) number
= -number
;
308 number
= fetch_number(size
, 1);
312 buf
= &cbuf
[CBUFSIZE
];
315 *--buf
= digits
[number
% base
];
324 /* single character */
326 /* Some space for the result */
330 *buf
= fetch_number(size
, 1);
334 /* '%' before '\0'? */
337 This is nonsense - but do something useful:
338 Instead of reading over the '\0' reuse the '\0'.
341 /* Get compiler happy */
345 /* Convert '%unknown' to 'unknown'. This includes '%%' to '%'. */
347 buf
= (UBYTE
*)FormatString
;
352 if (width
> maxwidth
) width
= maxwidth
;
354 /* Skip the format character */
358 Now everything I need is known:
359 buf - contains the string to be printed
360 width - the size of the string
361 minus - is 1 if there is a '-' to print
362 fill - is the pad character
363 left - is 1 if the string should be left aligned
364 minwidth - is the minimal width of the field
365 (maxwidth is already part of width)
370 /* Print '-' (if there is one and the pad character is no space) */
371 if (minus
&& fill
!= ' ')
374 /* Pad left if not left aligned */
376 for (i
= width
+ minus
; i
< minwidth
; i
++)
379 /* Print '-' (if there is one and the pad character is a space) */
380 if(minus
&& fill
== ' ')
383 /* Print body upto width */
384 for(i
=0; i
<width
; i
++) {
389 /* Pad right if left aligned */
391 for(i
= width
+ minus
; i
<minwidth
; i
++)
396 /* No '%' sign? Put the formatstring out */
397 PutCh(*FormatString
);
401 /* All done. Put the terminator out. */
404 /* Return the rest of the DataStream or buffer. */
405 return is_va_list(VaListStream
) ? (APTR
)PutChData
: DataStream
;
408 /*****************************************************************************
412 AROS_LH4I(APTR
,RawDoFmt
,
415 AROS_LHA(CONST_STRPTR
, FormatString
, A0
),
416 AROS_LHA(APTR
, DataStream
, A1
),
417 AROS_LHA(VOID_FUNC
, PutChProc
, A2
),
418 AROS_LHA(APTR
, PutChData
, A3
),
421 struct ExecBase
*, SysBase
, 87, Exec
)
424 printf-style formatting function with callback hook.
427 FormatString - Pointer to the format string with any of the following
428 DataStream formatting options allowed:
430 %[leftalign][minwidth.][maxwidth][size][type]
432 leftalign - '-' means align left. Default: align right.
433 minwidth - minimum width of field. Defaults to 0.
434 maxwidth - maximum width of field (for strings only).
435 Defaults to no limit.
437 size - 'l' means LONG. Defaults to WORD, if nothing is specified.
439 type - 'b' BSTR. It will use the internal representation
440 of the BSTR defined by the ABI.
441 'c' single character.
442 'd' signed decimal number.
443 's' C string. NULL terminated.
444 'u' unsigned decimal number.
445 'x' unsigned hexadecimal number.
446 'P' pointer. Size depends on the architecture.
447 'p' The same as 'P', for AmigaOS v4 compatibility.
449 DataStream - Pointer to a zone of memory containing the data. Data has to be
452 PutChProc - Callback function. Called for each character, including
453 the NULL terminator. The fuction is called as follow:
455 AROS_UFC2(void, PutChProc,
456 AROS_UFCA(UBYTE, char, D0),
457 AROS_UFCA(APTR , PutChData, A3));
459 Additionally, PutChProc can be set to one of the following
462 RAWFMTFUNC_STRING - Write output to string buffer pointed
463 to by PutChData which is incremented
465 RAWFMTFUNC_SERIAL - Write output to debug output. PutChData
466 is ignored and not touched.
467 RAWFMTFUNC_COUNT - Count number of characters in the result.
468 PutChData is a pointer to ULONG which
469 is incremented every character. Initial
470 value of the cointer is kept as it is.
472 If you want to be compatible with AmigaOS you
473 should check that exec.library has at least version 45.
475 PutChData - Data propagated to each call of the callback hook.
478 Pointer to the rest of the DataStream.
481 The field size defaults to WORD which may be different from the
482 default integer size of the compiler. If you don't take care about
483 this the result will be messy.
485 There are different solutions for GCC:
486 - Define Datastream between #pragma pack(2) / #pragma pack().
487 - Use __attribute__((packed)) for Datastream.
488 - Only use type of LONG/ULONG for integer variables. Additionally only use
489 %ld/%lu in FormatString.
492 Build a sprintf style function:
494 __stackparm void my_sprintf(UBYTE *buffer, UBYTE *format, ...);
496 static void callback(UBYTE chr __reg(d0), UBYTE **data __reg(a3))
501 void my_sprintf(UBYTE *buffer, UBYTE *format, ...)
503 RawDoFmt(format, &format+1, &callback, &buffer);
506 The above example uses __stackparm attribute in the function
507 prototype in order to make sure that arguments are all passed on
508 the stack on all architectures. The alternative is to use
509 VNewRawDoFmt() function which takes va_list instead of array
513 PutChData cannot be modified from the callback hook on non-m68k
519 In AROS this function supports also 'i' type specifier
520 standing for full IPTR argument. This makes difference on
521 64-bit machines. At the moment this addition is not stable
522 and subject to change. Consider using %P or %p to output
523 full 64-bit pointers.
525 When locale.library starts up this function is replaced
526 with advanced version, supporting extensions supported
527 by FormatString() function.
529 ******************************************************************************/
533 null_va_list(vaListStream
);
535 return InternalRawDoFmt(FormatString
, DataStream
, PutChProc
, PutChData
, vaListStream
);