2 Copyright © 1995-2001, 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 <proto/exec.h>
16 /* Fetch the data from a va_list.
18 Variables are allocated in the va_list using the default argument
19 promotion rule of the C standard, which states that:
21 "types char and short int are promoted to int, and float is promoted
22 to double" (http://www.eskimo.com/~scs/C-faq/q15.2.html)
24 That rule directly translates into relations on types sizes, rather
25 than the types themselves, since sizeof(char) and sizeof(short) is always
26 less than or equal to sizeof(int), and sizeof(float) is always less than
27 or equal to sizeof(double). In addition, we also handle the case of
28 sizeof(long), whilst the sizeof(double) case is not handled for two reasons:
30 1) RawDoFmt() doesn't handle floating point values.
31 2) Given (1), sizeof(type) > sizeof(long) would hold true if
32 and only if type were a 64 bit pointer and long's and pointers
33 had different sizes (quite unusual). */
35 #define fetch_va_arg(type) \
39 if (sizeof(type) <= sizeof(int)) \
40 res = (type)va_arg(VaListStream, int); \
42 if (sizeof(type) == sizeof(long)) \
43 res = (type)va_arg(VaListStream, long); \
45 res = (type)(IPTR)va_arg(VaListStream, void *); \
50 /* Fetch an argument from memory.
52 We can be sure data is always aligned to a WORD boundary,
53 which is what we need.
55 However, on some architectures some kind of data needs to
56 have a certain alignment greater than 2 bytes, and this
57 code will miserably fail if DataStream is not properly
58 aligned; in such cases data should be fetched in pieces,
59 taking into account the endianess of the machine.
61 Another possibility would be to assume that DataStream
62 is a pointer to an array of objects on systems other
63 than 68k, and arrays are always properly aligned. */
66 #define fetch_mem_arg(type) \
68 type __retval = *(type *)DataStream; \
69 DataStream = (type *)DataStream + 1; \
73 /* Fetch the data either from memory or from the va_list, depending
74 on the value of user_va_list_ptr. */
75 #define fetch_arg(type) \
76 (user_va_list_ptr ? fetch_va_arg(type) : fetch_mem_arg(type))
78 /* Fetch a number from the stream.
80 size - one of 'w', 'l', 'i'
82 #define fetch_number(size, sign) \
84 ? (size == 'w' ? fetch_arg(UWORD) : (size == 'l' ? fetch_arg(ULONG) : fetch_arg(IPTR))) \
85 : (size == 'w' ? fetch_arg(WORD) : (size == 'l' ? fetch_arg(LONG) : fetch_arg(SIPTR))))
88 /* Call the PutCharProc funtion with the given parameters. */
92 if (PutChProc != NULL) \
94 AROS_UFC2(void, PutChProc, \
95 AROS_UFCA(UBYTE, (ch), D0), \
96 AROS_UFCA(APTR , PutChData, A3)); \
100 *UPutChData++ = ch; \
105 /*****************************************************************************
109 AROS_LH4I(APTR
,RawDoFmt
,
112 AROS_LHA(CONST_STRPTR
, FormatString
, A0
),
113 AROS_LHA(APTR
, DataStream
, A1
),
114 AROS_LHA(VOID_FUNC
, PutChProc
, A2
),
115 AROS_LHA(APTR
, PutChData
, A3
),
118 struct ExecBase
*, SysBase
, 87, Exec
)
121 printf-style formatting function with callback hook.
124 FormatString - Pointer to the format string with any of the following
125 DataStream formatting options allowed:
127 %[leftalign][minwidth.][maxwidth][size][type]
129 leftalign - '-' means align left. Default: align right.
130 minwidth - minimum width of field. Defaults to 0.
131 maxwidth - maximum width of field (for strings only).
132 Defaults to no limit.
134 size - 'w' means WORD.
138 defaults to WORD, if nothing is specified.
140 type - 'b' BCPL string. A BPTR to a one byte
141 byte count followed by the characters.
142 'c' single character.
143 'd' signed decimal number.
144 's' C string. NULL terminated.
145 'u' unsigned decimal number.
146 'x' unsigned hexdecimal number.
148 As an AROS extension, the following special options
153 type - 'v' means that the current data in the DataStream is a pointer
154 to a an object of type va_list, as defined in <stdarg.h>.
156 From this point on the data is fetched from the va_list
157 and not from the original DataStream array anymore.
159 If the pointer is NULL, however, then nothing changes
160 and data fetching proceeds as if no %v had been specified
163 - 'V' it's like 'v', but requires an additional parameter which,
164 if non NULL, instructs RawDoFmt to switch to another
165 format string, whose address is the value of the parameter.
167 Look at the EXAMPLE section to see an example about how
170 DataStream - Pointer to a zone of memory containing the data. Data has to be
173 PutChProc - Callback function. In caseCalled for each character, including
174 the NULL terminator. The fuction is called as follow:
176 AROS_UFC2(void, PutChProc,
177 AROS_UFCA(UBYTE, char, D0),
178 AROS_UFCA(APTR , PutChData, A3));
180 PutChData - Data propagated to each call of the callback hook.
183 Pointer to the rest of the DataStream.
185 NOTE: If the format string contains one of the va_list-related options, then
186 the result will be the same pointer to the va_list object
189 The field size defaults to words which may be different from the
190 default integer size of the compiler.
193 Build a sprintf style function:
195 static void callback(UBYTE chr __reg(d0), UBYTE **data __reg(a3))
200 void my_sprintf(UBYTE *buffer, UBYTE *format, ...)
202 RawDoFmt(format, &format+1, &callback, &buffer);
205 The above example makes the assumption that arguments are
206 all passed on the stack, which is true on some architectures
207 but is not on some others. In the general case you should NOT
208 use that approach, you should rather use the %v or %V options
209 and the standard <stdarg.h> facilities, like this:
213 static void callback(UBYTE chr __reg(d0), UBYTE **data __reg(a3))
218 void my_sprintf(UBYTE *buffer, UBYTE *format, ...)
221 va_start(args, format);
223 APTR raw_args[] = { &args, format };
225 RawDoFmt("%V", raw_args, &callback, &buffer);
231 PutChData cannot be modified from the callback hook on non-m68k
238 ******************************************************************************/
243 register APTR __PutChData
asm("a3") = PutChData
;
244 # define PutChData __PutChData
247 UBYTE
*UPutChData
= PutChData
;
248 va_list *user_va_list_ptr
= NULL
;
249 va_list VaListStream
;
251 /* As long as there is something to format left */
252 while (*FormatString
)
254 /* Check for '%' sign */
255 if (*FormatString
== '%')
258 left - left align flag
260 minus - 1: number is negative
261 minwidth - minimum width
262 maxwidth - maximum width
263 size - one of 'w', 'l', 'i'.
264 width - width of printable string
265 buf - pointer to printable string
276 /* Number of decimal places required to convert a unsigned long to
277 ascii. The formula is: ceil(number_of_bits*log10(2)).
278 Since I can't do this here I use .302 instead of log10(2) and
279 +1 instead of ceil() which most often leads to exactly the
280 same result (and never becomes smaller).
282 Note that when the buffer is large enough for decimal it's
283 large enough for hexdecimal as well. */
285 #define CBUFSIZE (sizeof(ULONG)*8*302/1000+1)
286 /* The buffer for converting long to ascii. */
287 UBYTE cbuf
[CBUFSIZE
];
290 /* Skip over '%' character */
293 /* Possibly switch to a va_list type stream. */
294 if (*FormatString
== 'v')
296 user_va_list_ptr
= fetch_arg(va_list *);
300 if (user_va_list_ptr
!= NULL
)
301 va_copy(VaListStream
, *user_va_list_ptr
);
306 /* Possibly switch to a va_list type stream and also to a new
308 if (*FormatString
== 'V')
313 list_ptr
= fetch_arg(va_list *);
314 new_format
= fetch_arg(char *);
318 if (list_ptr
!= NULL
)
320 user_va_list_ptr
= list_ptr
;
321 va_copy(VaListStream
, *list_ptr
);
324 if (new_format
!= NULL
)
325 FormatString
= new_format
;
330 /* '-' modifier? (left align) */
331 if (*FormatString
== '-')
332 left
= *FormatString
++;
334 /* '0' modifer? (pad with zeros) */
335 if (*FormatString
== '0')
336 fill
= *FormatString
++;
338 /* Get minimal width */
339 while (*FormatString
>= '0' && *FormatString
<= '9')
341 minwidth
= minwidth
* 10 + (*FormatString
++ - '0');
344 /* Dot following width modifier? */
345 if(*FormatString
== '.')
348 /* Get maximum width */
350 if(*FormatString
>= '0' && *FormatString
<= '9')
354 maxwidth
= maxwidth
*10 + (*FormatString
++ - '0');
355 while (*FormatString
>= '0' && *FormatString
<= '9');
360 switch (*FormatString
)
364 size
= *FormatString
++;
368 /* Switch over possible format characters. Sets minus, width and buf. */
369 switch(*FormatString
)
373 buf
= BADDR(fetch_arg(BPTR
));
382 buf
= fetch_arg(UBYTE
*);
391 IPTR number
= 0; int base
;
392 static const char digits
[] = "0123456789ABCDEF";
396 number
= fetch_number(size
, 1);
402 number
= fetch_number(size
, -1);
403 minus
= (SIPTR
)number
< 0;
405 if (minus
) number
= -number
;
411 number
= fetch_number(size
, 1);
415 buf
= &cbuf
[CBUFSIZE
];
418 *--buf
= digits
[number
% base
];
427 /* single character */
429 /* Some space for the result */
433 *buf
= fetch_number(size
, 1);
437 /* '%' before '\0'? */
440 This is nonsense - but do something useful:
441 Instead of reading over the '\0' reuse the '\0'.
444 /* Get compiler happy */
448 /* Convert '%unknown' to 'unknown'. This includes '%%' to '%'. */
450 buf
= (UBYTE
*)FormatString
;
455 if (width
> maxwidth
) width
= maxwidth
;
457 /* Skip the format character */
461 Now everything I need is known:
462 buf - contains the string to be printed
463 width - the size of the string
464 minus - is 1 if there is a '-' to print
465 fill - is the pad character
466 left - is 1 if the string should be left aligned
467 minwidth - is the minimal width of the field
468 (maxwidth is already part of width)
473 /* Print '-' (if there is one and the pad character is no space) */
474 if (minus
&& fill
!= ' ')
477 /* Pad left if not left aligned */
479 for (i
= width
+ minus
; i
< minwidth
; i
++)
482 /* Print '-' (if there is one and the pad character is a space) */
483 if(minus
&& fill
== ' ')
486 /* Print body upto width */
487 for(i
=0; i
<width
; i
++)
490 /* Pad right if left aligned */
492 for(i
= width
+ minus
; i
<minwidth
; i
++)
497 /* No '%' sign? Put the formatstring out */
498 PutCh(*FormatString
++);
501 /* All done. Put the terminator out. */
504 /* Return the rest of the DataStream. */
505 return (user_va_list_ptr
!= NULL
)
507 (va_copy(*user_va_list_ptr
, *(va_list *)&VaListStream
), user_va_list_ptr
)