-/*!
+/**
* \file
* <!--
- * Copyright 2003, 2004 Develer S.r.l. (http://www.develer.com/)
- * This file is part of DevLib - See devlib/README for information.
+ * This file is part of BeRTOS.
+ *
+ * Bertos is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * As a special exception, you may use this file as part of a free software
+ * library without restriction. Specifically, if other files instantiate
+ * templates or use macros or inline functions from this file, or you compile
+ * this file and link it with other files to produce an executable, this
+ * file does not by itself cause the resulting executable to be covered by
+ * the GNU General Public License. This exception does not however
+ * invalidate any other reasons why the executable file might be covered by
+ * the GNU General Public License.
+ *
+ * Copyright 2003, 2004, 2005 Develer S.r.l. (http://www.develer.com/)
+ *
* -->
*
* \version $Id$
* width and precision arguments.
*/
-/*
- * $Log$
- * Revision 1.6 2004/07/30 14:34:10 rasky
- * Vari fix per documentazione e commenti
- * Aggiunte PP_CATn e STATIC_ASSERT
- *
- * Revision 1.5 2004/07/29 22:57:09 bernie
- * Switch to new-style config handling.
- *
- * Revision 1.4 2004/07/21 00:20:20 bernie
- * Allow completely disabling printf()-like formatter.
- *
- * Revision 1.3 2004/07/18 22:00:15 bernie
- * Reorganize configuration parameters to match DevLib's convention.
- *
- * Revision 1.2 2004/06/03 11:27:09 bernie
- * Add dual-license information.
- *
- * Revision 1.1 2004/05/23 15:43:16 bernie
- * Import mware modules.
- *
- */
#include "formatwr.h"
-#include <compiler.h> /* progmem macros */
-#include <config.h> /* CONFIG_ macros */
+#include <mware/pgm.h>
+#include <mware/hex.h>
+#include <cfg/debug.h> /* ASSERT */
+#include <appconfig.h> /* CONFIG_ macros */
+
+#ifndef CONFIG_PRINTF_N_FORMATTER
+ /** Disable the arcane %n formatter. */
+ #define CONFIG_PRINTF_N_FORMATTER 0
+#endif
+
+#ifndef CONFIG_PRINTF_OCTAL_FORMATTER
+ /** Disable the %o formatter. */
+ #define CONFIG_PRINTF_OCTAL_FORMATTER 0
+#endif
+
+/* True if we must keep a count of the number of characters we print. */
+#define CONFIG_PRINTF_COUNT_CHARS (CONFIG_PRINTF_RETURN_COUNT || CONFIG_PRINTF_N_FORMATTER)
#if CONFIG_PRINTF
#if CONFIG_PRINTF > PRINTF_NOFLOAT
-#include <float.h>
-#endif /* CONFIG_PRINTF > PRINTF_NOFLOAT */
+ #include <float.h>
+
+ /* Maximum precision for floating point values */
+ typedef long double max_float_t;
-#ifndef FRMWRI_BUFSIZE
-/*bernie: save some memory, who cares about floats with lots of decimals? */
-/*#define FRMWRI_BUFSIZE 134*/
-#define FRMWRI_BUFSIZE 32
+ /*bernie: save some memory, who cares about floats with lots of decimals? */
+ #define FRMWRI_BUFSIZE 134
+ #warning 134 is too much, the code must be fixed to have a lower precision limit
+#else
+ /*
+ * Conservative estimate. Should be (probably) 12 (which is the size necessary
+ * to represent (2^32-1) in octal plus the sign bit.
+ */
+ #define FRMWRI_BUFSIZE 16
#endif
+/* Probably useful for fancy microcontrollers such as the PIC, nobody knows. */
#ifndef MEM_ATTRIBUTE
#define MEM_ATTRIBUTE
#endif
#if CONFIG_PRINTF > PRINTF_NOFLOAT
-static char *float_conversion(MEM_ATTRIBUTE long double value,
+static char *float_conversion(MEM_ATTRIBUTE max_float_t value,
MEM_ATTRIBUTE short nr_of_digits,
MEM_ATTRIBUTE char *buf,
MEM_ATTRIBUTE char format_flag,
MEM_ATTRIBUTE char g_flag,
- MEM_ATTRIBUTE char alternate_flag)
+ MEM_ATTRIBUTE bool alternate_flag)
{
MEM_ATTRIBUTE char *cp;
MEM_ATTRIBUTE char *buf_pointer;
g_flag = 0;
else
/* %G - Removal of trailing zeros */
- alternate_flag = 1;
+ alternate_flag = true;
}
/* %e or %E */
i = dec_point_pos;
while (i <= nr_of_digits )
{
- value -= (long double)(n = (short)value); /* n=Digit value=Remainder */
+ value -= (max_float_t)(n = (short)value); /* n=Digit value=Remainder */
value *= 10; /* Prepare for next shot */
*buf_pointer++ = n + '0';
if ( ! i++ && (nr_of_digits || alternate_flag))
#endif /* CONFIG_PRINTF > PRINTF_NOFLOAT */
-/*!
+/**
* This routine forms the core and entry of the formatter.
*
* The conversion performed conforms to the ANSI specification for "printf".
MEM_ATTRIBUTE static char bad_conversion[] = "???";
MEM_ATTRIBUTE static char null_pointer[] = "<NULL>";
- MEM_ATTRIBUTE char format_flag;
MEM_ATTRIBUTE int precision;
MEM_ATTRIBUTE int n;
- MEM_ATTRIBUTE int field_width, nr_of_chars;
- MEM_ATTRIBUTE char plus_space_flag, left_adjust, l_L_modifier;
- MEM_ATTRIBUTE char h_modifier, alternate_flag;
- MEM_ATTRIBUTE char nonzero_value;
- MEM_ATTRIBUTE unsigned long ulong, div_factor;
+#if CONFIG_PRINTF_COUNT_CHARS
+ MEM_ATTRIBUTE int nr_of_chars;
+#endif
+ MEM_ATTRIBUTE int field_width;
+ MEM_ATTRIBUTE char format_flag;
+ enum PLUS_SPACE_FLAGS {
+ PSF_NONE, PSF_PLUS, PSF_MINUS
+ };
+ enum DIV_FACTOR {
+ DIV_DEC, DIV_HEX,
+#if CONFIG_PRINTF_OCTAL_FORMATTER
+ DIV_OCT,
+#endif
+ };
+ MEM_ATTRIBUTE struct {
+ enum PLUS_SPACE_FLAGS plus_space_flag : 2;
+#if CONFIG_PRINTF_OCTAL_FORMATTER
+ enum DIV_FACTOR div_factor : 2;
+#else
+ enum DIV_FACTOR div_factor : 1;
+#endif
+ bool left_adjust : 1;
+ bool l_L_modifier : 1;
+ bool h_modifier : 1;
+ bool alternate_flag : 1;
+ bool nonzero_value : 1;
+ bool zeropad : 1;
+#if CPU_HARVARD
+ bool progmem : 1;
+#endif
+ } flags;
+ MEM_ATTRIBUTE unsigned long ulong;
#if CONFIG_PRINTF > PRINTF_NOFLOAT
- MEM_ATTRIBUTE long double fvalue;
+ MEM_ATTRIBUTE max_float_t fvalue;
#endif
MEM_ATTRIBUTE char *buf_pointer;
MEM_ATTRIBUTE char *ptr;
MEM_ATTRIBUTE const char *hex;
- MEM_ATTRIBUTE char zeropad;
MEM_ATTRIBUTE char buf[FRMWRI_BUFSIZE];
+#if CONFIG_PRINTF_COUNT_CHARS
nr_of_chars = 0;
+#endif
for (;;) /* Until full format string read */
{
while ((format_flag = PGM_READ_CHAR(format++)) != '%') /* Until '%' or '\0' */
{
if (!format_flag)
+#if CONFIG_PRINTF_RETURN_COUNT
return (nr_of_chars);
+#else
+ return 0;
+#endif
put_one_char(format_flag, secret_pointer);
+#if CONFIG_PRINTF_COUNT_CHARS
nr_of_chars++;
+#endif
}
if (PGM_READ_CHAR(format) == '%') /* %% prints as % */
{
format++;
put_one_char('%', secret_pointer);
+#if CONFIG_PRINTF_COUNT_CHARS
nr_of_chars++;
+#endif
continue;
}
- plus_space_flag = left_adjust = alternate_flag = zeropad = 0;
+ flags.left_adjust = false;
+ flags.alternate_flag = false;
+ flags.plus_space_flag = PSF_NONE;
+ flags.zeropad = false;
+#if CPU_HARVARD
+ flags.progmem = false;
+#endif
ptr = buf_pointer = &buf[0];
- hex = "0123456789ABCDEF";
+ hex = HEX_tab;
/* check for leading '-', '+', ' ','#' or '0' flags */
for (;;)
switch (PGM_READ_CHAR(format))
{
case ' ':
- if (plus_space_flag)
+ if (flags.plus_space_flag)
goto NEXT_FLAG;
case '+':
- plus_space_flag = PGM_READ_CHAR(format);
+ flags.plus_space_flag = PSF_PLUS;
goto NEXT_FLAG;
case '-':
- left_adjust++;
+ flags.left_adjust = true;
goto NEXT_FLAG;
case '#':
- alternate_flag++;
+ flags.alternate_flag = true;
goto NEXT_FLAG;
case '0':
- zeropad++;
+ flags.zeropad = true;
goto NEXT_FLAG;
}
break;
if (field_width < 0)
{
field_width = -field_width;
- left_adjust++;
+ flags.left_adjust = true;
}
format++;
}
field_width = field_width * 10 + (PGM_READ_CHAR(format++) - '0');
}
- if (left_adjust)
- zeropad = 0;
+ if (flags.left_adjust)
+ flags.zeropad = false;
/* Optional precision (or '*') */
if (PGM_READ_CHAR(format) == '.')
* decimal point, "precision" will be -1.
*/
- l_L_modifier = h_modifier = 0;
+ flags.l_L_modifier = false;
+ flags.h_modifier = false;
- /* Optional 'l','L' r 'h' modifier? */
+ /* Optional 'l','L','z' or 'h' modifier? */
switch (PGM_READ_CHAR(format))
{
case 'l':
case 'L':
- l_L_modifier++;
+ case 'z':
+ flags.l_L_modifier = true;
format++;
break;
case 'h':
- h_modifier++;
+ flags.h_modifier = true;
format++;
break;
}
*/
switch (format_flag = PGM_READ_CHAR(format++))
{
-#if 0 /* bernie */
+#if CONFIG_PRINTF_N_FORMATTER
case 'n':
if (sizeof(short) != sizeof(int))
{
{
if (h_modifier)
*va_arg(ap, short *) = nr_of_chars;
- else if (l_L_modifier)
+ else if (flags.l_L_modifier)
*va_arg(ap, long *) = nr_of_chars;
else
*va_arg(ap, int *) = nr_of_chars;
}
else
{
- if (l_L_modifier)
+ if (flags.l_L_modifier)
*va_arg(ap, long *) = nr_of_chars;
else
*va_arg(ap, int *) = nr_of_chars;
ptr++;
break;
+ /* Custom formatter for strings in program memory. */
+ case 'S':
+#if CPU_HARVARD
+ flags.progmem = true;
+#endif
+ /* Fall trough */
+
case 's':
if ( !(buf_pointer = va_arg(ap, char *)) )
buf_pointer = null_pointer;
if (precision < 0)
precision = 10000;
- for (n=0; *buf_pointer++ && n < precision; n++)
- ;
- ptr = --buf_pointer;
- buf_pointer -= n;
+
+ /*
+ * Move `ptr' to the last character of the
+ * string that will be actually printed.
+ */
+ ptr = buf_pointer;
+#if CPU_HARVARD
+ if (flags.progmem)
+ {
+ for (n=0; pgm_read_char(ptr) && n < precision; n++)
+ ++ptr;
+ }
+ else
+#endif
+ for (n=0; *ptr && n < precision; n++)
+ ++ptr;
break;
+#if CONFIG_PRINTF_OCTAL_FORMATTER
case 'o':
- if (alternate_flag && !precision)
+ if (flags.alternate_flag && !precision)
precision++;
+#endif
case 'x':
- hex = "0123456789abcdef";
+ hex = hex_tab;
case 'u':
case 'p':
case 'X':
#if defined(__AVR__) || defined(__I196__) /* 16bit pointers */
ulong = (unsigned long)(unsigned short)va_arg(ap, char *);
#else /* 32bit pointers */
- ulong = (unsigned long)va_arg(ap, char *);
+ ulong = (unsigned long)va_arg(ap, char *);
#endif /* 32bit pointers */
- else if (sizeof(short) == sizeof(int))
- ulong = l_L_modifier ?
- va_arg(ap, unsigned long) : (unsigned long)va_arg(ap, unsigned int);
+ else if (flags.l_L_modifier)
+ ulong = va_arg(ap, unsigned long);
+ else if (flags.h_modifier)
+ ulong = (unsigned long)(unsigned short)va_arg(ap, unsigned int);
else
- ulong = h_modifier ?
- (unsigned long)(unsigned short) va_arg(ap, int)
- : (unsigned long)va_arg(ap, int);
- div_factor = (format_flag == 'o') ?
- 8 : (format_flag == 'u') ? 10 : 16;
- plus_space_flag = 0;
+ ulong = va_arg(ap, unsigned int);
+
+ flags.div_factor =
+#if CONFIG_PRINTF_OCTAL_FORMATTER
+ (format_flag == 'o') ? DIV_OCT :
+#endif
+ (format_flag == 'u') ? DIV_DEC : DIV_HEX;
+ flags.plus_space_flag = PSF_NONE;
goto INTEGRAL_CONVERSION;
case 'd':
case 'i':
- if (sizeof(short) == sizeof(long))
- {
- if ( (long)(ulong = va_arg(ap, unsigned long)) < 0)
- {
- plus_space_flag = '-';
- ulong = (unsigned long)(-((signed long)ulong));
- }
- }
- else if (sizeof(short) == sizeof(int))
- {
- if ( (long)(ulong = l_L_modifier ?
- va_arg(ap,unsigned long) : (unsigned long)va_arg(ap,int)) < 0)
- {
- plus_space_flag = '-';
- ulong = (unsigned long)(-((signed long)ulong));
- }
- }
+ if (flags.l_L_modifier)
+ ulong = (unsigned long)(long)va_arg(ap, long);
else
+ ulong = (unsigned long)(long)va_arg(ap, int);
+
+ /* Extract sign */
+ if ((signed long)ulong < 0)
{
- if ( (signed long)(ulong = (unsigned long) (h_modifier ?
- (short) va_arg(ap, int) : va_arg(ap,int))) < 0)
- {
- plus_space_flag = '-';
- ulong = (unsigned long)(-((signed long)ulong));
- }
+ flags.plus_space_flag = PSF_MINUS;
+ ulong = (unsigned long)(-((signed long)ulong));
}
- div_factor = 10;
+
+ flags.div_factor = DIV_DEC;
/* Now convert to digits */
INTEGRAL_CONVERSION:
ptr = buf_pointer = &buf[FRMWRI_BUFSIZE - 1];
- nonzero_value = (ulong != 0);
+ flags.nonzero_value = (ulong != 0);
/* No char if zero and zero precision */
- if (precision != 0 || nonzero_value)
- do
- *--buf_pointer = hex[ulong % div_factor];
- while (ulong /= div_factor);
+ if (precision != 0 || flags.nonzero_value)
+ {
+ switch (flags.div_factor)
+ {
+ case DIV_DEC:
+ do
+ *--buf_pointer = hex[ulong % 10];
+ while (ulong /= 10);
+ break;
+
+ case DIV_HEX:
+ do
+ *--buf_pointer = hex[ulong % 16];
+ while (ulong /= 16);
+ break;
+#if CONFIG_PRINTF_OCTAL_FORMATTER
+ case DIV_OCT:
+ do
+ *--buf_pointer = hex[ulong % 8];
+ while (ulong /= 8);
+ break;
+#endif
+ }
+ }
/* "precision" takes precedence */
if (precision < 0)
- if (zeropad)
- precision = field_width - (plus_space_flag != 0);
+ if (flags.zeropad)
+ precision = field_width - (flags.plus_space_flag != PSF_NONE);
while (precision > (int)(ptr - buf_pointer))
*--buf_pointer = '0';
- if (alternate_flag && nonzero_value)
+ if (flags.alternate_flag && flags.nonzero_value)
{
if (format_flag == 'x' || format_flag == 'X')
{
*--buf_pointer = format_flag;
*--buf_pointer = '0';
}
+#if CONFIG_PRINTF_OCTAL_FORMATTER
else if ((format_flag == 'o') && (*buf_pointer != '0'))
{
*--buf_pointer = '0';
}
+#endif
}
+ ASSERT(buf_pointer >= buf);
break;
#if CONFIG_PRINTF > PRINTF_NOFLOAT
{
precision = 6;
}
- if (sizeof(double) != sizeof(long double))
+
+ if (sizeof(double) != sizeof(max_float_t))
{
- if ( (fvalue = l_L_modifier ?
- va_arg(ap,long double) : va_arg(ap,double)) < 0)
- {
- plus_space_flag = '-';
- fvalue = -fvalue;
- }
+ fvalue = flags.l_L_modifier ?
+ va_arg(ap,max_float_t) : va_arg(ap,double);
}
- else if ( (fvalue = va_arg(ap,long double)) < 0)
+ else
+ fvalue = va_arg(ap,max_float_t);
+
+ if (fvalue < 0)
{
- plus_space_flag = '-';
+ flags.plus_space_flag = PSF_MINUS;
fvalue = -fvalue;
}
ptr = float_conversion (fvalue,
buf_pointer += field_width,
format_flag,
(char)n,
- alternate_flag);
- if (zeropad)
+ flags.alternate_flag);
+ if (flags.zeropad)
{
- precision = field_width - (plus_space_flag != 0);
+ precision = field_width - (flags.plus_space_flag != PSF_NONE);
while (precision > ptr - buf_pointer)
*--buf_pointer = '0';
}
break;
-#else /* CONFIG_PRINTF <= PRINTF_NOFLOAT */
- case 'g':
- case 'G':
- case 'f':
- case 'e':
- case 'E':
- ptr = buf_pointer = bad_conversion;
- while (*ptr)
- ptr++;
- break;
#endif /* CONFIG_PRINTF <= PRINTF_NOFLOAT */
case '\0': /* Really bad place to find NUL in */
default:
/* Undefined conversion! */
ptr = buf_pointer = bad_conversion;
- ptr += 3;
+ ptr += sizeof(bad_conversion) - 1;
break;
}
}
else
{
- n = field_width - precision - (plus_space_flag != 0);
+ n = field_width - precision - (flags.plus_space_flag != PSF_NONE);
}
/* emit any leading pad characters */
- if (!left_adjust)
+ if (!flags.left_adjust)
while (--n >= 0)
{
put_one_char(' ', secret_pointer);
+#if CONFIG_PRINTF_COUNT_CHARS
nr_of_chars++;
+#endif
}
/* emit flag characters (if any) */
- if (plus_space_flag)
+ if (flags.plus_space_flag)
{
- put_one_char(plus_space_flag, secret_pointer);
+ put_one_char(flags.plus_space_flag == PSF_PLUS ? '+' : '-', secret_pointer);
+#if CONFIG_PRINTF_COUNT_CHARS
nr_of_chars++;
+#endif
}
- /* emit the string itself */
- while (--precision >= 0)
+#if CPU_HARVARD
+ if (flags.progmem)
{
- put_one_char(*buf_pointer++, secret_pointer);
- nr_of_chars++;
+ while (--precision >= 0)
+ {
+ put_one_char(pgm_read_char(buf_pointer++), secret_pointer);
+#if CONFIG_PRINTF_COUNT_CHARS
+ nr_of_chars++;
+#endif
+ }
+ }
+ else
+#endif /* CPU_HARVARD */
+ {
+ /* emit the string itself */
+ while (--precision >= 0)
+ {
+ put_one_char(*buf_pointer++, secret_pointer);
+#if CONFIG_PRINTF_COUNT_CHARS
+ nr_of_chars++;
+#endif
+ }
}
/* emit trailing space characters */
- if (left_adjust)
+ if (flags.left_adjust)
while (--n >= 0)
{
put_one_char(' ', secret_pointer);
+#if CONFIG_PRINTF_COUNT_CHARS
nr_of_chars++;
+#endif
}
}
#else /* PRINTF_REDUCED starts here */
#if CONFIG_PRINTF > PRINTF_NOMODIFIERS
- char l_modifier, h_modifier;
+ bool l_modifier, h_modifier;
unsigned long u_val, div_val;
#else
unsigned int u_val, div_val;
/*=================================*/
/* Optional 'l' or 'h' modifiers ? */
/*=================================*/
- l_modifier = h_modifier = 0;
+ l_modifier = h_modifier = false;
switch (PGM_READ_CHAR(format))
{
case 'l':
- l_modifier = 1;
+ l_modifier = true;
format++;
break;
case 'h':
- h_modifier = 1;
+ h_modifier = true;
format++;
break;
}