/*
openisis - an open implementation of the CDS/ISIS database
Version 0.8.x (patchlevel see file Version)
Copyright (C) 2001-2003 by Erik Grziwotz, erik@openisis.org
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
see README for more information
EOH */
/*
$Id: lrec.c,v 1.53 2003/05/29 18:03:35 kripke Exp $
implementation of record cooking.
*/
#include <stddef.h>
#include <stdarg.h>
#include <stdio.h> /* vsnprintf */
#include <stdlib.h> /* free */
#include <string.h> /* memset et al */
#ifdef WIN32
# define vsnprintf _vsnprintf
#endif /* WIN32 */
#include "ldb.h"
#include "lio.h"
#include "lcs.h"
/* ************************************************************
private types
*/
typedef struct {
int len; /* length of tagf */
int tagf[2]; /* tag and modification flags */
} LrecMF;
#define MF_SET 0x01
#define MF_DEL 0x02
/* ************************************************************
private data
*/
/* ************************************************************
private functions
*/
static LrecMF* mfCtor (Rec *rec) {
int len = (int)(2 * rec->len);
int *M;
Field *F;
LrecMF *lmf = (LrecMF*) mAlloc ((1 + len) * sizeof (int));
if (0 == lmf) {
return 0;
}
memset (lmf->tagf, 0, len * sizeof (int));
lmf->len = len;
for (M = lmf->tagf, F = rec->field; len > M - lmf->tagf; ++F, M += 2) {
*M = (int) F->tag;
}
return lmf;
}
static int rCompact (Rec *rec) {
int rt = 0;
if (0 != rec) {
char *V = (char*)rec + rec->base;
Field *F = rec->field;
Field *L = F + rec->len;
int used = rec->base;
while (F < L) {
if (V != F->val) {
rt = !0;
break;
}
V += F->len;
used += F->len;
++F;
}
if (rt) {
char *buf = (char*) mAlloc (rec->used - (V - (char*)rec));
char *B = buf;
int nb = 0;
if (0 == buf) {
return 0;
}
while (F < L) {
memcpy (B, F->val, F->len);
F->val = V;
B += F->len;
V += F->len;
nb += F->len;
++F;
}
memcpy ((char*)rec + used, buf, nb);
rec->used = used + nb;
mFree (buf);
}
}
return rt;
}
/** @return
-2 - malloc failed,
-1 - tag not present,
0 - compactification not neccessary,
1 - rec data buffer has gaps
*/
static int rReplace (
Rec **rec, LrecMF *lmf,
int tag, int occr, int lmode, const char *val, int disc
) {
Rec *nr;
Field *F;
int *M;
int vlen;
int fnd = -1;
int ocnt = -1;
for (M = lmf->tagf; lmf->len > M - lmf->tagf; M += 2) {
if (tag == M[0]) {
if (0 <= occr) {
if (++ocnt == occr) {
M[1] = lmode;
fnd = (M - lmf->tagf) / 2;
break;
}
}
else {
if (0 == (MF_SET & M[1])) {
M[1] = lmode;
fnd = (M - lmf->tagf) / 2;
if (lmode) {
for (M += 2; lmf->len > M - lmf->tagf; M += 2) {
if (tag == M[0]) {
M[1] = MF_DEL;
}
}
}
break;
}
}
}
}
if (0 > fnd) {
return -1;
}
if (MF_SET != lmode) {
return 1;
}
F = (*rec)->field + fnd;
vlen = strlen (val);
if (F->len == vlen) {
memcpy ((char*)F->val, val, vlen);
return 0;
}
if (F->len > vlen) {
memcpy ((char*)F->val, val, vlen);
F->len = vlen;
return 1;
}
if ((*rec)->bytes < (*rec)->used + vlen) {
nr = rDup (*rec, vlen, disc);
if (0 == nr) {
return -2;
}
F = (*rec = nr)->field + fnd;
}
F->val = (char*)(*rec) + (*rec)->used;
F->len = vlen;
memcpy ((char*)F->val, val, vlen);
(*rec)->used += vlen;
return 1;
}
static int rDel (Rec *rec, LrecMF *lmf) {
Field *F;
int *M;
int idx;
int dcnt = 0;
int done = 0;
for (M = lmf->tagf; lmf->len > M - lmf->tagf; M += 2) {
if (0 != (MF_DEL & M[1])) {
idx = (M - lmf->tagf) / 2 - dcnt;
if (idx < rec->len - 1) {
F = rec->field + idx;
memmove (F, F + 1, (rec->len - idx - 1) * sizeof (Field));
}
--(rec->len);
++dcnt;
done = !0;
}
}
return done;
}
/* ************************************************************
package functions
*/
/* ************************************************************
public functions
*/
Field *rGet ( Rec *r, int tag, int *pos )
{
if (r) {
int p = pos ? *pos : 0;
for ( ; p < r->len; p++ )
if ( tag == r->field[p].tag ) {
if ( pos )
*pos = p+1;
return &r->field[p];
}
if ( pos )
*pos = r->len;
}
return 0;
} /* rGet */
Field *rOccurence ( Rec *r, int tag, int occ ) {
Field *F;
int pos = 0;
do {
F = rGet (r, tag, &pos);
} while (F && 0 <= --occ);
return F;
}
char *rString (Rec *rec, int tag, int *pos, char *buf, int len) {
Field *F = rGet (rec, tag, pos);
if (!F) {
return 0;
}
if (len > 1 + F->len) {
len = 1 + (int) F->len;
}
if (0 >= --len) {
*buf = 0;
return buf;
}
strncpy (buf, F->val, len) [len] = 0;
return buf;
}
char *rString2 (Rec *rec, Rec *dflt, int tag, char *buf, int len) {
char *rt = rString (rec, tag, 0, buf, len);
if (rt) {
return rt;
}
return rString (dflt, tag, 0, buf, len);
}
int rInt ( Rec *r, int tag, int def, int *pos )
{
if (r) {
int p = pos ? *pos : 0;
int rt;
for ( ; p < r->len; p++ )
if ( tag == r->field[p].tag ) {
if ( pos )
*pos = p+1;
rt = a2i( r->field[p].val, r->field[p].len );
if (rt) {
return rt;
}
if (0 < luti_true (r->field[p].val, r->field[p].len)) {
return 1;
}
return 0;
}
if ( pos )
*pos = r->len;
}
return def;
} /* rInt */
int rInt2 (Rec *rec, Rec *dflt, int tag, int def) {
int rt1 = rInt (rec, tag, def, 0);
if (def == rt1) {
return rInt (dflt, tag, def, 0);
}
return rt1;
}
int rEnum (Fdt *fdt, Rec *rec, int tag, int def, int *pos) {
char buf[OPENISIS_FD_NAMELEN];
Field *F;
Fd *D;
int len, ev;
if (! fdt || ! rec) {
return def;
}
D = fById (fdt, tag, 0);
if (! D) {
return def;
}
F = rGet (rec, tag, pos);
if (! F) {
return def;
}
len = (int) F->len;
if (OPENISIS_FD_NAMELEN <= len) {
len = OPENISIS_FD_NAMELEN - 1;
}
strncpy (buf, F->val, len) [len] = 0;
ev = fEnum (fdt, tag, buf);
if (NOENUM == ev) {
return def;
}
return ev;
}
Rec* rDup ( Rec *r, int room, int discard )
{
Rec *nr = 0;
int nfields, nbytes, hadfields, hadcontent;
if ( ! r ) {
nfields = 80; /* for a 1K base */
nbytes = 8*1024;
if ( nbytes < BASESZ(80) + room*3/2 )
nbytes = BASESZ(80) + room*3/2;
hadfields = 0;
hadcontent = 0;
} else {
rCompact (r);
hadfields = r->len;
hadcontent = r->used - r->base;
if ( 0 > room ) { /* shrink to fit */
nfields = r->len;
nbytes = BASESZ(nfields) + hadcontent;
} else {
nfields = 54 > r->len ? 80 : (r->len * 3 / 2); /* add 50% */
if ( nfields < r->fields )
nfields = r->fields;
nbytes = 6*1024 > r->bytes ? 8*1024 : (r->bytes *3 / 2);
if ( nbytes < r->used + room )
nbytes = r->used + room*3/2;
nbytes += (nfields - r->fields)*sizeof(Field);
}
}
LOG_DBG( LOG_DEBUG,
"extending rec size %d (cont %d) to %d bytes, %d -> %d fields",
!r ? -1 : r->bytes, hadcontent, nbytes, hadfields, nfields );
assert( nbytes >= BASESZ( nfields ) + hadcontent + room );
if ( ! (nr = mAlloc( nbytes )) )
return 0;
memset( nr, 0, nbytes ); /* paranoia */
nr->bytes = nbytes;
nr->fields = nfields;
nr->base = BASESZ( nfields );
nr->used = nr->base + hadcontent;
nr->len = hadfields;
if ( ! r ) {
nr->dbid = -1; /* no valid dbid if new rec */
}
else {
char *obuf = ((char*)r) + r->base;
char *nbuf = ((char*)nr) + nr->base;
nr->dbid = r->dbid;
/* for ! r, the following are 0 by memset */
nr->rowid = r->rowid;
nr->state = r->state;
if ( hadcontent )
memcpy( nbuf, obuf, hadcontent );
if ( hadfields ) {
int i;
int displace = nbuf - obuf; /* ptrdiff_t */
Field *f = nr->field;
memcpy( nr->field, r->field, hadfields*sizeof(Field) );
for ( i=hadfields; i--; )
if ( f[i].val )
f[i].val += displace;
#ifndef NDEBUG
{
char *end = ((char*)nr) + nr->used;
for ( i=hadfields; i--; )
if ( f[i].val
&& (f[i].val < nbuf || f[i].val + f[i].len > end)
) {
int wasok = r->field[i].val >= obuf
&& r->field[i].val + r->field[i].len <= obuf + hadcontent;
sMsg( LOG_ERROR, "OOPS! nuked field %d which previously was %s",
i, wasok ? "ok" : "already broken" );
return 0; /* no cleanup, we're nearly dead anyway */
}
}
#endif
}
if ( discard )
free( r );
}
assert( RECOK(nr) );
return nr;
} /* rDup */
Rec* rMsg ( Rec *r, int discard, int tag, const char *fmt, ... )
{
char buf[1024];
int l;
va_list ap;
va_start( ap, fmt );
l = vsnprintf( buf, sizeof(buf), fmt, ap );
if ( 0 > l ) /* older versions return -1 */
l = sizeof(buf);
va_end( ap );
RADD( r, tag, buf, l, discard );
return r;
} /* rMsg */
/*
#define openIsisPrintf( r, d, t, f, a... ) \
openIsisRMsg( OPENISIS_SES0(), r, d, t, f, ## a )
requires gcc ...
*/
Rec* openIsisPrintf ( Rec *r, int discard, int tag, const char *fmt, ... )
{
char buf[1024];
int l;
va_list ap;
va_start( ap, fmt );
l = vsnprintf( buf, sizeof(buf), fmt, ap );
if ( 0 > l ) /* older versions return -1 */
l = sizeof(buf);
va_end( ap );
RADD( r, tag, buf, l, discard );
return r;
} /* openIsisPrintf */
Rec *rAddI (Rec *rec, int tag, int val, int discard) {
char buf[32];
int len = i2a (buf, (int)val);
RADD (rec, tag, buf, len, discard);
return rec;
}
Rec* rSet ( Rec *oldr, int mode, ... )
{
const char **argv = 0;
LrecMF *lmf = 0;
Fdt *fdt = 0;
Rec *newr = oldr;
int argc = 0xffff & mode;
int dis = RDIS & mode;
int op = ROP & mode;
int lmode = MF_SET;
int compact = 0;
va_list ap;
va_start( ap, mode );
if (RFDT & mode) {
fdt = va_arg( ap, Fdt* );
}
if (RARGV & mode) {
argv = va_arg( ap, const char** );
}
if (! fdt) {
fdt = luti_fdt_from_rec (oldr);
}
if (op) {
switch (op) {
case RDEL:
lmode = MF_DEL;
break;
case RCHG:
break;
case RDFLT:
lmode = 0;
break;
default:
sMsg (ERR_IDIOT, "rSet: illegal mode %x", op);
return newr;
}
if (oldr) {
if (0 == (lmf = mfCtor (oldr))) {
goto done;
}
}
else if (RDEL == op) {
goto done;
}
}
for ( ;; ) {
char ib[16];
Rec *tmpr;
const char *v = 0;
int id = -1;
int occr = -1;
Fd *fd = 0;
int rpl;
if ( argv ) {
if ( 1 > argc )
break;
v = luti_parse_path (*argv, fdt, &fd, &id, &occr);
if (!v || *v) {
fd = 0;
id = -1;
}
v = 0;
++argv;
--argc;
}
else {
id = va_arg( ap, int );
if ( ! id )
break;
fd = fdt ? fById( fdt, id, 0 ) : 0;
if (RDEL != op) {
v = va_arg( ap, char* );
}
}
if ( fd ) {
if (RDEL != op) {
int e;
if (argv) {
if ( 1 > argc ) {
if (FTB != fd->type) {
break;
}
}
else {
v = *argv++;
--argc;
}
}
switch ( fd->type ) {
case FTE:
e = fEnum( fdt, fd->id, v );
if ( NOENUM == e ) {
if ( RIGN & mode )
continue;
sMsg( LOG_ERROR,
"bad enum value '%s' for id %d", v, fd->id );
goto error;
}
i2a( ib, e );
v = ib;
break;
case FTB:
e = v ? luti_true (v, -1) : 1;
if (0 > e) {
/* non-given values default to true */
++argc;
--argv;
v = "1";
}
else {
v = e ? "1" : "0";
}
break;
}
} /* RDEL != op */
} /* fd */
else {
if (argv) {
if (RDEL != op) {
if ( 1 > argc ) {
break;
}
v = *argv++;
--argc;
}
}
if (fdt /*|| 0 >= id*/) {
if ( RIGN & mode )
continue;
if ( argv )
sMsg( LOG_ERROR, "unknown field name '%s'",
argv[RDEL == op ? -1 : -2] );
else
sMsg( LOG_ERROR, "unknown field id %d", id );
break;
}
}
if (!v) {
v = "";
}
if (op) {
rpl = lmf ? rReplace (
&newr, lmf, id, occr, lmode, v, dis || newr != oldr) :
-1;
if (RDFLT == op) {
if (-1 != rpl) {
continue;
}
}
else {
if (-2 == rpl) {
goto done;
}
if (-1 != rpl) {
if (1 == rpl) {
compact = !0;
}
continue;
}
if (RDEL == op) {
continue;
}
}
/* fall thru */
}
tmpr = newr;
RADDS( newr, id, v, dis || newr != oldr );
if (0 == newr) {
goto done;
}
if (newr != tmpr) {
compact = 0;
}
sMsg( LOG_VERBOSE,
"added v '%s' id %d as %dth", v, id, newr->len );
}
error:
if (lmf) {
if (rDel (newr, lmf)) {
compact = !0;
}
}
if (compact && 0 == (RNOC & mode)) {
rCompact (newr);
}
done:
if (lmf) {
mFree (lmf);
}
va_end( ap );
return newr;
} /* rSet */
Rec *dFmt ( Rec *buf, const char *fmt, int db, int rowid )
{
Rec *r = dRead( db, rowid );
Rec *q;
if ( ! r )
return 0;
q = rFmt( buf, fmt, r );
free( r );
return q;
} /* dFmt */
Rec *dScan ( int db, int rowid, int tag, const char *txt )
{
int max = dMaxId( db );
int tlen = strlen( txt );
const char f = *txt;
for ( ; rowid <= max; rowid++ ) {
int i, found = 0;
Rec *r = dRead( db, rowid );
if ( ! r )
continue;
for ( i=0; i < r->len; i++ ) {
const char *c, *e;
if ( found
|| (tag > 0 && tag != r->field[i].tag)
|| tlen > r->field[i].len
)
continue;
c = r->field[i].val;
e = c + r->field[i].len - tlen;
for ( ; c<=e; c++ )
if ( f == *c && !memcmp( c, txt, tlen ) ) {
found = !0;
break;
}
}
if ( found )
return r;
}
return 0;
} /* dScan */
Rec *rSplitf ( Rec *r, const Field* field )
{
int i;
int nfields = 0; /* number of subfields */
int size; /* byte length of field list */
const char *p, *e;
if ( ! field || 2 > field->len )
return 0;
if ( '^' != field->val[0] ) /* initial anonymous subfield */
nfields = 1;
/* go counting hats ... */
for ( e = (p=field->val) + field->len - 1; p<e; )
if ( '^' == *p++ && '^' != *p ) /* ignore first of ^^ */
nfields++;
if ( '^' == *p )
sMsg( LOG_ERROR, "found trailing '^' in field '%.*s'",
(int)field->len, field->val );
if ( r ) {
if ( 1 > r->len )
return 0;
size = sizeof(Rec) + (r->len - 1) * sizeof(Field);
if ( nfields > r->len )
nfields = r->len;
} else {
/* first field already counted in sizeof(Rec) */
size = sizeof(Rec) + (nfields - 1) * sizeof(Field);
r = (Rec *)mAlloc( size );
if ( ! r )
return 0;
}
memset( r, 0, size );
r->rowid = 0;
r->len = nfields;
e = (p = field->val) + field->len;
if ( '^' == *p ) /* else initial anonymous subfield */
p++;
for ( i=0; i<nfields; i++ ) {
/* p is on hat or end ... skip all consecutive hats */
while ( p<e && '^' == *p )
p++;
if ( p >= e ) {
sMsg( LOG_ERROR,
"confused cats hound counting hats at %d in '%.*s'",
i, field->len, field->val );
r->len = i;
break;
}
/* p is after a hat */
r->field[ i ].tag = p==field->val ? 0 : (unsigned char)*p++;
r->field[ i ].val = p;
/* advance in buf */
while ( p<e && '^' != *p )
p++;
r->field[ i ].len = p - r->field[ i ].val;
/* log_msg( LOG_ERROR, "subf %d of %d tag %c off %d",
i, nfields, r->field[ i ].tag, r->field[ i ].val - field->val ); */
}
return r;
} /* rSplitf */
enum { /* stream reading state */
RS_REC, /* not in any record */
RS_TAG, /* in tag: done is at beginning of line */
RS_SEP, /* in sep: done is at beginning of sep */
RS_VAL, /* done is somewhere in the value ... */
RS_EOL /* not really a state, but a flag */
};
/**
read a record from a stream.
This may either
<ol>
<li> read a record and return 1
</li>
<li> not get enough bytes on a non-blocking stream and return 0
</li>
<li> have some error and return negative
</li>
</ol>
*/
int sGetr ( OpenIsisRecStream *s )
{
Ios *lio = s->in;
int state = 3 & s->flg;
int emptyline = 0;
int ret = 0;
if ( ! s->rec
&& ! (s->rec = s->buf) /* have buffer ? */
&& ! (s->rec = rDup( 0, 0, 0 ))
/* allocate and prepare an 8k standard record */
) {
ret = sMsg( LOG_ERROR, "could not get record" );
goto fatal;
}
if ( RS_REC == state ) { /* initialize */
s->rec->rowid = 0;
s->rec->len = 0; /* nuke all fields */
s->rec->used = s->rec->base; /* nuke value buffer */
/* let base/fields untouched, it hopefully has a reasonable value */
state = RS_TAG;
}
if ( ! s->rec->len )
state = RS_TAG;
for (;;) {
unsigned char *b, *p, *e; /* begin, pointer, end */
if ( lio->b.done >= lio->b.fill ) {
reload:
if ( ! LIO_FILL( lio ) )
goto done; /* EAGAIN -- see ya later */
/* so we have bytes or EOF */
if ( lio->b.done >= lio->b.fill ) {
if ( LIO_IN & lio->file ) {
sMsg( LOG_ERROR, "confused in instream: no bytes and no EOF" );
lio_close( &lio->file, LIO_IN );
}
break;
}
}
/* so we have bytes */
p = b = lio->b.c + lio->b.done;
e = lio->b.c + lio->b.fill;
/* try to eat one line */
switch ( state ) {
case RS_TAG: {
int tag = -1;
int recognized = 0;
if ( LCS_ISFR(lcs_latin1_ct[*p]) ) { /* line starts with line/record sep */
if ( '\n' == *p && (RS_EOL & s->flg) ) { /* TODO */
s->flg &= ~RS_EOL;
continue;
}
emptyline = !0;
break;
}
for (;;p++) {
if ( p < e ) {
if ( LCS_ISWORD(lcs_latin1_ct[*p]) )
continue; /* tight loop */
break;
}
if ( 4096 > p - b )
goto reload;
sMsg( ERR_TRASH, "tag too long" );
goto fatal;
}
/* identify tag between s and p */
if ( p == b )
recognized = !0;/* empty tag */
else if ( '0' <= *b && *b <= '9' ) {
unsigned char *d = b; /* digit ? */
tag = 0;
while ( d < p && '0' <= *d && *d <= '9' )
tag = 10*tag + *d++ - '0';
if ( !(recognized = d == p) ) /* all digits */
tag = -1;
}
if ( ! recognized && s->dict ) { /* dict lookup */
int i=s->dict->len, l = p-b;
Field *f = s->dict->field;
for ( ; i--; f++ )
if ( l == f->len && memcmp( b, f->val, l ) ) {
tag = f->tag;
break;
}
}
/* create new field unless it's a continuation line */
if ( p == b && s->rec->len )
;
else if ( recognized )
RADD( s->rec, tag, 0, 0, s->rec != s->buf );
else
RADD( s->rec, tag, b, p-b, s->rec != s->buf );
if ( ! s->rec )
goto outamem;
/* did that */
lio->b.done = (b = p) - lio->b.c;
if ( LCS_ISFR(lcs_latin1_ct[*p]) )
break;
state = RS_SEP;
} case RS_SEP:
/* p = b is at beginning of separator */
while ( p < e && ' ' == *p ) /* skip leading blanks */
p++;
if ( p < e && LCS_ISCST(lcs_latin1_ct[*p]) && '\t' != *p++ )
while ( p < e && ' ' == *p ) /* skip trailing blanks */
p++;
if ( e == p ) {
if ( 4096 > p - b )
goto reload;
sMsg( ERR_TRASH, "sep too long" );
goto fatal;
}
assert( s->rec->len );
if ( s->rec->field[s->rec->len - 1].len )
RCAT( s->rec, b, p-b, s->rec != s->buf );
if ( ! s->rec )
goto outamem;
lio->b.done = (b = p) - lio->b.c;
if ( LCS_ISFR(lcs_latin1_ct[*p]) )
break;
state = RS_VAL;
case RS_VAL: default: /* ??? */
while ( p < e && !LCS_ISFR(lcs_latin1_ct[*p]) )
p++;
assert( s->rec->len );
if ( p > b )
RCAT( s->rec, b, p-b, s->rec != s->buf );
if ( ! s->rec )
goto outamem;
lio->b.done = (b = p) - lio->b.c;
if ( p == e )
goto reload;
/* else we found line or record separator */
} /* switch state */
if ( '\r' == *p ) { /* do that ole' ugly CR/NL quatsch */
if ( e-p < 2 && 0 < LIO_FILL(lio) ) /* extend end */
e = lio->b.c + lio->b.fill;
if ( e-p < 2 )
s->flg |= RS_EOL; /* possibly check later */
else if ( '\n' == p[1] )
p++;
}
lio->b.done = 1 + p - lio->b.c; /* consider p eaten */
if ( LCS_R == lcs_latin1_ct[*p]
|| (emptyline && (OPENISIS_STOPONEMPTY & s->flg))
)
break;
state = RS_TAG;
}
/* got record */
state = RS_REC;
if ( s->rec && s->rec->len ) {
if ( (OPENISIS_AUTOCLONE & s->flg) && s->rec == s->buf )
s->rec = rDup( s->buf, 0, 0 );
ret = 1;
}
goto done;
outamem:
fatal:
if ( LIO_SISOPEN( s->in ) )
LIO_CLOSE( s->in );
done:
s->flg = state | (~3 & s->flg); /* save state */
/* sMsg( LOG_ERROR, "fill %d done %d", lio->b.fill, lio->b.done ); */
return ret;
} /* openIsisReadStream */
int rSer ( char *buf, OpenIsisRec *rec )
{
Field *f = rec->field;
int i = rec->len;
char *p = buf, *e;
for ( ; i--; f++ ) {
p += i2a( p, f->tag );
*p++ = 9; /* TAB */
memcpy( p, f->val, f->len );
for ( e = p + f->len; p<e; )
if ( 10 == *p++ )
p[-1] = 11;
*p++ = 10;
}
*p++ = 10;
return p - buf;
} /* rSer */
/** binary serialize, turns newline into newline tab
buf must have len >= 2*rec->used
*/
int rSerB ( char *buf, OpenIsisRec *rec )
{
Field *f = rec->field;
int i = rec->len;
char *p = buf;
const char *s, *e;
for ( ; i--; f++ ) {
p += i2a( p, f->tag );
*p++ = 9; /* TAB */
for ( e = (s = f->val) + f->len; s<e; )
if ( 10 == (*p++ = *s++) )
*p++ = 9;
*p++ = 10;
}
*p++ = 10;
return p - buf;
} /* rSerB */
char* rSerA (Rec *rec, char *buf, int *len) {
if (!len) {
return 0;
}
if (!rec) {
*len = 0;
return buf;
}
if (*len <= rec->used) {
buf = (char*) mAlloc (1 + rec->used);
if (!buf) {
sMsg (ERR_NOMEM, "rSerA: cannot malloc %d", (1 + rec->used));
return 0;
}
}
*len = rSer (buf, rec);
return buf;
}
int rDeser ( OpenIsisRec **rp, const char *buf, int len, int flg )
{
Rec *rec = *rp;
Field *f;
const char *p = buf, *e = buf+len;
char *q;
int dis = RDIS & flg;
int tag;
/* non-transparent mode - no continuation lines */
for ( ; e > p; p++ ) {
if ( 10 == *p ) { /* blank line */
if ( OPENISIS_STOPONEMPTY & flg )
break;
continue;
}
p += a2il( p, e-p, &tag );
if ( e > p && 9 == *p ) /* TAB */
p++;
RADD( rec, tag, 0, e-p, dis || rec!=*rp );
if ( !rec )
break;
f = rec->field + rec->len-1;
for ( q=(char*)f->val; e>p && 10 != *p; )
if ( 11 == (*q++ = *p++) )
q[-1] = 10;
rec->used += f->len = q - f->val;
}
*rp = rec;
return p-buf;
} /* rDeser */