/* qlib.c - run-time library */
 
/* Written 1995-1997 by Werner Almesberger, EPFL-LRC */
 

#ifdef DUMP_MODE
static int q_dump = 0;
#else
int q_dump = 0;
#endif

#ifndef STANDALONE
#define DUMP qd_dump
#else
#define DUMP printf

#include <stdarg.h>
#include <stdio.h>
#include <unistd.h>

#include "common.h"
#include "qlib.h"
#include "op.h"


static int debug = 0;


void PREFIX(report)(int severity,const char *msg,...)
{
    va_list ap;

    if (!debug && severity > Q_ERROR) return;
    va_start(ap,msg);
    vprintf(msg,ap);
    printf("\n");
    va_end(ap);
    if (severity == Q_FATAL) exit(1);
}

#endif


#define LENGTH_STACK 10
#define LENGTH_R_STACK 5


typedef struct {
    int pos,size;
    unsigned char *start;
} LEN_BUF;

typedef struct _rstack {
    int *pc;
    int sp;
    unsigned char *pos;
    unsigned char *end;
    struct _rstack *next;
} RSTACK;


static int q_test(unsigned char *table,int pos)
{
    return !!(table[pos >> 3] & (1 << (pos & 7)));
}


static void q_set(unsigned char *table,int pos)
{
    table[pos >> 3] |= 1 << (pos & 7);
}


static void q_clear(unsigned char *table,int pos)
{
    table[pos >> 3] &= ~(1 << (pos & 7));
}


/* slightly ugly */


static void q_put(unsigned char *table,int pos,int size,unsigned long value)
{
    int end;
 
    PREFIX(report)(Q_DEBUG,"put %d %d %ld",pos,size,value);
    end = pos+size;
    if (((pos | size) & 7) && ((pos ^ (end-1)) & ~7))
	PREFIX(report)(Q_FATAL,"unsupported alignment (put %d,%d)",pos,size);
    if (size <= 8) {
	unsigned char *here;
	int shift;

	here = &table[pos >> 3];
	shift = pos & 7;
	*here = (*here & ~(((1 << size)-1) << shift)) | value << shift;
    }
    else {
	table = table+end/8-1;
	while (size > 0) {
	    *table-- = value;
	    value >>= 8;
	    size -= 8;
        }
    }
}


static unsigned long q_get(unsigned char *table,int pos,int size)
{
    unsigned long value;
    int end;
 
    PREFIX(report)(Q_DEBUG,"get %d %d ...",pos,size);
    end = pos+size;
    if (((pos | size) & 7) && ((pos ^ (end-1)) & ~7))
	PREFIX(report)(Q_FATAL,"unsupported alignment (get %d,%d)",pos,size);
    if (size <= 8) value = (table[pos >> 3] >> (pos & 7)) & ((1 << size)-1);
    else {
	table += pos >> 3;
	value = 0;
	while (size > 0) {
	    value = (value << 8) | *table++;
	    size -= 8;
        }
    }
    PREFIX(report)(Q_DEBUG,"  %ld",value);
    return value;
}


static void q_copy(unsigned char *src,int pos,unsigned char *dst,int size)
{
    src += pos >> 3;
    pos &= 7;
    if (pos+size <= 8) *dst |= *src & (((1 << size)-1) << pos);
    else {
	if (pos) {
	    *dst++ |= *src++ & (0xff << pos);
	    size -= 8-pos;
	}
	while (size >= 8) {
	    *dst++ = *src++;
	    size -= 8;
	}
	if (size > 0) *dst |= *src & ((1 << size)-1);
    }
}


void PREFIX(start)(void)
{
    q_init_global();
}


static int q_init(Q_DSC *dsc)
{
    size_t bytes;
    int i;

    /* initialize verything in case anything goes wrong during allocations. */
    dsc->errors = NULL;
    dsc->field_present = NULL;
    dsc->group_present = NULL;
    dsc->data = NULL;
    dsc->required = NULL;
    dsc->length = NULL;
    dsc->field_map = NULL;
    dsc->group_map = NULL;
    dsc->data = malloc((size_t) Q_DATA_BYTES);
    dsc->error = 1;
    if (!dsc->data) {
	perror("out of memory");
	return -1;
    }
    memcpy(dsc->data,q_initial,Q_DATA_BYTES);
    bytes = (Q_FIELDS+7) >> 3;
    dsc->required = malloc(bytes);
    if (!dsc->required) {
	perror("out of memory");
	return -1;
    }
    memset(dsc->required,0,bytes);
    dsc->field_present = malloc(bytes);
    if (!dsc->field_present) {
	perror("out of memory");
	return -1;
    }
    memset(dsc->field_present,0,bytes);
    bytes = (Q_GROUPS+(sizeof(unsigned long)*8-1)) >> 3;
    dsc->group_present = malloc(bytes);
    if (!dsc->group_present) {
	perror("out of memory");
	return -1;
    }
    memset(dsc->group_present,0,bytes);
    if (!Q_VARLEN_FIELDS) dsc->length = NULL;
    else {
	dsc->length = malloc(sizeof(int)*Q_VARLEN_FIELDS);
	if (!dsc->length) {
	    perror("out of memory");
	    return -1;
	}
	memset(dsc->length,0,sizeof(int)*Q_VARLEN_FIELDS);
    }
    dsc->field_map = malloc(sizeof(int)*Q_FIELDS);
    if (!dsc->field_map) {
	perror("out of memory");
	return -1;
    }
    for (i = 0; i < Q_FIELDS; i++) dsc->field_map[i] = i;
    dsc->group_map = malloc(sizeof(int)*Q_GROUPS);
    if (!dsc->group_map) {
	perror("out of memory");
	return -1;
    }
    for (i = 0; i < Q_GROUPS; i++) dsc->group_map[i] = i;
    dsc->error = 0;
    return 0;
}


#ifndef DUMP_MODE

static void use_group(Q_DSC *dsc,int group)
{
    int *scan;

    while (group != -1) {
	q_set((unsigned char *) dsc->group_present,group);
	for (scan = groups[group].required; scan && *scan != -1; scan++)
	    q_set(dsc->required,*scan);
	group = groups[group].parent;
    }
}


void q_assign(Q_DSC *dsc,int field,unsigned long value)
{
    int *walk;

    if (field < 0 || field >= Q_FIELDS)
	PREFIX(report)(Q_FATAL,"invalid field value (%d)",field);
    field = dsc->field_map[field];
    if (!fields[field].values) {
	if (q_test(dsc->field_present,field)) /* probably an error ... */
	    PREFIX(report)(Q_ERROR,"changing field %d",field);
	q_set(dsc->field_present,field);
	q_put(dsc->data,fields[field].pos,fields[field].size,value);
	use_group(dsc,fields[field].parent);
    }
    else {
	if (q_test(dsc->field_present,field))
	    PREFIX(report)(Q_FATAL,"can't change field %d",field);
	q_set(dsc->field_present,field);
	q_put(dsc->data,fields[field].pos,fields[field].size,value);
	for (walk = fields[field].values; walk[1] != -1; walk += 2)
	    if (*walk == value || *walk == -2) {
		use_group(dsc,walk[1]);
		return;
	    }
	PREFIX(report)(Q_ERROR,"invalid value (%d in field %d)",value,field);
	dsc->error = 1;
    }
}


void q_write(Q_DSC *dsc,int field,const void *buf,int size)
{
    if (field < 0 || field >= Q_FIELDS)
	PREFIX(report)(Q_FATAL,"invalid field value (%d)",field);
    field = dsc->field_map[field];
    if (fields[field].pos & 7)
	PREFIX(report)(Q_FATAL,"invalid use of q_write (%d)",field);
    if (fields[field].actual >= 0) {
	if (size > fields[field].size/8) {
	    PREFIX(report)(Q_ERROR,"%d bytes too big for %d byte field %d",
	      size,fields[field].size/8,field);
	    dsc->error = 1;
	    return;
	}
	dsc->length[fields[field].actual] = size;
    }
    else if ((fields[field].pos | fields[field].size) & 7)
	    PREFIX(report)(Q_FATAL,"field %d is neither var-len nor "
	      "well-shaped",field);
    memcpy(dsc->data+(fields[field].pos/8),buf,(size_t) size);
    q_set(dsc->field_present,field);
    use_group(dsc,fields[field].parent);
}


int q_present(const Q_DSC *dsc,int field)
{
    if (field < 0) {
	if (field < -Q_GROUPS)
	    PREFIX(report)(Q_FATAL,"invalid group number (%d)",field);
	if (!dsc->group_present) return 0;
	field = dsc->group_map[-field-1];
	return q_test((unsigned char *) dsc->group_present,field);
    }
    else {
	if (field >= Q_FIELDS)
	    PREFIX(report)(Q_FATAL,"invalid field number (%d)",field);
	if (!dsc->field_present) return 0;
	field = dsc->field_map[field];
	if (q_test(dsc->field_present,field)) return 1;
	return q_test((unsigned char *) dsc->group_present,
	  fields[field].parent);
    }
}


unsigned long q_fetch(const Q_DSC *dsc,int field)
{
    if (field < 0 || field >= Q_FIELDS)
	PREFIX(report)(Q_FATAL,"invalid field value (%d)",field);
    field = dsc->field_map[field];
    return q_get(dsc->data,fields[field].pos,fields[field].size);
}


int q_length(const Q_DSC *dsc,int field)
{
    if (field < 0 || field >= Q_FIELDS)
	PREFIX(report)(Q_FATAL,"invalid field value (%d)",field);
    field = dsc->field_map[field];
    if (fields[field].pos & 7)
	PREFIX(report)(Q_FATAL,"invalid use of q_length (%d)",field);
    if (fields[field].actual < 0)
	PREFIX(report)(Q_FATAL,"field %d is not var-len",field);
    return dsc->length[fields[field].actual];
}


int q_read(Q_DSC *dsc,int field,void *buf,int size)
{
    int len;

    len = 0; /* for gcc */
    if (field < 0 || field >= Q_FIELDS)
	PREFIX(report)(Q_FATAL,"invalid field value (%d)",field);
    field = dsc->field_map[field];
    if (fields[field].pos & 7)
	PREFIX(report)(Q_FATAL,"invalid use of q_read (%d)",field);
    if (fields[field].actual >= 0) len = dsc->length[fields[field].actual];
    else if (!(fields[field].size & 7)) len = fields[field].size >> 3;
	else PREFIX(report)(Q_FATAL,"field %d is not byte-sized (%d bits)",
	      field,fields[field].size);
    if (size < len) {
	PREFIX(report)(Q_ERROR,"%d bytes too big for %d byte buffer (field "
	  "%d)",len,size,field);
	dsc->error = 1;
	return -1;
    }
    memcpy(buf,dsc->data+(fields[field].pos/8),len);
    return len;
}


void q_instance(Q_DSC *dsc,int group)
{
    int i;

    if (group >= 0 || group < -Q_GROUPS)
	PREFIX(report)(Q_FATAL,"invalid group number (%d)",group);
    if (groups[-group-1].start == -1)
	PREFIX(report)(Q_FATAL,"group %d is unique",group);
    for (i = 0; i < groups[-group-1].length; i++)
	dsc->field_map[groups[-group-1].start+i] = groups[-group-1].start+i+
	  groups[-group-1].offset;
}


static int q_compose(Q_DSC *dsc,unsigned char *buf,int size)
{
    LEN_BUF stack[LENGTH_STACK];
    unsigned char *pos;
    int *pc;
    int j,i,sp;

    for (i = 0; i < Q_FIELDS; i++)
	if (q_test(dsc->required,i) && !q_test(dsc->field_present,i))
	    PREFIX(report)(Q_ERROR,"required field %d is missing",i);
    memset(buf,0,(size_t) size);
    if (q_dump)
	for (j = 0; j < 100; j += 20) {
	    fprintf(stderr,"%3d:",j);
	    for (i = 0; i < 20; i++) fprintf(stderr," %02X",dsc->data[i+j]);
	    putc('\n',stderr);
	}
    pos = buf;
    pc = construct; 
    sp = 0;
    while (1) {
	PREFIX(report)(Q_DEBUG,"%d(%d):",pc-construct,pos-buf);
	switch (*pc++) {
	    case OP_COPY:
		if (size < *pc) {
		    PREFIX(report)(Q_ERROR,"not enough space (%d < %d)",size,
		      *pc);
		    dsc->error = 1;
		    return -1;
		}
		PREFIX(report)(Q_DEBUG,"copy %d %d %d",pc[1],pos-buf,pc[2]);
		q_copy(dsc->data,pc[1],pos,pc[2]);
		if (q_dump) {
		    for (i = 0; i < 50; i++) fprintf(stderr,"%02X ",buf[i]);
		    putc('\n',stderr);
		}
		pos += *pc;
		size -= *pc;
		pc += 3;
		break;
	    case OP_COPYVAR:
		if (size < dsc->length[*pc]) {
		    PREFIX(report)(Q_ERROR,"not enough space (%d < %d)",size,
		      dsc->length[*pc]);
		    dsc->error = 1;
		    return -1;
		}
		memcpy(pos,dsc->data+pc[1]/8,dsc->length[*pc]);
		pos += dsc->length[*pc];
		size -= dsc->length[*pc];
		pc += 3;
		break;
	    case OP_BEGIN_LEN:
		if (size < *pc) {
		    PREFIX(report)(Q_ERROR,"not enough space (%d < %d)",size,
		      *pc);
		    dsc->error = 1;
		    return -1;
		}
		if (sp == LENGTH_STACK) {
		    PREFIX(report)(Q_ERROR,"length stack overflow");
		    dsc->error = 1;
		    return -1;
		}
		stack[sp].pos = pc[1]; /* not used */
		stack[sp].size = pc[2];
		stack[sp].start = pos;
		pos += *pc; /* allocate length here */
		size -= *pc;
		sp++;
		pc += 3;
		break;
	    case OP_END_LEN:
		if (!sp--) PREFIX(report)(Q_FATAL,"length stack underflow");
		q_put(stack[sp].start,0,stack[sp].size,
		  (size_t) ((pos-stack[sp].start)-((stack[sp].size+7) >> 3)));
		break;
	    case OP_IFGROUP:
		if (q_test((unsigned char *) dsc->group_present,*pc++)) pc++;
		else pc += *pc+1;
		break;
#if 0
	    case OP_CASE:
		{
		    int len;

		    for (len = *pc++; len; len--)
			if (!q_test((unsigned char *) dsc->group_present,*pc++))
			    pc++;
			else {
			    pc += *pc+1;
			    break;
			}
		    if (!len)
			PREFIX(report)(Q_FATAL,"multi failed (pc %d)",
			  pc-construct);
		}
		break;
#endif
	    case OP_JUMP:
		pc += *pc+1;
		break;
	    case OP_END:
		return pos-buf;
	    default:
		PREFIX(report)(Q_FATAL,"unrecognized opcode %d",pc[-1]);
		return -1; /* for gcc */
	}
    }
}

#endif


static const char *q_err_msg[] = { "???","not enough space (%d left)",
  "case failed (value 0x%x)","application-specific error (code %d)" };


/*
 * Rather messy ... too bad C doesn't have function-local functions ...
 */

static void handle_error(Q_DSC *dsc,int size,unsigned char *buf,
  unsigned char **stack,RSTACK *r_stack,int **pc,int *sp,int *rp,
  unsigned char **pos,unsigned char **end,Q_ERR_TYPE type,int value)
{
    Q_ERR_DSC *error,**last;

    PREFIX(report)(Q_ERROR,q_err_msg[type],value);
    PREFIX(report)(Q_ERROR,"[ PC=%d SP=%d RP=%d, pos=%d end=%d ]",*pc-parse,
      *sp,*rp,*pos-buf,*end-buf);
    error = malloc(sizeof(Q_ERR_DSC));
    if (!error) {
	perror("out of memory");
	exit(0);
    }
    error->type = type;
    for (last = &dsc->errors; *last; last = &(*last)->next);
    *last = error;
    error->next = NULL;
    if (!pc) return;
    error->pc = *pc-parse;
    error->offset = *pos-buf;
    error->value = value;
    if (*rp) {
	(*rp)--;
	error->id = r_stack[*rp].pc[0];
	error->start = r_stack[*rp].pos-buf;
	*sp = r_stack[*rp].sp;
	*pos = r_stack[*rp].end;
	error->length = *pos-r_stack[*rp].pos;
	error->group = r_stack[*rp].pc[1];
	*pc = parse+r_stack[*rp].pc[2];
    }
    else {
	error->id = 0;
	error->start = 0;
	error->length = size;
	error->group = 0;
	*sp = 0;
	*pc = parse+sizeof(parse)/sizeof(*parse)-1;
	*pos = buf+size;
    }
}


#define ERROR(type,value) \
    handle_error(dsc,size,buf,stack,r_stack,&pc,&sp,&rp,&pos,&end,type,value)


static int _q_parse(Q_DSC *dsc,unsigned char *buf,int size)
{
    RSTACK r_stack[LENGTH_R_STACK];
    unsigned char *stack[LENGTH_STACK];
    unsigned char *pos,*end;
    int *pc;
    int i,sp,rp;

    end = buf+size;
    pos = buf;
    pc = parse; 
    sp = rp = 0;
    while (1) {
	PREFIX(report)(Q_DEBUG,"%d(%d):",pc-parse,pos-buf);
	switch (*pc++) {
#ifdef DUMP_MODE
	    case OP_DUMP:
		{
		    unsigned long value;
		    int len;

		    for (i = dump_fields[*pc].level; i; i--) DUMP("  ");
		    DUMP("%s =",dump_fields[*pc++].name);
		    len = *pc == OP_COPYVAR ? (end-pos)*8 : pc[3];
		    if (len <= 32) {
			const SYM_NAME *sym;

			value = q_get(pos,pc[2] & 7,len);
			if (!(sym = dump_fields[pc[-1]].sym))
			    DUMP(" %ld (0x%lx)\n",value,value);
			else {
			    while (sym->name)
				if (sym->value == value) break;
				else sym++;
			    if (sym->name) DUMP(" %s\n",sym->name);
			    else DUMP(" %ld (0x%lx)\n",value,value);
			}
		    }
		    else {
			for (i = 0; i < len/8; i++)
			    DUMP(" %02x",pos[i]);
			DUMP("\n");
		    }
		}
		break;
#endif
	    case OP_COPY:
		if (pos+*pc > end) {
		    ERROR(qet_space,end-pos);
		    continue;
		}
		PREFIX(report)(Q_DEBUG,"copy %d %d %d",pc[1],pos-buf,pc[2]);
		q_copy(pos,pc[1] & 7,dsc->data+(pc[1] >> 3),pc[2]);
		if (q_dump) {
		    for (i = 0; i < 20; i++)
			fprintf(stderr,"%02X ",dsc->data[i]);
		    putc('\n',stderr);
		}
		pos += *pc;
		pc += 3;
		break;
	    case OP_COPYVAR:
		{
		    int len;

		    len = end-pos;
		    if (len > pc[2]) len = pc[2];
		    memcpy(dsc->data+pc[1]/8,pos,(size_t) len);
		    PREFIX(report)(Q_DEBUG,"len %d for %d",len,*pc);
		    dsc->length[*pc] = len;
		    pos += len;
		    pc += 3;
		    break;
		}
	    case OP_BEGIN_LEN:
		if (pos+*pc > end) {
		    ERROR(qet_space,end-pos);
		    continue;
		}
		if (sp == LENGTH_STACK)
		    PREFIX(report)(Q_FATAL,"length stack overflow");
		stack[sp] = end;
		end = pos+q_get(pos,pc[1] & 7,pc[2])+*pc;
		if (end > stack[sp])
		    PREFIX(report)(Q_FATAL,"length has grown");
		pos += *pc;
		sp++;
		pc += 3;
		break;
	    case OP_BEGIN_REC:
		PREFIX(report)(Q_DEBUG,"begin_rec pc %d sp %d pos %d end %d",
		  pc-parse,sp,pos-buf,end-buf);
		if (rp == LENGTH_R_STACK)
		    PREFIX(report)(Q_FATAL,"recovery stack overflow");
		r_stack[rp].pc = pc;
		r_stack[rp].sp = sp;
		r_stack[rp].pos = pos;
		r_stack[rp].end = end;
		rp++;
		pc += 3;
		break;
	    case OP_END_LEN:
		if (!sp--) PREFIX(report)(Q_FATAL,"length stack underflow");
		end = stack[sp];
		break;
	    case OP_END_REC:
		PREFIX(report)(Q_DEBUG,"end_rec");
		if (!rp--) PREFIX(report)(Q_FATAL,"recovery stack underflow");
		break;
	    case OP_CASE:
		{
		    int len,value,group;

		    if (pos+*pc > end) {
			ERROR(qet_space,end-pos);
			continue;
		    }
		    value = q_get(pos,pc[1] & 7,pc[2]);
		    pos += *pc;
		    pc += 3;
		    for (len = *pc++; len; len--)
			if (*pc != value && *pc != -1) pc += 3;
			else {
			    pc++;
			    if (*pc != -1 && q_test((unsigned char *)
			      dsc->group_present,*pc)) {
				pc += 2;
				continue;
			    }
			    for (group = *pc++; group != -1; group =
			      groups[group].parent)
				q_set((unsigned char *) dsc->group_present,
				  group);
			    pc += *pc+1;
			    break;
			}
		    if (!len) {
			ERROR(qet_case,value);
			continue;
		    }
		}
		break;
	    case OP_JUMP:
		pc += *pc+1;
		break;
	    case OP_IFEND:
		PREFIX(report)(Q_DEBUG,"ifend - %d/%d",pos-buf,end-buf);
		if (pos == end) pc += *pc;
		pc++;
		break;
	    case OP_ABORT:
		ERROR(qet_abort,*pc);
		continue;
	    case OP_END:
		return dsc->errors ? -1 : 0;
	    default:
		PREFIX(report)(Q_FATAL,"unrecognized opcode %d",pc[-1]);
		return -1; /* for gcc */
	}
    }
}


#define GROUP_STACK_SIZE 100 /* make sure it's big enough ... */


static void fixups(Q_DSC *dsc)
{
    int gs[GROUP_STACK_SIZE];
    Q_ERR_DSC *walk;
    int gp,i,j;

    for (walk = dsc->errors; walk; walk = walk->next) {
	gs[gp = 0] = walk->group;
	q_clear((unsigned char *) dsc->group_present,walk->group);
	for (i = walk->group+1; i < Q_GROUPS; i++) {
	    while (groups[i].parent != gs[gp])
		if (!gp--) break;
	    if (gp < 0) break;
	    gs[++gp] = i;
	    q_clear((unsigned char *) dsc->group_present,i);
	}
	for (j = 0; j < Q_FIELDS; j++)
	     if (fields[j].parent == walk->group) break;
	while (j < Q_FIELDS && fields[j].parent < i) {
	    q_clear(dsc->field_present,j);
	    j++;
	}
    }
}


static int q_parse(Q_DSC *dsc,unsigned char *buf,int size)
{
    int error;
    int j,i;
    int *p;

    if (q_dump)
	for (j = 0; j < 100; j += 20) {
	    fprintf(stderr,"%3d:",j);
	    for (i = 0; i < 20; i++) fprintf(stderr," %02X",dsc->data[i+j]);
	    putc('\n',stderr);
	}
    error = _q_parse(dsc,buf,size);
    if (error) {
	fixups(dsc);
	return error;
    }
    if (q_dump) {
	putc('\n',stderr);
	for (j = 0; j < 100; j += 20) {
	    fprintf(stderr,"%3d:",j);
	    for (i = 0; i < 20; i++) fprintf(stderr," %02X",dsc->data[i+j]);
	    putc('\n',stderr);
	}
	for (i = 0; i < Q_GROUPS; i++)
	    if (q_test((unsigned char *) dsc->group_present,i))
		for (p = groups[i].required; p && *p != -1; p++)
		    fprintf(stderr,"%d: %ld / 0x%lx\n",*p,
		      q_get(dsc->data,fields[*p].pos,fields[*p].size),
		      q_get(dsc->data,fields[*p].pos,fields[*p].size));
    }
    return 0;
}


int PREFIX(open)(Q_DSC *dsc,void *buf,int size)
{
    int error;

    dsc->buffer = NULL;
    error = q_init(dsc);
    if (error) {
	handle_error(dsc,size,buf,NULL,NULL,NULL,NULL,NULL,NULL,NULL,qet_init,
	  0);
	return error;
    }
    return q_parse(dsc,buf,size);
}


#ifndef DUMP_MODE

int q_create(Q_DSC *dsc,void *buf,int size)
{
   dsc->buffer = buf;
   dsc->buf_size = size;
   return q_init(dsc);
}

#endif


int PREFIX(close)(Q_DSC *dsc)
{
    int size;

    size = 0; /* for gcc */
#ifndef DUMP_MODE
    if (dsc->buffer && !dsc->error)
	size = q_compose(dsc,dsc->buffer,dsc->buf_size);
#endif
    if (dsc->data) free(dsc->data);
    if (dsc->required) free(dsc->required);
    if (dsc->field_present) free(dsc->field_present);
    if (dsc->group_present) free(dsc->group_present);
    if (dsc->length) free(dsc->length);
    if (dsc->field_map) free(dsc->field_map);
    if (dsc->group_map) free(dsc->group_map);
    while (dsc->errors) {
	Q_ERR_DSC *next;

	next = dsc->errors->next;
	free(dsc->errors);
	dsc->errors = next;
    }
    return dsc->error ? -1 : dsc->buffer ? size : 0;
}


#ifdef STANDALONE

int main(int argc,const char **argv)
{
    unsigned char msg[5000]; /* should be large enough for that */
    Q_DSC dsc;
    int len,c;

    debug = argc != 1;
    len = 0;
    while (scanf("%x",&c) == 1) msg[len++] = c;
    qd_start();
    qd_open(&dsc,msg,len);
    qd_close(&dsc);
    return 0;
}

#endif
