diff options
Diffstat (limited to 'nuttx/lib/lib_libvsprintf.c')
-rw-r--r-- | nuttx/lib/lib_libvsprintf.c | 854 |
1 files changed, 854 insertions, 0 deletions
diff --git a/nuttx/lib/lib_libvsprintf.c b/nuttx/lib/lib_libvsprintf.c new file mode 100644 index 000000000..da03f7f31 --- /dev/null +++ b/nuttx/lib/lib_libvsprintf.c @@ -0,0 +1,854 @@ +/************************************************************ + * lib_libvsprintf.c + * + * Copyright (C) 2007 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt <spudmonkey@racsa.co.cr> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name Gregory Nutt nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ************************************************************/ + +/************************************************************ + * Compilation Switches + ************************************************************/ + +/************************************************************ + * Included Files + ************************************************************/ + +#include <sys/types.h> +#include <stdio.h> +#include <string.h> + +#include "lib_internal.h" + +/************************************************************ + * Definitions + ************************************************************/ + +enum +{ + FMT_RJUST = 0, /* Default */ + FMT_LJUST, + FMT_RJUST0, + FMT_CENTER +}; + +/************************************************************ + * Private Type Declarations + ************************************************************/ + +/************************************************************ + * Private Function Prototypes + ************************************************************/ + +static void utodec(char **pp, unsigned int n); +static void _utodec(char **pp, unsigned int n); +static void utohex(char **pp, unsigned int n); +static void _utohex(char **pp, unsigned int n); +static void utooct(char **pp, unsigned int n); +static void _utooct(char **pp, unsigned int n); +static void utobin(char **pp, unsigned int n); +static void _utobin(char **pp, unsigned int n); + +#ifdef CONFIG_HAVE_LONG_LONG +static void llutodec(char **pp, unsigned long long n); +static void _llutodec(char **pp, unsigned long long n); +static void llutohex(char **pp, unsigned long long n); +static void _llutohex(char **pp, unsigned long long n); +static void llutooct(char **pp, unsigned long long n); +static void _llutooct(char **pp, unsigned long long n); +static void llutobin(char **pp, unsigned long long n); +static void _llutobin(char **pp, unsigned long long n); +#endif + +/************************************************************ + * Global Constant Data + ************************************************************/ + +/************************************************************ + * Global Variables + ************************************************************/ + +/************************************************************ + * Private Constant Data + ************************************************************/ + +static const char g_nullstring[] = "(null)"; + +/************************************************************ + * Private Variables + ************************************************************/ + +/************************************************************ + * Private Functions + ************************************************************/ + +static void utodec(char **pp, unsigned int n) +{ + _utodec(pp, n); + **pp = 0; +} + +static void _utodec(char **pp, unsigned int n) +{ + unsigned int remainder = n % 10; + unsigned int dividend = n / 10; + + if (dividend) + { + _utodec(pp, dividend); + } + + **pp = (char)(remainder + (unsigned int)'0'); + (*pp)++; +} + +static void utohex(char **pp, unsigned int n) +{ + _utohex(pp, n); + **pp = 0; +} + +static void _utohex(char **pp, unsigned int n) +{ + unsigned int remainder = n & 0xf; + unsigned int dividend = n >> 4; + + if (dividend) + { + _utohex(pp, dividend); + } + + if (remainder < 10) + { + **pp = (char)(remainder + (unsigned int)'0'); + } + else + { + **pp = (char)(remainder + ((unsigned int)'a' - 10)); + } + (*pp)++; +} + +static void utooct(char **pp, unsigned int n) +{ + _utooct(pp, n); + **pp = 0; +} + +static void _utooct(char **pp, unsigned int n) +{ + unsigned int remainder = n & 0x7; + unsigned int dividend = n >> 3; + + if (dividend) + { + _utooct(pp, dividend); + } + + **pp = (char)(remainder + (unsigned int)'0'); + (*pp)++; +} + +static void utobin(char **pp, unsigned int n) +{ + _utobin(pp, n); + **pp = 0; +} + +static void _utobin(char **pp, unsigned int n) +{ + unsigned int remainder = n & 1; + unsigned int dividend = n >> 1; + + if (dividend) + { + _utobin(pp, dividend); + } + + **pp = (char)(remainder + (unsigned int)'0'); + (*pp)++; +} + +#ifdef CONFIG_HAVE_LONG_LONG +static void llutodec(char **pp, unsigned long long n) +{ + _llutodec(pp, n); + **pp = 0; +} + +static void _llutodec(char **pp, unsigned long long n) +{ + unsigned int remainder = n % 10; + unsigned long long dividend = n / 10; + + if (dividend) + { + _llutodec(pp, dividend); + } + + **pp = (char)(remainder + (unsigned int)'0'); + (*pp)++; +} + +static void llutohex(char **pp, unsigned long long n) +{ + _llutohex(pp, n); + **pp = 0; +} + +static void _llutohex(char **pp, unsigned long long n) +{ + unsigned int remainder = n & 0xf; + unsigned long long dividend = n >> 4; + if (dividend) + { + _llutohex(pp, dividend); + } + + if (remainder < 10) + { + **pp = (char)(remainder + (unsigned int)'0'); + } + else + { + **pp = (char)(remainder + ((unsigned int)'a' - 10)); + } + (*pp)++; +} + +static void llutooct(char **pp, unsigned long long n) +{ + _llutooct(pp, n); + **pp = 0; +} + +static void _llutooct(char **pp, unsigned long long n) +{ + unsigned int remainder = n & 0x7; + unsigned long long dividend = n >> 3; + + if (dividend) + { + _llutooct(pp, dividend); + } + + **pp = (char)(remainder + (unsigned int)'0'); + (*pp)++; +} + +static void llutobin(char **pp, unsigned long long n) +{ + _llutobin(pp, n); + **pp = 0; +} + +static void _llutobin(char **pp, unsigned long long n) +{ + unsigned int remainder = n & 1; + unsigned long long dividend = n >> 1; + + if (dividend) + { + _llutobin(pp, dividend); + } + + **pp = (char)(remainder + (unsigned int)'0'); + (*pp)++; +} +#endif /* CONFIG_HAVE_LONG_LONG */ + +/************************************************************ + * Public Functions + ************************************************************/ + +/************************************************************ + * lib_vsprintf + ************************************************************/ + +int lib_vsprintf(struct lib_stream_s *obj, const char *src, va_list ap) +{ + char *ptmp; + char tmp[40]; + const char *pfmt; + unsigned int n; + int fmt, width, trunc, tmpwidth; + int showplus, altform, longlong; + int hasdot, hasasteriskwidth, hasasterisktrunc; + + for (; *src; src++) + { + /* Just copy regular characters */ + + if (*src != '%') + { + obj->put(obj, *src); + } + else + { + /* We have found a format specifier. Move past it. */ + + pfmt = src; + src++; + + fmt = FMT_RJUST; /* Assume right justification. */ + width = trunc = 0; + showplus = altform = longlong = 0; + hasdot = hasasteriskwidth = hasasterisktrunc = 0; + + /* Process each format qualifier. */ + + for (; *src; src++) + { + /* Break out of the loop when the format is known. */ + + if (strchr("diuxXpobeEfgGlLsc%", *src)) + { + break; + } + + /* Check for left justification. */ + + else if (*src == '-') + { + fmt = FMT_LJUST; + } + + /* Check for leading zero fill right justification. */ + + else if (*src == '0') + { + fmt = FMT_RJUST0; + } +#if 0 + /* Center justification. */ + + else if (*src == '~') + { + fmt = FMT_CENTER; + } +#endif + + else if (*src == '*') + { + int value = va_arg(ap, int); + if (hasdot) + { + trunc = value; + hasasterisktrunc = 1; + } + else + { + width = value; + hasasteriskwidth = 1; + } + } + + /* Check for field width */ + + else if (((*src) >= '1') && ((*src) <= '9')) + { + /* Accumulate the field width integer. */ + + n = ((int)(*src)) - (int)'0'; + for (src++; (((*src) >= '0') && ((*src) <= '9')); src++) + { + n = 10*n + (((int)(*src)) - (int)'0'); + } + + if (hasdot) trunc = n; + else width = n; + + /* Back up to the last digit. */ + + src--; + } + + /* Check for a decimal point. */ + + else if (*src == '.') + { + hasdot = 1; + } + + /* Check for leading plus sign. */ + + else if (*src == '+') + { + showplus = 1; + } + + /* Check for alternate form. */ + + else if (*src == '#') + { + altform = 1; + } + } + + /* "%%" means that a literal '%' was intended (instead of a format + * specification). + */ + + if (*src == '%') + { + obj->put(obj, '%'); + } + + /* Check for the string format. */ + + else if (*src == 's') + { + /* Just concatenate the string into the output */ + + ptmp = va_arg(ap, char *); + if (!ptmp) ptmp = (char*)g_nullstring; + + while(*ptmp) + { + obj->put(obj, *ptmp); + ptmp++; + } + } + + /* Check for the character output */ + + else if (*src == 'c') + { + /* Just copy the character into the output. */ + + n = va_arg(ap, int); + obj->put(obj, n); + } + else + { + /* Check for the long long prefix. */ + + if (*src == 'L') + { + longlong = 1; + ++src; + } + else if (*src == 'l') + { + if (*++src == 'l') + { + longlong = 1; + ++src; + } + } + + /* Get the ascii format into the tmp[] buffer. */ + + ptmp = tmp; + + /* Handle integer conversions */ + + if (strchr("diuxXpob", *src)) + { +#ifdef CONFIG_HAVE_LONG_LONG + if ((longlong) && (*src != 'p')) + { + /* Extract the long long value. */ + + long long lln = va_arg(ap, long long); + + /* Perform the integer conversion according to the + * format specifier + */ + + switch (*src) + { + case 'd': + case 'i': + /* Signed base 10 */ + { + /* Check for leading +/- */ + + if (lln < 0) + { + *ptmp++ = '-'; + *ptmp = 0; + lln = -lln; + } + else if (showplus) + { + *ptmp++ = '+'; + *ptmp = 0; + } + + /* Convert the unsigned value to a string. */ + + llutodec(&ptmp, (unsigned long long)lln); + } + break; + + case 'u': + /* Unigned base 10 */ + { + /* Check for leading + */ + + if (showplus) + { + *ptmp++ = '+'; + *ptmp = 0; + } + + /* Convert the unsigned value to a string. */ + + llutodec(&ptmp, (unsigned long long)lln); + } + break; + + case 'p': + case 'x': + case 'X': + /* Hexadecimal */ + { + /* Check for alternate form */ + + if (altform) + { + /* Prefix the number with "0x" */ + + *ptmp++ = '0'; + *ptmp++ = 'x'; + *ptmp = 0; + } + + /* Convert the unsigned value to a string. */ + + llutohex(&ptmp, (unsigned long long)lln); + + /* Check for upper case conversion. */ + + if ((*src) == 'X') + { + for (ptmp = tmp; *ptmp; ptmp++) + { + if (((*ptmp) >= 'a') && ((*ptmp) <= 'z')) + { + /* Convert to upper case. */ + + *ptmp += (char)((int)'A' - (int)'a'); + } + } + } + } + break; + + case 'o': + /* Octal */ + { + /* Check for alternate form */ + + if (altform) + { + /* Prefix the number with '0' */ + + *ptmp++ = '0'; + *ptmp = 0; + } + + /* Convert the unsigned value to a string. */ + + llutooct(&ptmp, (unsigned long long)lln); + } + break; + + case 'b': + /* Binary */ + { + /* Convert the unsigned value to a string. */ + + llutobin(&ptmp, (unsigned long long)lln); + } + break; + + default: + break; + } + } + else + { +#endif /* CONFIG_HAVE_LONG_LONG */ + /* Extract the integer value */ + + n = va_arg(ap, int); + + /* Perform the integer conversion according to the + * format specifier + */ + + switch (*src) + { + case 'd': + case 'i': + /* Signed base 10 */ + { + /* Check for leading +/- */ + + if ((int)n < 0) + { + *ptmp++ = '-'; + *ptmp = 0; + n = -n; + } + else if (showplus) + { + *ptmp++ = '+'; + *ptmp = 0; + } + + /* Convert the unsigned value to a string. */ + + utodec(&ptmp, (unsigned int)n); + } + break; + + case 'u': + /* Unigned base 10 */ + { + /* Check for leading + */ + + if (showplus) + { + *ptmp++ = '+'; + *ptmp = 0; + } + + /* Convert the unsigned value to a string. */ + + utodec(&ptmp, (unsigned int)n); + } + break; + + case 'x': + case 'X': + case 'p': + /* Hexadecimal */ + { + /* Check for alternate form */ + + if (altform) + { + /* Prefix the number with "0x" */ + + *ptmp++ = '0'; + *ptmp++ = 'x'; + *ptmp = 0; + } + + /* Convert the unsigned value to a string. */ + + utohex(&ptmp, (unsigned int)n); + + /* Check for upper case conversion. */ + + if ((*src) == 'X') + { + for (ptmp = tmp; *ptmp; ptmp++) + { + if (((*ptmp) >= 'a') && ((*ptmp) <= 'z')) + { + /* Convert to upper case. */ + + *ptmp += (char)((int)'A' - (int)'a'); + } + } + } + else if ((*src) == 'p') + { + if ((!width) && (fmt == FMT_RJUST)) + { + if (altform) width = 10; + else width = 8; + + fmt = FMT_RJUST0; + } + } + } + break; + + case 'o': + /* Octal */ + { + /* Check for alternate form */ + + if (altform) + { + /* Prefix the number with '0' */ + + *ptmp++ = '0'; + *ptmp = 0; + } + + /* Convert the unsigned value to a string. */ + + utooct(&ptmp, (unsigned int)n); + } + break; + + case 'b': + /* Binary */ + { + /* Convert the unsigned value to a string. */ + + utobin(&ptmp, (unsigned int)n); + } + break; + + default: + break; + } +#ifdef CONFIG_HAVE_LONG_LONG + } +#endif /* CONFIG_HAVE_LONG_LONG */ + + /* Now, get the "real" field width of the integer value*/ + + tmpwidth = strlen(tmp); + if (width <= tmpwidth) + { + /* Just copy the string. */ + + for (ptmp = tmp; *ptmp; ) + { + obj->put(obj, *ptmp); + ptmp++; + } + } + else + { + /* Apply field justification to the integer value. */ + + switch (fmt) + { + default: + case FMT_RJUST: + for (n = width - tmpwidth; n; n--) + { + obj->put(obj, ' '); + } + + for (ptmp = tmp; *ptmp; ) + { + obj->put(obj, *ptmp); + ptmp++; + } + break; + + case FMT_RJUST0: + ptmp = tmp; + if (((*ptmp) == '-') || ((*ptmp) == '+')) + { + obj->put(obj, *ptmp); + ptmp++; + } + + for (n = width - tmpwidth; n; n--) + { + obj->put(obj, '0'); + } + + while (*ptmp) + { + obj->put(obj, *ptmp); + ptmp++; + } + break; + + case FMT_LJUST: + for (ptmp = tmp; *ptmp; ) + { + obj->put(obj, *ptmp); + ptmp++; + } + + for (n = width - tmpwidth; n; n--) + { + obj->put(obj, ' '); + } + break; + } + } + } + + /* Handle floating point conversions */ + + else if (strchr("eEfgG", *src)) + { + char tmpfmt[40]; + const char *psrc; + char *pdst; + double dbl; + + /* Reconstruct the floating point format. */ + + psrc = pfmt; + pdst = tmpfmt; + while (psrc <= src) *pdst++ = *psrc++; + *pdst = 0; + + /* Extract the floating point number. */ + + dbl = va_arg(ap, double); + + /* Then let the lib_sprintf do the work. */ + + if (hasasteriskwidth) + { + if (hasasterisktrunc) + { + lib_sprintf(obj, tmpfmt, width, trunc, dbl); + } + else + { + lib_sprintf(obj, tmpfmt, width, dbl); + } + } + else + { + if (hasasterisktrunc) + { + lib_sprintf(obj, tmpfmt, trunc, dbl); + } + else + { + lib_sprintf(obj, tmpfmt, dbl); + } + } + } + } + } + } + + return obj->nput; +} + |