1 /* grecs - Gray's Extensible Configuration System
2 Copyright (C) 2007-2016 Sergey Poznyakoff
3
4 Grecs is free software; you can redistribute it and/or modify it
5 under the terms of the GNU General Public License as published by the
6 Free Software Foundation; either version 3 of the License, or (at your
7 option) any later version.
8
9 Grecs is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License along
15 with Grecs. If not, see <http://www.gnu.org/licenses/>. */
16
17 #ifdef HAVE_CONFIG_H
18 # include <config.h>
19 #endif
20 #include <stdlib.h>
21 #include <string.h>
22 #include <errno.h>
23 #include "grecs.h"
24
25 int
grecs_vasprintf(char ** pbuf,size_t * psize,const char * fmt,va_list ap)26 grecs_vasprintf(char **pbuf, size_t *psize, const char *fmt, va_list ap)
27 {
28 char *buf = *pbuf;
29 size_t buflen = *psize;
30 int rc = 0;
31
32 if (!buf) {
33 if (buflen == 0)
34 buflen = 512; /* Initial allocation */
35
36 buf = calloc(1, buflen);
37 if (buf == NULL)
38 return ENOMEM;
39 }
40
41 for (;;) {
42 va_list aq;
43 ssize_t n;
44
45 va_copy(aq, ap);
46 n = vsnprintf(buf, buflen, fmt, aq);
47 va_end(aq);
48 if (n < 0 || n >= buflen || !memchr(buf, '\0', n + 1)) {
49 char *newbuf;
50 size_t newlen = buflen * 2;
51 if (newlen < buflen) {
52 rc = ENOMEM;
53 break;
54 }
55 newbuf = realloc(buf, newlen);
56 if (newbuf == NULL) {
57 rc = ENOMEM;
58 break;
59 }
60 buflen = newlen;
61 buf = newbuf;
62 } else
63 break;
64 }
65
66 if (rc) {
67 if (!*pbuf) {
68 /* We made first allocation, now free it */
69 free(buf);
70 buf = NULL;
71 buflen = 0;
72 }
73 }
74
75 *pbuf = buf;
76 *psize = buflen;
77 return rc;
78 }
79
80 int
grecs_asprintf(char ** pbuf,size_t * psize,const char * fmt,...)81 grecs_asprintf(char **pbuf, size_t *psize, const char *fmt, ...)
82 {
83 int rc;
84 va_list ap;
85
86 va_start(ap, fmt);
87 rc = grecs_vasprintf(pbuf, psize, fmt, ap);
88 va_end(ap);
89 return rc;
90 }
91