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