From 5c6a7861bf4d371a16eb51e0f3a512e6b405d0b7 Mon Sep 17 00:00:00 2001 From: Luke Leighton Date: Sat, 27 May 2000 01:21:27 +0000 Subject: cifs parser. (This used to be commit 96fd33b8982d4d7ff1eef6f2ef836381d443b143) --- source3/aparser/cifs.struct | 579 +++++++++++++++++++++ source3/aparser/parsefn.awk | 24 +- source3/aparser/parser.c | 75 +++ source3/aparser/parser.h | 15 +- source3/aparser/templates/prs_array_remainder.tpl | 7 + source3/aparser/templates/prs_array_remainder.tpl~ | 7 + source3/aparser/templates/prs_uint8s.tpl | 2 + source3/aparser/templates/prs_uint8s_fixed.tpl | 2 + source3/aparser/templates/prs_wstring_fixed.tpl | 2 + 9 files changed, 707 insertions(+), 6 deletions(-) create mode 100644 source3/aparser/cifs.struct create mode 100644 source3/aparser/templates/prs_array_remainder.tpl create mode 100644 source3/aparser/templates/prs_array_remainder.tpl~ create mode 100644 source3/aparser/templates/prs_uint8s.tpl create mode 100644 source3/aparser/templates/prs_uint8s_fixed.tpl create mode 100644 source3/aparser/templates/prs_wstring_fixed.tpl (limited to 'source3') diff --git a/source3/aparser/cifs.struct b/source3/aparser/cifs.struct new file mode 100644 index 0000000000..8d5cef4d8f --- /dev/null +++ b/source3/aparser/cifs.struct @@ -0,0 +1,579 @@ +module cifs + +#define BOOL uint32 +#define UCHAR uint8 +#define USHORT uint16 +#define LONG uint32 +#define ULONG uint32 +#define DWORD uint32 +#define SMB_TIME uint16 +#define SMB_DATE uint16 + +typedef struct { + ULONG low; + LONG high; +} TIME; + +typedef struct { + ULONG low; + ULONG high; +} hyper; + +typedef struct { + uint8 cmd; + uint8 reserved; + uint16 offset; +} ANDX_INFO; + + +typedef struct { + uint8 tag2; + fstring protocol; +} BUF2; + +typedef struct { + uint16 bcount; + BUF2 protocol[*]; +} Q_NEGPROT_0; + +typedef struct { + uint8 wcount; + union ctr[wcount] { + case 0 Q_NEGPROT_0 q0; + } +} Q_NEGPROT; + +typedef struct { + USHORT DialectIndex; /* Index of selected dialect */ + USHORT SecurityMode; /* Security mode: */ + /* bit 0: 0 = share, 1 = user */ + /* bit 1: 1 = use challenge/response */ + /* authentication */ + USHORT MaxBufferSize; /* Max transmit buffer size (>= 1024) */ + USHORT MaxMpxCount; /* Max pending multiplexed requests */ + USHORT MaxNumberVcs; /* Max VCs between client and server */ + USHORT RawMode; /* Raw modes supported: */ + /* bit 0: 1 = Read Raw supported */ + /* bit 1: 1 = Write Raw supported */ + ULONG SessionKey; /* Unique token identifying this session */ + SMB_TIME ServerTime; /* Current time at server */ + SMB_DATE ServerDate; /* Current date at server */ + USHORT ServerTimeZone; /* Current time zone at server */ + USHORT ChallengeLength; /* Length of Challenge; MBZ if not LM2.1 + /* dialect or later */ + USHORT Reserved; /* MBZ */ + USHORT ByteCount; /* Count of data bytes */ + UCHAR Challenge[ChallengeLength]; /* The challenge */ + fstring PrimaryDomain; /* The server's primary domain */ + + uint16 bcount; + uint8 chal[8]; + fstring domain; +} R_NEGPROT_12; + +typedef struct { + USHORT DialectIndex; /*Index of selected dialect */ + UCHAR SecurityMode; /*Security mode: */ + /* bit 0: 0 = share, 1 = user */ + /* bit 1: 1 = use challenge/response */ + /* authentication */ + /* bit 2: 1 = Security Signatures (SMB integrity */ + /* check) enabled */ + /* bit 3: 1 = Security Signatures (SMB integrity */ + /* check) required */ + USHORT MaxMpxCount; /*Max pending outstanding requests */ + USHORT MaxNumberVcs; /*Max VCs between client and server */ + ULONG MaxBufferSize; /*Max transmit buffer size */ + ULONG MaxRawSize; /*Maximum raw buffer size */ + ULONG SessionKey; /*Unique token identifying this session */ + ULONG Capabilities; /*Server capabilities */ + ULONG SystemTimeLow; /*System (UTC) time of the server (low). */ + ULONG SystemTimeHigh; /*System (UTC) time of the server (high). */ + USHORT ServerTimeZone;/*Time zone of server (minutes from UTC) */ + UCHAR SecurityBlobLength;/*Length of SecurityBlob */ + + USHORT bcount; /*Count of data bytes */ + /*UCHAR GUID[16]; A globally unique identifier assigned to the */ + /* server; present only when */ + /* CAP_EXTENDED_SECURITY is on in the */ + /* Capabilities field. */ + UCHAR SecurityBlob[SecurityBlobLength]; /*Opaque Security Blob associated with the */ + /* security package if CAP_EXTENDED_SECURITY is */ + /* on in the Capabilities field; else challenge */ + /* for CIFS challenge/response authentication. */ + fstring OemDomainName; /*The name of the domain (in OEM chars); not */ + /* present if CAP_EXTENDED_SECURITY is on in the */ + /* Capabilities field */ +} R_NEGPROT_17; + + +typedef struct { + uint8 wcount; + union ctr[wcount] { + case 17 R_NEGPROT_17 r17; + } +} R_NEGPROT; + +typedef struct { + uint8 wcount; + uint16 vwv[wcount]; + uint16 bcount; + uint8 none[bcount]; +} Q_TDIS; + +typedef struct { + uint8 wcount; + uint16 vwv[wcount]; + uint16 bcount; + uint8 none[bcount]; +} R_TDIS; + +typedef struct { + ANDX_INFO andx; + uint16 bcount; + uint8 none[bcount]; +} R_ULOGOFF_ANDX_2; + +typedef struct { + uint8 wcount; + union ctr[wcount] { + case 2 R_ULOGOFF_ANDX_2 q2; + } +} R_ULOGOFF_ANDX; + +typedef struct { + ANDX_INFO andx; + uint16 bcount; + uint8 none[bcount]; +} Q_ULOGOFF_ANDX_2; + +typedef struct { + uint8 wcount; + union ctr[wcount] { + case 2 Q_ULOGOFF_ANDX_2 q2; + } +} Q_ULOGOFF_ANDX; + +typedef struct { + ANDX_INFO andx; + uint16 bufsize; + uint16 max_mpx; + uint16 vc; + ULONG sess_key; + uint16 pwlen; + ULONG reserved; + + uint16 bcount; + uint8 password[pwlen]; + fstring domain; + fstring os; + fstring server; + +} Q_SESSION_SETUP_ANDX_10; + +typedef struct { + ANDX_INFO andx; + uint16 bufsize; + uint16 max_mpx; + uint16 vc; + ULONG sess_key; + uint16 pwlen; + uint16 upwlen; + ULONG capabilities; + ULONG reserved; + + uint16 bcount; + uint8 password[pwlen]; + uint8 upassword[upwlen]; + fstring user; + fstring domain; + fstring os; + fstring server; + +} Q_SESSION_SETUP_ANDX_13; + +typedef struct _Q_SESSION_SETUP_ANDX { + uint8 wcount; + union ctr[wcount] { + case 10 Q_SESSION_SETUP_ANDX_10 q10; + case 13 Q_SESSION_SETUP_ANDX_13 q13; + } +} Q_SESSION_SETUP_ANDX; + +typedef struct { + ANDX_INFO andx; + uint16 vwv2; + uint16 passlen; + uint16 bcount; + uint8 password[passlen]; + fstring path; + fstring device; +} Q_TCON_ANDX_4; + +typedef struct _Q_TCON_ANDX { + uint8 wcount; + union ctr[wcount] { + case 4 Q_TCON_ANDX_4 q4; + } +} Q_TCON_ANDX; + +typedef struct { + ANDX_INFO andx; + uint16 vwv2; + uint16 bcount; + fstring share; +} R_TCON_ANDX_3; + +typedef struct _R_TCON_ANDX { + uint8 wcount; + union ctr[wcount] { + case 3 R_TCON_ANDX_3 q3; + } +} R_TCON_ANDX; + +typedef struct { + ANDX_INFO andx; + uint16 action; + + uint16 count; + fstring os; + fstring server; + fstring domain; +} R_SESSION_SETUP_ANDX_10; + +typedef struct _R_SESSION_SETUP_ANDX { + uint8 wcount; + union ctr[wcount] { + case 3 R_SESSION_SETUP_ANDX_10 r3; + } +} R_SESSION_SETUP_ANDX; + + +typedef struct _R_CLOSE { + uint8 wcount; + uint16 count; + uint8 none[count]; + +} R_CLOSE; + +typedef struct _Q_CLOSE { + uint8 wcount; + uint16 fnum; + uint32 vwv1; + + uint16 count; + uint8 none[count]; + +} Q_CLOSE; + +typedef struct { + uint16 dsize; + uint16 bsizehi; + uint16 bsizelo; + uint16 avail; + uint16 vwv4; + + uint16 bcount; + uint8 none[bcount]; + +} R_DSKATTR_5; + +typedef struct { + uint8 wcount; + union ctr[wcount] { + case 5 R_DSKATTR_5 r5; + } +} R_DSKATTR; + +typedef struct { + uint16 count; + uint8 none[count]; + +} Q_DSKATTR_0; + +typedef struct _Q_DSKATTR { + uint8 wcount; + union ctr[wcount] { + case 0 Q_DSKATTR_0 q1; + } + +} Q_DSKATTR; + +typedef struct { + uint16 bcount; + uint8 protocols[bcount]; + +} R_UNLINK_0; + +typedef struct { + uint8 wcount; + union ctr[wcount] { + case 0 R_UNLINK_0 r0; + } +} R_UNLINK; + +typedef struct { + uint16 dirtype; + + uint16 count; + uint8 fname[count]; + +} Q_UNLINK_1; + +typedef struct _Q_UNLINK { + uint8 wcount; + union ctr[wcount] { + case 1 Q_UNLINK_1 q1; + } + +} Q_UNLINK; + +typedef struct _R_OPEN_ANDX{ + uint8 wcount; + ANDX_INFO andx; + uint16 fnum; + uint16 fmode; + uint32 mtime; + uint32 size; + uint16 rmode; + uint16 vwv9; + uint16 vwv10; + uint16 smb_action; + uint16 vwv12; + uint16 vwv13; + uint16 vwv14; + + uint16 count; + uint8 none[count]; + +} R_OPEN_ANDX; + +typedef struct _Q_OPEN_ANDX{ + uint8 wcount; + ANDX_INFO andx; + uint16 fnum; + uint16 fmode; + uint32 mtime; + uint32 size; + uint16 rmode; + uint16 vwv9; + uint16 vwv10; + uint16 smb_action; + uint16 vwv12; + uint16 vwv13; + uint16 vwv14; + + uint16 count; + uint8 fname[count]; + +} Q_OPEN_ANDX; + +typedef struct _R_READ_ANDX { + uint8 wcount; + ANDX_INFO andx; + uint16 vwv2; + uint16 vwv3; + uint16 vwv4; + uint16 nread; + uint16 offset; + uint16 vwv7; + uint16 vwv8; + uint16 vwv9; + uint16 vwv10; + uint16 vwv11; + + uint16 count; + uint8 data[count]; + +} R_READ_ANDX; + +typedef struct _Q_READ_ANDX_10 { + ANDX_INFO andx; + uint16 fnum; + uint32 startpos; + uint16 smb_maxcnt; + uint16 smb_mincnt; + uint16 vwv7; + uint16 vwv8; + uint16 vwv9; + + uint16 count; + uint8 none[count]; + +} Q_READ_ANDX_10; + +typedef struct _Q_READ_ANDX_12 { + ANDX_INFO andx; + uint16 fnum; + uint32 startpos; + uint16 smb_maxcnt; + uint16 smb_mincnt; + uint16 vwv7; + uint16 vwv8; + uint16 vwv9; + uint32 startposhi; + + uint16 count; + uint8 none[count]; + +} Q_READ_ANDX_12; + +typedef struct _Q_READ_ANDX { + uint8 wcount; + union ctr[wcount] { + case 10 Q_READ_ANDX_10 q10; + case 12 Q_READ_ANDX_12 q12; + } +} Q_READ_ANDX; + +typedef struct _R_WRITE_ANDX { + uint8 wcount; + ANDX_INFO andx; + uint16 nwritten; + uint16 vwv3; + uint16 vwv4; + uint16 vwv5; + + uint16 count; + uint8 none[count]; + +} R_WRITE_ANDX; + +typedef struct _Q_WRITE_ANDX_12 { + ANDX_INFO andx; + uint16 fnum; + uint32 startpos; + uint16 vwv5; + uint16 vwv6; + uint16 write_through; + uint16 vwv8; + uint16 vwv9; + uint16 numtowrite; + uint16 smb_doff; + + uint16 count; + uint8 data[count]; + +} Q_WRITE_ANDX_12; + +typedef struct _Q_WRITE_ANDX_14 { + ANDX_INFO andx; + uint16 fnum; + uint32 startpos; + uint16 vwv5; + uint16 vwv6; + uint16 write_through; + uint16 vwv8; + uint16 vwv9; + uint16 numtowrite; + uint16 smb_doff; + uint32 startposhi; + + uint16 count; + uint8 data[count]; + +} Q_WRITE_ANDX_14; + +typedef struct _Q_WRITE_ANDX { + uint8 wcount; + union ctr[wcount] { + case 12 Q_WRITE_ANDX_12 q12; + case 14 Q_WRITE_ANDX_14 q14; + } +} Q_WRITE_ANDX; + +typedef struct _Q_NT_CREATE_ANDX{ + uint8 wcount; + ANDX_INFO andx; + uint8 reserved; + uint16 name_len; + ULONG flags; + ULONG rootfid; + ULONG access; + hyper allocsize; + ULONG attribs; + ULONG sharing; + ULONG creat_disp; + ULONG impersonation; + uint8 sec_flags; + + uint16 count; + uint8 name[name_len]; + +} Q_NT_CREATE_ANDX; + +typedef struct { + uint8 wcount; + uint8 oplock_level; + uint16 fid; + ULONG action; + TIME create_time; + TIME write_time; + TIME change_time; + ULONG ext_attribs; + hyper allocsize; + hyper size; + uint16 type; + uint16 state; + uint8 directory; + + uint16 count; + +} R_NT_CREATE_ANDX; + +typedef struct _R_SMB { + ULONG nbhdr; + ULONG smbhdr; + uint8 com; + uint8 rcls; + uint8 reh; + uint16 err; + uint8 flg; + uint8 flg2; + uint8 reb[13]; + uint16 tid; + uint16 pid; + uint16 uid; + uint16 mid; + union ctr[com] { + case 4 R_CLOSE r4; + case 6 R_UNLINK r6; + case 45 R_OPEN_ANDX r45; + case 46 R_READ_ANDX r46; + case 47 R_WRITE_ANDX r47; + case 113 R_TDIS r113; + case 114 R_NEGPROT r114; + case 115 R_SESSION_SETUP_ANDX r115; + case 117 R_TCON_ANDX q117; + case 128 R_DSKATTR r128; + } +} R_SMB; + +typedef struct _Q_SMB { + ULONG nbhdr; + ULONG smbhdr; + uint8 com; + uint8 rcls; + uint8 reh; + uint16 err; + uint8 flg; + uint8 flg2; + uint8 reb[13]; + uint16 tid; + uint16 pid; + uint16 uid; + uint16 mid; + union ctr[com] { + case 4 Q_CLOSE q4; + case 6 Q_UNLINK q6; + case 45 Q_OPEN_ANDX q45; + case 46 Q_READ_ANDX q46; + case 47 Q_WRITE_ANDX q47; + case 113 Q_TDIS q113; + case 114 Q_NEGPROT q114; + case 115 Q_SESSION_SETUP_ANDX q115; + case 117 Q_TCON_ANDX q117; + case 128 Q_DSKATTR q128; + } +} Q_SMB; + diff --git a/source3/aparser/parsefn.awk b/source3/aparser/parsefn.awk index 45c41d770b..bff4521ba6 100644 --- a/source3/aparser/parsefn.awk +++ b/source3/aparser/parsefn.awk @@ -6,17 +6,33 @@ function elem_name(v, elem) } function parse_array(f, v, elnum, flags, - LOCAL, type, elem) + LOCAL, type, elem, array_len) { type = elements[elnum, "type"]; elem = elements[elnum, "elem"]; + array_len = elements[elnum, "array_len"]; v["ELEM"] = elem_name(v, elem); v["TYPE"] = type; v["FLAGS"] = flags; - v["ARRAY_LEN"] = elements[elnum, "array_len"]; + v["ARRAY_LEN"] = array_len; - if (type == "wchar") { - print_template(f, "prs_wstring.tpl", v); + if (array_len=="*") { + print_template(f,"prs_array_remainder.tpl", v); + return; + } + + if (type == "wchar" || type == "uint16") { + if (match(array_len,"[0-9]") == 1) { + print_template(f, "prs_wstring_fixed.tpl", v); + } else { + print_template(f, "prs_wstring.tpl", v); + } + } else if (type == "uint8") { + if (match(array_len,"[0-9]") == 1) { + print_template(f, "prs_uint8s_fixed.tpl", v); + } else { + print_template(f, "prs_uint8s.tpl", v); + } } else { print_template(f, "prs_array.tpl", v); } diff --git a/source3/aparser/parser.c b/source3/aparser/parser.c index 0e5f29b2bc..289cf95c7b 100644 --- a/source3/aparser/parser.c +++ b/source3/aparser/parser.c @@ -171,6 +171,8 @@ BOOL prs_align(prs_struct *ps, int align) { uint32 mod = ps->data_offset & (align-1); + return True; + if (align != 0 && mod != 0) { uint32 extra_space = (align - mod); if(!prs_grow(ps, extra_space)) @@ -320,6 +322,69 @@ BOOL io_pointer(char *desc, prs_struct *ps, int depth, void **p, unsigned flags) return True; } +/******************************************************************* + Stream a null-terminated string. + ********************************************************************/ +BOOL io_fstring(char *name, prs_struct *ps, int depth, fstring *str, unsigned flags) +{ + char *q; + uint8 *start; + int i; + int len = sizeof(fstring)-1; + + if (!(flags & PARSE_SCALARS)) return True; + + if (MARSHALLING(ps)) { + len = MIN(len, strlen(*str)); + } + + start = (uint8*)q; + + for(i = 0; i < len; i++) { + q = prs_mem_get(ps, 1); + if (q == NULL) + return False; + + RW_CVAL(ps->io, q, (*str)[i],0); + if ((*str)[i] == 0) + break; + ps->data_offset++; + } + + /* The terminating null. */ + (*str)[i] = '\0'; + + if (MARSHALLING(ps)) { + RW_CVAL(ps->io, q, (*str)[i], 0); + } + + ps->data_offset++; + + DEBUG(5,("%s %s: %s\n", tab_depth(depth), name, *str)); + return True; +} + +/****************************************************************** + do IO on a byte array + ********************************************************************/ +BOOL io_uint8s(char *name, prs_struct *ps, int depth, uint8 *data8s, int len, unsigned flags) +{ + char *q; + + if (!(flags & PARSE_SCALARS)) return True; + + if (!prs_align(ps, 2)) return False; + + q = prs_mem_get(ps, len * sizeof(uint8)); + if (q == NULL) return False; + + DBG_RW_PCVAL(True, name, depth, ps->data_offset, ps->io, q, data8s, len) + ps->data_offset += (len * sizeof(uint8)); + + return True; +} + + /****************************************************************** do IO on a unicode array ********************************************************************/ @@ -351,3 +416,13 @@ BOOL io_alloc(char *name, prs_struct *ps, void **ptr, unsigned size) return False; } +/****************************************************************** +realloc some memory for a parse structure + ********************************************************************/ +BOOL io_realloc(char *name, prs_struct *ps, void **ptr, unsigned size) +{ + (*ptr) = (void *)Realloc(*ptr, size); + if (*ptr) return True; + return False; +} + diff --git a/source3/aparser/parser.h b/source3/aparser/parser.h index 52d1b83941..51234d3dea 100644 --- a/source3/aparser/parser.h +++ b/source3/aparser/parser.h @@ -28,11 +28,20 @@ typedef unsigned short uint16; typedef unsigned short wchar; typedef unsigned uint32; +#ifndef _PSTRING + +#define PSTRING_LEN 1024 +#define FSTRING_LEN 128 + +typedef char pstring[PSTRING_LEN]; +typedef char fstring[FSTRING_LEN]; + +#define _PSTRING + +#endif #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))); } @@ -68,5 +77,7 @@ BOOL io_uint32(char *name, prs_struct *ps, int depth, uint32 *data32, unsigned f BOOL io_uint16(char *name, prs_struct *ps, int depth, uint16 *data16, unsigned flags); BOOL io_uint8(char *name, prs_struct *ps, int depth, uint8 *data8, unsigned flags); BOOL io_pointer(char *desc, prs_struct *ps, int depth, void **p, unsigned flags); +BOOL io_fstring(char *name, prs_struct *ps, int depth, fstring *str, unsigned flags); BOOL io_wstring(char *name, prs_struct *ps, int depth, uint16 *data16s, int len, unsigned flags); +BOOL io_uint8s(char *name, prs_struct *ps, int depth, uint8 *data8s, int len, unsigned flags); diff --git a/source3/aparser/templates/prs_array_remainder.tpl b/source3/aparser/templates/prs_array_remainder.tpl new file mode 100644 index 0000000000..80c5d8b9af --- /dev/null +++ b/source3/aparser/templates/prs_array_remainder.tpl @@ -0,0 +1,7 @@ + { + int i; + for (i=0;ps->data_offset < ps->buffer_size;i++) { + if (!io_alloc("@ELEM@", ps, (void **)&il->@ELEM@, sizeof(*(il->@ELEM@))*(i+1))) goto fail; + if (!io_@TYPE@("@ELEM@...", ps, depth+1, &il->@ELEM@[i], @FLAGS@)) goto fail; + } + } diff --git a/source3/aparser/templates/prs_array_remainder.tpl~ b/source3/aparser/templates/prs_array_remainder.tpl~ new file mode 100644 index 0000000000..8050903f06 --- /dev/null +++ b/source3/aparser/templates/prs_array_remainder.tpl~ @@ -0,0 +1,7 @@ + { + int i; + for (i=0;ps->data_offset < ps->buffer_size;i++) { + if (!io_alloc("@ELEM@", ps, (void **)&il->@ELEM@, sizeof(*(il->@ELEM@))*(i+1)) goto fail; + if (!io_@TYPE@("@ELEM@...", ps, depth+1, &il->@ELEM@[i], @FLAGS@)) goto fail; + } + } diff --git a/source3/aparser/templates/prs_uint8s.tpl b/source3/aparser/templates/prs_uint8s.tpl new file mode 100644 index 0000000000..cfc4bab1d2 --- /dev/null +++ b/source3/aparser/templates/prs_uint8s.tpl @@ -0,0 +1,2 @@ + if (!io_alloc("@ELEM@", ps, (void **)&il->@ELEM@, sizeof(*(il->@ELEM@))*il->@ARRAY_LEN@)) goto fail; + if (!io_uint8s("@ELEM@", ps, depth+1, il->@ELEM@, il->@ARRAY_LEN@, @FLAGS@)) goto fail; diff --git a/source3/aparser/templates/prs_uint8s_fixed.tpl b/source3/aparser/templates/prs_uint8s_fixed.tpl new file mode 100644 index 0000000000..c8f5581130 --- /dev/null +++ b/source3/aparser/templates/prs_uint8s_fixed.tpl @@ -0,0 +1,2 @@ + if (!io_alloc("@ELEM@", ps, (void **)&il->@ELEM@, sizeof(*(il->@ELEM@))*@ARRAY_LEN@)) goto fail; + if (!io_uint8s("@ELEM@", ps, depth+1, il->@ELEM@, @ARRAY_LEN@, @FLAGS@)) goto fail; diff --git a/source3/aparser/templates/prs_wstring_fixed.tpl b/source3/aparser/templates/prs_wstring_fixed.tpl new file mode 100644 index 0000000000..bf2539b216 --- /dev/null +++ b/source3/aparser/templates/prs_wstring_fixed.tpl @@ -0,0 +1,2 @@ + if (!io_alloc("@ELEM@", ps, (void **)&il->@ELEM@, sizeof(*(il->@ELEM@))*@ARRAY_LEN@)) goto fail; + if (!io_wstring("@ELEM@", ps, depth+1, il->@ELEM@, @ARRAY_LEN@, @FLAGS@)) goto fail; -- cgit