Support rastport clipping rectangle for layerless rastports
[tangerine.git] / rom / exec / rawdofmt.c
blob9125db5a15c95a3d10e8e3590e75f4abff676ea2
1 /*
2 Copyright © 1995-2001, 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' 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
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':
373 buf = BADDR(fetch_arg(BPTR));
375 /* Set width */
376 width = *buf++;
378 break;
380 /* C string */
381 case 's':
382 buf = fetch_arg(UBYTE *);
384 if (!buf)
385 buf = "(null)";
387 width = strlen(buf);
389 break;
391 IPTR number = 0; int base;
392 static const char digits[] = "0123456789ABCDEF";
394 case 'x':
395 base = 16;
396 number = fetch_number(size, 1);
398 goto do_number;
400 case 'd':
401 base = 10;
402 number = fetch_number(size, -1);
403 minus = (SIPTR)number < 0;
405 if (minus) number = -number;
407 goto do_number;
409 case 'u':
410 base = 10;
411 number = fetch_number(size, 1);
413 do_number:
415 buf = &cbuf[CBUFSIZE];
418 *--buf = digits[number % base];
419 number /= base;
420 width++;
421 } while (number);
423 break;
427 /* single character */
428 case 'c':
429 /* Some space for the result */
430 buf = cbuf;
431 width = 1;
433 *buf = fetch_number(size, 1);
435 break;
437 /* '%' before '\0'? */
438 case '\0':
440 This is nonsense - but do something useful:
441 Instead of reading over the '\0' reuse the '\0'.
443 FormatString--;
444 /* Get compiler happy */
445 buf = NULL;
446 break;
448 /* Convert '%unknown' to 'unknown'. This includes '%%' to '%'. */
449 default:
450 buf = (UBYTE *)FormatString;
451 width = 1;
452 break;
455 if (width > maxwidth) width = maxwidth;
457 /* Skip the format character */
458 FormatString++;
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)
470 So just print it.
473 /* Print '-' (if there is one and the pad character is no space) */
474 if (minus && fill != ' ')
475 PutCh('-');
477 /* Pad left if not left aligned */
478 if (!left)
479 for (i = width + minus; i < minwidth; i++)
480 PutCh(fill);
482 /* Print '-' (if there is one and the pad character is a space) */
483 if(minus && fill == ' ')
484 PutCh('-');
486 /* Print body upto width */
487 for(i=0; i<width; i++)
488 PutCh(*buf++);
490 /* Pad right if left aligned */
491 if(left)
492 for(i = width + minus; i<minwidth; i++)
493 PutCh(fill);
495 else
497 /* No '%' sign? Put the formatstring out */
498 PutCh(*FormatString++);
501 /* All done. Put the terminator out. */
502 PutCh('\0');
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)
509 DataStream;
511 AROS_LIBFUNC_EXIT
512 } /* RawDoFmt */