summaryrefslogtreecommitdiff
path: root/source3/aparser
diff options
context:
space:
mode:
authorAndrew Tridgell <tridge@samba.org>2000-05-12 14:35:13 +0000
committerAndrew Tridgell <tridge@samba.org>2000-05-12 14:35:13 +0000
commit5c3588c430557d091683de7b7cc6f9d9534ca397 (patch)
treedef09d40d13f78cb378cf32fae8d21188ead04a6 /source3/aparser
parent54a447f9b862eedfe0d7d2a0fb441e3f88a52e4e (diff)
downloadsamba-5c3588c430557d091683de7b7cc6f9d9534ca397.tar.gz
samba-5c3588c430557d091683de7b7cc6f9d9534ca397.tar.bz2
samba-5c3588c430557d091683de7b7cc6f9d9534ca397.zip
this is a awk based code generator. Very primitive at the moment, but
it was sufficient to generate info level 6 of the add printer driver for spoolss It has two capabilities: 1) generate the header and C code for parsing a structure, given a *.struct file that describes the structure. Evetually I hope to make *.struct compatible with MIDL. 2) it provides a harness that can read a binary data file from prs_dump() which contains real data from a client and display the parse of it from the output of the awk parser. This allows you to "play" with the structure definition until it comes out right, it takes just a few mimutes of playing with the .struct file to work out the right layout. all this is very primitive right now. I hope next to completely replace the spoolss parser with a single .struct file that describes all the structures, and auto-generates working code. That will require quite a bit more work. (This used to be commit 7bc87d20ea47ba25851512cc6f9de6076ce1cec8)
Diffstat (limited to 'source3/aparser')
-rw-r--r--source3/aparser/Makefile20
-rw-r--r--source3/aparser/parser.awk200
-rw-r--r--source3/aparser/parser.c411
-rw-r--r--source3/aparser/parser.h98
-rw-r--r--source3/aparser/test.struct23
-rw-r--r--source3/aparser/vluke.c38
6 files changed, 790 insertions, 0 deletions
diff --git a/source3/aparser/Makefile b/source3/aparser/Makefile
new file mode 100644
index 0000000000..e9ff8ad010
--- /dev/null
+++ b/source3/aparser/Makefile
@@ -0,0 +1,20 @@
+CFLAGS=-Wall -g
+CC=gcc
+
+%.h : %.struct
+ awk -f parser.awk < $*.struct > $*.h
+
+OBJ = harness.o parser.o
+
+all: test.h harness
+
+test.h : test.struct
+
+harness: test.h $(OBJ)
+ $(CC) $(CFLAGS) -o harness $(OBJ)
+
+clean:
+ rm -f *.o test.h
+
+test: harness
+ ./harness test.dat > test.out
diff --git a/source3/aparser/parser.awk b/source3/aparser/parser.awk
new file mode 100644
index 0000000000..a32d4b727b
--- /dev/null
+++ b/source3/aparser/parser.awk
@@ -0,0 +1,200 @@
+function add_elem(type, elem)
+{
+ types[num_elems] = type;
+ elems[num_elems] = elem;
+ num_elems++;
+}
+
+function produce_preamble() {
+ printf("#define TEST_STRUCT %s\n", struct_name);
+ printf("#define TEST_FUNC %s\n", func_name);
+ printf("#define TEST_NAME \"%s\"\n", func_name);
+ printf("\n\n");
+}
+
+function produce_header() {
+ printf("\n/* %s structure */\n", struct_name);
+ printf("typedef struct {\n");
+ for (i=0;i<num_elems;i++) {
+ if (types[i] == "UNISTR2") {
+ printf("\tuint32 %s_ptr;\n", elems[i]);
+ } else if (types[i] == "BUFFER5") {
+ printf("\tuint32 %s_len;\n", elems[i]);
+ printf("\tuint32 %s_ptr;\n", elems[i]);
+ } else {
+ printf("\t%s\t%s;\n", types[i], elems[i]);
+ }
+ }
+ for (i=0;i<num_elems;i++) {
+ if (types[i] == "UNISTR2" ||
+ types[i] == "BUFFER5") {
+ printf("\t%s\t%s;\n", types[i], elems[i]);
+ }
+ }
+ printf("} %s;\n\n", struct_name);
+}
+
+
+function parse_structs() {
+ printf("\n\t/* parse the structures in the packet */\n\n");
+ for (i=0;i<num_elems;i++) {
+ if (types[i] == "UNISTR2") {
+ io_unistr2(elems[i]);
+ }
+ if (types[i] == "BUFFER5") {
+ io_buffer5(elems[i]);
+ }
+ }
+}
+
+function io_unistr2(elem) {
+ printf("\
+ if(!smb_io_unistr2(\"%s\", &il->%s, il->%s_ptr, ps, depth))\n\
+ return False;\n\
+ if(!prs_align(ps))\n\
+ return False;\n\
+", elem, elem, elem);
+}
+
+function io_buffer5(elem) {
+ printf("\
+ if (il->%s_ptr) {\n\
+ if(!smb_io_buffer5(\"%s\", &il->%s, ps, depth))\n\
+ return False;\n\
+ if(!prs_align(ps))\n\
+ return False;\n\
+ }\n\
+", elem, elem, elem);
+}
+
+function start_struct(name) {
+ num_elems=0;
+ struct_name=toupper(module"_"name);
+ func_name=tolower(module"_io_"name);
+}
+
+function parse_elems() {
+ printf("\n\t/* parse the main elements the packet */\n\n");
+ for (i=0;i<num_elems;i++) {
+ if (types[i] == "uint32") {
+ uint32_parser(elems[i]);
+ }
+ if (types[i] == "UINT64_S") {
+ uint64_parser(elems[i]);
+ }
+ if (types[i] == "UNISTR2") {
+ unistr2_parser(elems[i]);
+ }
+ if (types[i] == "BUFFER5") {
+ buffer5_parser(elems[i]);
+ }
+ if (types[i] == "NTTIME") {
+ nttime_parser(elems[i]);
+ }
+ }
+}
+
+function end_struct() {
+ produce_preamble();
+ produce_header();
+ func_header(func_name, struct_name);
+ parse_elems();
+ parse_structs();
+ func_footer();
+}
+
+
+
+function func_footer() {
+ printf("\n\
+\n\
+ return True;\n\
+}\n");
+}
+
+function func_header(func_name, struct_name)
+{
+ printf("\
+/*******************************************************************\n\
+parse a %s structure\n\
+********************************************************************/ \n\
+BOOL %s(char *desc, %s **q_u, \n\
+ prs_struct *ps, int depth)\n\
+{ \n\
+ %s *il;\n\
+ \n\
+ prs_debug(ps, depth, desc, \"%s\");\n\
+ depth++;\n\
+ \n\
+ /* reading */\n\
+ if (UNMARSHALLING(ps)) {\n\
+ il=(%s *)malloc(sizeof(%s));\n\
+ if(il == NULL)\n\
+ return False;\n\
+ ZERO_STRUCTP(il);\n\
+ *q_u=il;\n\
+ }\n\
+ else {\n\
+ il=*q_u;\n\
+ }\n\
+ \n\
+ if(!prs_align(ps))\n\
+ return False;\n\
+\n\
+", struct_name, func_name, struct_name, struct_name, func_name, struct_name, struct_name);
+}
+
+function uint32_parser(elem) {
+ printf("\
+ if(!prs_uint32(\"%s\", ps, depth, &il->%s))\n\
+ return False;\n\
+", elem, elem);
+}
+
+function unistr2_parser(elem) {
+ printf("\
+ if(!prs_uint32(\"%s_ptr\", ps, depth, &il->%s_ptr))\n\
+ return False;\n\
+", elem, elem);
+}
+
+function buffer5_parser(elem) {
+ printf("\
+ if(!prs_uint32(\"%s_len\", ps, depth, &il->%s_len))\n\
+ return False;\n\
+ if(!prs_uint32(\"%s_ptr\", ps, depth, &il->%s_ptr))\n\
+ return False;\n\
+", elem, elem, elem, elem);
+}
+
+function nttime_parser(elem) {
+ printf("\
+ if(!smb_io_time(\"%s\", &il->%s, ps, depth))\n\
+ return False;\n\
+", elem, elem);
+}
+
+function uint64_parser(elem) {
+ printf("\
+ if(!prs_uint64(\"%s\", ps, depth, &il->%s))\n\
+ return False;\n\
+", elem, elem);
+}
+
+/^module/ {
+ module=$2;
+}
+
+/^struct/ {
+ start_struct($2);
+}
+
+
+/^\}/ {
+ end_struct();
+}
+
+/uint32|UINT64_S|UNISTR2|BUFFER5|NTTIME/ {
+ split($0,a,"[ ;]*");
+ add_elem(a[2], a[3]);
+}
diff --git a/source3/aparser/parser.c b/source3/aparser/parser.c
new file mode 100644
index 0000000000..f938c58e8b
--- /dev/null
+++ b/source3/aparser/parser.c
@@ -0,0 +1,411 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <malloc.h>
+#include <unistd.h>
+#include "parser.h"
+
+char *tab_depth(int depth)
+{
+ static pstring spaces;
+ memset(spaces, ' ', depth * 4);
+ spaces[depth * 4] = 0;
+ return spaces;
+}
+
+/****************************************************************************
+expand a pointer to be a particular size
+****************************************************************************/
+void *Realloc(void *p,size_t size)
+{
+ void *ret=NULL;
+
+ if (size == 0) {
+ if (p) free(p);
+ DEBUG(5,("Realloc asked for 0 bytes\n"));
+ return NULL;
+ }
+
+ if (!p)
+ ret = (void *)malloc(size);
+ else
+ ret = (void *)realloc(p,size);
+
+ if (!ret)
+ DEBUG(0,("Memory allocation error: failed to expand to %d bytes\n",(int)size));
+
+ return(ret);
+}
+
+/*******************************************************************
+ Attempt, if needed, to grow a data buffer.
+ Also depends on the data stream mode (io).
+ ********************************************************************/
+
+BOOL prs_grow(prs_struct *ps, uint32 extra_space)
+{
+ uint32 new_size;
+ char *new_data;
+
+ if(ps->data_offset + extra_space <= ps->buffer_size)
+ return True;
+
+ /*
+ * We cannot grow the buffer if we're not reading
+ * into the prs_struct, or if we don't own the memory.
+ */
+
+ if(UNMARSHALLING(ps) || !ps->is_dynamic) {
+ DEBUG(0,("prs_grow: Buffer overflow - unable to expand buffer by %u bytes.\n",
+ (unsigned int)extra_space));
+ return False;
+ }
+
+ /*
+ * Decide how much extra space we really need.
+ */
+
+ extra_space -= (ps->buffer_size - ps->data_offset);
+ if(ps->buffer_size == 0) {
+ /*
+ * Ensure we have at least a PDU's length, or extra_space, whichever
+ * is greater.
+ */
+
+ new_size = MAX(MAX_PDU_FRAG_LEN,extra_space);
+
+ if((new_data = malloc(new_size)) == NULL) {
+ DEBUG(0,("prs_grow: Malloc failure for size %u.\n", (unsigned int)new_size));
+ return False;
+ }
+ memset(new_data, '\0', new_size );
+ } else {
+ /*
+ * If the current buffer size is bigger than the space needed, just
+ * double it, else add extra_space.
+ */
+ new_size = MAX(ps->buffer_size*2, ps->buffer_size + extra_space);
+
+ if ((new_data = Realloc(ps->data_p, new_size)) == NULL) {
+ DEBUG(0,("prs_grow: Realloc failure for size %u.\n",
+ (unsigned int)new_size));
+ return False;
+ }
+ }
+ ps->buffer_size = new_size;
+ ps->data_p = new_data;
+
+ return True;
+}
+
+
+/*******************************************************************
+ Ensure we can read/write to a given offset.
+ ********************************************************************/
+
+char *prs_mem_get(prs_struct *ps, uint32 extra_size)
+{
+ if(UNMARSHALLING(ps)) {
+ /*
+ * If reading, ensure that we can read the requested size item.
+ */
+ if (ps->data_offset + extra_size > ps->buffer_size) {
+ DEBUG(0,("prs_mem_get: reading data of size %u would overrun buffer.\n",
+ (unsigned int)extra_size ));
+ return NULL;
+ }
+ } else {
+ /*
+ * Writing - grow the buffer if needed.
+ */
+ if(!prs_grow(ps, extra_size))
+ return False;
+ }
+ return &ps->data_p[ps->data_offset];
+}
+
+
+/*******************************************************************
+ Stream a uint32.
+ ********************************************************************/
+
+BOOL prs_uint32(char *name, prs_struct *ps, int depth, uint32 *data32)
+{
+ char *q = prs_mem_get(ps, sizeof(uint32));
+ if (q == NULL)
+ return False;
+
+ DBG_RW_IVAL(name, depth, ps->data_offset, ps->io, ps->bigendian_data, q, *data32)
+ ps->data_offset += sizeof(uint32);
+
+ return True;
+}
+
+/*******************************************************************
+ Initialise a parse structure - malloc the data if requested.
+ ********************************************************************/
+
+BOOL prs_init(prs_struct *ps, uint32 size, uint8 align, BOOL io)
+{
+ ZERO_STRUCTP(ps);
+ ps->io = io;
+ ps->bigendian_data = False;
+ ps->align = align;
+ ps->is_dynamic = False;
+ ps->data_offset = 0;
+ ps->buffer_size = 0;
+ ps->data_p = NULL;
+
+ if (size != 0) {
+ ps->buffer_size = size;
+ if((ps->data_p = (char *)malloc((size_t)size)) == NULL) {
+ DEBUG(0,("prs_init: malloc fail for %u bytes.\n", (unsigned int)size));
+ return False;
+ }
+ ps->is_dynamic = True; /* We own this memory. */
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ debug output for parsing info.
+
+ XXXX side-effect of this function is to increase the debug depth XXXX
+
+ ********************************************************************/
+void prs_debug(prs_struct *ps, int depth, char *desc, char *fn_name)
+{
+ DEBUG(5+depth, ("%s%06x %s %s\n", tab_depth(depth), ps->data_offset, fn_name, desc));
+}
+
+/*******************************************************************
+ Align a the data_len to a multiple of align bytes - filling with
+ zeros.
+ ********************************************************************/
+
+BOOL prs_align(prs_struct *ps)
+{
+ uint32 mod = ps->data_offset & (ps->align-1);
+
+ if (ps->align != 0 && mod != 0) {
+ uint32 extra_space = (ps->align - mod);
+ if(!prs_grow(ps, extra_space))
+ return False;
+ memset(&ps->data_p[ps->data_offset], '\0', (size_t)extra_space);
+ ps->data_offset += extra_space;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes an NTTIME structure.
+********************************************************************/
+
+BOOL smb_io_time(char *desc, NTTIME *nttime, prs_struct *ps, int depth)
+{
+ if (nttime == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "smb_io_time");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("low ", ps, depth, &nttime->low)) /* low part */
+ return False;
+ if(!prs_uint32("high", ps, depth, &nttime->high)) /* high part */
+ return False;
+
+ return True;
+}
+
+
+/*******************************************************************
+ Reads or writes a UNISTR2 structure.
+ XXXX NOTE: UNISTR2 structures need NOT be null-terminated.
+ the uni_str_len member tells you how long the string is;
+ the uni_max_len member tells you how large the buffer is.
+********************************************************************/
+
+BOOL smb_io_unistr2(char *desc, UNISTR2 *uni2, uint32 buffer, prs_struct *ps, int depth)
+{
+ if (uni2 == NULL)
+ return False;
+
+ if (buffer) {
+
+ prs_debug(ps, depth, desc, "smb_io_unistr2");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("uni_max_len", ps, depth, &uni2->uni_max_len))
+ return False;
+ if(!prs_uint32("undoc ", ps, depth, &uni2->undoc))
+ return False;
+ if(!prs_uint32("uni_str_len", ps, depth, &uni2->uni_str_len))
+ return False;
+
+ /* oops! XXXX maybe issue a warning that this is happening... */
+ if (uni2->uni_max_len > MAX_UNISTRLEN)
+ uni2->uni_max_len = MAX_UNISTRLEN;
+ if (uni2->uni_str_len > MAX_UNISTRLEN)
+ uni2->uni_str_len = MAX_UNISTRLEN;
+
+ /* buffer advanced by indicated length of string
+ NOT by searching for null-termination */
+ if(!prs_unistr2(True, "buffer ", ps, depth, uni2))
+ return False;
+
+ } else {
+
+ prs_debug(ps, depth, desc, "smb_io_unistr2 - NULL");
+ depth++;
+ memset((char *)uni2, '\0', sizeof(*uni2));
+
+ }
+
+ return True;
+}
+
+/******************************************************************
+ Stream a unicode string, length/buffer specified separately,
+ in uint16 chars. We use DBG_RW_PCVAL, not DBG_RW_PSVAL here
+ as the unicode string is already in little-endian format.
+ ********************************************************************/
+
+BOOL prs_unistr2(BOOL charmode, char *name, prs_struct *ps, int depth, UNISTR2 *str)
+{
+ char *p = (char *)str->buffer;
+ char *q = prs_mem_get(ps, str->uni_str_len * sizeof(uint16));
+ if (q == NULL)
+ return False;
+
+ /* If we're using big-endian, reverse to get little-endian. */
+ if(ps->bigendian_data)
+ DBG_RW_PSVAL(charmode, name, depth, ps->data_offset, ps->io, ps->bigendian_data, q, p, str->uni_str_len)
+ else
+ DBG_RW_PCVAL(charmode, name, depth, ps->data_offset, ps->io, q, p, str->uni_str_len * 2)
+ ps->data_offset += (str->uni_str_len * sizeof(uint16));
+
+ return True;
+}
+
+void print_asc(int level, unsigned char *buf,int len)
+{
+ int i;
+ for (i=0;i<len;i++)
+ DEBUG(level,("%c", isprint(buf[i])?buf[i]:'.'));
+}
+
+/*******************************************************************
+ read from a socket into memory.
+ ********************************************************************/
+BOOL prs_read(prs_struct *ps, int fd, size_t len, int timeout)
+{
+ BOOL ok;
+ size_t prev_size = ps->buffer_size;
+ if (!prs_grow(ps, len))
+ {
+ return False;
+ }
+
+ if (timeout > 0)
+ {
+ ok = (read(fd, &ps->data_p[prev_size], len) == len);
+ }
+ else
+ {
+ ok = (read(fd, &ps->data_p[prev_size], len) == len);
+ }
+ return ok;
+}
+
+void dump_data(int level,char *buf1,int len)
+{
+ unsigned char *buf = (unsigned char *)buf1;
+ int i=0;
+ if (len<=0) return;
+
+ DEBUG(level,("[%03X] ",i));
+ for (i=0;i<len;) {
+ DEBUG(level,("%02X ",(int)buf[i]));
+ i++;
+ if (i%8 == 0) DEBUG(level,(" "));
+ if (i%16 == 0) {
+ print_asc(level,&buf[i-16],8); DEBUG(level,(" "));
+ print_asc(level,&buf[i-8],8); DEBUG(level,("\n"));
+ if (i<len) DEBUG(level,("[%03X] ",i));
+ }
+ }
+ if (i%16) {
+ int n;
+
+ n = 16 - (i%16);
+ DEBUG(level,(" "));
+ if (n>8) DEBUG(level,(" "));
+ while (n--) DEBUG(level,(" "));
+
+ n = MIN(8,i%16);
+ print_asc(level,&buf[i-(i%16)],n); DEBUG(level,(" "));
+ n = (i%16) - n;
+ if (n>0) print_asc(level,&buf[i-n],n);
+ DEBUG(level,("\n"));
+ }
+}
+
+/*******************************************************************
+ Stream a uint64_struct
+ ********************************************************************/
+BOOL prs_uint64(char *desc, prs_struct *ps, int depth, UINT64_S *data64)
+{
+ prs_debug(ps, depth, desc, "prs_uint64");
+ return prs_uint32("low", ps, depth+1, &data64->low) &&
+ prs_uint32("high", ps, depth+1, &data64->high);
+}
+
+
+
+/*******************************************************************
+reads or writes a BUFFER5 structure.
+the buf_len member tells you how large the buffer is.
+********************************************************************/
+BOOL smb_io_buffer5(char *desc, BUFFER5 *buf5, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "smb_io_buffer5");
+ depth++;
+
+ if (buf5 == NULL) return False;
+
+ prs_align(ps);
+ prs_uint32("buf_len", ps, depth, &(buf5->buf_len));
+
+ /* reading: alloc the buffer first */
+ if ( ps->io )
+ {
+ buf5->buffer=(uint16 *)malloc( sizeof(uint16)*buf5->buf_len );
+ }
+
+ prs_uint16s(True, "buffer", ps, depth, buf5->buffer, buf5->buf_len);
+
+ return True;
+}
+
+/******************************************************************
+ Stream an array of uint16s. Length is number of uint16s.
+ ********************************************************************/
+
+BOOL prs_uint16s(BOOL charmode, char *name, prs_struct *ps, int depth, uint16 *data16s, int len)
+{
+ char *q = prs_mem_get(ps, len * sizeof(uint16));
+ if (q == NULL)
+ return False;
+
+ DBG_RW_PSVAL(charmode, name, depth, ps->data_offset, ps->io, ps->bigendian_data, q, data16s, len)
+ ps->data_offset += (len * sizeof(uint16));
+
+ return True;
+}
diff --git a/source3/aparser/parser.h b/source3/aparser/parser.h
new file mode 100644
index 0000000000..196aa6b855
--- /dev/null
+++ b/source3/aparser/parser.h
@@ -0,0 +1,98 @@
+#include <ctype.h>
+#include "../include/byteorder.h"
+
+#ifndef MIN
+#define MIN(a,b) ((a)<(b)?(a):(b))
+#endif
+
+#ifndef MAX
+#define MAX(a,b) ((a)>(b)?(a):(b))
+#endif
+/* Maximum PDU fragment size. */
+#define MAX_PDU_FRAG_LEN 0x1630
+
+#define DEBUG(lvl, str) printf str;
+
+#define MARSHALL 0
+#define UNMARSHALL 1
+
+#define MARSHALLING(ps) (!(ps)->io)
+#define UNMARSHALLING(ps) ((ps)->io)
+
+typedef int BOOL;
+typedef unsigned char uint8;
+typedef unsigned short uint16;
+typedef unsigned uint32;
+
+#define False 0
+#define True 1
+
+typedef char pstring[1024];
+
+/* zero a structure given a pointer to the structure */
+#define ZERO_STRUCTP(x) { if ((x) != NULL) memset((char *)(x), 0, sizeof(*(x))); }
+
+typedef struct {
+ uint32 low;
+ uint32 high;
+} UINT64_S;
+
+typedef struct
+{
+ uint32 low;
+ uint32 high;
+} NTTIME;
+
+#define MAX_UNISTRLEN 256
+#define MAX_STRINGLEN 256
+#define MAX_BUFFERLEN 512
+
+/* UNISTR2 - unicode string size (in uint16 unicode chars) and buffer */
+typedef struct unistr2_info
+{
+ uint32 uni_max_len;
+ uint32 undoc;
+ uint32 uni_str_len;
+ /* unicode characters. ***MUST*** be little-endian. **NOT** necessarily null-terminated */
+ uint16 buffer[MAX_UNISTRLEN];
+
+} UNISTR2;
+
+/* BUFFER5 */
+typedef struct buffer5_info
+{
+ uint32 buf_len;
+ uint16 *buffer; /* data */
+} BUFFER5;
+
+typedef struct _prs_struct
+{
+ BOOL io; /* parsing in or out of data stream */
+ /*
+ * If the (incoming) data is big-endian. On output we are
+ * always little-endian.
+ */
+ BOOL bigendian_data;
+ uint8 align; /* data alignment */
+ BOOL is_dynamic; /* Do we own this memory or not ? */
+ uint32 data_offset; /* Current working offset into data. */
+ uint32 buffer_size; /* Current size of the buffer. */
+ char *data_p; /* The buffer itself. */
+} prs_struct;
+
+
+char *prs_mem_get(prs_struct *ps, uint32 extra_size);
+BOOL prs_uint32(char *name, prs_struct *ps, int depth, uint32 *data32);
+BOOL prs_init(prs_struct *ps, uint32 size, uint8 align, BOOL io);
+void prs_debug(prs_struct *ps, int depth, char *desc, char *fn_name);
+BOOL prs_align(prs_struct *ps);
+BOOL smb_io_time(char *desc, NTTIME *nttime, prs_struct *ps, int depth);
+BOOL smb_io_unistr2(char *desc, UNISTR2 *uni2, uint32 buffer, prs_struct *ps, int depth);
+BOOL prs_unistr2(BOOL charmode, char *name, prs_struct *ps, int depth, UNISTR2 *str);
+void print_asc(int level, unsigned char *buf,int len);
+BOOL prs_read(prs_struct *ps, int fd, size_t len, int timeout);
+void dump_data(int level,char *buf1,int len);
+BOOL prs_uint64(char *name, prs_struct *ps, int depth, UINT64_S *data64);
+BOOL smb_io_buffer5(char *desc, BUFFER5 *buf5, prs_struct *ps, int depth);
+BOOL prs_uint16s(BOOL charmode, char *name, prs_struct *ps, int depth, uint16 *data16s, int len);
+
diff --git a/source3/aparser/test.struct b/source3/aparser/test.struct
new file mode 100644
index 0000000000..517197bac3
--- /dev/null
+++ b/source3/aparser/test.struct
@@ -0,0 +1,23 @@
+module spool
+struct PRINTER_DRIVER_INFO_LEVEL_6 {
+ uint32 dummy1;
+ uint32 version;
+ UNISTR2 name;
+ UNISTR2 environment;
+ UNISTR2 driverpath;
+ UNISTR2 datafile;
+ UNISTR2 configfile;
+ UNISTR2 helpfile;
+ UNISTR2 monitorname;
+ UNISTR2 defaultdatatype;
+ BUFFER5 dependentfiles;
+ BUFFER5 previousnames;
+ NTTIME driverdate;
+ UINT64_S driverversion;
+ uint32 dummy4;
+ UNISTR2 mfgname;
+ UNISTR2 oemurl;
+ UNISTR2 hardwareid;
+ UNISTR2 provider;
+};
+
diff --git a/source3/aparser/vluke.c b/source3/aparser/vluke.c
new file mode 100644
index 0000000000..5e143016be
--- /dev/null
+++ b/source3/aparser/vluke.c
@@ -0,0 +1,38 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <malloc.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include "parser.h"
+#include "test.h"
+
+int main(int argc, char *argv[])
+{
+ BOOL ret;
+ TEST_STRUCT *q_u;
+ char *desc = TEST_NAME;
+ char *fname = argv[1];
+ int fd;
+ struct stat st;
+ prs_struct ps;
+
+ if (argc < 2) {
+ printf("usage: harness <file>\n");
+ exit(1);
+ }
+
+ fd = open(fname,O_RDONLY);
+ fstat(fd, &st);
+
+ prs_init(&ps, 0, 4, MARSHALL);
+ ps.is_dynamic=True;
+ prs_read(&ps, fd, st.st_size, 0);
+ dump_data(0, ps.data_p, ps.buffer_size);
+ ps.data_offset = 0;
+ ps.io = UNMARSHALL;
+ ret = TEST_FUNC(desc, &q_u, &ps, 1);
+ printf("ret=%d\n", ret);
+ return !ret;
+}