2 openisis - an open implementation of the CDS/ISIS database
3 Version 0.8.x (patchlevel see file Version)
4 Copyright (C) 2001-2003 by Erik Grziwotz, erik@openisis.org
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public
17 License along with this library; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 see README for more information
24 $Id: lrec.c,v 1.53 2003/05/29 18:03:35 kripke Exp $
25 implementation of record cooking.
30 #include <stdio.h> /* vsnprintf */
31 #include <stdlib.h> /* free */
32 #include <string.h> /* memset et al */
35 # define vsnprintf _vsnprintf
43 /* ************************************************************
48 int len; /* length of tagf */
49 int tagf[2]; /* tag and modification flags */
55 /* ************************************************************
58 /* ************************************************************
62 static LrecMF* mfCtor (Rec *rec) {
63 int len = (int)(2 * rec->len);
66 LrecMF *lmf = (LrecMF*) mAlloc ((1 + len) * sizeof (int));
70 memset (lmf->tagf, 0, len * sizeof (int));
72 for (M = lmf->tagf, F = rec->field; len > M - lmf->tagf; ++F, M += 2) {
78 static int rCompact (Rec *rec) {
81 char *V = (char*)rec + rec->base;
82 Field *F = rec->field;
83 Field *L = F + rec->len;
95 char *buf = (char*) mAlloc (rec->used - (V - (char*)rec));
102 memcpy (B, F->val, F->len);
109 memcpy ((char*)rec + used, buf, nb);
110 rec->used = used + nb;
119 -1 - tag not present,
120 0 - compactification not neccessary,
121 1 - rec data buffer has gaps
123 static int rReplace (
124 Rec **rec, LrecMF *lmf,
125 int tag, int occr, int lmode, const char *val, int disc
133 for (M = lmf->tagf; lmf->len > M - lmf->tagf; M += 2) {
136 if (++ocnt == occr) {
138 fnd = (M - lmf->tagf) / 2;
143 if (0 == (MF_SET & M[1])) {
145 fnd = (M - lmf->tagf) / 2;
147 for (M += 2; lmf->len > M - lmf->tagf; M += 2) {
161 if (MF_SET != lmode) {
164 F = (*rec)->field + fnd;
166 if (F->len == vlen) {
167 memcpy ((char*)F->val, val, vlen);
171 memcpy ((char*)F->val, val, vlen);
175 if ((*rec)->bytes < (*rec)->used + vlen) {
176 nr = rDup (*rec, vlen, disc);
180 F = (*rec = nr)->field + fnd;
182 F->val = (char*)(*rec) + (*rec)->used;
184 memcpy ((char*)F->val, val, vlen);
185 (*rec)->used += vlen;
189 static int rDel (Rec *rec, LrecMF *lmf) {
195 for (M = lmf->tagf; lmf->len > M - lmf->tagf; M += 2) {
196 if (0 != (MF_DEL & M[1])) {
197 idx = (M - lmf->tagf) / 2 - dcnt;
198 if (idx < rec->len - 1) {
199 F = rec->field + idx;
200 memmove (F, F + 1, (rec->len - idx - 1) * sizeof (Field));
210 /* ************************************************************
214 /* ************************************************************
219 Field *rGet ( Rec *r, int tag, int *pos )
222 int p = pos ? *pos : 0;
223 for ( ; p < r->len; p++ )
224 if ( tag == r->field[p].tag ) {
235 Field *rOccurence ( Rec *r, int tag, int occ ) {
239 F = rGet (r, tag, &pos);
240 } while (F && 0 <= --occ);
244 char *rString (Rec *rec, int tag, int *pos, char *buf, int len) {
245 Field *F = rGet (rec, tag, pos);
249 if (len > 1 + F->len) {
250 len = 1 + (int) F->len;
256 strncpy (buf, F->val, len) [len] = 0;
261 char *rString2 (Rec *rec, Rec *dflt, int tag, char *buf, int len) {
262 char *rt = rString (rec, tag, 0, buf, len);
266 return rString (dflt, tag, 0, buf, len);
270 int rInt ( Rec *r, int tag, int def, int *pos )
273 int p = pos ? *pos : 0;
275 for ( ; p < r->len; p++ )
276 if ( tag == r->field[p].tag ) {
279 rt = a2i( r->field[p].val, r->field[p].len );
283 if (0 < luti_true (r->field[p].val, r->field[p].len)) {
295 int rInt2 (Rec *rec, Rec *dflt, int tag, int def) {
296 int rt1 = rInt (rec, tag, def, 0);
298 return rInt (dflt, tag, def, 0);
304 int rEnum (Fdt *fdt, Rec *rec, int tag, int def, int *pos) {
305 char buf[OPENISIS_FD_NAMELEN];
309 if (! fdt || ! rec) {
312 D = fById (fdt, tag, 0);
316 F = rGet (rec, tag, pos);
321 if (OPENISIS_FD_NAMELEN <= len) {
322 len = OPENISIS_FD_NAMELEN - 1;
324 strncpy (buf, F->val, len) [len] = 0;
325 ev = fEnum (fdt, tag, buf);
333 Rec* rDup ( Rec *r, int room, int discard )
336 int nfields, nbytes, hadfields, hadcontent;
338 nfields = 80; /* for a 1K base */
340 if ( nbytes < BASESZ(80) + room*3/2 )
341 nbytes = BASESZ(80) + room*3/2;
347 hadcontent = r->used - r->base;
348 if ( 0 > room ) { /* shrink to fit */
350 nbytes = BASESZ(nfields) + hadcontent;
352 nfields = 54 > r->len ? 80 : (r->len * 3 / 2); /* add 50% */
353 if ( nfields < r->fields )
355 nbytes = 6*1024 > r->bytes ? 8*1024 : (r->bytes *3 / 2);
356 if ( nbytes < r->used + room )
357 nbytes = r->used + room*3/2;
358 nbytes += (nfields - r->fields)*sizeof(Field);
362 "extending rec size %d (cont %d) to %d bytes, %d -> %d fields",
363 !r ? -1 : r->bytes, hadcontent, nbytes, hadfields, nfields );
364 assert( nbytes >= BASESZ( nfields ) + hadcontent + room );
365 if ( ! (nr = mAlloc( nbytes )) )
367 memset( nr, 0, nbytes ); /* paranoia */
369 nr->fields = nfields;
370 nr->base = BASESZ( nfields );
371 nr->used = nr->base + hadcontent;
374 nr->dbid = -1; /* no valid dbid if new rec */
377 char *obuf = ((char*)r) + r->base;
378 char *nbuf = ((char*)nr) + nr->base;
380 /* for ! r, the following are 0 by memset */
381 nr->rowid = r->rowid;
382 nr->state = r->state;
384 memcpy( nbuf, obuf, hadcontent );
387 int displace = nbuf - obuf; /* ptrdiff_t */
388 Field *f = nr->field;
389 memcpy( nr->field, r->field, hadfields*sizeof(Field) );
390 for ( i=hadfields; i--; )
392 f[i].val += displace;
395 char *end = ((char*)nr) + nr->used;
396 for ( i=hadfields; i--; )
398 && (f[i].val < nbuf || f[i].val + f[i].len > end)
400 int wasok = r->field[i].val >= obuf
401 && r->field[i].val + r->field[i].len <= obuf + hadcontent;
402 sMsg( LOG_ERROR, "OOPS! nuked field %d which previously was %s",
403 i, wasok ? "ok" : "already broken" );
404 return 0; /* no cleanup, we're nearly dead anyway */
417 Rec* rMsg ( Rec *r, int discard, int tag, const char *fmt, ... )
423 l = vsnprintf( buf, sizeof(buf), fmt, ap );
424 if ( 0 > l ) /* older versions return -1 */
427 RADD( r, tag, buf, l, discard );
432 #define openIsisPrintf( r, d, t, f, a... ) \
433 openIsisRMsg( OPENISIS_SES0(), r, d, t, f, ## a )
436 Rec* openIsisPrintf ( Rec *r, int discard, int tag, const char *fmt, ... )
442 l = vsnprintf( buf, sizeof(buf), fmt, ap );
443 if ( 0 > l ) /* older versions return -1 */
446 RADD( r, tag, buf, l, discard );
448 } /* openIsisPrintf */
451 Rec *rAddI (Rec *rec, int tag, int val, int discard) {
453 int len = i2a (buf, (int)val);
454 RADD (rec, tag, buf, len, discard);
459 Rec* rSet ( Rec *oldr, int mode, ... )
461 const char **argv = 0;
465 int argc = 0xffff & mode;
466 int dis = RDIS & mode;
472 va_start( ap, mode );
475 fdt = va_arg( ap, Fdt* );
478 argv = va_arg( ap, const char** );
481 fdt = luti_fdt_from_rec (oldr);
494 sMsg (ERR_IDIOT, "rSet: illegal mode %x", op);
498 if (0 == (lmf = mfCtor (oldr))) {
502 else if (RDEL == op) {
518 v = luti_parse_path (*argv, fdt, &fd, &id, &occr);
528 id = va_arg( ap, int );
531 fd = fdt ? fById( fdt, id, 0 ) : 0;
533 v = va_arg( ap, char* );
541 if (FTB != fd->type) {
550 switch ( fd->type ) {
552 e = fEnum( fdt, fd->id, v );
557 "bad enum value '%s' for id %d", v, fd->id );
564 e = v ? luti_true (v, -1) : 1;
566 /* non-given values default to true */
588 if (fdt /*|| 0 >= id*/) {
592 sMsg( LOG_ERROR, "unknown field name '%s'",
593 argv[RDEL == op ? -1 : -2] );
595 sMsg( LOG_ERROR, "unknown field id %d", id );
603 rpl = lmf ? rReplace (
604 &newr, lmf, id, occr, lmode, v, dis || newr != oldr) :
628 RADDS( newr, id, v, dis || newr != oldr );
636 "added v '%s' id %d as %dth", v, id, newr->len );
641 if (rDel (newr, lmf)) {
646 if (compact && 0 == (RNOC & mode)) {
659 Rec *dFmt ( Rec *buf, const char *fmt, int db, int rowid )
661 Rec *r = dRead( db, rowid );
665 q = rFmt( buf, fmt, r );
671 Rec *dScan ( int db, int rowid, int tag, const char *txt )
673 int max = dMaxId( db );
674 int tlen = strlen( txt );
677 for ( ; rowid <= max; rowid++ ) {
679 Rec *r = dRead( db, rowid );
683 for ( i=0; i < r->len; i++ ) {
686 || (tag > 0 && tag != r->field[i].tag)
687 || tlen > r->field[i].len
691 e = c + r->field[i].len - tlen;
693 if ( f == *c && !memcmp( c, txt, tlen ) ) {
708 Rec *rSplitf ( Rec *r, const Field* field )
711 int nfields = 0; /* number of subfields */
712 int size; /* byte length of field list */
715 if ( ! field || 2 > field->len )
717 if ( '^' != field->val[0] ) /* initial anonymous subfield */
719 /* go counting hats ... */
720 for ( e = (p=field->val) + field->len - 1; p<e; )
721 if ( '^' == *p++ && '^' != *p ) /* ignore first of ^^ */
724 sMsg( LOG_ERROR, "found trailing '^' in field '%.*s'",
725 (int)field->len, field->val );
730 size = sizeof(Rec) + (r->len - 1) * sizeof(Field);
731 if ( nfields > r->len )
734 /* first field already counted in sizeof(Rec) */
735 size = sizeof(Rec) + (nfields - 1) * sizeof(Field);
736 r = (Rec *)mAlloc( size );
740 memset( r, 0, size );
744 e = (p = field->val) + field->len;
745 if ( '^' == *p ) /* else initial anonymous subfield */
747 for ( i=0; i<nfields; i++ ) {
748 /* p is on hat or end ... skip all consecutive hats */
749 while ( p<e && '^' == *p )
753 "confused cats hound counting hats at %d in '%.*s'",
754 i, field->len, field->val );
758 /* p is after a hat */
759 r->field[ i ].tag = p==field->val ? 0 : (unsigned char)*p++;
760 r->field[ i ].val = p;
762 while ( p<e && '^' != *p )
764 r->field[ i ].len = p - r->field[ i ].val;
765 /* log_msg( LOG_ERROR, "subf %d of %d tag %c off %d",
766 i, nfields, r->field[ i ].tag, r->field[ i ].val - field->val ); */
775 enum { /* stream reading state */
776 RS_REC, /* not in any record */
777 RS_TAG, /* in tag: done is at beginning of line */
778 RS_SEP, /* in sep: done is at beginning of sep */
779 RS_VAL, /* done is somewhere in the value ... */
780 RS_EOL /* not really a state, but a flag */
785 read a record from a stream.
788 <li> read a record and return 1
790 <li> not get enough bytes on a non-blocking stream and return 0
792 <li> have some error and return negative
796 int sGetr ( OpenIsisRecStream *s )
799 int state = 3 & s->flg;
803 && ! (s->rec = s->buf) /* have buffer ? */
804 && ! (s->rec = rDup( 0, 0, 0 ))
805 /* allocate and prepare an 8k standard record */
807 ret = sMsg( LOG_ERROR, "could not get record" );
810 if ( RS_REC == state ) { /* initialize */
812 s->rec->len = 0; /* nuke all fields */
813 s->rec->used = s->rec->base; /* nuke value buffer */
814 /* let base/fields untouched, it hopefully has a reasonable value */
820 unsigned char *b, *p, *e; /* begin, pointer, end */
821 if ( lio->b.done >= lio->b.fill ) {
823 if ( ! LIO_FILL( lio ) )
824 goto done; /* EAGAIN -- see ya later */
825 /* so we have bytes or EOF */
826 if ( lio->b.done >= lio->b.fill ) {
827 if ( LIO_IN & lio->file ) {
828 sMsg( LOG_ERROR, "confused in instream: no bytes and no EOF" );
829 lio_close( &lio->file, LIO_IN );
834 /* so we have bytes */
835 p = b = lio->b.c + lio->b.done;
836 e = lio->b.c + lio->b.fill;
837 /* try to eat one line */
842 if ( LCS_ISFR(lcs_latin1_ct[*p]) ) { /* line starts with line/record sep */
843 if ( '\n' == *p && (RS_EOL & s->flg) ) { /* TODO */
852 if ( LCS_ISWORD(lcs_latin1_ct[*p]) )
853 continue; /* tight loop */
858 sMsg( ERR_TRASH, "tag too long" );
861 /* identify tag between s and p */
863 recognized = !0;/* empty tag */
864 else if ( '0' <= *b && *b <= '9' ) {
865 unsigned char *d = b; /* digit ? */
867 while ( d < p && '0' <= *d && *d <= '9' )
868 tag = 10*tag + *d++ - '0';
869 if ( !(recognized = d == p) ) /* all digits */
872 if ( ! recognized && s->dict ) { /* dict lookup */
873 int i=s->dict->len, l = p-b;
874 Field *f = s->dict->field;
876 if ( l == f->len && memcmp( b, f->val, l ) ) {
881 /* create new field unless it's a continuation line */
882 if ( p == b && s->rec->len )
884 else if ( recognized )
885 RADD( s->rec, tag, 0, 0, s->rec != s->buf );
887 RADD( s->rec, tag, b, p-b, s->rec != s->buf );
891 lio->b.done = (b = p) - lio->b.c;
892 if ( LCS_ISFR(lcs_latin1_ct[*p]) )
896 /* p = b is at beginning of separator */
897 while ( p < e && ' ' == *p ) /* skip leading blanks */
899 if ( p < e && LCS_ISCST(lcs_latin1_ct[*p]) && '\t' != *p++ )
900 while ( p < e && ' ' == *p ) /* skip trailing blanks */
905 sMsg( ERR_TRASH, "sep too long" );
908 assert( s->rec->len );
909 if ( s->rec->field[s->rec->len - 1].len )
910 RCAT( s->rec, b, p-b, s->rec != s->buf );
913 lio->b.done = (b = p) - lio->b.c;
914 if ( LCS_ISFR(lcs_latin1_ct[*p]) )
917 case RS_VAL: default: /* ??? */
918 while ( p < e && !LCS_ISFR(lcs_latin1_ct[*p]) )
920 assert( s->rec->len );
922 RCAT( s->rec, b, p-b, s->rec != s->buf );
925 lio->b.done = (b = p) - lio->b.c;
928 /* else we found line or record separator */
930 if ( '\r' == *p ) { /* do that ole' ugly CR/NL quatsch */
931 if ( e-p < 2 && 0 < LIO_FILL(lio) ) /* extend end */
932 e = lio->b.c + lio->b.fill;
934 s->flg |= RS_EOL; /* possibly check later */
935 else if ( '\n' == p[1] )
938 lio->b.done = 1 + p - lio->b.c; /* consider p eaten */
939 if ( LCS_R == lcs_latin1_ct[*p]
940 || (emptyline && (OPENISIS_STOPONEMPTY & s->flg))
947 if ( s->rec && s->rec->len ) {
948 if ( (OPENISIS_AUTOCLONE & s->flg) && s->rec == s->buf )
949 s->rec = rDup( s->buf, 0, 0 );
955 if ( LIO_SISOPEN( s->in ) )
958 s->flg = state | (~3 & s->flg); /* save state */
959 /* sMsg( LOG_ERROR, "fill %d done %d", lio->b.fill, lio->b.done ); */
961 } /* openIsisReadStream */
964 int rSer ( char *buf, OpenIsisRec *rec )
966 Field *f = rec->field;
971 p += i2a( p, f->tag );
973 memcpy( p, f->val, f->len );
974 for ( e = p + f->len; p<e; )
984 /** binary serialize, turns newline into newline tab
985 buf must have len >= 2*rec->used
987 int rSerB ( char *buf, OpenIsisRec *rec )
989 Field *f = rec->field;
995 p += i2a( p, f->tag );
997 for ( e = (s = f->val) + f->len; s<e; )
998 if ( 10 == (*p++ = *s++) )
1007 char* rSerA (Rec *rec, char *buf, int *len) {
1015 if (*len <= rec->used) {
1016 buf = (char*) mAlloc (1 + rec->used);
1018 sMsg (ERR_NOMEM, "rSerA: cannot malloc %d", (1 + rec->used));
1022 *len = rSer (buf, rec);
1027 int rDeser ( OpenIsisRec **rp, const char *buf, int len, int flg )
1031 const char *p = buf, *e = buf+len;
1033 int dis = RDIS & flg;
1035 /* non-transparent mode - no continuation lines */
1036 for ( ; e > p; p++ ) {
1037 if ( 10 == *p ) { /* blank line */
1038 if ( OPENISIS_STOPONEMPTY & flg )
1042 p += a2il( p, e-p, &tag );
1043 if ( e > p && 9 == *p ) /* TAB */
1045 RADD( rec, tag, 0, e-p, dis || rec!=*rp );
1048 f = rec->field + rec->len-1;
1049 for ( q=(char*)f->val; e>p && 10 != *p; )
1050 if ( 11 == (*q++ = *p++) )
1052 rec->used += f->len = q - f->val;