avr-libc-dev
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[avr-libc-dev] Re: printf in avr-libc


From: Jörgen
Subject: [avr-libc-dev] Re: printf in avr-libc
Date: Thu, 12 Sep 2002 02:18:58 -0700 (PDT)

Hi Guys,
Here's a version of printf that I have tried to
optimize for AVR+GCC. Float is not supported. 
An extension - %z - takes strings from EEPROM. %s
takes ram string. PSTRs could easily be added.
All formatting should be supported.

Unfortunatly it is quite big (even with my
simplifications), but it is fairly complete and very
configurable.

Same flash code is used for sprintf_P and printf_P.
Same source code can be used for printf and printf_P.

I can contribute with doxygen documentation if you
want to include it in the library (if it is possible
according to the licenses).

One idea (not mine...) is to compile three versions of
the formatted_write function. formatted_write_small,
formatted_write_full and formatted_write_all. with
different capablities. At link you can set the weak
symbol _formatted_write to one of those three,
selecting how many features you need.

/Jörgen

--- Joerg Wunsch <address@hidden> wrote:
> As E. Weddington wrote:
> 
> > >  1) Mix PSTR and memory variables in one printf,
> i.e.:
> > >   kprintf("my pstr is %S and my ram str is
> %s\n", pstr, str);
> > > 
> > 
> > That would be a good extension to have (to mix
> different pointers 
> > like that). Can we incorporate that into an
> (s)printf()?
> 
> I'll keep it in mind.
> 
> -- 
> J"org Wunsch                                         Unix support engineer
> address@hidden       
> http://www.interface-systems.de/~j/
> 
> 
> _______________________________________________
> AVR-libc-dev mailing list
> address@hidden
>
http://mail.freesoftware.fsf.org/mailman/listinfo/avr-libc-dev



__________________________________________________
Do you Yahoo!?
Yahoo! News - Today's headlines
http://news.yahoo.com
#ifndef _PRINTF_P_H_
#define _PRINTF_P_H_

#include <pgmspace.h>
#include <io.h>




///The printf functions, uses callback to output character
int _formatted_write(const char*fmt0,void put_one_char(char, void *),void 
*secret_pointer, ...);

///for sprintf
void put_c_in_string (char c, void *ptr);

///sprintf function
#define sprintf(s,format, args...)   
_formatted_write(PSTR(format),put_c_in_string,&s, ## args)


///External sendchar function, for example UART
extern void sendchar(char c);

void put_c_to_sendchar (char c, void *ptr);

#define printf(format, args...)   
_formatted_write(PSTR(format),put_c_to_sendchar,0 , ## args)


//External dbg_sendchar, for example debug monitor or UART.
extern void dbg_sendchar(char data, void* not_used);

#define dbg_printf(format, args...)   
_formatted_write(PSTR(format),dbg_sendchar,0, ## args)


#endif
/* 
Copyright (C) 1993 Free Software Foundation

This file is part of the GNU IO Library.  This library 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, or (at your option)
any later version.

This library 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 library; see the file COPYING.  If not, write to the Free
Software Foundation, 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

As a special exception, if you link this library with files
compiled with a GNU compiler to produce an executable, this does not 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 (c) 1990 Regents of the University of California.
 * All rights reserved.
 *
 * 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. [rescinded 22 July 1999]
 * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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.
 */

/******************************************************************************
 This file is a patched version of printf called _printf_P
 It is made to work with avr-gcc for Atmel AVR MCUs.
 There are some differences from standard printf:
        1. There is no floating point support (with fp the code is about 8K!)
        2. Return type is void
        3. Format string must be in program memory (by using macro printf this 
is
           done automaticaly)
        4. %n is not implemented (just remove the comment around it if you need 
it)
        5. If LIGHTPRINTF is defined, the code is about 550 bytes smaller and 
the
           folowing specifiers are disabled :
                space # * . - + p s o O
        6. A function void uart_sendchar(char c) is used for output. The UART 
must
                be initialized before using printf.

 Alexander Popov
 address@hidden

 small modifications to make it work with liblcd_dip122.a
 replaced uart_sendchar with (*write_char)(char)
 2001 Michael Schaenzler                                                  
*******************************************************************************/
 
/**
 * @file
 * formatted printf
 *
 * @author Jörgen Birkler (mailto:address@hidden)
 * 
 * Even more updates and defines for AVR.
 * Evaluated the .list files and did some optimisations.
 * Using callback to output characters so code can be reused for 
 * sprintf, printf, printf_P, etc.
 * 
 * \par Configuration
 * Many options can be configured fo minimum RAM and FLASH usage.
 * Some configurations will use strlen() and memchr().
 * It will always use modulus (%) and division / from the std library, depending
 * on configuration it will use either unsigned long int or unsigned int
 * versions.
 *
 * \par Callback for char output
 * The characters are output using a callback function supplied
 * when calling the printf function. This will of course use more 
 * code but the advantage is that you can use the same function
 * for outputing to Debug monitor, UART and LCD.
 *
 * \par Extensions for string in EEPROM
 * You can enable type modifiers to read string from EEPROM.
 * This is very useful as it can save you some of the precious
 * FLASH memory. The type modifier for EEPROM strings is %z
 * Example:
 * {
 *   //FIXME:Insert code here
 *
 * }
 *
 */



///Configuration
//@{
//Base size (all enabled except %n): 2986

///Support the %n format
#define FORMATTED_WRITE_SUPPORT_N (0)

///Support * for wdith and precision flags
#define FORMATTED_WRITE_SUPPORT_STAR (0) //2908  (-78)

///Support the # flag to output '0x' or '0' for hex and octal numbers
#define FORMATTED_WRITE_SUPPORT_ALT (0) //2868 (-128)

///Support of outputing '(NULL)' if string ptr is NULL
#define FORMATTED_WRITE_SUPPORT_NULL_STRINGS (0) //2940 (-46)

///Support flags, precision and width modifiers
#define FORMATTED_WRITE_SUPPORT_FLAGS_WIDTH_PRECISION (1) //2188 (-798)

///Support of width/precision modifiers for strings
#define FORMATTED_WRITE_SUPPORT_PRECISION_FOR_STRINGS (0) //2896 (-90)

///Support both long, int and short parameters
#define FORMATTED_WRITE_SUPPORT_LONG_SHORT (0) //2734 (-250)

///Support for strings in EEPROM
#define FORMATTED_WRITE_SUPPORT_EEPROM_STRINGS (0)


//@}


//Check configuration
////////////////////////////

#ifdef FORMATTED_WRITE_SUPPORT_N
#ifdef FORMATTED_WRITE_SUPPORT_STAR 
#ifdef FORMATTED_WRITE_SUPPORT_ALT
#ifdef FORMATTED_WRITE_SUPPORT_NULL_STRINGS
#ifdef FORMATTED_WRITE_SUPPORT_FLAGS_WIDTH_PRECISION
#ifdef FORMATTED_WRITE_SUPPORT_PRECISION_FOR_STRINGS
#ifdef FORMATTED_WRITE_SUPPORT_LONG_SHORT
#ifdef FORMATTED_WRITE_SUPPORT_EEPROM_STRINGS
#define FORMATTED_WRITE_CONFIG_OK (1)
#endif
#endif
#endif
#endif
#endif
#endif
#endif
#endif


#ifndef FORMATTED_WRITE_CONFIG_OK
#error Configuration not complete. Review configuration
#endif



#include <string.h>
#include <stdarg.h>

#ifndef _WIN32
#include "printf_P.h"
#endif //_WIN32


#define PRINT_CHAR(c) {put_one_char(c,appl_p);length++;}



#define PRINT(ptr,len) { \
                unsigned int i; \
                for(i=len;i > 0;i--) \
                        PRINT_CHAR(*ptr++); \
        }

#define PRINT_EEPROM(ptr,len) { \
                unsigned int i; \
                for(i=len;i > 0;i--) \
                        PRINT_CHAR(eeprom_rb(ptr++)); \
        }



#define PAD_SP(howmany) { \
                unsigned int i; \
                for(i=howmany;i > 0;i--) \
                        PRINT_CHAR(' '); \
        } 

#define PAD_0(howmany) { \
                unsigned int i; \
                for(i=howmany;i > 0;i--) \
                        PRINT_CHAR('0'); \
        }


/// Macro for converting letters to digits
#define is_digit(c)     ((c)>='0' && (c)<='9')




/** @page FORMATTED_WRITE_FlagsModifiers Flags and Modifiers
 *
 * %[flags] [width] [.precision] [{h | l | I64 | L}]type
 *
 * [flags] = 
 * - '–' Left align the result within the given field width. Default:Right 
align.
 * - '+' Prefix the output value with a sign (+ or –) if the output value 
 *       is of a signed type. Default:Sign appears only for negative signed 
values (–).
 * - '0' If width is prefixed with 0, zeros are added until the minimum width 
 *       is reached. If 0 and – appear, the 0 is ignored. If 0 is specified 
 *       with an integer format (i, u, x, X, o, d) the 0 is ignored.  
Default:No padding.)
 * - ' ' Blank prefixes the output value with a blank if the output value is 
signed and
 *       positive; the blank is ignored if both the blank and + flags appear. 
Default:No blank appears.
 * - '#' When used with the o, x, or X format, the # flag prefixes any nonzero 
output
 *       value with 0, 0x, or 0X, respectively. Default:No blank appears.
 *       When used with the e, E, or f format, the # flag forces the output 
value 
 *       to contain a decimal point in all cases.  Default:Decimal point 
appears only if digits follow it.
 *       When used with the g or G format, the # flag forces the output value 
 *       to contain a decimal point in all cases and prevents the truncation of 
trailing zeros. 
 *       Ignored when used with c, d, i, u, or s. Default: Decimal point 
appears only
 *       if digits follow it. Trailing zeros are truncated. 
 *
 * [width] = decimal number. Or '*' if to be read from argument list
 *
 * [.precision] = decimal number or '*' if to be read from argument list
 *
 * [{h | l | I64 | L}] = type specifier for the argument list variable
 *
 * type =
 * - 'c' int or wint_t When used with printf functions, specifies a single-byte 
character; when used with wprintf functions, specifies a wide character. 
 * - 'C' int or wint_t When used with printf functions, specifies a wide 
character; when used with wprintf functions, specifies a single-byte character. 
 * - 'd' int Signed decimal integer. 
 * - 'i' int  Signed decimal integer. 
 * - 'o' int  Unsigned octal integer. 
 * - 'u' int  Unsigned decimal integer. 
 * - 'x' int Unsigned hexadecimal integer, using "abcdef." 
 * - 'X' int Unsigned hexadecimal integer, using "ABCDEF." 
 * - 'e' double Signed value having the form [ – ]d.dddd e [sign]ddd where d is 
a single decimal digit, dddd is one or more decimal digits, ddd is exactly 
three decimal digits, and sign is + or –. 
 * - 'E' double Identical to the e format except that E rather than e 
introduces the exponent. 
 * - 'f' double Signed value having the form [ – ]dddd.dddd, where dddd is one 
or more decimal digits. The number of digits before the decimal point depends 
on the magnitude of the number, and the number of digits after the decimal 
point depends on the requested precision. 
 * - 'g' double Signed value printed in f or e format, whichever is more 
compact for the given value and precision. The e format is used only when the 
exponent of the value is less than –4 or greater than or equal to the precision 
argument. Trailing zeros are truncated, and the decimal point appears only if 
one or more digits follow it. 
 * - 'G' double Identical to the g format, except that E, rather than e, 
introduces the exponent (where appropriate). 
 * - 'n' Pointer to integer  Number of characters successfully written so far 
to the stream or buffer; this value is stored in the integer whose address is 
given as the argument. 
 * - 'p' Pointer to void Prints the address of the argument in hexadecimal 
digits. 
 * - 's' String  When used with printf functions, specifies a 
single-byte–character string; when used with wprintf functions, specifies a 
wide-character string. Characters are printed up to the first null character or 
until the precision value is reached. 
 * - 'S' String When used with printf functions, specifies a wide-character 
string; when used with wprintf functions, specifies a single-byte–character 
string. Characters are printed up to the first null character or until the 
precision value is reached. 
 *
 */


///Flags used during conversion.
 


//Setup value type and flags
/////////////////////////////////////

//#define       LONGDBL         0x02            /* long double; unimplemented */

#if (FORMATTED_WRITE_SUPPORT_LONG_SHORT==1)
//Support for long and short types
typedef unsigned long value_t;
typedef signed long signed_value_t;
#define FLAG_LONGINT    0x01            // long integer
#define FLAG_SHORTINT    0x04           // short integer
#else //FORMATTED_WRITE_SUPPORT_LONG_SHORT
typedef unsigned int value_t;
typedef signed int signed_value_t;
#endif //FORMATTED_WRITE_SUPPORT_LONG_SHORT

#if (FORMATTED_WRITE_SUPPORT_FLAGS_WIDTH_PRECISION==1)
#if (FORMATTED_WRITE_SUPPORT_ALT==1)
#define FLAG_ALT                        0x08            // alternate form '0x' 
for hex and '0' for octal
#endif //(FORMATTED_WRITE_SUPPORT_ALT==1)
#define FLAG_LADJUST            0x10            // left adjusted
#define FLAG_ZEROPAD            0x20            // zero (as opposed to blank) 
pad 
#endif //(FORMATTED_WRITE_SUPPORT_FLAGS_WIDTH_PRECISION==1)
#define FLAG_HEXPREFIX  0x40            // add 0x or 0X prefix 

#if (FORMATTED_WRITE_SUPPORT_EEPROM_STRINGS==1)
#define FLAG_EEPROM_STRING 0x80
#endif //(FORMATTED_WRITE_SUPPORT_EEPROM_STRINGS==1)




/**
 * Size of decimal/hex/octal conversion buffer
 * Octal conversion will require the most characters since it's the smallest 
base
 * To get the # characters solve:
 * 8^chars >= 2^bits
 * bits = sizeof(value_t) * 8
 * 8^chars = (2^3)^chars = 2^(3*chars) >= 2^(bits) = 2^(sizeof(value_t)*8) =>
 *3*chars >= sizeof(value_t)*8 => chars >= sizeof(value_t)*8 / 3
 * Round up (sizeof(value_t)*8 + 2) / 3 >= sizeof(value_t)*8 / 3
 */
#define BUF_SIZE                ((sizeof(value_t)*8 + 2) / 3)


#ifndef FORMATTED_WRITE_FORMAT_READ

#define FORMATTED_WRITE_FORMAT_READ(ptr) PRG_RDB(ptr)

#endif FORMATTED_WRITE_FORMAT_READ




#define to_digit(_ch) ((_ch)-'0')




#ifndef FORMATTED_WRITE_FORMAT_STRING_TYPE
#define FORMATTED_WRITE_FORMAT_STRING_TYPE PGM_P
#endif //FORMATTED_WRITE_FORMAT_STRING_TYPE



int 
        _formatted_write(
                FORMATTED_WRITE_FORMAT_STRING_TYPE fmt0,
                void put_one_char(char, void *),
                void* appl_p, ...)      
{
        va_list ap;

        const char *fmt = fmt0; // format string 
        char ch;        // character from fmt 

        int length = 0; //Length of output characters

        va_start(ap, appl_p);
        
        // Scan the format for conversions (`%' character).
        for (;;)
        {
                ch = FORMATTED_WRITE_FORMAT_READ(fmt++);
                if (ch=='\0')
                {
                        va_end(ap);
                        return length;
                }
                else if (ch!='%')
                {
                        //Normal character
                        PRINT_CHAR(ch);
                }
                else
                {
                        unsigned char flags = 0x00;     // flags as above 
                        char sign = '\0';                                   // 
sign prefix (' ', '+', '-', or '\0')
#if (FORMATTED_WRITE_SUPPORT_FLAGS_WIDTH_PRECISION==1)

                        signed char width = 0;          // width from format 
(%8d), or 0 
                        signed char prec = -1;            // precision from 
format (%.3d), or -1 

                        for (;;)
                        {
                                ch = FORMATTED_WRITE_FORMAT_READ(fmt++);
                
                                //Read flags
                                //////////////////////////////
                                if (ch=='-')
                                {
                                        //Left align wihtin the width
                                        flags |= FLAG_LADJUST;
                                }
                                else if (ch=='+') 
                                {
                                        sign = '+';
                                }
                                
                                #if (FORMATTED_WRITE_SUPPORT_ALT==1)
                                else if (ch=='#') 
                                {
                                        flags |= FLAG_ALT;
                                }
                                #endif //#ifdef FORMATTED_WRITE_SUPPORT_ALT
                                
                                else if (ch==' ') 
                                {
                                        sign = ' ';
                                        /*
                                        * ``If the space and + flags both 
appear, the space
                                        * flag will be ignored.''
                                        *       -- ANSI X3J11
                                        */
                                        /*if (sign=='\0')
                                        {
                                                sign = ' ';
                                                goto rflag;
                                        }
                                        else if (ch=='*'||ch=='-') 
                                        {
                                                if (ch=='*') 
                                                        // ``A negative field 
width argument is taken as a
                                                        // - flag followed by a 
positive field width.''
                                                        //      -- ANSI X3J11
                                                        // They don't exclude 
field widths read from args.
                                                        
                                                        if ((width = va_arg(ap, 
int)) >= 0)
                                                                goto rflag;
                                                width = -width;
                                        }
                                        
                                        flags &= ~FLAG_ZEROPAD; // '-' disables 
'0' 
                                        //goto rflag; */
                                }
                                else if (ch=='0') 
                                {
                                        //0 is taken as a flag, not as the 
beginning of a field width.
                                        //-- ANSI X3J11
                                        if (!(flags & FLAG_LADJUST))
                                        {
                                                flags |= FLAG_ZEROPAD; /* '-' 
disables '0' */
                                        }
                                }
                                //Read width
                                ////////////////////////////
#if (FORMATTED_WRITE_SUPPORT_STAR==1)
                                else if (ch == '*') 
                                {
                                        width = (unsigned char)va_arg(ap, int);
                                }
#endif //FORMATTED_WRITE_SUPPORT_STAR
                                else if (is_digit(ch))
                                {
                                        signed char n = 0;
                                        do
                                        {
                                                n *= 10;
                                                n += to_digit(ch);
                                                ch = 
FORMATTED_WRITE_FORMAT_READ(fmt++);
                                        } while (is_digit(ch));
                                        fmt--;
                                        width = n;
                                }
                                //Read precision
                                ////////////////////////////
                                else if (ch=='.') 
                                {
                                        signed char n;
                                        ch = FORMATTED_WRITE_FORMAT_READ(fmt++);
#if (FORMATTED_WRITE_SUPPORT_STAR==1)
                                        if (ch == '*') 
                                        {
                                                n = (signed char)va_arg(ap, 
int);
                                        }
                                        else 
#endif //FORMATTED_WRITE_SUPPORT_STAR
                                        {
                                                n = 0;
                                                do 
                                                {
                                                        n = n*10 + to_digit(ch);
                                                        ch = 
FORMATTED_WRITE_FORMAT_READ(fmt++);
                                                } while (is_digit(ch));
                                                fmt--;
                                        }
                                        prec = n;
                                }
#if (FORMATTED_WRITE_SUPPORT_TYPESPECIFIER==1)
                                else if (ch=='h') 
                                {
                                        flags |= FLAG_SHORTINT;
                                }
                                else if (ch=='l') 
                                {
                                        flags |= FLAG_LONGINT;
                                } 
#endif //FORMATTED_WRITE_SUPPORT_TYPESPECIFIER
                                else 
                                {
                                        //End of flags
                                        //////////////////////

#else //FORMATTED_WRITE_SUPPORT_FLAGS_WIDTH_PRECISION
                        while (1)
                        {
                                ch = FORMATTED_WRITE_FORMAT_READ(fmt++);
                                {
#endif //FORMATTED_WRITE_SUPPORT_FLAGS_WIDTH_PRECISION

                                        char buf[BUF_SIZE];             // 
space for %c, %[diouxX], %[eEfgG]
                                        value_t value;  /* integer arguments 
%[diouxX] */
                                        unsigned char size;             // size 
of converted field or string
                                        const char* out_p;
                                        
#if (FORMATTED_WRITE_SUPPORT_LONG_SHORT==1)
                                        //Check capital letters
                                        if (!ch&~0x20 && ch!='X')
                                        {
                                                //Presume longint
                                                flags |= FLAG_LONGINT;
                                        }
#endif //(FORMATTED_WRITE_SUPPORT_LONG_SHORT==1)

                                        //Small letters
                                        ch|=0x20;

                                        //Check zero terminated
                                        if (ch == '\0')
                                        {
                                                return length;
                                        }
                                        else
                                        
                                        //%n construct not supported
                                        #if (FORMATTED_WRITE_SUPPORT_N==1)
                                        if (ch=='n') {
                                                if (flags & FLAG_LONGINT)
                                                        *va_arg(ap, long *) = 
ret;
                                                else if (flags & FLAG_SHORTINT)
                                                        *va_arg(ap, short *) = 
ret;
                                                else
                                                        *va_arg(ap, int *) = 
ret;
                                                continue;       // no output 
                                        }
                                        #endif //FORMATTED_WRITE_SUPPORT_N

                                        //
                                        if (ch == 'c')
                                        {
                                                buf[0] = (char)va_arg(ap, int);
                                                size = 1;
                                                sign = '\0';
                                                out_p = buf;
                                        }
                                        else if (ch == '%')
                                        {
                                                buf[0] = '%';
                                                size = 1;
                                                sign = '\0';
                                                out_p = buf;
                                        }
                                        else if (ch=='s')
                                        {
                                                // print a string from RAM
                                                const char* str = va_arg(ap, 
char *);

                                                //No sign for strings
                                                sign = '\0';

                                                #if 
(FORMATTED_WRITE_SUPPORT_NULL_STRINGS==1)
                                                if (str == NULL) 
                                                {
                                                        unsigned char i=0;
                                                        buf[i++] = '(';
                                                        buf[i++] = 'N';
                                                        buf[i++] = 'U';
                                                        buf[i++] = 'L';
                                                        buf[i++] = 'L';
                                                        buf[i++] = ')';
                                                        size = i;
                                                        out_p = buf;
                                                }
                                                else 
                                                #endif 
//FORMATTED_WRITE_SUPPORT_NULL_STRINGS
                                                {
                                                        out_p = str;
                                                        #if 
(FORMATTED_WRITE_SUPPORT_PRECISION_FOR_STRINGS==1 && 
FORMATTED_WRITE_SUPPORT_FLAGS_WIDTH_PRECISION==1)
                                                        if (prec >= 0) 
                                                        {
                                                                // can't use 
strlen; can only look for the
                                                                // NUL in the 
first 'prec' characters, and
                                                                // strlen() 
will go further.
                                                                while (*src++ 
!= '\0' && size < prec)
                                                                {
                                                                        size++;
                                                                }
                                                                /*
                                                                char *p = 
(char*)memchr(str, 0, prec);

                                                                if (p != NULL) {
                                                                        size = 
(unsigned char)(p - str);
                                                                        if 
(size > prec)
                                                                                
size = prec;
                                                                } 
                                                                else
                                                                {
                                                                        size = 
prec;
                                                                } */
                                                        } 
                                                        else
                                                        #endif 
//defined(FORMATTED_WRITE_SUPPORT_PRECISION_FOR_STRINGS) && 
defined(FORMATTED_WRITE_SUPPORT_FLAGS_WIDTH_PRECISION)
                                                        {
                                                                size = 
(unsigned char)strlen(str);
                                                        }
                                                }
                                        }
#if (FORMATTED_WRITE_SUPPORT_EEPROM_STRINGS==1)
                                        else if (ch=='z')
                                        {
                                                // str is an address into EEPROM
                                                const char* str = va_arg(ap, 
char *);

                                                //No sign for strings
                                                sign = '\0';

                                                size = 0;
                                                out_p = str;
                                                flags|=FLAG_EEPROM_STRING;

                                                #if 
(FORMATTED_WRITE_SUPPORT_PRECISION_FOR_STRINGS==1 && 
FORMATTED_WRITE_SUPPORT_FLAGS_WIDTH_PRECISION==1)
                                                if (prec >= 0) 
                                                {
                                                        // can't use strlen; 
can only look for the
                                                        // NUL in the first 
'prec' characters, and
                                                        // strlen() will go 
further.

                                                        while 
(eeprom_rb((unsigned int)str++) != '\0' && size < prec)
                                                        {
                                                                size++;
                                                        }
                                                } 
                                                else
                                                #endif 
//defined(FORMATTED_WRITE_SUPPORT_PRECISION_FOR_STRINGS) && 
defined(FORMATTED_WRITE_SUPPORT_FLAGS_WIDTH_PRECISION)
                                                {
                                                        while 
(eeprom_rb((unsigned int)str++) != '\0')
                                                        {
                                                                size++;
                                                        }
                                                }
                                        }
#endif //(FORMATTED_WRITE_SUPPORT_EEPROM_STRINGS==1)
                                        else {

                                                //presume one of %pdiux
                                                #define OCT 8
                                                #define DEC 10
                                                #define HEX 16
                                                unsigned char base;             
// base for [diouxX] conversion
        
                                                if (ch == 'p')
                                                {
                                                        base=HEX;
                                                        value = 
(value_t)va_arg(ap, void*);
                                                        sign = '$';
                                                }
                                                else if (ch=='d' || ch=='i')
                                                {
                                                        signed_value_t val;

                                                        //Signed decimal integer
                                                        base=DEC;

                                                        #if 
(FORMATTED_WRITE_SUPPORT_LONG_SHORT==1)
                                                        if (flags&FLAG_LONGINT) 
                                                        {
                                                                val = 
va_arg(ap, signed long);
                                                        }
                                                        else 
                                                        {
                                                                int _d = 
va_arg(ap, int);
                                                                val = 
(flags&FLAG_SHORTINT) ? (signed long)(signed short)_d : (signed long)_d;
                                                        }
                                                        #else 
//FORMATTED_WRITE_SUPPORT_LONG_SHORT
                                                        val = va_arg(ap, int);
                                                        
#endif//FORMATTED_WRITE_SUPPORT_LONG_SHORT

                                                        if (val < 0) 
                                                        {
                                                                value = 
(value_t)-val;
                                                                sign = '-';
                                                        }
                                                        else {
                                                                value = val;
                                                        }
                                                }
                                                else if (ch=='x' || ch == 'o' 
|| ch == 'u')
                                                {
                                                        #if 
(FORMATTED_WRITE_SUPPORT_LONG_SHORT==1)
                                                        if (flags&FLAG_LONGINT) 
                                                        {
                                                                value = 
va_arg(ap, unsigned long);
                                                        }
                                                        else 
                                                        {
                                                                int _d = 
va_arg(ap, int);
                                                                value = 
(flags&FLAG_SHORTINT) ? (unsigned long)(unsigned short)_d : (unsigned long)_d;
                                                        }
                                                        #else 
//FORMATTED_WRITE_SUPPORT_LONG_SHORT
                                                        value = va_arg(ap, 
unsigned int);
                                                        
#endif//FORMATTED_WRITE_SUPPORT_LONG_SHORT

                                                        if (ch=='x')
                                                        {
                                                                //Hex number
                                                                base=HEX;

                                                                #if 
(FORMATTED_WRITE_SUPPORT_ALT==1)
                                                                //leading 0x/X 
only if non-zero
                                                                if (flags & 
FLAG_ALT && value != 0)
                                                                        flags 
|= FLAG_HEXPREFIX;

                                                                #endif 
//FORMATTED_WRITE_SUPPORT_ALT
                                                        }
                                                        else if (ch=='o')
                                                        {
                                                                //Octal number
                                                                sign = '\0';
                                                                base=OCT;
                                                        }
                                                        else
                                                        {
                                                                //Unsigned 
decimal integer
                                                                base=DEC;
                                                        }
                                                }
                                                else {
                                                        //Unrecognized format 
specifier, use sign sepcifier to output character
                                                        base = 1;
                                                        value = 0;
                                                        sign = ch;
#if (FORMATTED_WRITE_SUPPORT_FLAGS_WIDTH_PRECISION==1)
                                                        prec = -1;
                                                        width = 0;
#endif //FORMATTED_WRITE_SUPPORT_FLAGS_WIDTH_PRECISION
                                                }
                                                
                                                //Start conversion
                                                {
                                                        char* cp = 
&buf[BUF_SIZE];

                                                        //The result of 
converting a zero value with an
                                                        //explicit precision of 
zero is no characters.
                                                        //-- ANSI X3J11
                                 
                                                        size = 0;

#if (FORMATTED_WRITE_SUPPORT_FLAGS_WIDTH_PRECISION==1)
                                                        if (value > 0 || prec < 
0) 
#endif //FORMATTED_WRITE_SUPPORT_FLAGS_WIDTH_PRECISION
                                                        {
                                                                unsigned char 
notlastdigit;
                                                                do {
                                                                        
unsigned char _d = (unsigned char)(value % base);
                                                                        
notlastdigit=(unsigned char)(value>=base);

                                                                        if 
(_d<10) {
                                                                                
_d+='0';
                                                                        } 
                                                                        else {
                                                                                
_d+='a'-10;
                                                                                
//if (ch=='X') _d&=~0x20;
                                                                        }
                                                                        
*--cp=_d;
                                                                        value 
/= base;
                                                                } while 
(notlastdigit);

                                                                #if 
(FORMATTED_WRITE_SUPPORT_ALT==1)
                                                                // handle octal 
leading 0 
                                                                if (base==OCT 
&& flags & FLAG_ALT && *cp != '0')
                                                                        *--cp = 
'0';
                                                                #endif

                                                                size = 
(unsigned char)(&buf[BUF_SIZE] - cp);
                                                        }
                                                        out_p = cp;
                                                }
                                        }

                                        /*
                                         * All reasonable formats wind up here. 
 At this point,
                                         * `cp' points to a string which (if 
not flags&FLAG_LADJUST)
                                         * should be padded out to `width' 
places.  If
                                         * flags&FLAG_ZEROPAD, it should first 
be prefixed by any
                                         * sign or other prefix; otherwise, it 
should be blank
                                         * padded before the prefix is emitted. 
 After any
                                         * left-hand padding and prefixing, 
emit zeroes
                                         * required by a decimal [diouxX] 
precision, then print
                                         * the string proper, then emit zeroes 
required by any
                                         * leftover floating precision; 
finally, if FLAG_LADJUST,
                                         * pad with blanks.
                                         */
                                        {
                                                signed char fieldsz = size;     
        // field size expanded by sign, dpad etc

#if (FORMATTED_WRITE_SUPPORT_FLAGS_WIDTH_PRECISION==1)
                                                signed char padding = prec - 
size;
                                                if (padding < 0) padding = 0;
                                                fieldsz += padding;
#endif //FORMATTED_WRITE_SUPPORT_FLAGS_WIDTH_PRECISION

                                                if (sign != '\0') fieldsz++;

                                                if (flags & FLAG_HEXPREFIX) 
fieldsz += 2;

#if (FORMATTED_WRITE_SUPPORT_FLAGS_WIDTH_PRECISION==1)
                                                if (width < fieldsz) width = 
fieldsz;

                                                //right-adjusting blank padding
                                                if ((flags & 
(FLAG_LADJUST|FLAG_ZEROPAD)) == 0)
                                                {
                                                        PAD_SP(width - fieldsz);
                                                }
#endif //FORMATTED_WRITE_SUPPORT_FLAGS_WIDTH_PRECISION

                                                // prefix 
                                                if (sign) {
                                                        PRINT_CHAR(sign);
                                                } 
                                                #if 
(FORMATTED_WRITE_SUPPORT_ALT==1)
                                                else if (flags & 
FLAG_HEXPREFIX) 
                                                {
                                                        PRINT_CHAR('0');
                                                        PRINT_CHAR(ch);
                                                }
                                                #endif 
//FORMATTED_WRITE_SUPPORT_ALT

#if (FORMATTED_WRITE_SUPPORT_FLAGS_WIDTH_PRECISION==1)
                                                // right-adjusting zero padding
                                                if ((flags & 
(FLAG_LADJUST|FLAG_ZEROPAD)) == FLAG_ZEROPAD)
                                                {
                                                        PAD_0(width - fieldsz);
                                                }

                                                // leading zeroes from decimal 
precision
                                                PAD_0(padding);
#endif //FORMATTED_WRITE_SUPPORT_FLAGS_WIDTH_PRECISION

                                                // the string or number
                                                while (size-- > 0)
                                                {
                                                        unsigned char out;
#if (FORMATTED_WRITE_SUPPORT_EEPROM_STRINGS==1)
                                                        if 
(flags&FLAG_EEPROM_STRING)
                                                                out = 
eeprom_rb((unsigned int)out_p++);
                                                        else  
#endif //(FORMATTED_WRITE_SUPPORT_EEPROM_STRINGS==1)
                                                                out = *out_p++;

                                                        PRINT_CHAR(out);
                                                }
                                        

#if (FORMATTED_WRITE_SUPPORT_FLAGS_WIDTH_PRECISION==1)
                                                // left-adjusting padding 
(always blank)
                                                if (flags & FLAG_LADJUST)
                                                        PAD_SP(width - fieldsz);
#endif //FORMATTED_WRITE_SUPPORT_FLAGS_WIDTH_PRECISION
                                        }

                                        //Set ch to 0 to abort %
                                        break; //break from flag while loop
                                }
                        } //while (ch != '\0');
                } //if (ch != '%')
        } //while (ch != '\0')
}

void put_c_in_string (char c, void *ptr)  /* Low-level output */
{
        *(*(char **) ptr)++ = c;
}


extern void sendchar(char ch);

void put_c_to_sendchar (char c, void *ptr)  /* Low-level output */
{
        ptr=ptr;
        sendchar(c);
}




reply via email to

[Prev in Thread] Current Thread [Next in Thread]