1 /**
2 @Cond IGNORE
3
4 ======================================================
5 SFSEXP: Small, Fast S-Expression Library
6 Written by Matthew Sottile (mjsottile@gmail.com)
7 ======================================================
8
9 Copyright (2003-2006). The Regents of the University of California. This
10 material was produced under U.S. Government contract W-7405-ENG-36 for Los
11 Alamos National Laboratory, which is operated by the University of
12 California for the U.S. Department of Energy. The U.S. Government has rights
13 to use, reproduce, and distribute this software. NEITHER THE GOVERNMENT NOR
14 THE UNIVERSITY MAKES ANY WARRANTY, EXPRESS OR IMPLIED, OR ASSUMES ANY
15 LIABILITY FOR THE USE OF THIS SOFTWARE. If software is modified to produce
16 derivative works, such modified software should be clearly marked, so as not
17 to confuse it with the version available from LANL.
18
19 Additionally, this library is free software; you can redistribute it and/or
20 modify it under the terms of the GNU Lesser General Public License as
21 published by the Free Software Foundation; either version 2.1 of the
22 License, or (at your option) any later version.
23
24 This library is distributed in the hope that it will be useful, but WITHOUT
25 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
26 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
27 for more details.
28
29 You should have received a copy of the GNU Lesser General Public License
30 along with this library; if not, write to the Free Software Foundation,
31 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, U SA
32
33 LA-CC-04-094
34
35 @endcond
36 **/
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include "sexp.h"
41 #include "faststack.h"
42
43 /*
44 * global error code that can be set by sexp library calls. default
45 * is SEXP_ERR_OK.
46 */
47 sexp_errcode_t sexp_errno = SEXP_ERR_OK;
48
reset_sexp_errno()49 void reset_sexp_errno() {
50 sexp_errno = SEXP_ERR_OK;
51 }
52
53 /**
54 * Recursively walk an s-expression and free it.
55 */
56 void
destroy_sexp(sexp_t * s)57 destroy_sexp (sexp_t * s)
58 {
59 if (s == NULL)
60 return;
61
62 if (s->ty == SEXP_LIST) {
63 destroy_sexp (s->list);
64 } else if (s->ty == SEXP_VALUE) {
65 if (s->aty == SEXP_BINARY && s->bindata != NULL) {
66 sexp_free(s->bindata, s->binlength);
67 } else if (s->val != NULL) {
68 sexp_free(s->val, s->val_allocated);
69 }
70 }
71
72 s->val = NULL;
73 s->bindata = NULL;
74
75 destroy_sexp (s->next);
76
77 s->next = s->list = NULL;
78
79 sexp_t_deallocate(s);
80 }
81
82 /**
83 * Iterative method to walk sx and turn it back into the string
84 * representation of the s-expression. Fills the buffer if there
85 * is space. If there is not, the buffer will be partially filled
86 * up to but not exceeding the buffer size.
87 */
88 int
print_sexp(char * buf,size_t size,const sexp_t * sx)89 print_sexp (char *buf, size_t size, const sexp_t * sx)
90 {
91 int retval;
92 size_t sz;
93 char *b = buf, *tc;
94 size_t left = size;
95 int depth = 0;
96 faststack_t *stack;
97 stack_lvl_t *top;
98 sexp_t *tdata;
99 sexp_t *fakehead;
100 sexp_t tmp;
101
102 /* if no space left, then cleanup, set the error
103 flag, null terminate b, and return -1. */
104 #define out_of_space() { \
105 sexp_errno = SEXP_ERR_BUFFER_FULL; \
106 b--; \
107 b[0] = 0; \
108 retval = -1; \
109 destroy_stack(stack); \
110 sexp_t_deallocate(fakehead); \
111 return retval; \
112 }
113
114 /* macro for adding one char to b. */
115 #define add_char_break_full(c) { \
116 b[0] = c; \
117 b++; \
118 left--; \
119 if (left == 0) \
120 { \
121 out_of_space(); \
122 } \
123 }
124
125 if (sx == NULL)
126 {
127 buf[0] = '\0';
128 return 0;
129 }
130
131 if (size < 1)
132 {
133 return -1;
134 }
135
136 tmp = *sx;
137 tmp.next = tmp.list = NULL;
138
139 fakehead = copy_sexp(&tmp);
140
141 if (fakehead == NULL)
142 {
143 sexp_errno = SEXP_ERR_MEMORY;
144 return -1;
145 }
146
147 fakehead->list = sx->list;
148 fakehead->next = NULL; /* this is the important part of fakehead */
149
150 stack = make_stack ();
151 if (stack == NULL)
152 {
153 sexp_errno = SEXP_ERR_MEMORY;
154 sexp_t_deallocate(fakehead);
155 return -1;
156 }
157
158 push (stack, fakehead);
159
160 while (stack->top != NULL)
161 {
162 top = stack->top;
163 tdata = (sexp_t *) top->data;
164
165 if (tdata == NULL)
166 {
167 pop (stack);
168
169 if (depth > 0)
170 {
171 depth--;
172 add_char_break_full(')');
173 }
174
175 if (stack->top == NULL)
176 break;
177
178 top = stack->top;
179 top->data = ((sexp_t *) top->data)->next;
180 if (top->data != NULL)
181 {
182 add_char_break_full(' ');
183 }
184 }
185 else if (tdata->ty == SEXP_VALUE)
186 {
187 if (tdata->aty == SEXP_DQUOTE)
188 {
189 add_char_break_full('\"');
190 }
191 else if (tdata->aty == SEXP_SQUOTE)
192 {
193 add_char_break_full('\'');
194 }
195
196 if (tdata->aty != SEXP_BINARY && tdata->val_used > 0) {
197 tc = tdata->val;
198 /* copy value into string */
199 while (tc[0] != 0 && left > 0)
200 {
201 /* escape characters that need escaping. */
202 if ((tc[0] == '\"' || tc[0] == '\\') &&
203 tdata->aty == SEXP_DQUOTE)
204 {
205 add_char_break_full('\\');
206 }
207
208 add_char_break_full(tc[0]);
209 tc++;
210 }
211 } else {
212 if (left > 3) {
213 add_char_break_full('#');
214 add_char_break_full('b');
215 add_char_break_full('#');
216
217 #ifndef WIN32
218 if ((size_t)(sz = snprintf(b,left,"%lu#",(unsigned long)tdata->binlength)) >= left) {
219 #else
220 if ((sz = _snprintf(b,left,"%lu#",tdata->binlength)) >= left) {
221 #endif
222 out_of_space();
223 }
224 if (sz < 0) {
225 out_of_space();
226 }
227
228 b += sz;
229 left -= sz;
230
231 if (left < tdata->binlength) {
232 out_of_space();
233 }
234
235 if (tdata->binlength > 0) {
236 memcpy(b,tdata->bindata,tdata->binlength);
237 left -= tdata->binlength;
238 b+=tdata->binlength;
239 }
240
241 add_char_break_full(' ');
242 } else {
243 out_of_space();
244 }
245 }
246
247 if (tdata->aty == SEXP_DQUOTE && left > 0)
248 {
249 add_char_break_full('\"');
250 }
251
252 if (left == 0)
253 {
254 out_of_space();
255 }
256
257 top->data = ((sexp_t *) top->data)->next;
258
259 if (top->data != NULL)
260 {
261 add_char_break_full(' ');
262 }
263 }
264 else if (tdata->ty == SEXP_LIST)
265 {
266 depth++;
267 add_char_break_full('(');
268
269 push (stack, tdata->list);
270 }
271 else
272 {
273 sexp_errno = SEXP_ERR_BADCONTENT;
274 destroy_stack (stack);
275 sexp_t_deallocate(fakehead);
276 return -1;
277 }
278
279 }
280 while (depth != 0)
281 {
282 add_char_break_full(')');
283 depth--;
284 }
285
286 if (left != 0)
287 {
288 b[0] = 0;
289 retval = (int) (size-left);
290 }
291 else
292 {
293 b--;
294 b[0] = 0;
295 retval = -1;
296 }
297
298 destroy_stack (stack);
299 sexp_t_deallocate(fakehead);
300
301 return retval;
302 }
303
304 /**
305 * Iterative method to walk sx and turn it back into the string
306 * representation of the s-expression. Fills the CSTRING that is
307 * passed in. If *s == NULL (new CSTRING, never used), snew() is called
308 * and passed back. If *s != NULL, *s is used as the CSTRING to print
309 * into. In the last case, the recycled CSTRING must have sempty() called
310 * to reset the allocated vs. used counters to make it appear to be empty.
311 * the code will assume that sempty() was called by the user!
312 */
313 int
314 print_sexp_cstr (CSTRING **s, const sexp_t *sx, size_t ss)
315 {
316 int retval;
317 char *tc;
318 int depth = 0;
319 faststack_t *stack;
320 stack_lvl_t *top;
321 sexp_t *tdata;
322 sexp_t *fakehead;
323 CSTRING *_s = NULL;
324 char sbuf[32];
325 unsigned int i;
326 sexp_t tmp;
327
328 if (sx == NULL)
329 {
330 return -1;
331 }
332
333 if (*s == NULL)
334 _s = snew(ss);
335 else
336 _s = *s;
337
338 tmp = *sx;
339 tmp.next = tmp.list = NULL;
340
341 fakehead = copy_sexp(&tmp);
342
343 if (fakehead == NULL) {
344 sexp_errno = SEXP_ERR_MEMORY;
345 return -1;
346 }
347
348 fakehead->list = sx->list;
349 fakehead->next = NULL; /* this is the important part of fakehead */
350
351 stack = make_stack ();
352 if (stack == NULL) {
353 sexp_errno = SEXP_ERR_MEMORY;
354 sexp_t_deallocate(fakehead);
355 return -1;
356 }
357
358 push (stack, fakehead);
359
360 while (stack->top != NULL)
361 {
362 top = stack->top;
363 tdata = (sexp_t *) top->data;
364
365 if (tdata == NULL)
366 {
367 pop (stack);
368
369 if (depth > 0)
370 {
371 _s = saddch(_s, ')');
372 depth--;
373 }
374
375 if (stack->top == NULL)
376 break;
377
378 top = stack->top;
379 top->data = ((sexp_t *) top->data)->next;
380 if (top->data != NULL)
381 {
382 _s = saddch(_s, ' ');
383 }
384 }
385 else if (tdata->ty == SEXP_VALUE)
386 {
387 if (tdata->aty == SEXP_DQUOTE)
388 {
389 _s = saddch(_s,'\"');
390 }
391 else if (tdata->aty == SEXP_SQUOTE)
392 {
393 _s = saddch(_s,'\'');
394 }
395
396 if (tdata->aty == SEXP_BINARY) {
397 sprintf(sbuf,"#b#%lu#",(unsigned long)tdata->binlength);
398
399 _s = sadd(_s,sbuf);
400
401 for (i=0;i<tdata->binlength;i++)
402 _s = saddch(_s,tdata->bindata[i]);
403 _s = saddch(_s,' ');
404 } else {
405 if (tdata->val_used > 0) {
406 tc = tdata->val;
407
408 /* copy value into string */
409 while (tc[0] != 0)
410 {
411 /* escape characters that need escaping. */
412 if ((tc[0] == '\"' ||
413 tc[0] == '\\') && tdata->aty == SEXP_DQUOTE)
414 {
415 _s = saddch(_s,'\\');
416 }
417
418 _s = saddch(_s,tc[0]);
419 tc++;
420 }
421 }
422 }
423
424 if (tdata->aty == SEXP_DQUOTE)
425 {
426 _s = saddch(_s,'\"');
427 }
428
429 top->data = ((sexp_t *) top->data)->next;
430
431 if (top->data != NULL)
432 {
433 _s = saddch(_s,' ');
434 }
435 }
436 else if (tdata->ty == SEXP_LIST)
437 {
438 depth++;
439 _s = saddch(_s,'(');
440 push (stack, tdata->list);
441 }
442 else
443 {
444 sexp_errno = SEXP_ERR_BADCONTENT;
445 destroy_stack (stack);
446 sexp_t_deallocate(fakehead);
447 return -1;
448 }
449
450 }
451 while (depth != 0)
452 {
453 _s = saddch(_s,')');
454 depth--;
455 }
456
457 *s = _s;
458 if (_s == NULL)
459 retval = 0;
460 else
461 retval = (int) _s->curlen;
462
463 destroy_stack (stack);
464 sexp_t_deallocate(fakehead);
465
466 return retval;
467 }
468
469 /**
470 * Allocate a new sexp_t element representing a list.
471 */
472 sexp_t *new_sexp_list(sexp_t *l) {
473 sexp_t *sx = sexp_t_allocate();
474
475 if (sx == NULL) {
476 sexp_errno = SEXP_ERR_MEMORY;
477 return NULL;
478 }
479
480 sx->ty = SEXP_LIST;
481
482 sx->list = l;
483 sx->next = NULL;
484
485 sx->val = NULL;
486 sx->val_used = sx->val_allocated = 0;
487
488 return sx;
489 }
490
491 /**
492 * allocate a new sexp_t element representing a raw binary value
493 */
494 sexp_t *new_sexp_binary_atom(char *data, size_t binlength) {
495 sexp_t *sx = sexp_t_allocate();
496
497 if (sx == NULL) {
498 sexp_errno = SEXP_ERR_MEMORY;
499 return NULL;
500 }
501
502 sx->ty = SEXP_VALUE;
503 sx->next = sx->list = NULL;
504 sx->aty = SEXP_BINARY;
505 sx->bindata = data;
506 sx->binlength = binlength;
507 sx->val = NULL;
508 sx->val_used = sx->val_allocated = 0;
509
510 return sx;
511 }
512
513 /**
514 * allocate a new sexp_t element representing a value
515 */
516 sexp_t *new_sexp_atom(const char *buf, size_t bs, atom_t aty) {
517 sexp_t *sx = NULL;
518
519 if (aty == SEXP_BINARY) {
520 sexp_errno = SEXP_ERR_BAD_CONSTRUCTOR;
521 return NULL;
522 }
523
524 sx = sexp_t_allocate();
525
526 if (sx == NULL) {
527 sexp_errno = SEXP_ERR_MEMORY;
528 return NULL;
529 }
530
531 sx->ty = SEXP_VALUE;
532 sx->aty = aty;
533
534 #ifdef __cplusplus
535 sx->val = (char *)sexp_malloc(sizeof(char)*(bs+1));
536 #else
537 sx->val = sexp_malloc(sizeof(char)*(bs+1));
538 #endif
539
540 if (sx->val == NULL) {
541 sexp_t_deallocate(sx);
542 sexp_errno = SEXP_ERR_MEMORY;
543 return NULL;
544 }
545
546 sx->val_used = sx->val_allocated = bs+1;
547
548 strcpy(sx->val,buf);
549
550 sx->list = sx->next = NULL;
551
552 return sx;
553 }
554