1 | /**** |
---|
2 | Copyright 2015 Alexej Magura |
---|
3 | |
---|
4 | Licensed under the Apache License, Version 2.0 (the "License"); |
---|
5 | you may not use this file except in compliance with the License. |
---|
6 | You may obtain a copy of the License at |
---|
7 | |
---|
8 | http://www.apache.org/licenses/LICENSE-2.0 |
---|
9 | |
---|
10 | Unless required by applicable law or agreed to in writing, software |
---|
11 | distributed under the License is distributed on an "AS IS" BASIS, |
---|
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
---|
13 | See the License for the specific language governing permissions and |
---|
14 | limitations under the License. |
---|
15 | ****/ |
---|
16 | |
---|
17 | #include <stdio.h> |
---|
18 | #include <stdlib.h> |
---|
19 | #include <stdbool.h> |
---|
20 | #include <string.h> |
---|
21 | #include <limits.h> |
---|
22 | #include <stdarg.h> |
---|
23 | |
---|
24 | #include "foreign.h" |
---|
25 | #include "musthaves.h" |
---|
26 | |
---|
27 | char *concat(const char *s1, ...) |
---|
28 | { |
---|
29 | va_list args; |
---|
30 | const char *s; |
---|
31 | char *p, *result; |
---|
32 | unsigned long l, m, n; |
---|
33 | |
---|
34 | m = n = strlen(s1); |
---|
35 | va_start(args, s1); |
---|
36 | while ((s = va_arg(args, char *))) { |
---|
37 | l = strlen(s); |
---|
38 | if ((m += l) < l) break; |
---|
39 | } |
---|
40 | va_end(args); |
---|
41 | if (s || m >= INT_MAX) return NULL; |
---|
42 | |
---|
43 | #if defined(__cplusplus) |
---|
44 | result = (char *)malloc(m + 1); |
---|
45 | #else |
---|
46 | result = malloc(m + 1); |
---|
47 | #endif |
---|
48 | if (!result) return NULL; |
---|
49 | |
---|
50 | memcpy(p = result, s1, n); |
---|
51 | p += n; |
---|
52 | va_start(args, s1); |
---|
53 | while ((s = va_arg(args, char *))) { |
---|
54 | l = strlen(s); |
---|
55 | if ((n += l) < l || n > m) break; |
---|
56 | memcpy(p, s, l); |
---|
57 | p += l; |
---|
58 | } |
---|
59 | va_end(args); |
---|
60 | if (s || m != n || p != result + n) { |
---|
61 | free(result); |
---|
62 | return NULL; |
---|
63 | } |
---|
64 | |
---|
65 | *p = '\0'; |
---|
66 | return result; |
---|
67 | } |
---|
68 | |
---|
69 | /* unlike `concat', which returns a |
---|
70 | * new pointer that must then be copied |
---|
71 | * or acted upon in some meaningfully meaningless |
---|
72 | * manner, `catl' returns the number of bytes belonging |
---|
73 | * to `buf', which could _NOT_ be filled, always copying |
---|
74 | * no more than `bufsiz` of data into `buf' |
---|
75 | * |
---|
76 | * If the return value is an integral value, which |
---|
77 | * we'll call `y', that is less than 0, |
---|
78 | * then the resulting catenation has been truncated by `!y' |
---|
79 | * many bytes. Similarlly, if a positive value is returned: |
---|
80 | * `y' many bytes is how much of `buf', which was _NOT_ used. |
---|
81 | * |
---|
82 | * XXX A failure is indicated by a return value _equal to |
---|
83 | * the destination buffers size_, which may make errors somewhat |
---|
84 | * harder to spot! */ |
---|
85 | size_t concatl(char *dst, size_t sz, const char *s1, ...) |
---|
86 | { |
---|
87 | va_list args; |
---|
88 | const char *s = NULL; |
---|
89 | char *p, *tmp; |
---|
90 | unsigned long ldx, mdx, ndx; |
---|
91 | size_t used = 0; |
---|
92 | |
---|
93 | mdx = ndx = strlen(s1); |
---|
94 | va_start(args, s1); |
---|
95 | while ((s = va_arg(args, char *))) { |
---|
96 | ldx = strlen(s); |
---|
97 | if ((mdx += ldx) < ldx) break; |
---|
98 | } |
---|
99 | va_end(args); |
---|
100 | if (s || mdx >= INT_MAX) return sz; |
---|
101 | |
---|
102 | #if defined(__cplusplus) |
---|
103 | tmp = (char *)malloc(mdx + 1); |
---|
104 | #else |
---|
105 | tmp = malloc(mdx + 1); |
---|
106 | #endif |
---|
107 | if (!tmp) return sz; |
---|
108 | bzero(tmp, mdx + 1); |
---|
109 | bzero(dst, mdx + 1); |
---|
110 | |
---|
111 | p = tmp; |
---|
112 | p = mempcpy(p, (char *)s1, ndx); |
---|
113 | |
---|
114 | used += ndx; |
---|
115 | RD_DBG("p: `%s`\n", p); |
---|
116 | RD_DBG("used: %lu\n", used - 0); |
---|
117 | |
---|
118 | va_start(args, s1); |
---|
119 | while ((s = va_arg(args, char *))) { |
---|
120 | ldx = strlen(s); |
---|
121 | if ((ndx += ldx) < ldx || ndx > mdx) break; |
---|
122 | p = mempcpy(p, (char *)s, ldx); |
---|
123 | used += ldx; |
---|
124 | } |
---|
125 | va_end(args); |
---|
126 | if (s || mdx != ndx || p != tmp + ndx) { |
---|
127 | free(tmp); |
---|
128 | return sz; |
---|
129 | } |
---|
130 | |
---|
131 | RD_DBG("tmp: `%s'\n", tmp); |
---|
132 | p = mempcpy(dst, tmp, (used > sz ? sz : used)); |
---|
133 | free(tmp); |
---|
134 | *p = '\0'; |
---|
135 | ++used; |
---|
136 | |
---|
137 | RD_DBG("dst: `%s'\n", dst); |
---|
138 | RD_DBG("*p: `%c'\n", *p); |
---|
139 | RD_DBG("*--p: `%c'\n", cpeek(p, dst, 0)); |
---|
140 | RD_DBG("strlen(dst): %lu\n", strlen(dst)); |
---|
141 | RD_DBG("used#2: %lu\n", used - 0); |
---|
142 | |
---|
143 | return (used > sz ? 0 : sz - used); |
---|
144 | } |
---|
145 | |
---|
146 | /* concatm is a little different: |
---|
147 | * unlike `concatl' or `concat', concatm _moves_ memory: that is, the destination |
---|
148 | * pointer can be passed as an argument. */ |
---|
149 | size_t concatm(char *dst, size_t sz, const char *s1, ...) |
---|
150 | { |
---|
151 | va_list args; |
---|
152 | const char *s = NULL; |
---|
153 | char *p, *tmp; |
---|
154 | unsigned long ldx, mdx, ndx; |
---|
155 | size_t used = 0; |
---|
156 | |
---|
157 | mdx = ndx = strlen(s1); |
---|
158 | va_start(args, s1); |
---|
159 | while ((s = va_arg(args, char *))) { |
---|
160 | ldx = strlen(s); |
---|
161 | if ((mdx += ldx) < ldx) break; |
---|
162 | } |
---|
163 | va_end(args); |
---|
164 | if (s || mdx >= INT_MAX) return sz; |
---|
165 | |
---|
166 | #if defined(__cplusplus) |
---|
167 | tmp = (char *)malloc(mdx + 1); |
---|
168 | #else |
---|
169 | tmp = malloc(mdx + 1); |
---|
170 | #endif |
---|
171 | if (!tmp) return sz; |
---|
172 | bzero(tmp, mdx + 1); |
---|
173 | |
---|
174 | p = tmp; |
---|
175 | p = mempcpy(p, (char *)s1, ndx); |
---|
176 | |
---|
177 | used += ndx; |
---|
178 | RD_DBG("p: `%s`\n", p); |
---|
179 | RD_DBG("used: %lu\n", used - 0); |
---|
180 | |
---|
181 | va_start(args, s1); |
---|
182 | while ((s = va_arg(args, char *))) { |
---|
183 | ldx = strlen(s); |
---|
184 | if ((ndx += ldx) < ldx || ndx > mdx) break; |
---|
185 | p = mempcpy(p, (char *)s, ldx); |
---|
186 | used += ldx; |
---|
187 | } |
---|
188 | va_end(args); |
---|
189 | if (s || mdx != ndx || p != tmp + ndx) { |
---|
190 | free(tmp); |
---|
191 | return sz; |
---|
192 | } |
---|
193 | RD_DBG("tmp: `%s'\n", tmp); |
---|
194 | #if defined(mempmove) && 0 |
---|
195 | p = mempmove(dst, tmp, (used > sz ? sz : used)); |
---|
196 | #else |
---|
197 | memmove(dst, tmp, (used > sz ? sz : used)); |
---|
198 | p = &dst[(used > sz ? sz : used)]; |
---|
199 | #endif |
---|
200 | free(tmp); |
---|
201 | *p = '\0'; |
---|
202 | ++used; |
---|
203 | |
---|
204 | RD_DBG("dst: `%s'\n", dst); |
---|
205 | RD_DBG("*p: `%c'\n", *p); |
---|
206 | RD_DBG("*--p: `%c'\n", cpeek(p, dst, 0)); |
---|
207 | RD_DBG("strlen(dst): %lu\n", strlen(dst)); |
---|
208 | RD_DBG("used#2: %lu\n", used - 0); |
---|
209 | |
---|
210 | return (used > sz ? 0 : sz - used); |
---|
211 | } |
---|
212 | |
---|
213 | char cpeek(const char *c, const char *s, const short fwd) |
---|
214 | { |
---|
215 | if (fwd > 0) { |
---|
216 | if (*c == '\0' |
---|
217 | # if defined(_GNU_SOURCE) |
---|
218 | || c == strchr(s, '\0') - 1 |
---|
219 | # else |
---|
220 | || c == &s[strlen(s)] |
---|
221 | # endif |
---|
222 | ) |
---|
223 | return *c; |
---|
224 | else |
---|
225 | return *(c + 1); |
---|
226 | } |
---|
227 | return (c == s) ? *c : *(c - 1); |
---|
228 | } |
---|
229 | |
---|
230 | # if 0 |
---|
231 | static int strnof_delim(char *str, char odelim, char cdelim, int count[2]) |
---|
232 | { |
---|
233 | memset(count, 0, sizeof(*count)*2); |
---|
234 | RL_EGG_BEGIN_TRACE; |
---|
235 | RL_EGG_DEBUG("str: %s\n", str); |
---|
236 | char *cp = strchr(str, '\0'); |
---|
237 | RL_EGG_END_TRACE; |
---|
238 | |
---|
239 | if (cp == str) |
---|
240 | return 1; |
---|
241 | |
---|
242 | do { |
---|
243 | if (cp != str && cpeek(cp, str, false) == '\\') |
---|
244 | continue; |
---|
245 | |
---|
246 | if (*cp == cdelim) { |
---|
247 | ++count[1]; // increment close |
---|
248 | } else if (*cp == odelim) { |
---|
249 | ++count[0]; // increment open |
---|
250 | } |
---|
251 | } while(cp-- != str); |
---|
252 | |
---|
253 | if (odelim == cdelim && count[1] > 0) { |
---|
254 | if (count[1] % 2 == 1) |
---|
255 | while(count[0]++ < --count[1]); |
---|
256 | else { |
---|
257 | count[0] = count[1] * 0.5; |
---|
258 | count[1] *= 0.5; |
---|
259 | } |
---|
260 | } |
---|
261 | |
---|
262 | RL_EGG_BEGIN_TRACE; |
---|
263 | RL_EGG_DEBUG("open: %d\nclose: %d\n", count[0], count[1]); |
---|
264 | RL_EGG_END_TRACE; |
---|
265 | return 0; |
---|
266 | } |
---|
267 | # endif |
---|
268 | |
---|
269 | int *strndelim(const char *s, const char od, const char cd, int count[2]) |
---|
270 | { |
---|
271 | memset(count, 0, sizeof(*count)*2); |
---|
272 | char *c = strchr(s, '\0'); |
---|
273 | |
---|
274 | if (c == s) |
---|
275 | return NULL; |
---|
276 | |
---|
277 | do { |
---|
278 | if (c != s && cpeek(c, s, 0) == '\\') |
---|
279 | continue; |
---|
280 | if (*c == cd) |
---|
281 | ++count[1]; |
---|
282 | else if (*c == od) |
---|
283 | ++count[0]; |
---|
284 | } while (c-- != s); |
---|
285 | |
---|
286 | if (od == cd && count[1] > 0) { |
---|
287 | if (count[1] % 2 == 1) |
---|
288 | while (count[0]++ < --count[1]); |
---|
289 | else { |
---|
290 | count[0] = count[1] * 0.5; |
---|
291 | count[1] *= 0.5; |
---|
292 | } |
---|
293 | } |
---|
294 | |
---|
295 | return count; |
---|
296 | } |
---|
297 | |
---|
298 | char *strwodqp(const char *src) |
---|
299 | { |
---|
300 | size_t n = strlen(src) + 1; |
---|
301 | int c[2] = {0, 0}, even = 0; |
---|
302 | char *tmp, *token, *rest, *newp; |
---|
303 | tmp = token = rest = newp = NULL; |
---|
304 | |
---|
305 | if (!strndelim(src, '"', '"', c)) |
---|
306 | return NULL; |
---|
307 | |
---|
308 | if (c[0] == 0) |
---|
309 | return NULL; |
---|
310 | |
---|
311 | tmp = strdup(src); |
---|
312 | newp = malloc(n); |
---|
313 | even = c[0] - abs(c[0] - c[1]); |
---|
314 | |
---|
315 | token = strtok_r(tmp, "\"", &rest); |
---|
316 | |
---|
317 | if (token == NULL) { |
---|
318 | free(newp); |
---|
319 | return NULL; |
---|
320 | } |
---|
321 | |
---|
322 | catl(newp, n, token); |
---|
323 | while ((token = strtok_r(NULL, "\"", &rest)) != NULL) { |
---|
324 | if (even % 2 == 1) { |
---|
325 | catm(newp, n, newp, token); |
---|
326 | --even; |
---|
327 | } else { |
---|
328 | ++even; |
---|
329 | } |
---|
330 | } |
---|
331 | |
---|
332 | free(tmp); |
---|
333 | return newp; |
---|
334 | } |
---|