move VMWare SVGA driver to generic location
[AROS.git] / rom / exec / rawdofmt.c
bloba4ab3deadeaff076071ff2de1aae7b2dae96aae0
1 /*
2 Copyright © 1995-2007, The AROS Development Team. All rights reserved.
3 $Id$
5 Desc: Format a string and emit it.
6 Lang: english
7 */
8 #include <dos/dos.h>
9 #include <aros/libcall.h>
10 #include <aros/asmcall.h>
11 #include <proto/exec.h>
12 #include <string.h>
14 #include <stdarg.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) \
36 ({ \
37 type res; \
39 if (sizeof(type) <= sizeof(int)) \
40 res = (type)va_arg(VaListStream, int); \
41 else \
42 if (sizeof(type) == sizeof(long)) \
43 res = (type)va_arg(VaListStream, long); \
44 else \
45 res = (type)(IPTR)va_arg(VaListStream, void *); \
47 res; \
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) \
67 ({ \
68 type __retval = *(type *)DataStream; \
69 DataStream = (type *)DataStream + 1; \
70 __retval; \
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'
81 sign - <0 or >= 0. */
82 #define fetch_number(size, sign) \
83 (sign >= 0 \
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. */
89 #define PutCh(ch) \
90 do \
91 { \
92 if (PutChProc != NULL) \
93 { \
94 AROS_UFC2(void, PutChProc, \
95 AROS_UFCA(UBYTE, (ch), D0), \
96 AROS_UFCA(APTR , PutChData, A3)); \
97 } \
98 else \
99 { \
100 *UPutChData++ = ch; \
102 } while (0)
105 /*****************************************************************************
107 NAME */
109 AROS_LH4I(APTR,RawDoFmt,
111 /* SYNOPSIS */
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),
117 /* LOCATION */
118 struct ExecBase *, SysBase, 87, Exec)
120 /* FUNCTION
121 printf-style formatting function with callback hook.
123 INPUTS
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.
135 'l' means LONG.
136 'i' means IPTR.
138 defaults to WORD, if nothing is specified.
140 type - 'b' BSTR. It will use the internal representation
141 of the BSTR defined by the ABI.
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
149 are allowed:
151 %[type]
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
161 at all.
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
168 to use this option.
170 DataStream - Pointer to a zone of memory containing the data. Data has to be
171 WORD aligned.
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.
182 RESULT
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
188 NOTES
189 The field size defaults to words which may be different from the
190 default integer size of the compiler.
192 EXAMPLE
193 Build a sprintf style function:
195 static void callback(UBYTE chr __reg(d0), UBYTE **data __reg(a3))
197 *(*data)++=chr;
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:
211 #include <stdarg.h>
213 static void callback(UBYTE chr __reg(d0), UBYTE **data __reg(a3))
215 *(*data)++=chr;
218 void my_sprintf(UBYTE *buffer, UBYTE *format, ...)
220 va_list args;
221 va_start(args, format);
223 APTR raw_args[] = { &args, format };
225 RawDoFmt("%V", raw_args, &callback, &buffer);
227 va_end(args);
230 BUGS
231 PutChData cannot be modified from the callback hook on non-m68k
232 systems.
234 SEE ALSO
236 INTERNALS
238 ******************************************************************************/
240 AROS_LIBFUNC_INIT
242 #ifdef __mc68000
243 register APTR __PutChData asm("a3") = PutChData;
244 # define PutChData __PutChData
245 #endif
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
259 fill - pad character
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
267 int left = 0;
268 int fill = ' ';
269 int minus = 0;
270 int size = 'w';
271 ULONG minwidth = 0;
272 ULONG maxwidth = ~0;
273 ULONG width = 0;
274 UBYTE *buf;
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];
288 ULONG i;
290 /* Skip over '%' character */
291 FormatString++;
293 /* Possibly switch to a va_list type stream. */
294 if (*FormatString == 'v')
296 user_va_list_ptr = fetch_arg(va_list *);
298 FormatString++;
300 if (user_va_list_ptr != NULL)
301 va_copy(VaListStream, *user_va_list_ptr);
303 continue;
306 /* Possibly switch to a va_list type stream and also to a new
307 format string. */
308 if (*FormatString == 'V')
310 char *new_format;
311 va_list *list_ptr;
313 list_ptr = fetch_arg(va_list *);
314 new_format = fetch_arg(char *);
316 FormatString++;
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;
327 continue;
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 == '.')
347 FormatString++;
348 /* Get maximum width */
350 if(*FormatString >= '0' && *FormatString <= '9')
352 maxwidth = 0;
354 maxwidth = maxwidth *10 + (*FormatString++ - '0');
355 while (*FormatString >= '0' && *FormatString <= '9');
359 /* size modifiers */
360 switch (*FormatString)
362 case 'l':
363 case 'i':
364 size = *FormatString++;
365 break;
368 /* Switch over possible format characters. Sets minus, width and buf. */
369 switch(*FormatString)
371 /* BCPL string */
372 case 'b':
374 BSTR s = fetch_arg(BSTR);
376 buf = AROS_BSTR_ADDR(s);
377 width = AROS_BSTR_strlen(s);
379 break;
382 /* C string */
383 case 's':
384 buf = fetch_arg(UBYTE *);
386 if (!buf)
387 buf = "(null)";
389 width = strlen(buf);
391 break;
393 IPTR number = 0; int base;
394 static const char digits[] = "0123456789ABCDEF";
396 case 'x':
397 base = 16;
398 number = fetch_number(size, 1);
400 goto do_number;
402 case 'd':
403 base = 10;
404 number = fetch_number(size, -1);
405 minus = (SIPTR)number < 0;
407 if (minus) number = -number;
409 goto do_number;
411 case 'u':
412 base = 10;
413 number = fetch_number(size, 1);
415 do_number:
417 buf = &cbuf[CBUFSIZE];
420 *--buf = digits[number % base];
421 number /= base;
422 width++;
423 } while (number);
425 break;
429 /* single character */
430 case 'c':
431 /* Some space for the result */
432 buf = cbuf;
433 width = 1;
435 *buf = fetch_number(size, 1);
437 break;
439 /* '%' before '\0'? */
440 case '\0':
442 This is nonsense - but do something useful:
443 Instead of reading over the '\0' reuse the '\0'.
445 FormatString--;
446 /* Get compiler happy */
447 buf = NULL;
448 break;
450 /* Convert '%unknown' to 'unknown'. This includes '%%' to '%'. */
451 default:
452 buf = (UBYTE *)FormatString;
453 width = 1;
454 break;
457 if (width > maxwidth) width = maxwidth;
459 /* Skip the format character */
460 FormatString++;
463 Now everything I need is known:
464 buf - contains the string to be printed
465 width - the size of the string
466 minus - is 1 if there is a '-' to print
467 fill - is the pad character
468 left - is 1 if the string should be left aligned
469 minwidth - is the minimal width of the field
470 (maxwidth is already part of width)
472 So just print it.
475 /* Print '-' (if there is one and the pad character is no space) */
476 if (minus && fill != ' ')
477 PutCh('-');
479 /* Pad left if not left aligned */
480 if (!left)
481 for (i = width + minus; i < minwidth; i++)
482 PutCh(fill);
484 /* Print '-' (if there is one and the pad character is a space) */
485 if(minus && fill == ' ')
486 PutCh('-');
488 /* Print body upto width */
489 for(i=0; i<width; i++)
490 PutCh(*buf++);
492 /* Pad right if left aligned */
493 if(left)
494 for(i = width + minus; i<minwidth; i++)
495 PutCh(fill);
497 else
499 /* No '%' sign? Put the formatstring out */
500 PutCh(*FormatString++);
503 /* All done. Put the terminator out. */
504 PutCh('\0');
506 /* Return the rest of the DataStream. */
507 return (user_va_list_ptr != NULL)
509 (va_copy(*user_va_list_ptr, *(va_list *)&VaListStream), user_va_list_ptr)
511 DataStream;
513 AROS_LIBFUNC_EXIT
514 } /* RawDoFmt */