2 * Copyright ArcCore AB
\r
4 * A simple implementation of all formatted xxprintf functionallity.
\r
10 * What you normally would do is to print to a buffer of some kind and then
\r
11 * call write(). However little stack indicates that we can't place buffers
\r
12 * on the stack and reentrant tells us that we can't have a static buffer.
\r
13 * That leaves us with printing characters as it is ready. From a speed
\r
14 * point of view that is less than optimal, but that the way it's got to be.
\r
15 * (Could make a 16 long char buffer??)
\r
17 * This is just intended to be used as a replacement for newlibs implementation.
\r
18 * Newlib porting interface have the following API:
\r
19 * int write( int fd, char *buf, int nbytes)
\r
22 * Note that puts(), putc() are still the newlib variants....
\r
24 * printf() -> vfprintf(stdout,) -> vsnprintf(buf,)
\r
26 * vprintf() -> vfprintf(stdout,) -> vsnprintf(buf,)
\r
28 * sprintf(buf,) -> vsnprintf(buf,)
\r
29 * snprintf(buf,) -> vsnprintf(buf,)
\r
31 * IMPLEMENTATION NOTE:
\r
32 * - If printing more than the limit, e.g. using vsnprintf() then
\r
33 * the emit function will only stop printing, but not interrupted
\r
34 * (The code will get more complicated that way)
\r
35 * - ANSI-C and POSIX, streams and POSIX filenumbers.
\r
36 * POSIX file-numbers exist in unistd.h and are only to be used by the porting
\r
37 * newlib interface i.e. newlib_port.c.
\r
40 * NEWLIB: File handles vs files
\r
41 * This printf() family of functions does not use newlib at all.
\r
42 * At this point the following can have happend:
\r
43 * 1. A call to any of the file functions in newlib have been called.
\r
44 * This then calls a number of functions (sbrk, __sinit(_impure_ptr), etc ).
\r
45 * __sinit(_impure_ptr) initializes the standard files, stdin, stdout, stderr.
\r
46 * file->_file is the actual posix file number (stdin=0, stdout=1, stderr=2)
\r
47 * 2. No calls is done to newlib file functions. The impure_data is then empty and
\r
50 * Code for checking if the newlib have initialized (or we have explicitly called __sinit(_impure_ptr)
\r
51 * if( _impure_ptr->__sdidinit == 1 ) {
\r
52 * // We use the real filenumber
\r
53 * arc_putchar((int)(file->_file), c);
\r
63 #if defined(USE_NEWLIB) && defined(__GNUC__)
\r
66 //#define HOST_TEST 1
\r
68 int arc_putchar(int fd, int c);
\r
69 int print(FILE *file, char **buffer, size_t n, const char *format, va_list ap);
\r
70 static inline int emitChar( FILE *file, char **buf, char c, int *left );
\r
72 int fputs( const char *s, FILE *file ) {
\r
73 int left = ~(size_t)0;
\r
75 emitChar(file,NULL,*s++,&left);
\r
81 int printf(const char *format, ...) {
\r
85 va_start(ap, format);
\r
86 rv = vfprintf((FILE *)STDOUT_FILENO, format, ap);
\r
91 int fprintf(FILE *file, const char *format, ...) {
\r
95 va_start(ap, format);
\r
96 rv = vfprintf(file, format, ap);
\r
101 int sprintf(char *buffer, const char *format, ...) {
\r
105 va_start(ap, format);
\r
106 rv = vsnprintf(buffer, ~(size_t)0, format, ap);
\r
112 int snprintf(char *buffer, size_t n, const char *format, ...) {
\r
116 va_start(ap, format);
\r
117 rv = vsnprintf(buffer, n, format, ap);
\r
122 int vprintf(const char *format, va_list ap) {
\r
123 return vfprintf((FILE *)STDOUT_FILENO, format, ap);
\r
126 int vsprintf(char *buffer, const char *format, va_list ap) {
\r
127 return vsnprintf(buffer, ~(size_t)0, format, ap);
\r
130 int vfprintf(FILE *file, const char *format, va_list ap) {
\r
132 /* Just print to stdout */
\r
133 rv = print(file,NULL,~(size_t)0, format,ap);
\r
138 int vsnprintf(char *buffer, size_t n, const char *format, va_list ap) {
\r
141 rv = print(NULL, &buffer, n, format,ap);
\r
147 * The integer only counterpart
\r
149 int iprintf(const char *format, ...) __attribute__ ((alias("printf")));
\r
150 int fiprintf(FILE *file, const char *format, ...) __attribute__ ((alias("fprintf")));
\r
151 int siprintf(char *buffer, const char *format, ...) __attribute__ ((alias("sprintf")));
\r
152 int sniprintf(char *buffer, size_t n, const char *format, ...) __attribute__ ((alias("snprintf")));
\r
153 int viprintf(const char *format, va_list ap) __attribute__ ((alias("vprintf")));
\r
154 int vsiprintf(char *buffer, const char *format, va_list ap) __attribute__ ((alias("vsprintf")));
\r
155 int vfiprintf(FILE *file, const char *format, va_list ap) __attribute__ ((alias("vfprintf")));
\r
159 * @param file The file to print to
\r
160 * @param buf The buffer to print to
\r
161 * @param c The char to print
\r
164 static inline int emitChar( FILE *file, char **buf, char c, int *left ) {
\r
165 if( (*left) == 1 ) {
\r
169 if( buf == NULL ) {
\r
175 #if defined(USE_NEWLIB) && defined(__GNUC__)
\r
176 /* We are trying to print with newlib file descriptor.
\r
177 * That's wrong since we are using the POSIX file numbers here instead
\r
179 assert( file != _impure_ptr->_stdout );
\r
182 if( (unsigned )file > 10UL ) {
\r
183 arc_putchar((int)(file->_file), c);
\r
185 arc_putchar((int)(file), c);
\r
188 #endif /* HOST_TEST */
\r
197 #if defined(HOST_TEST)
\r
199 * Convert a number to a string
\r
201 * @param val The value to convert
\r
202 * @param str Pointer to a space where to put the string
\r
203 * @param base The base
\r
204 * @param negative If negative or not.
\r
206 void xtoa( unsigned long val, char* str, int base, int negative) {
\r
215 if( base < 10 && base > 16 ) {
\r
222 str[i++] = "0123456789abcdef"[ val % base ];
\r
223 } while ((val /= base) > 0);
\r
232 while(str > oStr) {
\r
239 extern void xtoa( unsigned long val, char* str, int base, int negative);
\r
243 #define FL_NONE (0<<0)
\r
244 #define FL_ZERO (1<<1)
\r
245 #define FL_HASH (1<<2)
\r
246 #define FL_SPACE (1<<3)
\r
247 #define FL_ALIGN_LEFT (1<<4)
\r
248 #define FL_TYPE_SIGNED_INT (1<<5)
\r
249 #define FL_TYPE_UNSIGNED_INT (1<<6)
\r
250 #define FL_TYPE_POINTER (1<<7)
\r
252 static void emitString( FILE *file, char **buffer, char *string, int width, int flags, int *left) {
\r
254 char *str = string;
\r
257 /* padding: FL_ZERO or normal ' '
\r
258 * align: FL_ALIGN_LEFT (left) or normal (right)
\r
260 pad = (flags & FL_ZERO) ? '0' : ' ';
\r
263 if( flags & FL_ALIGN_LEFT ) {
\r
264 for(i=0;str[i]; i++) {
\r
265 emitChar(file,buffer,str[i],left);
\r
269 for(;i<width;i++) {
\r
270 emitChar(file,buffer,pad,left);
\r
274 strLen = strlen(string);
\r
277 if( width > strLen ) {
\r
278 for(i=0;i<(width-strLen);i++) {
\r
279 emitChar(file,buffer,pad,left);
\r
283 for(i=0;i<strLen; i++) {
\r
284 emitChar(file,buffer,string[i],left);
\r
289 void emitInt( FILE *file, char **buffer, int val, int base, int width, int flags, int *left )
\r
291 char lBuf[12]; // longest is base 10, 2^32
\r
294 if( flags & FL_TYPE_SIGNED_INT ) {
\r
295 xtoa(val,str,base ,(val < 0));
\r
297 xtoa((unsigned)val,str,base ,0);
\r
300 emitString(file,buffer,str,width,flags,left);
\r
303 #define PRINT_CHAR(_c) *buffer++= (_c);
\r
307 * Write formatted output to an array with a maximum number of characters.
\r
309 * This is the mother of the formatted print family. The main goal here
\r
310 * is to be very small and memory efficient.
\r
314 * Flags : '-' and '0'
\r
315 * Width : Normal padding is supported, '*' is not.
\r
319 * Type : d,u,x,s,and c
\r
321 * @param file The file descriptor
\r
322 * @param buffer A pointer to the place to store the output
\r
323 * If NULL the output is instead
\r
324 * @param n The maximum number of characters to write
\r
325 * @param format The format string
\r
326 * @param ap The va list
\r
329 int print(FILE *file, char **buffer, size_t n, const char *format, va_list ap)
\r
337 while ( (ch = *format++) ) {
\r
343 emitChar(file,buffer,ch,&left);
\r
352 else if (ch == ' ')
\r
356 else if (ch == '-')
\r
358 flags = FL_ALIGN_LEFT;
\r
362 /* Not supported or no flag */
\r
370 if( (ch >= '0') && (ch <= '9') ) {
\r
380 emitChar(file,buffer,(char )va_arg( ap, int ),&left);
\r
382 else if (ch == 'd')
\r
384 flags |= FL_TYPE_SIGNED_INT;
\r
385 emitInt(file,buffer,va_arg( ap, int ),10,width,flags,&left);
\r
387 else if (ch == 'u')
\r
389 flags |= FL_TYPE_UNSIGNED_INT;
\r
390 emitInt(file,buffer,va_arg( ap, int ),10,width,flags,&left);
\r
392 else if (ch == 'x')
\r
394 flags |= FL_TYPE_UNSIGNED_INT;
\r
395 emitInt(file,buffer,va_arg( ap, int ),16,width,flags,&left);
\r
397 else if (ch == 'p')
\r
399 flags |= FL_TYPE_POINTER;
\r
400 emitInt(file,buffer,va_arg( ap, int ),16,width,flags,&left);
\r
402 else if (ch == 's')
\r
404 str = (char *)va_arg( ap, int );
\r
406 if( str == NULL ) {
\r
410 emitString(file,buffer,str,width,flags,&left);
\r
418 emitChar(file,buffer,ch,&left);
\r
421 // va_end(ap); // Removed, TODO: Check the va_start/va_end handling (used in calling functions also).
\r
424 emitChar(file,buffer,'\0',&left);
\r
426 return 0; // Wrong.. but for now.
\r
434 printf("char: %c %c = a B\n", 'a', 66);
\r
436 printf("string: %s = (null)\n", (char *)ptr);
\r
438 printf("string: %s = foobar \n", "foobar");
\r
440 printf("string: %2s = foobar \n", "foobar");
\r
441 printf("string: \"%8s\" = \" foobar\" \n", "foobar");
\r
443 printf("string: \"%-8s\" = \"foobar \" \n", "foobar");
\r
445 printf("decimal: 23 = %d \n", 23);
\r
446 printf("hex: c23 = %x \n", 0xc23);
\r
447 printf("hex: 0c23 = %04x \n", 0xc23);
\r
448 printf("decimal with blanks: 23 = %6d \n", 23);
\r
449 printf("decimal with zero: 000023 = %06d \n", 23);
\r
451 /* negative and large numbers */
\r
452 printf("decimal: -23 = %d \n", -23);
\r
453 printf("decimal: 4294967273 = %u \n", -23);
\r
454 printf("decimal: c0000000 = %x \n", 0xc0000000);
\r
456 printf("decimal: 00c000 = %06x \n", 0xc000);
\r
458 fprintf(stdout, "string: %s = foobar \n", "foobar");
\r
459 sprintf(buff, "string: %s = foobar \n", "foobar");
\r
462 snprintf(buff,10, "%s\n", "12345678901234567");
\r
463 printf("\"123456789\" = \"%s\"\n",buff);
\r
465 snprintf(buff,12, "%s\n", "abcdefghijklmn");
\r
466 printf("\"abcdefghijkl\" = \"%s\"\n",buff);
\r