1 /*
2  *  simple, small and self contained snprintf() function.
3  *  Copyright (C) 2005  Weston Schmidt
4  *
5  *  This library is free software; you can redistribute it and/or
6  *  modify it under the terms of the GNU Lesser General Public
7  *  License as published by the Free Software Foundation; either
8  *  version 2.1 of the License, or (at your option) any later version.
9  *
10  *  This library is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  *  Lesser General Public License for more details.
14  *
15  *  You should have received a copy of the GNU Lesser General Public
16  *  License along with this library; if not, write to the Free Software
17  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  */
19 #include <testfwk.h>
20 #include <stdarg.h>
21 #include <string.h>
22 
23 /*
24  *  General information about this file:
25  *
26  *  This file has been created with the ability to be compiled using sdcc
27  *  and gcc both.  If gcc is used, the output file can be executed and
28  *  the built in test vectors will be run.
29  *
30  *  The test vectors I created are very simple and in no way test
31  *  everything that should be tested or really come close.  The output
32  *  is visually inspected for correctness.  Additionally, I have not
33  *  done a thorough job testing this to ensure that no buffer overruns
34  *  take place.  Logically, everything looks pretty safe, and it works
35  *  for the debug generation that I needed it to work for.
36  *
37  *  I ran into problems when running this on my pic because I was allocating
38  *  80 byte long arrays on the stack (which was only 128 bytes is size).
39  *  The end result was "it just didn't work" with no real hints.  So if
40  *  this happens to you, think about up-ing the stack size or moving your
41  *  arrays out of automatic stack variables (defined inside {}s, which
42  *  scope the variable) and out to staticly declared memory (aka "global"
43  *  variables).
44  *
45  *  I hope this saves you the day it took me to write and debug.
46  */
47 
48 #include <stdio.h>
49 
50 #define SUPPORT_CHAR_C
51 
52 /* Normally a char is promoted to int when passed as varargs parameter */
53 /* but SDCC leaves it as char. */
54 #if !defined(__SDCC) || defined(__SDCC_pdk14) || defined(__SDCC_pdk15)
55 #   define VA_ARG_CHAR int
56 #   define VA_ARG(args,type) va_arg((args),type)
57 #   define SDCC_SNPRINTF    sdcc_snprintf
58 #else
59 #   define VA_ARG_CHAR char
60 #   define VA_ARG(args,type) va_arg((args),type)
61 #   define SDCC_SNPRINTF    snprintf
62 #endif
63 
64 /*
65  *  snprintf is a bound version of the popular sprintf() function.
66  *  The purpose of snprintf is to format just like sprintf(), but
67  *  to make sure that memory is never accidentally overwritten by
68  *  a buffer overrun.
69  *
70  *  A stripped down version of snprintf for the pic16 platform of the
71  *  sdcc project.  Depending on the compile flags set/unset above this
72  *  function varies in size from ~16k down to ~5.  Below is the
73  *  breakdown:
74  *
75  *  SUPPORT_CHAR_C  - c  format  - ~0.5k of program space required
76  *  SUPPORT_STRING  - s  format  - ~1.6k of program space required
77  *
78  *  I don't think I have any type of optimization on when I did these
79  *  measurements.  They were taken with a snapshot build of sdcc from
80  *  January 22, 2005 (end of day).
81  *
82  *  Supported formatting:
83  *  %%, %c, %[ 0][1-9][0-9]*s
84  *             \
85  *              \-- optional padding value
86  *
87  *  For more detail on what this formatting means, google 'man printf'
88  *
89  *  Parameters:
90  *  ----------------------------------------------------------------------
91  *  char *buffer             -- the buffer to write into (assumed to
92  *                              not be NULL)
93  *  const unsigned char size -- the maximum number of bytes to write
94  *                              into the buffer
95  *  const char *format       -- the printf style formatter string (see
96  *                              above for supported formats.
97  *
98  *  Return Values:
99  *  ----------------------------------------------------------------------
100  *  The actual number of characters written into the buffer.  The count
101  *  includes the '\0' located at the end of the string.  If there is
102  *  not enough room for the entire string, the '\0' value is written in
103  *  as the buffer[end - 1] value.
104  */
105 
106 #ifndef __SDCC_pdk14 // Lack of memory
107 #if !(defined (__SDCC_pdk15) && defined(__SDCC_STACK_AUTO)) // Lack of code memory
SDCC_SNPRINTF(char * buffer,const unsigned char size,const char * format,...)108 unsigned char SDCC_SNPRINTF( char *buffer, const unsigned char size,
109                              const char *format, ... )
110 {
111     va_list args;
112     char *start = buffer;
113     char *end   = &buffer[size];
114 
115     va_start( args, format );
116 
117     while( (buffer <  end) && ('\0' != *format) ) {
118         if( '%' == *format ) {
119             format++;
120             switch( *format ) {
121 #ifdef SUPPORT_CHAR_C
122                 case 'c':
123                     *buffer = VA_ARG( args, VA_ARG_CHAR );
124                     buffer++;
125                     break;
126 #endif /* SUPPORT_CHAR_C */
127 
128                 case '%':
129                     *buffer = '%';
130                     buffer++;
131                     break;
132 
133                 default:
134                 {
135                     char padding = ' ';
136                     short digits = 0;
137 
138                     /* Determine the padding */
139                     switch( *format ) {
140                         case '0':
141                             padding = '0';
142                         case ' ':
143                             format++;
144                             break;
145                     }
146 
147                     /* Determine how many digits to display */
148                     while(    ('\0' != *format)
149                            && ((*format >= '0') && (*format <= '9')) )
150                     {
151                         digits = digits * 10 + (*format - '0');
152                         format++;
153                     }
154 
155                     switch( *format ) {
156                         case 's':
157                         {
158                             char *val, *val_end;
159                             short length;
160 
161                             val = VA_ARG( args, char * );
162                             val_end = val;
163 
164                             /* Find the end of the string. */
165                             while( '\0' != *val_end ) { val_end++; }
166 
167                             length = val_end - val;
168 
169                             /* Optionally crop the output string */
170                             if( (digits > 0) && (length > digits) ) {
171                                 length = digits;
172                             }
173 
174                             /* Add padding */
175                             while( (digits > length) && (buffer < end) ) {
176                                 *buffer = padding;
177                                 buffer++;
178                                 digits--;
179                             }
180 
181                             /* Copy string */
182                             while( (length > 0) && (buffer < end) ) {
183                                 *buffer = *val;
184                                 buffer++;
185                                 val++;
186                                 length--;
187                             }
188 
189                             break;
190                         }
191 
192                         default:
193                             goto clean_and_bail;
194                     }
195                     break;
196                 }
197             }
198         } else {
199             *buffer = *format;
200             buffer++;
201         }
202 
203         format++;
204     }
205 
206 clean_and_bail:
207     if( buffer < end ) {
208         *buffer = '\0';
209         buffer++;
210     } else {
211         *(buffer-1) = '\0';
212     }
213 
214     va_end( args );
215 
216     return (buffer - start);
217 }
218 #endif
219 #endif
220 
test_s(void)221 void test_s( void )
222 {
223 #ifndef __SDCC_pdk14 // Lack of memory
224 #if !(defined (__SDCC_pdk15) && defined(__SDCC_STACK_AUTO)) // Lack of code memory
225     char buffer[32];
226     int ret;
227     int i = 0;
228 
229     for( i = 0; i < 31; i++ ) {
230         buffer[i] = 'P';
231     }
232     buffer[31] = '\0';
233 
234     ret = SDCC_SNPRINTF( buffer, 32, "->|%%|<-" );
235 //    printf( "->|%s|<- %d \n", buffer, ret );
236     ASSERT (strcmp(buffer, "->|%|<-") == 0);
237     ASSERT (ret == 8);
238     ret = SDCC_SNPRINTF( buffer, 32, "%s, %s.", "Hello", "world" );
239 //    printf( "->|%s|<- %d \n", buffer, ret );
240     ASSERT (strcmp(buffer, "Hello, world.") == 0);
241     ASSERT (ret == 14);
242     ret = SDCC_SNPRINTF( buffer, 32, "% 10s, % 10s.", "Hello", "world" );
243 //    printf( "->|%s|<- %d \n", buffer, ret );
244     ASSERT (strcmp(buffer, "     Hello,      world.") == 0);
245     ASSERT (ret == 24);
246     ret = SDCC_SNPRINTF( buffer, 32, "% 3s, % 3s.", "Hello", "world" );
247 //    printf( "->|%s|<- %d \n", buffer, ret );
248     ASSERT (strcmp(buffer, "Hel, wor.") == 0);
249     ASSERT (ret == 10);
250     ret = SDCC_SNPRINTF( buffer, 32, "%03s, %03s.", "Hello", "world" );
251 //    printf( "->|%s|<- %d \n", buffer, ret );
252     ASSERT (strcmp(buffer, "Hel, wor.") == 0);
253     ASSERT (ret == 10);
254     ret = SDCC_SNPRINTF( buffer, 10, "%s", "Hello, world" );
255 //    printf( "->|%s|<- %d \n", buffer, ret );
256     ASSERT (strcmp(buffer, "Hello, wo") == 0);
257     ASSERT (ret == 10);
258 #endif
259 #endif
260 }
261 
262 #if defined SDCC
263 extern void _putchar(char c);
264 
putchar(int c)265 int putchar(int c)
266 {
267 	_putchar(c);
268 	return(c);
269 }
270 #endif
271 
272