Fix bug with NULL buffers (caught with unit test).
[bertos.git] / mware / sprintf.c
1 /*!
2  * \file
3  * <!--
4  * Copyright 2002, 2004, 2005 Develer S.r.l. (http://www.develer.com/)
5  * This file is part of DevLib - See README.devlib for information.
6  * -->
7  *
8  * \brief sprintf() implementation based on _formatted_write()
9  *
10  * \version $Id$
11  * \author Bernardo Innocenti <bernie@develer.com>
12  */
13
14 /*#*
15  *#* $Log$
16  *#* Revision 1.9  2005/02/18 12:48:01  bernie
17  *#* Fix bug with NULL buffers (caught with unit test).
18  *#*
19  *#* Revision 1.8  2005/02/18 12:34:29  bernie
20  *#* Include <mware/pgm.h> explicitly for non-Harvard archs.
21  *#*
22  *#* Revision 1.7  2004/12/31 17:47:45  bernie
23  *#* Rename UNUSED() to UNUSED_ARG().
24  *#*
25  *#* Revision 1.6  2004/11/16 21:15:19  bernie
26  *#* Fix off-by-one bug in [v]snprintf().
27  *#*
28  *#* Revision 1.5  2004/10/03 18:54:36  bernie
29  *#* sprintf(): Fix a serious bug; snprintf(): New function.
30  *#*
31  *#* Revision 1.4  2004/08/25 14:12:09  rasky
32  *#* Aggiornato il comment block dei log RCS
33  *#*
34  *#* Revision 1.3  2004/06/27 15:20:26  aleph
35  *#* Change UNUSED() macro to accept two arguments: type and name;
36  *#* Add macro GNUC_PREREQ to detect GCC version during build;
37  *#* Some spacing cleanups and typo fix
38  *#*
39  *#* Revision 1.2  2004/06/03 11:27:09  bernie
40  *#* Add dual-license information.
41  *#*/
42
43 #include <mware/formatwr.h>
44 #include <mware/pgm.h>
45 #include "compiler.h"
46
47 #include <stdio.h>
48
49
50 static void __str_put_char(char c, void *ptr)
51 {
52         /*
53          * This Does not work on Code Warrior. Hmm...
54          *      *(*((char **)ptr))++ = c;
55          */
56
57         **((char **)ptr) = c;
58         (*((char **)ptr))++;
59 }
60
61 static void __null_put_char(UNUSED_ARG(char, c), UNUSED_ARG(void *, ptr))
62 {
63         /* nop */
64 }
65
66
67 int PGM_FUNC(vsprintf)(char *str, const char * PGM_ATTR fmt, va_list ap)
68 {
69         int result;
70
71         if (str)
72         {
73                 result = PGM_FUNC(_formatted_write)(fmt, __str_put_char, &str, ap);
74
75                 /* Terminate string */
76                 *str = '\0';
77         }
78         else
79                 result = PGM_FUNC(_formatted_write)(fmt, __null_put_char, 0, ap);
80
81
82         return result;
83 }
84
85
86 int PGM_FUNC(sprintf)(char *str, const char * fmt, ...)
87 {
88         int result;
89         va_list ap;
90
91         va_start(ap, fmt);
92         result = PGM_FUNC(vsprintf)(str, fmt, ap);
93         va_end(ap);
94
95         return result;
96 }
97
98 /*!
99  * State information for __sn_put_char()
100  */
101 struct __sn_state
102 {
103         char *str;
104         size_t len;
105 };
106
107 /*!
108  * formatted_write() callback used [v]snprintf().
109  */
110 static void __sn_put_char(char c, void *ptr)
111 {
112         struct __sn_state *state = (struct __sn_state *)ptr;
113
114         if (state->len)
115         {
116                 --state->len;
117                 *state->str++ = c;
118         }
119 }
120
121
122 int PGM_FUNC(vsnprintf)(char *str, size_t size, const char * PGM_ATTR fmt, va_list ap)
123 {
124         int result = 0;
125
126         /* Make room for traling '\0'. */
127         if (size--)
128         {
129                 if (str)
130                 {
131                         struct __sn_state state;
132                         state.str = str;
133                         state.len = size;
134
135                         result = PGM_FUNC(_formatted_write)(fmt, __sn_put_char, &state, ap);
136
137                         /* Terminate string. */
138                         *state.str = '\0';
139                 }
140                 else
141                         result = PGM_FUNC(_formatted_write)(fmt, __null_put_char, 0, ap);
142         }
143
144         return result;
145 }
146
147
148 int PGM_FUNC(snprintf)(char *str, size_t size, const char * fmt, ...)
149 {
150         int result;
151         va_list ap;
152
153         va_start(ap, fmt);
154         result = PGM_FUNC(vsnprintf)(str, size, fmt, ap);
155         va_end(ap);
156
157         return result;
158 }