initial revision
[ucgine.git] / tools / cfzy / libconfizery / cfzy_string.c
1 /*
2  * Copyright (c) 2013, Olivier MATZ <zer0@droids-corp.org>
3  * All rights reserved.
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are met:
6  *
7  *     * Redistributions of source code must retain the above copyright
8  *       notice, this list of conditions and the following disclaimer.
9  *     * Redistributions in binary form must reproduce the above copyright
10  *       notice, this list of conditions and the following disclaimer in the
11  *       documentation and/or other materials provided with the distribution.
12  *     * Neither the name of the University of California, Berkeley nor the
13  *       names of its contributors may be used to endorse or promote products
14  *       derived from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
17  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19  * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
20  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27
28 #include <stdlib.h>
29 #include <stdarg.h>
30 #include <stdio.h>
31 #include <string.h>
32
33 #include "cfzy_log.h"
34
35 #define LOG(level, fmt, args...)                                \
36         CFZY_LOG("string", level, fmt, ##args)
37
38 /* Acts as a asprintf, except that it appends data after buf (if not
39  * NULL), reallocating memory if necessary. */
40 int cfzy_string_asprintf(char **buf, const char *fmt, ...)
41 {
42         va_list ap;
43         char dummy;
44         int offset = 0, buflen, ret;
45
46         if (*buf != NULL)
47                 offset = strlen(*buf);
48
49         va_start(ap, fmt);
50         ret = vsnprintf(&dummy, 1, fmt, ap);
51         va_end(ap);
52         if (ret < 0) {
53                 LOG(ERR, "first vsnprintf failed\n");
54                 return ret;
55         }
56
57         buflen = offset + ret + 1;
58         *buf = realloc(*buf, buflen);
59         if (*buf == NULL) {
60                 LOG(ERR, "asprintf: not enough memory\n");
61                 return -1;
62         }
63
64         va_start(ap, fmt);
65         ret = vsnprintf(*buf + offset, buflen - offset, fmt, ap);
66         va_end(ap);
67
68         if (ret >= buflen || ret < 0) {
69                 free(*buf);
70                 *buf = NULL;
71                 LOG(ERR, "second vsnprintf failed\n");
72                 return -1;
73         }
74
75         return ret;
76 }
77
78 /*
79  * If the buffer is surrounded by quotes (simple or double), this
80  * function will allocate a new string and remove the quotes properly,
81  * also deleting the backslash in occurences of \\, \" or \'. If the
82  * buffer does not start by a quote, the function just return a
83  * duplicate of the input string. On sucess, the eatlen is updated to
84  * the number of consumed bytes (including quotes if any, but not
85  * including \0). On error, return NULL, in this case *eatlen is
86  * undefined.
87  */
88 char *cfzy_string_unquote(const char *buf, unsigned *eatlen)
89 {
90         char delim;
91         char *out, *s2;
92         const char *s;
93
94         s = buf;
95         out = strdup(s);
96         if (out == NULL) {
97                 LOG(ERR, "not enough memory\n");
98                 return NULL;
99         }
100
101         if (s[0] == '"')
102                 delim = '"';
103         else if (s[0] == '\'')
104                 delim = '\'';
105         else {
106                 *eatlen = strlen(s);
107                 return out;
108         }
109         s++;
110
111         s2 = out;
112         while (*s != delim) {
113                 if (*s == '\0') {
114                         free(out);
115                         return NULL;
116                 }
117                 if (*s == '\\' && *(s+1) == delim) {
118                         *s2 = delim;
119                         s += 2;
120                         s2 ++;
121                         continue;
122                 }
123                 if (*s == '\\' && *(s+1) == '\\') {
124                         *s2 = '\\';
125                         s += 2;
126                         s2 ++;
127                         continue;
128                 }
129                 *s2 = *s;
130                 s ++;
131                 s2 ++;
132         }
133         *s2 = '\0';
134
135         *eatlen = s - buf + 1;
136         return out;
137 }
138
139 /* quote a string and escape original quotes */
140 char *cfzy_string_quote(const char *src)
141 {
142         int s, d;
143         char *dst;
144
145         /* get dst buf len */
146         for (s = 0, d = 0; src[s] != '\0'; s++, d++) {
147                 if (src[s] == '"')
148                         d++;
149                 if (src[s] == '\\' && src[s+1] == '"')
150                         d++;
151         }
152
153         dst = malloc(d + 3); /* 3 for the 2 quotes and the \0 */
154         if (dst == NULL)
155                 return NULL;
156
157         dst[0] = '"';
158         for (s = 0, d = 1; src[s] != '\0'; s++, d++) {
159                 if (src[s] == '"')
160                         dst[d++] = '\\';
161                 if (src[s] == '\\' && src[s+1] == '"')
162                         dst[d++] = '\\';
163
164                 dst[d] = src[s];
165         }
166
167         dst[d++] = '"';
168         dst[d++] = '\0';
169
170         return dst;
171 }
172