1 /*
2  *  This file is part of the XForms library package.
3  *
4  *  XForms is free software; you can redistribute it and/or modify it
5  *  under the terms of the GNU Lesser General Public License as
6  *  published by the Free Software Foundation; either version 2.1, or
7  *  (at your option) any later version.
8  *
9  *  XForms is distributed in the hope that it will be useful, but
10  *  WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  *  Lesser General Public License for more details.
13  *
14  *  You should have received a copy of the GNU Lesser General Public License
15  *  along with XForms.  If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 
19 #ifndef FLVASPRINTF_H
20 #define FLVASPRINTF_H
21 
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25 
26 #include <stdio.h>
27 #include <stdarg.h>
28 #include "flsnprintf.h"
29 
30 #if defined ( HAVE_VASPRINTF ) && ! defined ( HAVE_DECL_VASPRINTF )
31 int
32 vasprintf( char       ** strp,
33            const char  * fmt,
34            va_list       ap );
35 #endif
36 
37 /* Macro for allocating a buffer and filling it with a string
38  * constructed from a format string and an unspecified number
39  * of arguments. It expects a char pointer first (which will
40  * be set to the address of the memory allocated for the resulting
41  * string) and a (const) char pointer with a printf-like format
42  * string (if the pointer to the format string is NULL memory is
43  * obtained for the empty string). Of course, the function from
44  * which the macro is invoked from must be a variadic function,
45  * called with the appropriate number of types of arguments for
46  * the format string.
47  *
48  * On success the first argument will be set to a buffer long
49  * enough and containing the intended string. If there wasn't
50  * enough memory available an attempted is make to at least
51  * allocate memory for the empty string. If even this fails
52  * the first macro argument is set to NULL.
53  *
54  * Of course it's the responsibility of whatever invoked
55  * this macro to release the memory allocated here at the
56  * appropriate time.
57  *
58  * The best function to use here would be vasprintf(), which
59  * exactly does what we need. Older systems may not have it -
60  * in this case 'HAVE_VASPRINTF isn't defined. Unfortunately,
61  * we can't use the implementation from the flsnprintf.c file
62  * since it doesn't get compiled in the way this file is made
63  * up (and for good reasonsm the way the necessary va_copy()
64  * function gets defined is broken in more than one way). But
65  * we can use fli_vsnprintf() from that file, though with some
66  * difficulties.
67  *
68  * For such systems we need a way to not "use up" the va_list
69  * by initializing it and then passing it to some function
70  * (that's actually the rationale for using a macro here
71  * instead of a function!), so we must do the memory allo-
72  * cation here (if necessary repeatedly) and only call
73  * fli_vsnprintf().
74  *
75  * So the whole existence of this macro is due to backward
76  * compatibility with old (pre C99) compilers that may have
77  * a uncommon way of defining a va_list. Messy, isnt' it?
78  *
79  * BTW, all locally used variables have names starting with 'l1I_'
80  * since this is a prefix no sane person would ever use - we try to
81  * avoid compiler warnings about local variables shadowing already
82  * defined ones.
83  */
84 
85 #if defined ( HAVE_VASPRINTF )
86 
87 #define EXPAND_FORMAT_STRING( buf, fmt )                                    \
88 do {                                                                        \
89     if ( ! fmt || ! *fmt )                                                  \
90         buf = NULL;                                                         \
91     else if ( ! strchr( fmt, '%' ) )                                        \
92     {                                                                       \
93         if ( ( buf = fl_malloc( strlen( fmt ) + 1 ) ) )                     \
94             strcpy( buf, fmt );                                             \
95     }                                                                       \
96     else                                                                    \
97     {                                                                       \
98         va_list l1I_ap;                                                     \
99                                                                             \
100         va_start( l1I_ap, fmt );                                            \
101         if ( ! vasprintf( &buf, fmt, l1I_ap ) )                             \
102             buf = NULL;                                                     \
103         va_end( l1I_ap );                                                   \
104     }                                                                       \
105                                                                             \
106     if ( ! buf && ( buf = fl_malloc( 1 ) ) )                                \
107             *buf = '\0';                                                    \
108 } while ( 0 )
109 
110 #else
111 
112 #define EXPAND_FORMAT_STRING( buf, fmt )                                    \
113 do {                                                                        \
114     if ( ! fmt || ! *fmt )                                                  \
115         buf = NULL;                                                         \
116     else if ( ! strchr( fmt, '%' ) )                                        \
117     {                                                                       \
118         if ( ( buf = fl_malloc( strlen( fmt ) + 1 ) ) )                     \
119             strcpy( buf, fmt );                                             \
120     }                                                                       \
121     else                                                                    \
122     {                                                                       \
123         int l1I_min_needed = strlen( fmt ) + 1;                             \
124         int l1I_len = l1I_min_needed;                                       \
125         char *l1I_p;                                                        \
126                                                                             \
127         for ( l1I_p = strchr( fmt, '%' ); l1I_p;                            \
128               l1I_p = strchr( ++l1I_p, '%' ) )                              \
129             l1I_len += 16;                                                  \
130                                                                             \
131         if ( ( buf = fl_malloc( l1I_len ) ) )                               \
132         {                                                                   \
133             while ( 1 )                                                     \
134             {                                                               \
135                 va_list l1I_ap;                                             \
136                 int l1I_written;                                            \
137                                                                             \
138                 va_start( l1I_ap, fmt );                                    \
139                 l1I_written = fli_vsnprintf( buf, l1I_len, fmt, l1I_ap );   \
140                 va_end( l1I_ap );                                           \
141                                                                             \
142                 /* Take care: older libc versions returned a negative       \
143                    value if the buffer wasn't large enough space while      \
144                    newer ones follow C99 and return the length of the       \
145                    string needed (without the trailing '\0') */             \
146                                                                             \
147                 if ( l1I_written > -1 && l1I_len > l1I_written )            \
148                 {                                                           \
149                     if ( l1I_len != l1I_written + 1 )                       \
150                     {                                                       \
151                         l1I_p = buf;                                        \
152                         if ( ! ( buf = fl_realloc( l1I_p,                   \
153                                                    l1I_written + 1 ) ) )    \
154                             buf = l1I_p;                                    \
155                     }                                                       \
156                     break;                                                  \
157                 }                                                           \
158                                                                             \
159                 l1I_len = l1I_written < 0 ?                                 \
160                           ( 2 * l1I_len ) : ( l1I_written + 16 );           \
161                 l1I_p = buf;                                                \
162                 if ( ! ( buf = fl_realloc( l1I_p, l1I_len ) ) )             \
163                 {                                                           \
164                     fl_free( l1I_p );                                       \
165                     break;                                                  \
166                 }                                                           \
167             }                                                               \
168         }                                                                   \
169     }                                                                       \
170                                                                             \
171     if ( ! buf && ( buf = fl_malloc( 1 ) ) )                                \
172             *buf = '\0';                                                    \
173 } while ( 0 )
174 
175 #endif
176 
177 
178 #endif
179 
180 
181 /*
182  * Local variables:
183  * tab-width: 4
184  * indent-tabs-mode: nil
185  * End:
186  */
187