1 /*
2   dbuf_string.c - Append formatted string to the dynamic buffer
3   version 1.2.2, March 20th, 2012
4 
5   Copyright (c) 2002-2012 Borut Razem
6 
7   This program is free software; you can redistribute it and/or modify
8   it under the terms of the GNU General Public License as published by
9   the Free Software Foundation; either version 2, or (at your option)
10   any later version.
11 
12   This program is distributed in the hope that it will be useful,
13   but WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   GNU General Public License for more details.
16 
17   You should have received a copy of the GNU General Public License
18   along with this program; if not, write to the Free Software
19   Foundation, Inc., 51 Franklin Street - Fifth Floor,
20   Boston, MA 02110-1301, USA.
21 */
22 
23 
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <string.h>
27 #include <assert.h>
28 #include "dbuf_string.h"
29 
30 
31 /*
32  * Append string to the end of the buffer.
33  * The buffer is null terminated.
34  */
35 
36 int
dbuf_append_str(struct dbuf_s * dbuf,const char * str)37 dbuf_append_str (struct dbuf_s *dbuf, const char *str)
38 {
39   size_t len;
40   assert (str != NULL);
41 
42   len = strlen (str);
43   if (dbuf_append (dbuf, str, len + 1))
44     {
45       --dbuf->len;
46       return 1;
47     }
48   else
49     return 0;
50 }
51 
52 /*
53  * Prepend string to the beginning of the buffer.
54  * The buffer is null terminated.
55  */
56 
57 int
dbuf_prepend_str(struct dbuf_s * dbuf,const char * str)58 dbuf_prepend_str (struct dbuf_s *dbuf, const char *str)
59 {
60   size_t len;
61   assert (str != NULL);
62 
63   len = strlen (str);
64   return (dbuf_prepend (dbuf, str, len));
65 }
66 
67 /*
68  * Append single character to the end of the buffer.
69  * The buffer is null terminated.
70  */
71 
72 int
dbuf_append_char(struct dbuf_s * dbuf,char chr)73 dbuf_append_char (struct dbuf_s *dbuf, char chr)
74 {
75   char buf[2];
76   buf[0] = chr;
77   buf[1] = '\0';
78   if (dbuf_append (dbuf, buf, 2))
79     {
80       --dbuf->len;
81       return 1;
82     }
83   else
84     return 0;
85 }
86 
87 /*
88  * Prepend single character to the end of the buffer.
89  * The buffer is null terminated.
90  */
91 
92 int
dbuf_prepend_char(struct dbuf_s * dbuf,char chr)93 dbuf_prepend_char (struct dbuf_s *dbuf, char chr)
94 {
95   char buf[2];
96   buf[0] = chr;
97   buf[1] = '\0';
98   return (dbuf_prepend_str (dbuf, buf));
99 }
100 
101 /*
102  * Calculate length of the resulting formatted string.
103  *
104  * Borrowed from vasprintf.c
105  */
106 
107 static int
calc_result_length(const char * format,va_list args)108 calc_result_length (const char *format, va_list args)
109 {
110   const char *p = format;
111   /* Add one to make sure that it is never zero, which might cause malloc
112      to return NULL.  */
113   int total_width = strlen (format) + 1;
114   va_list ap;
115 
116 #ifdef va_copy
117   va_copy (ap, args);
118 #else
119   memcpy (&ap, &args, sizeof (va_list));
120 #endif
121 
122   while (*p != '\0')
123     {
124       if (*p++ == '%')
125         {
126           while (strchr ("-+ #0", *p))
127             ++p;
128           if (*p == '*')
129             {
130               ++p;
131               total_width += abs (va_arg (ap, int));
132             }
133           else
134             total_width += strtoul (p, (char **) &p, 10);
135           if (*p == '.')
136             {
137               ++p;
138               if (*p == '*')
139                 {
140                   ++p;
141                   total_width += abs (va_arg (ap, int));
142                 }
143               else
144               total_width += strtoul (p, (char **) &p, 10);
145             }
146           while (strchr ("hlL", *p))
147             ++p;
148           /* Should be big enough for any format specifier except %s and floats.  */
149           total_width += 30;
150           switch (*p)
151             {
152             case 'd':
153             case 'i':
154             case 'o':
155             case 'u':
156             case 'x':
157             case 'X':
158             case 'c':
159               (void) va_arg (ap, int);
160               break;
161             case 'f':
162             case 'e':
163             case 'E':
164             case 'g':
165             case 'G':
166               (void) va_arg (ap, double);
167               /* Since an ieee double can have an exponent of 307, we'll
168                  make the buffer wide enough to cover the gross case. */
169               total_width += 307;
170               break;
171             case 's':
172               {
173                 const char *p = va_arg (ap, char *);
174                 total_width += strlen (p ? p : "(null)");
175               }
176               break;
177             case 'p':
178             case 'n':
179               (void) va_arg (ap, char *);
180               break;
181             }
182           p++;
183         }
184     }
185 #ifdef va_copy
186   va_end (ap);
187 #endif
188   return total_width;
189 }
190 
191 
192 /*
193  * Append the formatted string to the end of the buffer.
194  * The buffer is null terminated.
195  */
196 
197 int
dbuf_vprintf(struct dbuf_s * dbuf,const char * format,va_list args)198 dbuf_vprintf (struct dbuf_s *dbuf, const char *format, va_list args)
199 {
200   int size = calc_result_length (format, args);
201 
202   assert (dbuf != NULL);
203   assert (dbuf->alloc != 0);
204   assert (dbuf->buf != NULL);
205 
206   if (0 != _dbuf_expand (dbuf, size))
207     {
208       int len = vsprintf (&(((char *)dbuf->buf)[dbuf->len]), format, args);
209 
210       if (len >= 0)
211         {
212           /* if written length is greater then the calculated one,
213              we have a buffer overrun! */
214           assert (len <= size);
215           dbuf->len += len;
216         }
217       return len;
218     }
219 
220   return 0;
221 }
222 
223 
224 /*
225  * Append the formatted string to the end of the buffer.
226  * The buffer is null terminated.
227  */
228 
229 int
dbuf_printf(struct dbuf_s * dbuf,const char * format,...)230 dbuf_printf (struct dbuf_s *dbuf, const char *format, ...)
231 {
232   va_list arg;
233   int len;
234 
235   va_start (arg, format);
236   len = dbuf_vprintf (dbuf, format, arg);
237   va_end (arg);
238 
239   return len;
240 }
241 
242 
243 /*
244  * Append line from file to the dynamic buffer
245  * The buffer is null terminated.
246  */
247 
248 size_t
dbuf_getline(struct dbuf_s * dbuf,FILE * infp)249 dbuf_getline (struct dbuf_s *dbuf, FILE *infp)
250 {
251   int c;
252   char chr;
253 
254   while ((c = getc (infp)) != '\n' && c != EOF)
255     {
256       chr = c;
257 
258       dbuf_append (dbuf, &chr, 1);
259     }
260 
261   /* add trailing NL */
262   if (c == '\n')
263     {
264       chr = c;
265 
266       dbuf_append (dbuf, &chr, 1);
267     }
268 
269   /* terminate the line without increasing the length */
270   if (0 != _dbuf_expand (dbuf, 1))
271     ((char *)dbuf->buf)[dbuf->len] = '\0';
272 
273   return dbuf_get_length (dbuf);
274 }
275 
276 
277 /*
278  * Remove trailing newline from the string.
279  * The buffer is null terminated.
280  * It returns the total number of characters removed.
281  */
282 
283 size_t
dbuf_chomp(struct dbuf_s * dbuf)284 dbuf_chomp (struct dbuf_s *dbuf)
285 {
286   size_t i = dbuf->len;
287   size_t ret;
288 
289   if (i != 0 && '\n' == ((char *)dbuf->buf)[i - 1])
290     {
291       --i;
292       if (i != 0 && '\r' == ((char *)dbuf->buf)[i - 1])
293         {
294           --i;
295         }
296     }
297 
298   ret = dbuf->len - i;
299   dbuf->len = i;
300 
301   /* terminate the line without increasing the length */
302   if (_dbuf_expand(dbuf, 1) != 0)
303     ((char *)dbuf->buf)[dbuf->len] = '\0';
304 
305   return ret;
306 }
307 
308 
309 /*
310  * Write dynamic buffer to the file.
311  */
312 
313 void
dbuf_write(struct dbuf_s * dbuf,FILE * dest)314 dbuf_write (struct dbuf_s *dbuf, FILE *dest)
315 {
316   fwrite (dbuf_get_buf (dbuf), 1, dbuf_get_length (dbuf), dest);
317 }
318 
319 
320 /*
321  * Write dynamic buffer to the file and destroy it.
322  */
323 
324 void
dbuf_write_and_destroy(struct dbuf_s * dbuf,FILE * dest)325 dbuf_write_and_destroy (struct dbuf_s *dbuf, FILE *dest)
326 {
327   dbuf_write (dbuf, dest);
328 
329   dbuf_destroy (dbuf);
330 }
331