You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
libsxmp/sexpr/sexp.c

590 lines
12 KiB
C

/**
@Cond IGNORE
======================================================
SFSEXP: Small, Fast S-Expression Library version 1.2
Written by Matthew Sottile (mjsottile@gmail.com)
======================================================
Copyright (2003-2006). The Regents of the University of California. This
material was produced under U.S. Government contract W-7405-ENG-36 for Los
Alamos National Laboratory, which is operated by the University of
California for the U.S. Department of Energy. The U.S. Government has rights
to use, reproduce, and distribute this software. NEITHER THE GOVERNMENT NOR
THE UNIVERSITY MAKES ANY WARRANTY, EXPRESS OR IMPLIED, OR ASSUMES ANY
LIABILITY FOR THE USE OF THIS SOFTWARE. If software is modified to produce
derivative works, such modified software should be clearly marked, so as not
to confuse it with the version available from LANL.
Additionally, this library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of the
License, or (at your option) any later version.
This library is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation,
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, U SA
LA-CC-04-094
@endcond
**/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sexpr/sexp.h>
#include <sexpr/faststack.h>
/*
* global error code that can be set by sexp library calls. default
* is SEXP_ERR_OK.
*/
sexp_errcode_t sexp_errno = SEXP_ERR_OK;
void reset_sexp_errno() {
sexp_errno = SEXP_ERR_OK;
}
/**
* Recursively walk an s-expression and free it.
*/
void
destroy_sexp (sexp_t * s)
{
if (s == NULL)
return;
if (s->ty == SEXP_LIST) {
destroy_sexp (s->list);
} else if (s->ty == SEXP_VALUE) {
if (s->aty == SEXP_BINARY && s->bindata != NULL) {
sexp_free(s->bindata, s->binlength);
} else if (s->val != NULL) {
sexp_free(s->val, s->val_allocated);
}
}
s->val = NULL;
s->bindata = NULL;
destroy_sexp (s->next);
s->next = s->list = NULL;
sexp_t_deallocate(s);
}
/**
* Iterative method to walk sx and turn it back into the string
* representation of the s-expression. Fills the buffer.
*/
int
print_sexp (char *buf, size_t size, const sexp_t * sx)
{
int retval;
size_t sz;
char *b = buf, *tc;
size_t left = size;
int depth = 0;
faststack_t *stack;
stack_lvl_t *top;
sexp_t *tdata;
sexp_t *fakehead;
sexp_t tmp;
if (sx == NULL) {
buf[0] = '\0';
return 0;
}
tmp = *sx;
tmp.next = tmp.list = NULL;
fakehead = copy_sexp(&tmp);
if (fakehead == NULL) {
sexp_errno = SEXP_ERR_MEMORY;
return -1;
}
fakehead->list = sx->list;
fakehead->next = NULL; /* this is the important part of fakehead */
stack = make_stack ();
if (stack == NULL) {
sexp_errno = SEXP_ERR_MEMORY;
sexp_t_deallocate(fakehead);
return -1;
}
push (stack, fakehead);
while (stack->top != NULL)
{
top = stack->top;
tdata = (sexp_t *) top->data;
if (tdata == NULL)
{
pop (stack);
if (depth > 0)
{
b[0] = ')';
b++;
left--;
depth--;
if (left == 0)
{
sexp_errno = SEXP_ERR_BUFFER_FULL;
break;
}
}
if (stack->top == NULL)
break;
top = stack->top;
top->data = ((sexp_t *) top->data)->next;
if (top->data != NULL)
{
b[0] = ' ';
b++;
left--;
if (left == 0)
{
sexp_errno = SEXP_ERR_BUFFER_FULL;
break;
}
}
}
else if (tdata->ty == SEXP_VALUE)
{
if (tdata->aty == SEXP_DQUOTE)
{
b[0] = '\"';
b++;
left--;
}
else if (tdata->aty == SEXP_SQUOTE)
{
b[0] = '\'';
b++;
left--;
}
if (tdata->aty != SEXP_BINARY && tdata->val_used > 0) {
tc = tdata->val;
/* copy value into string */
while (tc[0] != 0 && left > 0)
{
/* escape characters that need escaping. */
if ((tc[0] == '\"' || tc[0] == '\\') &&
tdata->aty == SEXP_DQUOTE)
{
b[0] = '\\';
b++;
left--;
if (left == 0) break;
}
b[0] = tc[0];
b++;
tc++;
left--;
if (left == 0)
break;
}
} else {
if (left > 3) {
b[0] = '#'; b[1] = 'b'; b[2] = '#';
b+=3;
left-=3;
#ifndef WIN32
if ((size_t)(sz = snprintf(b,left,"%lu#",(unsigned long)tdata->binlength)) >= left) {
#else
if ((sz = _snprintf(b,left,"%lu#",tdata->binlength)) >= left) {
#endif
left = 0;
break;
}
b += sz;
left -= sz;
if (left < tdata->binlength) {
left = 0;
break;
}
if (tdata->binlength > 0) {
memcpy(b,tdata->bindata,tdata->binlength);
left -= tdata->binlength;
b+=tdata->binlength;
}
b[0] = ' ';
left--;
} else {
left = 0;
break;
}
}
if (tdata->aty == SEXP_DQUOTE && left > 0)
{
b[0] = '\"';
b++;
left--;
}
if (left == 0)
{
sexp_errno = SEXP_ERR_BUFFER_FULL;
break;
}
top->data = ((sexp_t *) top->data)->next;
if (top->data != NULL)
{
b[0] = ' ';
b++;
left--;
if (left == 0)
{
sexp_errno = SEXP_ERR_BUFFER_FULL;
break;
}
}
}
else if (tdata->ty == SEXP_LIST)
{
depth++;
b[0] = '(';
b++;
left--;
if (left == 0)
{
sexp_errno = SEXP_ERR_BUFFER_FULL;
break;
}
push (stack, tdata->list);
}
else
{
sexp_errno = SEXP_ERR_BADCONTENT;
destroy_stack (stack);
sexp_t_deallocate(fakehead);
return -1;
}
}
while (depth != 0)
{
b[0] = ')';
b++;
left--;
depth--;
if (left == 0)
{
sexp_errno = SEXP_ERR_BUFFER_FULL;
break;
}
}
if (left != 0) {
b[0] = 0;
retval = (int) (size-left);
} else {
b--;
b[0] = 0;
retval = -1;
}
destroy_stack (stack);
sexp_t_deallocate(fakehead);
return retval;
}
/**
* Iterative method to walk sx and turn it back into the string
* representation of the s-expression. Fills the CSTRING that is
* passed in. If *s == NULL (new CSTRING, never used), snew() is called
* and passed back. If *s != NULL, *s is used as the CSTRING to print
* into. In the last case, the recycled CSTRING must have sempty() called
* to reset the allocated vs. used counters to make it appear to be empty.
* the code will assume that sempty() was called by the user!
*/
int
print_sexp_cstr (CSTRING **s, const sexp_t *sx, size_t ss)
{
int retval;
char *tc;
int depth = 0;
faststack_t *stack;
stack_lvl_t *top;
sexp_t *tdata;
sexp_t *fakehead;
CSTRING *_s = NULL;
char sbuf[32];
unsigned int i;
sexp_t tmp;
if (sx == NULL) {
return -1;
}
if (*s == NULL)
_s = snew(ss);
else
_s = *s;
tmp = *sx;
tmp.next = tmp.list = NULL;
fakehead = copy_sexp(&tmp);
if (fakehead == NULL) {
sexp_errno = SEXP_ERR_MEMORY;
return -1;
}
fakehead->list = sx->list;
fakehead->next = NULL; /* this is the important part of fakehead */
stack = make_stack ();
if (stack == NULL) {
sexp_errno = SEXP_ERR_MEMORY;
sexp_t_deallocate(fakehead);
return -1;
}
push (stack, fakehead);
while (stack->top != NULL)
{
top = stack->top;
tdata = (sexp_t *) top->data;
if (tdata == NULL)
{
pop (stack);
if (depth > 0)
{
_s = saddch(_s, ')');
depth--;
}
if (stack->top == NULL)
break;
top = stack->top;
top->data = ((sexp_t *) top->data)->next;
if (top->data != NULL)
{
_s = saddch(_s, ' ');
}
}
else if (tdata->ty == SEXP_VALUE)
{
if (tdata->aty == SEXP_DQUOTE)
{
_s = saddch(_s,'\"');
}
else if (tdata->aty == SEXP_SQUOTE)
{
_s = saddch(_s,'\'');
}
if (tdata->aty == SEXP_BINARY) {
sprintf(sbuf,"#b#%lu#",(unsigned long)tdata->binlength);
_s = sadd(_s,sbuf);
for (i=0;i<tdata->binlength;i++)
_s = saddch(_s,tdata->bindata[i]);
_s = saddch(_s,' ');
} else {
if (tdata->val_used > 0) {
tc = tdata->val;
/* copy value into string */
while (tc[0] != 0)
{
/* escape characters that need escaping. */
if ((tc[0] == '\"' ||
tc[0] == '\\') && tdata->aty == SEXP_DQUOTE)
{
_s = saddch(_s,'\\');
}
_s = saddch(_s,tc[0]);
tc++;
}
}
}
if (tdata->aty == SEXP_DQUOTE)
{
_s = saddch(_s,'\"');
}
top->data = ((sexp_t *) top->data)->next;
if (top->data != NULL)
{
_s = saddch(_s,' ');
}
}
else if (tdata->ty == SEXP_LIST)
{
depth++;
_s = saddch(_s,'(');
push (stack, tdata->list);
}
else
{
sexp_errno = SEXP_ERR_BADCONTENT;
destroy_stack (stack);
sexp_t_deallocate(fakehead);
return -1;
}
}
while (depth != 0)
{
_s = saddch(_s,')');
depth--;
}
*s = _s;
if (_s == NULL)
retval = 0;
else
retval = (int) _s->curlen;
destroy_stack (stack);
sexp_t_deallocate(fakehead);
return retval;
}
/**
* Allocate a new sexp_t element representing a list.
*/
sexp_t *new_sexp_list(sexp_t *l) {
sexp_t *sx = sexp_t_allocate();
if (sx == NULL) {
sexp_errno = SEXP_ERR_MEMORY;
return NULL;
}
sx->ty = SEXP_LIST;
sx->list = l;
sx->next = NULL;
sx->val = NULL;
sx->val_used = sx->val_allocated = 0;
return sx;
}
/**
* allocate a new sexp_t element representing a raw binary value
*/
sexp_t *new_sexp_binary_atom(char *data, size_t binlength) {
sexp_t *sx = sexp_t_allocate();
if (sx == NULL) {
sexp_errno = SEXP_ERR_MEMORY;
return NULL;
}
sx->ty = SEXP_VALUE;
sx->next = sx->list = NULL;
sx->aty = SEXP_BINARY;
sx->bindata = data;
sx->binlength = binlength;
sx->val = NULL;
sx->val_used = sx->val_allocated = 0;
return sx;
}
/**
* allocate a new sexp_t element representing a value
*/
sexp_t *new_sexp_atom(const char *buf, size_t bs, atom_t aty) {
sexp_t *sx = NULL;
if (aty == SEXP_BINARY) {
sexp_errno = SEXP_ERR_BAD_CONSTRUCTOR;
return NULL;
}
sx = sexp_t_allocate();
if (sx == NULL) {
sexp_errno = SEXP_ERR_MEMORY;
return NULL;
}
sx->ty = SEXP_VALUE;
sx->aty = aty;
#ifdef __cplusplus
sx->val = (char *)sexp_malloc(sizeof(char)*(bs+1));
#else
sx->val = sexp_malloc(sizeof(char)*(bs+1));
#endif
if (sx->val == NULL) {
sexp_t_deallocate(sx);
sexp_errno = SEXP_ERR_MEMORY;
return NULL;
}
sx->val_used = sx->val_allocated = bs+1;
strcpy(sx->val,buf);
sx->list = sx->next = NULL;
return sx;
}
/* sexp helpers */
int sexp_list_car(sexp_t *expr, sexp_t **sx)
{
if (!SEXP_IS_LIST(expr) || expr->list->ty != SEXP_VALUE) return 1;
*sx = expr->list;
return 0;
}
int sexp_list_cdr(sexp_t *expr, sexp_t **sx)
{
/* Dummy function. Can we do cdr properly? */
if (!SEXP_IS_LIST(expr) || expr->list->ty != SEXP_VALUE) return 1;
if (!expr->list->next) *sx = NULL;
else *sx = expr->list->next;
return 0;
}