/******************************************************************************* Copyright 2001, 2002 Georges Menie () This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /******************************************************************************* putchar is the only external dependency for this file, if you have a working putchar, just remove the following define. If the function should be called something else, replace outbyte(c) by your own function call. */ // ******************************************************************************* // Updated by Daniel D Miller. Changes to the original Menie code are // Copyright 2009-2012 Daniel D Miller // All such changes are distributed under the same license as the original, // as described above. // 11/06/09 - adding floating-point support // 03/19/10 - pad fractional portion of floating-point number with 0s // 03/30/10 - document the \% bug // 07/20/10 - Fix a round-off bug in floating-point conversions // ( 0.99 with %.1f did not round to 1.0 ) // 10/25/11 - Add support for %+ format (always show + on positive numbers) // 01/19/12 - fix handling of %f with no decimal; it was defaulting to 0 // decimal places, rather than printf's 6. // ******************************************************************************* // BUGS // If '%' is included in a format string, in the form \% with the intent // of including the percent sign in the output string, this function will // mis-handle the data entirely!! // Essentially, it will just discard the character following the percent sign. // This bug is not easy to fix in the existing code; // for now, I'll just try to remember to use %% instead of \% ... // ******************************************************************************* // lint -esym(752, debug_output) // lint -esym(766, stdio.h) // #define TEST_PRINTF 1 #include static uint use_leading_plus = 0; /* based on a example-code from Keil for CS G++ */ /* for caddr_t (typedef char * caddr_t;) */ #include // * NEWLIB STUBS *// #include #include #include #include #include /*============================================================================== * Environment variables. * A pointer to a list of environment variables and their values. For a minimal * environment, this empty list is adequate: */ char *__env[1] = { 0 }; char * *environ = __env; /*============================================================================== * Close a file. */ int _close(__attribute__((unused)) int file) { return -1; } /*============================================================================== * Transfer control to a new process. */ int _execve(__attribute__((unused)) char *name, __attribute__((unused)) char * *argv, __attribute__((unused)) char * *env) { errno = ENOMEM; return -1; } /*============================================================================== * Exit a program without cleaning up files. */ void _exit(__attribute__((unused)) int code) { /* Should we force a system reset? */ while (1) { ; } } /*============================================================================== * Create a new process. */ int _fork(void) { errno = EAGAIN; return -1; } /*============================================================================== * Status of an open file. */ int _fstat(__attribute__((unused)) int file, struct stat *st) { st->st_mode = S_IFCHR; return 0; } /*============================================================================== * Process-ID */ int _getpid(void) { return 1; } /*============================================================================== * Query whether output stream is a terminal. */ int _isatty(__attribute__((unused)) int file) { return 1; } /*============================================================================== * Send a signal. */ int _kill(__attribute__((unused)) int pid, __attribute__((unused)) int sig) { errno = EINVAL; return -1; } /*============================================================================== * Establish a new name for an existing file. */ int _link(__attribute__((unused)) char *old, __attribute__((unused)) char *new) { errno = EMLINK; return -1; } /*============================================================================== * Set position in a file. */ int _lseek(__attribute__((unused)) int file, __attribute__((unused)) int ptr, __attribute__((unused)) int dir) { return 0; } /*============================================================================== * Open a file. */ int _open(__attribute__((unused)) const char *name, __attribute__((unused)) int flags, __attribute__((unused)) int mode) { return -1; } /*============================================================================== * Read from a file. */ int _read(__attribute__((unused)) int file, __attribute__((unused)) char *ptr, __attribute__((unused)) int len) { return 0; } /*============================================================================== * Write to a file. libc subroutines will use this system routine for output to * all files, including stdout�so if you need to generate any output, for * example to a serial port for debugging, you should make your minimal write * capable of doing this. */ int _write_r(__attribute__((unused)) void *reent, __attribute__((unused)) int file, __attribute__((unused)) char *ptr, __attribute__((unused)) int len) { return 0; } /*============================================================================== * Increase program data space. As malloc and related functions depend on this, * it is useful to have a working implementation. The following suffices for a * standalone system; it exploits the symbol _end automatically defined by the * GNU linker. */ caddr_t _sbrk(int incr) { extern char _end; /* Defined by the linker */ static char *heap_end; char *prev_heap_end; char *stack_ptr; if (heap_end == 0) { heap_end = &_end; } prev_heap_end = heap_end; asm volatile ("MRS %0, msp" : "=r" (stack_ptr)); if (heap_end + incr > stack_ptr) { _write_r((void *)0, 1, "Heap and stack collision\n", 25); _exit(1); } heap_end += incr; return (caddr_t)prev_heap_end; } /*============================================================================== * Status of a file (by name). */ int _stat(__attribute__((unused)) char *file, struct stat *st) { st->st_mode = S_IFCHR; return 0; } /*============================================================================== * Timing information for current process. */ int _times(__attribute__((unused)) struct tms *buf) { return -1; } /*============================================================================== * Remove a file's directory entry. */ int _unlink(__attribute__((unused)) char *name) { errno = ENOENT; return -1; } /*============================================================================== * Wait for a child process. */ int _wait(__attribute__((unused)) int *status) { errno = ECHILD; return -1; } // * NEWLIB STUBS *// // **************************************************************************** static void printchar(char * *str, int c) { if (str) { **str = c; ++(*str); } #ifdef TEST_PRINTF else { extern int putchar(int c); (void)putchar(c); } #endif } // **************************************************************************** static uint my_strlen(char *str) { if (str == 0) { return 0; } uint slen = 0; while (*str != 0) { slen++; str++; } return slen; } // **************************************************************************** // This version returns the length of the output string. // It is more useful when implementing a walking-string function. // **************************************************************************** static const double round_nums[8] = { 0.5L, 0.05L, 0.005L, 0.0005L, 0.00005L, 0.000005L, 0.0000005L, 0.00000005L }; static unsigned dbl2stri(char *outbfr, double dbl, unsigned dec_digits) { static char local_bfr[128]; char *output = (outbfr == 0) ? local_bfr : outbfr; // ******************************************* // extract negative info // ******************************************* if (dbl < 0.0L) { *output++ = '-'; dbl *= -1.0L; } else { if (use_leading_plus) { *output++ = '+'; } } // handling rounding by adding .5LSB to the floating-point data if (dec_digits < 8) { dbl += round_nums[dec_digits]; } // ************************************************************************** // construct fractional multiplier for specified number of digits. // ************************************************************************** uint mult = 1; uint idx; for (idx = 0; idx < dec_digits; idx++) { mult *= 10; } // printf("mult=%u\n", mult) ; uint wholeNum = (uint)dbl; uint decimalNum = (uint)((dbl - wholeNum) * mult); // ******************************************* // convert integer portion // ******************************************* char tbfr[40]; idx = 0; while (wholeNum != 0) { tbfr[idx++] = '0' + (wholeNum % 10); wholeNum /= 10; } // printf("%.3f: whole=%s, dec=%d\n", dbl, tbfr, decimalNum) ; if (idx == 0) { *output++ = '0'; } else { while (idx > 0) { *output++ = tbfr[idx - 1]; // lint !e771 idx--; } } if (dec_digits > 0) { *output++ = '.'; // ******************************************* // convert fractional portion // ******************************************* idx = 0; while (decimalNum != 0) { tbfr[idx++] = '0' + (decimalNum % 10); decimalNum /= 10; } // pad the decimal portion with 0s as necessary; // We wouldn't want to report 3.093 as 3.93, would we?? while (idx < dec_digits) { tbfr[idx++] = '0'; } // printf("decimal=%s\n", tbfr) ; if (idx == 0) { *output++ = '0'; } else { while (idx > 0) { *output++ = tbfr[idx - 1]; idx--; } } } *output = 0; // prepare output output = (outbfr == 0) ? local_bfr : outbfr; return my_strlen(output); } // **************************************************************************** #define PAD_RIGHT 1 #define PAD_ZERO 2 static int prints(char * *out, const char *string, int width, int pad) { register int pc = 0, padchar = ' '; if (width > 0) { int len = 0; const char *ptr; for (ptr = string; *ptr; ++ptr) { ++len; } if (len >= width) { width = 0; } else { width -= len; } if (pad & PAD_ZERO) { padchar = '0'; } } if (!(pad & PAD_RIGHT)) { for (; width > 0; --width) { printchar(out, padchar); ++pc; } } for (; *string; ++string) { printchar(out, *string); ++pc; } for (; width > 0; --width) { printchar(out, padchar); ++pc; } return pc; } // **************************************************************************** /* the following should be enough for 32 bit int */ #define PRINT_BUF_LEN 12 static int printi(char * *out, int i, int b, int sg, int width, int pad, int letbase) { char print_buf[PRINT_BUF_LEN]; char *s; int t, neg = 0, pc = 0; unsigned u = (unsigned)i; if (i == 0) { print_buf[0] = '0'; print_buf[1] = '\0'; return prints(out, print_buf, width, pad); } if (sg && b == 10 && i < 0) { neg = 1; u = (unsigned)-i; } // make sure print_buf is NULL-term s = print_buf + PRINT_BUF_LEN - 1; *s = '\0'; while (u) { t = u % b; // lint !e573 Warning 573: Signed-unsigned mix with divide if (t >= 10) { t += letbase - '0' - 10; } *--s = t + '0'; u /= b; // lint !e573 Warning 573: Signed-unsigned mix with divide } if (neg) { if (width && (pad & PAD_ZERO)) { printchar(out, '-'); ++pc; --width; } else { *--s = '-'; } } else { if (use_leading_plus) { *--s = '+'; } } return pc + prints(out, s, width, pad); } // **************************************************************************** static int print(char * *out, int *varg) { int post_decimal; int width, pad; unsigned dec_width = 6; int pc = 0; char *format = (char *)(*varg++); char scr[2]; use_leading_plus = 0; // start out with this clear for (; *format != 0; ++format) { if (*format == '%') { dec_width = 6; ++format; width = pad = 0; if (*format == '\0') { break; } if (*format == '%') { goto out_lbl; } if (*format == '-') { ++format; pad = PAD_RIGHT; } if (*format == '+') { ++format; use_leading_plus = 1; } while (*format == '0') { ++format; pad |= PAD_ZERO; } post_decimal = 0; if (*format == '.' || (*format >= '0' && *format <= '9')) { while (1) { if (*format == '.') { post_decimal = 1; dec_width = 0; format++; } else if ((*format >= '0' && *format <= '9')) { if (post_decimal) { dec_width *= 10; dec_width += *format - '0'; } else { width *= 10; width += *format - '0'; } format++; } else { break; } } } if (*format == 'l') { ++format; } switch (*format) { case 's': { // char *s = *((char **) varg++); //lint !e740 char *s = (char *)*varg++; // lint !e740 !e826 convert to double pointer pc += prints(out, s ? s : "(null)", width, pad); use_leading_plus = 0; // reset this flag after printing one value } break; case 'd': pc += printi(out, *varg++, 10, 1, width, pad, 'a'); use_leading_plus = 0; // reset this flag after printing one value break; case 'x': pc += printi(out, *varg++, 16, 0, width, pad, 'a'); use_leading_plus = 0; // reset this flag after printing one value break; case 'X': pc += printi(out, *varg++, 16, 0, width, pad, 'A'); use_leading_plus = 0; // reset this flag after printing one value break; case 'u': pc += printi(out, *varg++, 10, 0, width, pad, 'a'); use_leading_plus = 0; // reset this flag after printing one value break; case 'c': /* char are converted to int then pushed on the stack */ scr[0] = *varg++; scr[1] = '\0'; pc += prints(out, scr, width, pad); use_leading_plus = 0; // reset this flag after printing one value break; case 'f': { // http://wiki.debian.org/ArmEabiPort#Structpackingandalignment // Stack alignment // // The ARM EABI requires 8-byte stack alignment at public function entry points, // compared to the previous 4-byte alignment. #ifdef USE_NEWLIB char *cptr = (char *)varg; // lint !e740 !e826 convert to double pointer uint caddr = (uint)cptr; if ((caddr & 0xF) != 0) { cptr += 4; } double *dblptr = (double *)cptr; // lint !e740 !e826 convert to double pointer #else double *dblptr = (double *)varg; // lint !e740 !e826 convert to double pointer #endif double dbl = *dblptr++; // increment double pointer varg = (int *)dblptr; // lint !e740 copy updated pointer back to base pointer char bfr[81]; // unsigned slen = dbl2stri(bfr, dbl, dec_width); // stuff_talkf("[%s], width=%u, dec_width=%u\n", bfr, width, dec_width) ; pc += prints(out, bfr, width, pad); use_leading_plus = 0; // reset this flag after printing one value } break; default: printchar(out, '%'); printchar(out, *format); use_leading_plus = 0; // reset this flag after printing one value break; } } else { // if (*format == '\\') { // // } else out_lbl: printchar(out, *format); ++pc; } } // for each char in format string if (out) { // lint !e850 **out = '\0'; } return pc; } // **************************************************************************** int stringf(char *out, const char *format, ...) { // create a pointer into the stack. // Thematically this should be a void*, since the actual usage of the // pointer will vary. However, int* works fine too. // Either way, the called function will need to re-cast the pointer // for any type which isn't sizeof(int) int *varg = (int *)(char *)(&format); return print(&out, varg); } int printf(const char *format, ...) { int *varg = (int *)(char *)(&format); return print(0, varg); } int sprintf(char *out, const char *format, ...) { int *varg = (int *)(char *)(&format); return print(&out, varg); } /** * @} */