1 | /* $NetBSD: prop_string.c,v 1.12 2014/03/26 18:12:46 christos Exp $ */ |
2 | |
3 | /*- |
4 | * Copyright (c) 2006 The NetBSD Foundation, Inc. |
5 | * All rights reserved. |
6 | * |
7 | * This code is derived from software contributed to The NetBSD Foundation |
8 | * by Jason R. Thorpe. |
9 | * |
10 | * Redistribution and use in source and binary forms, with or without |
11 | * modification, are permitted provided that the following conditions |
12 | * are met: |
13 | * 1. Redistributions of source code must retain the above copyright |
14 | * notice, this list of conditions and the following disclaimer. |
15 | * 2. Redistributions in binary form must reproduce the above copyright |
16 | * notice, this list of conditions and the following disclaimer in the |
17 | * documentation and/or other materials provided with the distribution. |
18 | * |
19 | * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS |
20 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
21 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
22 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS |
23 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
29 | * POSSIBILITY OF SUCH DAMAGE. |
30 | */ |
31 | |
32 | #include <prop/prop_string.h> |
33 | #include "prop_object_impl.h" |
34 | |
35 | struct _prop_string { |
36 | struct _prop_object ps_obj; |
37 | union { |
38 | char * psu_mutable; |
39 | const char * psu_immutable; |
40 | } ps_un; |
41 | #define ps_mutable ps_un.psu_mutable |
42 | #define ps_immutable ps_un.psu_immutable |
43 | size_t ps_size; /* not including \0 */ |
44 | int ps_flags; |
45 | }; |
46 | |
47 | #define PS_F_NOCOPY 0x01 |
48 | |
49 | _PROP_POOL_INIT(_prop_string_pool, sizeof(struct _prop_string), "propstng" ) |
50 | |
51 | _PROP_MALLOC_DEFINE(M_PROP_STRING, "prop string" , |
52 | "property string container object" ) |
53 | |
54 | static _prop_object_free_rv_t |
55 | _prop_string_free(prop_stack_t, prop_object_t *); |
56 | static bool _prop_string_externalize( |
57 | struct _prop_object_externalize_context *, |
58 | void *); |
59 | static _prop_object_equals_rv_t |
60 | _prop_string_equals(prop_object_t, prop_object_t, |
61 | void **, void **, |
62 | prop_object_t *, prop_object_t *); |
63 | |
64 | static const struct _prop_object_type _prop_object_type_string = { |
65 | .pot_type = PROP_TYPE_STRING, |
66 | .pot_free = _prop_string_free, |
67 | .pot_extern = _prop_string_externalize, |
68 | .pot_equals = _prop_string_equals, |
69 | }; |
70 | |
71 | #define prop_object_is_string(x) \ |
72 | ((x) != NULL && (x)->ps_obj.po_type == &_prop_object_type_string) |
73 | #define prop_string_contents(x) ((x)->ps_immutable ? (x)->ps_immutable : "") |
74 | |
75 | /* ARGSUSED */ |
76 | static _prop_object_free_rv_t |
77 | _prop_string_free(prop_stack_t stack, prop_object_t *obj) |
78 | { |
79 | prop_string_t ps = *obj; |
80 | |
81 | if ((ps->ps_flags & PS_F_NOCOPY) == 0 && ps->ps_mutable != NULL) |
82 | _PROP_FREE(ps->ps_mutable, M_PROP_STRING); |
83 | _PROP_POOL_PUT(_prop_string_pool, ps); |
84 | |
85 | return (_PROP_OBJECT_FREE_DONE); |
86 | } |
87 | |
88 | static bool |
89 | _prop_string_externalize(struct _prop_object_externalize_context *ctx, |
90 | void *v) |
91 | { |
92 | prop_string_t ps = v; |
93 | |
94 | if (ps->ps_size == 0) |
95 | return (_prop_object_externalize_empty_tag(ctx, "string" )); |
96 | |
97 | if (_prop_object_externalize_start_tag(ctx, "string" ) == false || |
98 | _prop_object_externalize_append_encoded_cstring(ctx, |
99 | ps->ps_immutable) == false || |
100 | _prop_object_externalize_end_tag(ctx, "string" ) == false) |
101 | return (false); |
102 | |
103 | return (true); |
104 | } |
105 | |
106 | /* ARGSUSED */ |
107 | static _prop_object_equals_rv_t |
108 | _prop_string_equals(prop_object_t v1, prop_object_t v2, |
109 | void **stored_pointer1, void **stored_pointer2, |
110 | prop_object_t *next_obj1, prop_object_t *next_obj2) |
111 | { |
112 | prop_string_t str1 = v1; |
113 | prop_string_t str2 = v2; |
114 | |
115 | if (str1 == str2) |
116 | return (_PROP_OBJECT_EQUALS_TRUE); |
117 | if (str1->ps_size != str2->ps_size) |
118 | return (_PROP_OBJECT_EQUALS_FALSE); |
119 | if (strcmp(prop_string_contents(str1), prop_string_contents(str2))) |
120 | return (_PROP_OBJECT_EQUALS_FALSE); |
121 | else |
122 | return (_PROP_OBJECT_EQUALS_TRUE); |
123 | } |
124 | |
125 | static prop_string_t |
126 | _prop_string_alloc(void) |
127 | { |
128 | prop_string_t ps; |
129 | |
130 | ps = _PROP_POOL_GET(_prop_string_pool); |
131 | if (ps != NULL) { |
132 | _prop_object_init(&ps->ps_obj, &_prop_object_type_string); |
133 | |
134 | ps->ps_mutable = NULL; |
135 | ps->ps_size = 0; |
136 | ps->ps_flags = 0; |
137 | } |
138 | |
139 | return (ps); |
140 | } |
141 | |
142 | /* |
143 | * prop_string_create -- |
144 | * Create an empty mutable string. |
145 | */ |
146 | prop_string_t |
147 | prop_string_create(void) |
148 | { |
149 | |
150 | return (_prop_string_alloc()); |
151 | } |
152 | |
153 | /* |
154 | * prop_string_create_cstring -- |
155 | * Create a string that contains a copy of the provided C string. |
156 | */ |
157 | prop_string_t |
158 | prop_string_create_cstring(const char *str) |
159 | { |
160 | prop_string_t ps; |
161 | char *cp; |
162 | size_t len; |
163 | |
164 | ps = _prop_string_alloc(); |
165 | if (ps != NULL) { |
166 | len = strlen(str); |
167 | cp = _PROP_MALLOC(len + 1, M_PROP_STRING); |
168 | if (cp == NULL) { |
169 | prop_object_release(ps); |
170 | return (NULL); |
171 | } |
172 | strcpy(cp, str); |
173 | ps->ps_mutable = cp; |
174 | ps->ps_size = len; |
175 | } |
176 | return (ps); |
177 | } |
178 | |
179 | /* |
180 | * prop_string_create_cstring_nocopy -- |
181 | * Create an immutable string that contains a refrence to the |
182 | * provided C string. |
183 | */ |
184 | prop_string_t |
185 | prop_string_create_cstring_nocopy(const char *str) |
186 | { |
187 | prop_string_t ps; |
188 | |
189 | ps = _prop_string_alloc(); |
190 | if (ps != NULL) { |
191 | ps->ps_immutable = str; |
192 | ps->ps_size = strlen(str); |
193 | ps->ps_flags |= PS_F_NOCOPY; |
194 | } |
195 | return (ps); |
196 | } |
197 | |
198 | /* |
199 | * prop_string_copy -- |
200 | * Copy a string. If the original string is immutable, then the |
201 | * copy is also immutable and references the same external data. |
202 | */ |
203 | prop_string_t |
204 | prop_string_copy(prop_string_t ops) |
205 | { |
206 | prop_string_t ps; |
207 | |
208 | if (! prop_object_is_string(ops)) |
209 | return (NULL); |
210 | |
211 | ps = _prop_string_alloc(); |
212 | if (ps != NULL) { |
213 | ps->ps_size = ops->ps_size; |
214 | ps->ps_flags = ops->ps_flags; |
215 | if (ops->ps_flags & PS_F_NOCOPY) |
216 | ps->ps_immutable = ops->ps_immutable; |
217 | else { |
218 | char *cp = _PROP_MALLOC(ps->ps_size + 1, M_PROP_STRING); |
219 | if (cp == NULL) { |
220 | prop_object_release(ps); |
221 | return (NULL); |
222 | } |
223 | strcpy(cp, prop_string_contents(ops)); |
224 | ps->ps_mutable = cp; |
225 | } |
226 | } |
227 | return (ps); |
228 | } |
229 | |
230 | /* |
231 | * prop_string_copy_mutable -- |
232 | * Copy a string, always returning a mutable copy. |
233 | */ |
234 | prop_string_t |
235 | prop_string_copy_mutable(prop_string_t ops) |
236 | { |
237 | prop_string_t ps; |
238 | char *cp; |
239 | |
240 | if (! prop_object_is_string(ops)) |
241 | return (NULL); |
242 | |
243 | ps = _prop_string_alloc(); |
244 | if (ps != NULL) { |
245 | ps->ps_size = ops->ps_size; |
246 | cp = _PROP_MALLOC(ps->ps_size + 1, M_PROP_STRING); |
247 | if (cp == NULL) { |
248 | prop_object_release(ps); |
249 | return (NULL); |
250 | } |
251 | strcpy(cp, prop_string_contents(ops)); |
252 | ps->ps_mutable = cp; |
253 | } |
254 | return (ps); |
255 | } |
256 | |
257 | /* |
258 | * prop_string_size -- |
259 | * Return the size of the string, not including the terminating NUL. |
260 | */ |
261 | size_t |
262 | prop_string_size(prop_string_t ps) |
263 | { |
264 | |
265 | if (! prop_object_is_string(ps)) |
266 | return (0); |
267 | |
268 | return (ps->ps_size); |
269 | } |
270 | |
271 | /* |
272 | * prop_string_mutable -- |
273 | * Return true if the string is a mutable string. |
274 | */ |
275 | bool |
276 | prop_string_mutable(prop_string_t ps) |
277 | { |
278 | |
279 | if (! prop_object_is_string(ps)) |
280 | return (false); |
281 | |
282 | return ((ps->ps_flags & PS_F_NOCOPY) == 0); |
283 | } |
284 | |
285 | /* |
286 | * prop_string_cstring -- |
287 | * Return a copy of the contents of the string as a C string. |
288 | * The string is allocated with the M_TEMP malloc type. |
289 | */ |
290 | char * |
291 | prop_string_cstring(prop_string_t ps) |
292 | { |
293 | char *cp; |
294 | |
295 | if (! prop_object_is_string(ps)) |
296 | return (NULL); |
297 | |
298 | cp = _PROP_MALLOC(ps->ps_size + 1, M_TEMP); |
299 | if (cp != NULL) |
300 | strcpy(cp, prop_string_contents(ps)); |
301 | |
302 | return (cp); |
303 | } |
304 | |
305 | /* |
306 | * prop_string_cstring_nocopy -- |
307 | * Return an immutable reference to the contents of the string |
308 | * as a C string. |
309 | */ |
310 | const char * |
311 | prop_string_cstring_nocopy(prop_string_t ps) |
312 | { |
313 | |
314 | if (! prop_object_is_string(ps)) |
315 | return (NULL); |
316 | |
317 | return (prop_string_contents(ps)); |
318 | } |
319 | |
320 | /* |
321 | * prop_string_append -- |
322 | * Append the contents of one string to another. Returns true |
323 | * upon success. The destination string must be mutable. |
324 | */ |
325 | bool |
326 | prop_string_append(prop_string_t dst, prop_string_t src) |
327 | { |
328 | char *ocp, *cp; |
329 | size_t len; |
330 | |
331 | if (! (prop_object_is_string(dst) && |
332 | prop_object_is_string(src))) |
333 | return (false); |
334 | |
335 | if (dst->ps_flags & PS_F_NOCOPY) |
336 | return (false); |
337 | |
338 | len = dst->ps_size + src->ps_size; |
339 | cp = _PROP_MALLOC(len + 1, M_PROP_STRING); |
340 | if (cp == NULL) |
341 | return (false); |
342 | snprintf(cp, len + 1, "%s%s" , prop_string_contents(dst), |
343 | prop_string_contents(src)); |
344 | ocp = dst->ps_mutable; |
345 | dst->ps_mutable = cp; |
346 | dst->ps_size = len; |
347 | if (ocp != NULL) |
348 | _PROP_FREE(ocp, M_PROP_STRING); |
349 | |
350 | return (true); |
351 | } |
352 | |
353 | /* |
354 | * prop_string_append_cstring -- |
355 | * Append a C string to a string. Returns true upon success. |
356 | * The destination string must be mutable. |
357 | */ |
358 | bool |
359 | prop_string_append_cstring(prop_string_t dst, const char *src) |
360 | { |
361 | char *ocp, *cp; |
362 | size_t len; |
363 | |
364 | if (! prop_object_is_string(dst)) |
365 | return (false); |
366 | |
367 | _PROP_ASSERT(src != NULL); |
368 | |
369 | if (dst->ps_flags & PS_F_NOCOPY) |
370 | return (false); |
371 | |
372 | len = dst->ps_size + strlen(src); |
373 | cp = _PROP_MALLOC(len + 1, M_PROP_STRING); |
374 | if (cp == NULL) |
375 | return (false); |
376 | snprintf(cp, len + 1, "%s%s" , prop_string_contents(dst), src); |
377 | ocp = dst->ps_mutable; |
378 | dst->ps_mutable = cp; |
379 | dst->ps_size = len; |
380 | if (ocp != NULL) |
381 | _PROP_FREE(ocp, M_PROP_STRING); |
382 | |
383 | return (true); |
384 | } |
385 | |
386 | /* |
387 | * prop_string_equals -- |
388 | * Return true if two strings are equivalent. |
389 | */ |
390 | bool |
391 | prop_string_equals(prop_string_t str1, prop_string_t str2) |
392 | { |
393 | if (!prop_object_is_string(str1) || !prop_object_is_string(str2)) |
394 | return (false); |
395 | |
396 | return prop_object_equals(str1, str2); |
397 | } |
398 | |
399 | /* |
400 | * prop_string_equals_cstring -- |
401 | * Return true if the string is equivalent to the specified |
402 | * C string. |
403 | */ |
404 | bool |
405 | prop_string_equals_cstring(prop_string_t ps, const char *cp) |
406 | { |
407 | |
408 | if (! prop_object_is_string(ps)) |
409 | return (false); |
410 | |
411 | return (strcmp(prop_string_contents(ps), cp) == 0); |
412 | } |
413 | |
414 | /* |
415 | * _prop_string_internalize -- |
416 | * Parse a <string>...</string> and return the object created from the |
417 | * external representation. |
418 | */ |
419 | /* ARGSUSED */ |
420 | bool |
421 | _prop_string_internalize(prop_stack_t stack, prop_object_t *obj, |
422 | struct _prop_object_internalize_context *ctx) |
423 | { |
424 | prop_string_t string; |
425 | char *str; |
426 | size_t len, alen; |
427 | |
428 | if (ctx->poic_is_empty_element) { |
429 | *obj = prop_string_create(); |
430 | return (true); |
431 | } |
432 | |
433 | /* No attributes recognized here. */ |
434 | if (ctx->poic_tagattr != NULL) |
435 | return (true); |
436 | |
437 | /* Compute the length of the result. */ |
438 | if (_prop_object_internalize_decode_string(ctx, NULL, 0, &len, |
439 | NULL) == false) |
440 | return (true); |
441 | |
442 | str = _PROP_MALLOC(len + 1, M_PROP_STRING); |
443 | if (str == NULL) |
444 | return (true); |
445 | |
446 | if (_prop_object_internalize_decode_string(ctx, str, len, &alen, |
447 | &ctx->poic_cp) == false || |
448 | alen != len) { |
449 | _PROP_FREE(str, M_PROP_STRING); |
450 | return (true); |
451 | } |
452 | str[len] = '\0'; |
453 | |
454 | if (_prop_object_internalize_find_tag(ctx, "string" , |
455 | _PROP_TAG_TYPE_END) == false) { |
456 | _PROP_FREE(str, M_PROP_STRING); |
457 | return (true); |
458 | } |
459 | |
460 | string = _prop_string_alloc(); |
461 | if (string == NULL) { |
462 | _PROP_FREE(str, M_PROP_STRING); |
463 | return (true); |
464 | } |
465 | |
466 | string->ps_mutable = str; |
467 | string->ps_size = len; |
468 | *obj = string; |
469 | |
470 | return (true); |
471 | } |
472 | |