2 Copyright © 1995-2008, The AROS Development Team. All rights reserved.
5 Desc: Format a string and emit it.
9 #include <aros/libcall.h>
10 #include <aros/asmcall.h>
11 #include <exec/rawfmt.h>
12 #include <proto/exec.h>
17 #include "exec_util.h"
19 /* Fetch the data from a va_list.
21 Variables are allocated in the va_list using the default argument
22 promotion rule of the C standard, which states that:
24 "types char and short int are promoted to int, and float is promoted
25 to double" (http://www.eskimo.com/~scs/C-faq/q15.2.html)
27 That rule directly translates into relations on types sizes, rather
28 than the types themselves, since sizeof(char) and sizeof(short) is always
29 less than or equal to sizeof(int), and sizeof(float) is always less than
30 or equal to sizeof(double). In addition, we also handle the case of
31 sizeof(long), whilst the sizeof(double) case is not handled for two reasons:
33 1) RawDoFmt() doesn't handle floating point values.
34 2) Given (1), sizeof(type) > sizeof(long) would hold true if
35 and only if type were a 64 bit pointer and long's and pointers
36 had different sizes (quite unusual). */
38 #define fetch_va_arg(type) \
42 if (sizeof(type) <= sizeof(int)) \
43 res = (type)va_arg(VaListStream, int); \
45 if (sizeof(type) == sizeof(long)) \
46 res = (type)va_arg(VaListStream, long); \
48 res = (type)(IPTR)va_arg(VaListStream, void *); \
53 /* Fetch an argument from memory.
55 We can be sure data is always aligned to a WORD boundary,
56 which is what we need.
58 However, on some architectures some kind of data needs to
59 have a certain alignment greater than 2 bytes, and this
60 code will miserably fail if DataStream is not properly
61 aligned; in such cases data should be fetched in pieces,
62 taking into account the endianess of the machine.
64 Another possibility would be to assume that DataStream
65 is a pointer to an array of objects on systems other
66 than 68k, and arrays are always properly aligned. */
69 #define fetch_mem_arg(type) \
71 type __retval = *(type *)DataStream; \
72 DataStream = (type *)DataStream + 1; \
76 /* Fetch the data either from memory or from the va_list, depending
77 on the value of VaListStream. */
78 #define fetch_arg(type) \
79 (DataStream ? fetch_mem_arg(type) : fetch_va_arg(type))
81 /* Fetch a number from the stream.
83 size - one of 'w', 'l', 'i'
85 #define fetch_number(size, sign) \
87 ? (size == 'w' ? fetch_arg(UWORD) : fetch_arg(IPTR)) \
88 : (size == 'w' ? fetch_arg(WORD) : fetch_arg(SIPTR)))
91 /* Call the PutCharProc funtion with the given parameters. */
95 switch ((IPTR)PutChProc) \
97 case (IPTR)RAWFMTFUNC_STRING: \
100 case (IPTR)RAWFMTFUNC_SERIAL: \
103 case (IPTR)RAWFMTFUNC_COUNT: \
104 (*((ULONG *)PutChData))++; \
107 AROS_UFC2(void, PutChProc, \
108 AROS_UFCA(UBYTE, (ch), D0), \
109 AROS_UFCA(APTR , PutChData, A3)); \
113 APTR
InternalRawDoFmt(CONST_STRPTR FormatString
, APTR DataStream
, VOID_FUNC PutChProc
,
114 APTR PutChData
, va_list VaListStream
)
117 register APTR __PutChData
asm("a3") = PutChData
;
118 # define PutChData __PutChData
121 UBYTE
*UPutChData
= PutChData
;
123 /* As long as there is something to format left */
124 while (*FormatString
)
126 /* Check for '%' sign */
127 if (*FormatString
== '%')
130 left - left align flag
132 minus - 1: number is negative
133 minwidth - minimum width
134 maxwidth - maximum width
135 size - one of 'w', 'l', 'i'.
136 width - width of printable string
137 buf - pointer to printable string
148 /* Number of decimal places required to convert a unsigned long to
149 ascii. The formula is: ceil(number_of_bits*log10(2)).
150 Since I can't do this here I use .302 instead of log10(2) and
151 +1 instead of ceil() which most often leads to exactly the
152 same result (and never becomes smaller).
154 Note that when the buffer is large enough for decimal it's
155 large enough for hexdecimal as well. */
157 #define CBUFSIZE (sizeof(ULONG)*8*302/1000+1)
158 /* The buffer for converting long to ascii. */
159 UBYTE cbuf
[CBUFSIZE
];
162 /* Skip over '%' character */
165 /* '-' modifier? (left align) */
166 if (*FormatString
== '-')
167 left
= *FormatString
++;
169 /* '0' modifer? (pad with zeros) */
170 if (*FormatString
== '0')
171 fill
= *FormatString
++;
173 /* Get minimal width */
174 while (*FormatString
>= '0' && *FormatString
<= '9')
176 minwidth
= minwidth
* 10 + (*FormatString
++ - '0');
179 /* Dot following width modifier? */
180 if(*FormatString
== '.')
183 /* Get maximum width */
185 if(*FormatString
>= '0' && *FormatString
<= '9')
189 maxwidth
= maxwidth
*10 + (*FormatString
++ - '0');
190 while (*FormatString
>= '0' && *FormatString
<= '9');
195 switch (*FormatString
)
199 size
= *FormatString
++;
203 /* Switch over possible format characters. Sets minus, width and buf. */
204 switch(*FormatString
)
209 BSTR s
= fetch_arg(BSTR
);
211 buf
= AROS_BSTR_ADDR(s
);
212 width
= AROS_BSTR_strlen(s
);
219 buf
= fetch_arg(UBYTE
*);
228 IPTR number
= 0; int base
;
229 static const char digits
[] = "0123456789ABCDEF";
233 number
= fetch_number(size
, 1);
239 number
= fetch_number(size
, -1);
240 minus
= (SIPTR
)number
< 0;
242 if (minus
) number
= -number
;
248 number
= fetch_number(size
, 1);
252 buf
= &cbuf
[CBUFSIZE
];
255 *--buf
= digits
[number
% base
];
264 /* single character */
266 /* Some space for the result */
270 *buf
= fetch_number(size
, 1);
274 /* '%' before '\0'? */
277 This is nonsense - but do something useful:
278 Instead of reading over the '\0' reuse the '\0'.
281 /* Get compiler happy */
285 /* Convert '%unknown' to 'unknown'. This includes '%%' to '%'. */
287 buf
= (UBYTE
*)FormatString
;
292 if (width
> maxwidth
) width
= maxwidth
;
294 /* Skip the format character */
298 Now everything I need is known:
299 buf - contains the string to be printed
300 width - the size of the string
301 minus - is 1 if there is a '-' to print
302 fill - is the pad character
303 left - is 1 if the string should be left aligned
304 minwidth - is the minimal width of the field
305 (maxwidth is already part of width)
310 /* Print '-' (if there is one and the pad character is no space) */
311 if (minus
&& fill
!= ' ')
314 /* Pad left if not left aligned */
316 for (i
= width
+ minus
; i
< minwidth
; i
++)
319 /* Print '-' (if there is one and the pad character is a space) */
320 if(minus
&& fill
== ' ')
323 /* Print body upto width */
324 for(i
=0; i
<width
; i
++)
327 /* Pad right if left aligned */
329 for(i
= width
+ minus
; i
<minwidth
; i
++)
334 /* No '%' sign? Put the formatstring out */
335 PutCh(*FormatString
++);
338 /* All done. Put the terminator out. */
341 /* Return the rest of the DataStream or buffer. */
342 return VaListStream
? UPutChData
: DataStream
;
346 /*****************************************************************************
350 AROS_LH4I(APTR
,RawDoFmt
,
353 AROS_LHA(CONST_STRPTR
, FormatString
, A0
),
354 AROS_LHA(APTR
, DataStream
, A1
),
355 AROS_LHA(VOID_FUNC
, PutChProc
, A2
),
356 AROS_LHA(APTR
, PutChData
, A3
),
359 struct ExecBase
*, SysBase
, 87, Exec
)
362 printf-style formatting function with callback hook.
365 FormatString - Pointer to the format string with any of the following
366 DataStream formatting options allowed:
368 %[leftalign][minwidth.][maxwidth][size][type]
370 leftalign - '-' means align left. Default: align right.
371 minwidth - minimum width of field. Defaults to 0.
372 maxwidth - maximum width of field (for strings only).
373 Defaults to no limit.
375 size - 'l' means IPTR (LONG on 32-bit architectures and QUAD on 64-bit).
376 Defaults to WORD, if nothing is specified.
378 type - 'b' BSTR. It will use the internal representation
379 of the BSTR defined by the ABI.
380 'c' single character.
381 'd' signed decimal number.
382 's' C string. NULL terminated.
383 'u' unsigned decimal number.
384 'x' unsigned hexdecimal number.
386 DataStream - Pointer to a zone of memory containing the data. Data has to be
389 PutChProc - Callback function. In caseCalled for each character, including
390 the NULL terminator. The fuction is called as follow:
392 AROS_UFC2(void, PutChProc,
393 AROS_UFCA(UBYTE, char, D0),
394 AROS_UFCA(APTR , PutChData, A3));
396 The argument may be NULL. This makes RawDoFmt() use an internal
397 function. If you want to be compatible with AmigaOS you
398 should check that exec.library has at least version 45.
400 PutChData - Data propagated to each call of the callback hook.
403 Pointer to the rest of the DataStream.
406 The field size defaults to WORD which may be different from the
407 default integer size of the compiler. If you don't take care about
408 this the result will be messy.
410 There are different solutions for GCC:
411 - Define Datastream between #pragma pack(2) / #pragma pack().
412 - Use __attribute__((packed)) for Datastream.
413 - Only use type of LONG/ULONG for integer variables. Additionally only use
414 %ld/%lu in FormatString.
417 Build a sprintf style function:
419 __stackparm void my_sprintf(UBYTE *buffer, UBYTE *format, ...);
421 static void callback(UBYTE chr __reg(d0), UBYTE **data __reg(a3))
426 void my_sprintf(UBYTE *buffer, UBYTE *format, ...)
428 RawDoFmt(format, &format+1, &callback, &buffer);
431 The above example uses __stackparm attribute in the function
432 prototype in order to make sure that arguments are all passed on
433 the stack on all architectures. The alternative is to use
434 VNewRawDoFmt() function which takes va_list instead of array
438 PutChData cannot be modified from the callback hook on non-m68k
445 ******************************************************************************/
449 return InternalRawDoFmt(FormatString
, DataStream
, PutChProc
, PutChData
, NULL
);