Documentation fix.
[AROS.git] / rom / exec / rawdofmt.c
blob7a8178495954b1aa72ec4993dd2de0f5a852bfeb
1 /*
2 Copyright © 1995-2011, The AROS Development Team. All rights reserved.
3 $Id$
5 Desc: Format a string and emit it.
6 Lang: english
7 */
9 #include <dos/dos.h>
10 #include <aros/libcall.h>
11 #include <aros/asmcall.h>
12 #include <exec/rawfmt.h>
13 #include <proto/exec.h>
14 #include <string.h>
16 #include <stdarg.h>
18 #include "exec_intern.h"
19 #include "exec_util.h"
21 #ifdef __arm__
23 #define is_va_list(ap) ap.__ap
24 #define null_va_list(ap) va_list ap = {NULL}
25 #define VA_NULL {NULL}
27 #else
29 #define is_va_list(ap) ap
30 #define null_va_list(ap) void *ap = NULL
32 #endif
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) \
54 ({ \
55 type res; \
57 if (sizeof(type) <= sizeof(int)) \
58 res = (type)(IPTR)va_arg(VaListStream, int); \
59 else \
60 if (sizeof(type) == sizeof(long)) \
61 res = (type)(IPTR)va_arg(VaListStream, long); \
62 else \
63 res = (type)(IPTR)va_arg(VaListStream, void *); \
65 res; \
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) \
84 ({ \
85 IPTR res; \
87 if (sizeof(type) <= sizeof(short)) \
88 { \
89 res = *(short *)DataStream ; \
90 DataStream = (short *)DataStream + 1; \
91 } \
92 else if (sizeof(type) <= sizeof(long)) \
93 { \
94 res = *(long *)DataStream; \
95 DataStream = (long *)DataStream + 1; \
96 } \
97 (type)res; \
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'
109 * sign - <0 or >= 0.
111 * EXPERIMENTAL: 'i' is used to represent full IPTR value on 64-bit systems
113 #define fetch_number(size, sign) \
114 (sign >= 0 \
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. */
119 #define PutCh(ch) \
120 do \
122 switch ((IPTR)PutChProc) \
124 case (IPTR)RAWFMTFUNC_STRING: \
125 *(PutChData++) = ch; \
126 break; \
127 case (IPTR)RAWFMTFUNC_SERIAL: \
128 RawPutChar(ch); \
129 break; \
130 case (IPTR)RAWFMTFUNC_COUNT: \
131 (*((ULONG *)PutChData))++; \
132 break; \
133 default: \
134 if (is_va_list(VaListStream)) \
136 APTR (*proc)(APTR, UBYTE) = (APTR)PutChProc;\
137 PutChData = proc((APTR)PutChData, ch); \
139 else \
141 AROS_UFC2NR(void, PutChProc, \
142 AROS_UFCA(UBYTE, (ch), D0), \
143 AROS_UFCA(APTR , PutChData, A3)); \
146 } while (0)
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
151 * m68k programs.
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)
158 #if defined(mc68000)
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");
164 #else
165 UBYTE *PutChData = inPutChData;
166 #endif
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
176 fill - pad character
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
184 int left = 0;
185 int fill = ' ';
186 int minus = 0;
187 int size = 'h';
188 ULONG minwidth = 0;
189 ULONG maxwidth = ~0;
190 ULONG width = 0;
191 UBYTE *buf;
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];
205 ULONG i;
207 /* Skip over '%' character */
208 FormatString++;
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 == '.')
227 FormatString++;
228 /* Get maximum width */
230 if(*FormatString >= '0' && *FormatString <= '9')
232 maxwidth = 0;
234 maxwidth = maxwidth *10 + (*FormatString++ - '0');
235 while (*FormatString >= '0' && *FormatString <= '9');
239 /* size modifiers */
240 switch (*FormatString)
242 case 'l':
243 case 'i':
244 size = *FormatString++;
245 break;
248 /* Switch over possible format characters. Sets minus, width and buf. */
249 switch(*FormatString)
251 /* BCPL string */
252 case 'b':
254 BSTR s = fetch_arg(BSTR);
256 if (s)
258 buf = AROS_BSTR_ADDR(s);
259 width = AROS_BSTR_strlen(s);
261 else
263 buf = "";
264 width = 0;
267 break;
270 /* C string */
271 case 's':
272 buf = fetch_arg(UBYTE *);
274 if (!buf)
275 buf = "";
276 width = strlen(buf);
278 break;
280 IPTR number = 0; int base;
281 static const char digits[] = "0123456789ABCDEF";
283 case 'p':
284 case 'P':
285 fill = '0';
286 minwidth = sizeof(APTR)*2;
287 size = 'i';
288 case 'x':
289 case 'X':
290 base = 16;
291 number = fetch_number(size, 1);
293 goto do_number;
295 case 'd':
296 case 'D':
297 base = 10;
298 number = fetch_number(size, -1);
299 minus = (SIPTR)number < 0;
301 if (minus) number = -number;
303 goto do_number;
305 case 'u':
306 case 'U':
307 base = 10;
308 number = fetch_number(size, 1);
310 do_number:
312 buf = &cbuf[CBUFSIZE];
315 *--buf = digits[number % base];
316 number /= base;
317 width++;
318 } while (number);
320 break;
324 /* single character */
325 case 'c':
326 /* Some space for the result */
327 buf = cbuf;
328 width = 1;
330 *buf = fetch_number(size, 1);
332 break;
334 /* '%' before '\0'? */
335 case '\0':
337 This is nonsense - but do something useful:
338 Instead of reading over the '\0' reuse the '\0'.
340 FormatString--;
341 /* Get compiler happy */
342 buf = NULL;
343 break;
345 /* Convert '%unknown' to 'unknown'. This includes '%%' to '%'. */
346 default:
347 buf = (UBYTE *)FormatString;
348 width = 1;
349 break;
352 if (width > maxwidth) width = maxwidth;
354 /* Skip the format character */
355 FormatString++;
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)
367 So just print it.
370 /* Print '-' (if there is one and the pad character is no space) */
371 if (minus && fill != ' ')
372 PutCh('-');
374 /* Pad left if not left aligned */
375 if (!left)
376 for (i = width + minus; i < minwidth; i++)
377 PutCh(fill);
379 /* Print '-' (if there is one and the pad character is a space) */
380 if(minus && fill == ' ')
381 PutCh('-');
383 /* Print body upto width */
384 for(i=0; i<width; i++) {
385 PutCh(*buf);
386 buf++;
389 /* Pad right if left aligned */
390 if(left)
391 for(i = width + minus; i<minwidth; i++)
392 PutCh(fill);
394 else
396 /* No '%' sign? Put the formatstring out */
397 PutCh(*FormatString);
398 FormatString++;
401 /* All done. Put the terminator out. */
402 PutCh('\0');
404 /* Return the rest of the DataStream or buffer. */
405 return is_va_list(VaListStream) ? (APTR)PutChData : DataStream;
408 /*****************************************************************************
410 NAME */
412 AROS_LH4I(APTR,RawDoFmt,
414 /* SYNOPSIS */
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),
420 /* LOCATION */
421 struct ExecBase *, SysBase, 87, Exec)
423 /* FUNCTION
424 printf-style formatting function with callback hook.
426 INPUTS
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
450 WORD aligned.
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
460 magic values:
462 RAWFMTFUNC_STRING - Write output to string buffer pointed
463 to by PutChData which is incremented
464 every character.
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.
477 RESULT
478 Pointer to the rest of the DataStream.
480 NOTES
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.
491 EXAMPLE
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))
498 *(*data)++=chr;
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
510 DataStream.
512 BUGS
513 PutChData cannot be modified from the callback hook on non-m68k
514 systems.
516 SEE ALSO
518 INTERNALS
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 ******************************************************************************/
531 AROS_LIBFUNC_INIT
533 null_va_list(vaListStream);
535 return InternalRawDoFmt(FormatString, DataStream, PutChProc, PutChData, vaListStream);
537 AROS_LIBFUNC_EXIT
538 } /* RawDoFmt */