fixed parsing for science direct html with more than one <a href=> per one <tr>
[webpac] / openisis / lrec.c
1 /*
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
5
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.
10
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.
15
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
19
20         see README for more information
21 EOH */
22
23 /*
24         $Id: lrec.c,v 1.53 2003/05/29 18:03:35 kripke Exp $
25         implementation of record cooking.
26 */
27
28 #include <stddef.h>
29 #include <stdarg.h>
30 #include <stdio.h> /* vsnprintf */
31 #include <stdlib.h> /* free */
32 #include <string.h> /* memset et al */
33
34 #ifdef WIN32
35 #       define vsnprintf _vsnprintf
36 #endif /* WIN32 */
37
38 #include "ldb.h"
39 #include "lio.h"
40 #include "lcs.h"
41
42
43 /* ************************************************************
44         private types
45 */
46
47 typedef struct {
48         int len;      /* length of tagf */
49         int tagf[2];  /* tag and modification flags */
50 } LrecMF;
51
52 #define MF_SET    0x01
53 #define MF_DEL    0x02
54
55 /* ************************************************************
56         private data
57 */
58 /* ************************************************************
59         private functions
60 */
61
62 static LrecMF* mfCtor (Rec *rec) {
63         int     len = (int)(2 * rec->len);
64         int    *M;
65         Field  *F;
66         LrecMF *lmf = (LrecMF*) mAlloc ((1 + len) * sizeof (int));
67         if (0 == lmf) {
68                 return 0;
69         }
70         memset (lmf->tagf, 0, len * sizeof (int));
71         lmf->len = len;
72         for (M = lmf->tagf, F = rec->field; len > M - lmf->tagf; ++F, M += 2) {
73                 *M = (int) F->tag;
74         }
75         return lmf;
76 }
77
78 static int rCompact (Rec *rec) {
79         int rt = 0;
80         if (0 != rec) {
81                 char  *V    = (char*)rec + rec->base;
82                 Field *F    = rec->field;
83                 Field *L    = F + rec->len;
84                 int    used = rec->base;
85                 while (F < L) {
86                         if (V != F->val) {
87                                 rt = !0;
88                                 break;
89                         }
90                         V += F->len;
91                         used += F->len;
92                         ++F;
93                 }
94                 if (rt) {
95                         char *buf = (char*) mAlloc (rec->used - (V - (char*)rec));
96                         char *B   = buf;
97                         int   nb  = 0;
98                         if (0 == buf) {
99                                 return 0;
100                         }
101                         while (F < L) {
102                                 memcpy (B, F->val, F->len);
103                                 F->val = V;
104                                 B += F->len;
105                                 V += F->len;
106                                 nb += F->len;
107                                 ++F;
108                         }
109                         memcpy ((char*)rec + used, buf, nb);
110                         rec->used = used + nb;
111                         mFree (buf);
112                 }
113         }
114         return rt;
115 }
116
117 /**     @return
118                 -2 - malloc failed,
119                 -1 - tag not present,
120                 0 - compactification not neccessary,
121                 1 - rec data buffer has gaps
122 */
123 static int rReplace (
124         Rec **rec, LrecMF *lmf,
125         int tag, int occr, int lmode, const char *val, int disc
126 ) {
127         Rec   *nr;
128         Field *F;
129         int   *M;
130         int    vlen;
131         int    fnd  = -1;
132         int    ocnt = -1;
133         for (M = lmf->tagf; lmf->len > M - lmf->tagf; M += 2) {
134                 if (tag == M[0]) {
135                         if (0 <= occr) {
136                                 if (++ocnt == occr) {
137                                         M[1] = lmode;
138                                         fnd = (M - lmf->tagf) / 2;
139                                         break;
140                                 }
141                         }
142                         else {
143                                 if (0 == (MF_SET & M[1])) {
144                                         M[1] = lmode;
145                                         fnd = (M - lmf->tagf) / 2;
146                                         if (lmode) {
147                                                 for (M += 2; lmf->len > M - lmf->tagf; M += 2) {
148                                                         if (tag == M[0]) {
149                                                                 M[1] = MF_DEL;
150                                                         }
151                                                 }
152                                         }
153                                         break;
154                                 }
155                         }
156                 }
157         }
158         if (0 > fnd) {
159                 return -1;
160         }
161         if (MF_SET != lmode) {
162                 return 1;
163         }
164         F = (*rec)->field + fnd;
165         vlen = strlen (val);
166         if (F->len == vlen) {
167                 memcpy ((char*)F->val, val, vlen);
168                 return 0;
169         }
170         if (F->len > vlen) {
171                 memcpy ((char*)F->val, val, vlen);
172                 F->len = vlen;
173                 return 1;
174         }
175         if ((*rec)->bytes < (*rec)->used + vlen) {
176                 nr = rDup (*rec, vlen, disc);
177                 if (0 == nr) {
178                         return -2;
179                 }
180                 F = (*rec = nr)->field + fnd;
181         }
182         F->val = (char*)(*rec) + (*rec)->used;
183         F->len = vlen;
184         memcpy ((char*)F->val, val, vlen);
185         (*rec)->used += vlen;
186         return 1;
187 }
188
189 static int rDel (Rec *rec, LrecMF *lmf) {
190         Field  *F;
191         int    *M;
192         int     idx;
193         int     dcnt = 0;
194         int     done = 0;
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));
201                         }
202                         --(rec->len);
203                         ++dcnt;
204                         done = !0;
205                 }
206         }
207         return done;
208 }
209
210 /* ************************************************************
211         package functions
212 */
213
214 /* ************************************************************
215         public functions
216 */
217
218
219 Field *rGet ( Rec *r, int tag, int *pos )
220 {
221         if (r) {
222                 int p = pos ? *pos : 0;
223                 for ( ; p < r->len; p++ )
224                         if ( tag == r->field[p].tag ) {
225                                 if ( pos )
226                                         *pos = p+1;
227                                 return &r->field[p];
228                         }
229                 if ( pos )
230                         *pos = r->len;
231         }
232         return 0;
233 }       /* rGet */
234
235 Field *rOccurence ( Rec *r, int tag, int occ ) {
236         Field *F;
237         int    pos = 0;
238         do {
239                 F = rGet (r, tag, &pos);
240         } while (F && 0 <= --occ);
241         return F;
242 }
243
244 char *rString (Rec *rec, int tag, int *pos, char *buf, int len) {
245         Field *F = rGet (rec, tag, pos);
246         if (!F) {
247                 return 0;
248         }
249         if (len > 1 + F->len) {
250                 len = 1 + (int) F->len;
251         }
252         if (0 >= --len) {
253                 *buf = 0;
254                 return buf;
255         }
256         strncpy (buf, F->val, len) [len] = 0;
257         return buf;
258 }
259
260
261 char *rString2 (Rec *rec, Rec *dflt, int tag, char *buf, int len) {
262         char *rt = rString (rec, tag, 0, buf, len);
263         if (rt) {
264                 return rt;
265         }
266         return rString (dflt, tag, 0, buf, len);
267 }
268
269
270 int rInt ( Rec *r, int tag, int def, int *pos )
271 {
272         if (r) {
273                 int p = pos ? *pos : 0;
274                 int rt;
275                 for ( ; p < r->len; p++ )
276                         if ( tag == r->field[p].tag ) {
277                                 if ( pos )
278                                         *pos = p+1;
279                                 rt = a2i( r->field[p].val, r->field[p].len );
280                                 if (rt) {
281                                         return rt;
282                                 }
283                                 if (0 < luti_true (r->field[p].val, r->field[p].len)) {
284                                         return 1;
285                                 }
286                                 return 0;
287                         }
288                 if ( pos )
289                         *pos = r->len;
290         }
291         return def;
292 }       /* rInt */
293
294
295 int rInt2 (Rec *rec, Rec *dflt, int tag, int def) {
296         int rt1 = rInt (rec, tag, def, 0);
297         if (def == rt1) {
298                 return rInt (dflt, tag, def, 0);
299         }
300         return rt1;
301 }
302
303
304 int rEnum (Fdt *fdt, Rec *rec, int tag, int def, int *pos) {
305         char    buf[OPENISIS_FD_NAMELEN];
306         Field  *F;
307         Fd     *D;
308         int     len, ev;
309         if (! fdt || ! rec) {
310                 return def;
311         }
312         D = fById (fdt, tag, 0);
313         if (! D) {
314                 return def;
315         }
316         F = rGet (rec, tag, pos);
317         if (! F) {
318                 return def;
319         }
320         len = (int) F->len;
321         if (OPENISIS_FD_NAMELEN <= len) {
322                 len = OPENISIS_FD_NAMELEN - 1;
323         }
324         strncpy (buf, F->val, len) [len] = 0;
325         ev = fEnum (fdt, tag, buf);
326         if (NOENUM == ev) {
327                 return def;
328         }
329         return ev;
330 }
331
332
333 Rec* rDup ( Rec *r, int room, int discard )
334 {
335         Rec *nr = 0;
336         int nfields, nbytes, hadfields, hadcontent;
337         if ( ! r ) {
338                 nfields = 80; /* for a 1K base */
339                 nbytes = 8*1024;
340                 if ( nbytes < BASESZ(80) + room*3/2 )
341                         nbytes = BASESZ(80) + room*3/2;
342                 hadfields = 0;
343                 hadcontent = 0;
344         } else {
345                 rCompact (r);
346                 hadfields = r->len;
347                 hadcontent = r->used - r->base;
348                 if ( 0 > room ) { /* shrink to fit */
349                         nfields = r->len;
350                         nbytes = BASESZ(nfields) + hadcontent;
351                 } else {
352                         nfields = 54 > r->len ? 80 : (r->len * 3 / 2); /* add 50% */
353                         if ( nfields < r->fields )
354                                 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);
359                 }
360         }
361         LOG_DBG( LOG_DEBUG,
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 )) )
366                 return 0;
367         memset( nr, 0, nbytes ); /* paranoia */
368         nr->bytes = nbytes;
369         nr->fields = nfields;
370         nr->base = BASESZ( nfields );
371         nr->used = nr->base + hadcontent;
372         nr->len = hadfields;
373         if ( ! r ) {
374                 nr->dbid = -1; /* no valid dbid if new rec */
375         }
376         else {
377                 char *obuf = ((char*)r) + r->base;
378                 char *nbuf = ((char*)nr) + nr->base;
379                 nr->dbid = r->dbid;
380                 /* for ! r, the following are 0 by memset */
381                 nr->rowid = r->rowid;
382                 nr->state = r->state;
383                 if ( hadcontent )
384                         memcpy( nbuf, obuf, hadcontent );
385                 if ( hadfields ) {
386                         int i;
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--; )
391                                 if ( f[i].val )
392                                         f[i].val += displace;
393 #ifndef NDEBUG
394                         {
395                                 char *end = ((char*)nr) + nr->used;
396                                 for ( i=hadfields; i--; )
397                                         if ( f[i].val
398                                                 && (f[i].val < nbuf || f[i].val + f[i].len > end)
399                                         ) {
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 */
405                                         }
406                         }
407 #endif
408                 }
409                 if ( discard )
410                         free( r );
411         }
412         assert( RECOK(nr) );
413         return nr;
414 }       /* rDup */
415
416
417 Rec* rMsg ( Rec *r, int discard, int tag, const char *fmt, ... )
418 {
419         char buf[1024];
420         int l;
421         va_list ap;
422         va_start( ap, fmt );
423         l = vsnprintf( buf, sizeof(buf), fmt, ap );
424         if ( 0 > l ) /* older versions return -1 */
425                 l = sizeof(buf);
426         va_end( ap );
427         RADD( r, tag, buf, l, discard );
428         return r;
429 }       /* rMsg */
430
431 /*
432 #define openIsisPrintf( r, d, t, f, a... ) \
433         openIsisRMsg( OPENISIS_SES0(), r, d, t, f, ## a )
434         requires gcc ...
435 */
436 Rec* openIsisPrintf ( Rec *r, int discard, int tag, const char *fmt, ... )
437 {
438         char buf[1024];
439         int l;
440         va_list ap;
441         va_start( ap, fmt );
442         l = vsnprintf( buf, sizeof(buf), fmt, ap );
443         if ( 0 > l ) /* older versions return -1 */
444                 l = sizeof(buf);
445         va_end( ap );
446         RADD( r, tag, buf, l, discard );
447         return r;
448 }       /* openIsisPrintf */
449
450
451 Rec *rAddI (Rec *rec, int tag, int val, int discard) {
452         char  buf[32];
453         int   len = i2a (buf, (int)val);
454         RADD (rec, tag, buf, len, discard);
455         return rec;
456 }
457
458
459 Rec* rSet ( Rec *oldr, int mode, ... )
460 {
461         const char **argv = 0;
462         LrecMF *lmf       = 0;
463         Fdt    *fdt       = 0;
464         Rec    *newr      = oldr;
465         int     argc      = 0xffff & mode;
466         int     dis       = RDIS & mode;
467         int     op        = ROP & mode;
468         int     lmode     = MF_SET;
469         int     compact   = 0;
470
471         va_list ap;
472         va_start( ap, mode );
473
474         if (RFDT & mode) {
475                 fdt = va_arg( ap, Fdt* );
476         }
477         if (RARGV & mode) {
478                 argv = va_arg( ap, const char** );
479         }
480         if (! fdt) {
481                 fdt = luti_fdt_from_rec (oldr);
482         }
483         if (op) {
484                 switch (op) {
485                 case RDEL:
486                         lmode = MF_DEL;
487                         break;
488                 case RCHG:
489                         break;
490                 case RDFLT:
491                         lmode = 0;
492                         break;
493                 default:
494                         sMsg (ERR_IDIOT, "rSet: illegal mode %x", op);
495                         return newr;
496                 }
497                 if (oldr) {
498                         if (0 == (lmf = mfCtor (oldr))) {
499                                 goto done;
500                         }
501                 }
502                 else if (RDEL == op) {
503                         goto done;
504                 }
505         }
506
507         for ( ;; ) {
508                 char   ib[16];
509                 Rec   *tmpr;
510                 const char *v = 0;
511                 int    id   = -1;
512                 int    occr = -1;
513                 Fd    *fd   = 0;
514                 int    rpl;
515                 if ( argv ) {
516                         if ( 1 > argc )
517                                 break;
518                         v = luti_parse_path (*argv, fdt, &fd, &id, &occr);
519                         if (!v || *v) {
520                                 fd = 0;
521                                 id = -1;
522                         }
523                         v = 0;
524                         ++argv;
525                         --argc;
526                 }
527                 else {
528                         id = va_arg( ap, int );
529                         if ( ! id )
530                                 break;
531                         fd = fdt ? fById( fdt, id, 0 ) : 0;
532                         if (RDEL != op) {
533                                 v = va_arg( ap, char* );
534                         }
535                 }
536                 if ( fd ) {
537                         if (RDEL != op) {
538                                 int e;
539                                 if (argv) {
540                                         if ( 1 > argc ) {
541                                                 if (FTB != fd->type) {
542                                                         break;
543                                                 }
544                                         }
545                                         else {
546                                                 v = *argv++;
547                                                 --argc;
548                                         }
549                                 }
550                                 switch ( fd->type ) {
551                                 case FTE:
552                                         e = fEnum( fdt, fd->id, v );
553                                         if ( NOENUM == e ) {
554                                                 if ( RIGN & mode )
555                                                         continue;
556                                                 sMsg( LOG_ERROR,
557                                                         "bad enum value '%s' for id %d", v, fd->id );
558                                                 goto error;
559                                         }
560                                         i2a( ib, e );
561                                         v = ib;
562                                         break;
563                                 case FTB:
564                                         e = v ? luti_true (v, -1) : 1;
565                                         if (0 > e) {
566                                                 /* non-given values default to true */
567                                                 ++argc;
568                                                 --argv;
569                                                 v = "1";
570                                         }
571                                         else {
572                                                 v = e ? "1" : "0";
573                                         }
574                                         break;
575                                 }
576                         } /* RDEL != op */
577                 } /* fd */
578                 else {
579                         if (argv) {
580                                 if (RDEL != op) {
581                                         if ( 1 > argc ) {
582                                                 break;
583                                         }
584                                         v = *argv++;
585                                         --argc;
586                                 }
587                         }
588                         if (fdt /*|| 0 >= id*/) {
589                                 if ( RIGN & mode )
590                                         continue;
591                                 if ( argv )
592                                         sMsg( LOG_ERROR, "unknown field name '%s'",
593                                                 argv[RDEL == op ? -1 : -2] );
594                                 else
595                                         sMsg( LOG_ERROR, "unknown field id %d", id );
596                                 break;
597                         }
598                 }
599                 if (!v) {
600                         v = "";
601                 }
602                 if (op) {
603                         rpl = lmf ? rReplace (
604                                 &newr, lmf, id, occr, lmode, v, dis || newr != oldr) :
605                                 -1;
606                         if (RDFLT == op) {
607                                 if (-1 != rpl) {
608                                         continue;
609                                 }
610                         }
611                         else {
612                                 if (-2 == rpl) {
613                                         goto done;
614                                 }
615                                 if (-1 != rpl) {
616                                         if (1 == rpl) {
617                                                 compact = !0;
618                                         }
619                                         continue;
620                                 }
621                                 if (RDEL == op) {
622                                         continue;
623                                 }
624                         }
625                         /* fall thru */
626                 }
627                 tmpr = newr;
628                 RADDS( newr, id, v, dis || newr != oldr );
629                 if (0 == newr) {
630                         goto done;
631                 }
632                 if (newr != tmpr) {
633                         compact = 0;
634                 }
635                 sMsg( LOG_VERBOSE,
636                         "added v '%s' id %d as %dth", v, id, newr->len );
637         }
638
639 error:
640         if (lmf) {
641                 if (rDel (newr, lmf)) {
642                         compact = !0;
643                 }
644         }
645
646         if (compact && 0 == (RNOC & mode)) {
647                 rCompact (newr);
648         }
649
650 done:
651         if (lmf) {
652                 mFree (lmf);
653         }
654         va_end( ap );
655         return newr;
656 }       /* rSet */
657
658
659 Rec *dFmt ( Rec *buf, const char *fmt, int db, int rowid )
660 {
661         Rec *r = dRead( db, rowid );
662         Rec *q;
663         if ( ! r )
664                 return 0;
665         q = rFmt( buf, fmt, r );
666         free( r );
667         return q;
668 }       /* dFmt */
669
670
671 Rec *dScan ( int db, int rowid, int tag, const char *txt )
672 {
673         int max = dMaxId( db );
674         int tlen = strlen( txt );
675         const char f = *txt;
676
677         for ( ; rowid <= max; rowid++ ) {
678                 int i, found = 0;
679                 Rec *r = dRead( db, rowid );
680                 if ( ! r )
681                         continue;
682
683                 for ( i=0; i < r->len; i++ ) {
684                         const char *c, *e;
685                         if ( found
686                                 || (tag > 0 && tag != r->field[i].tag)
687                                 || tlen > r->field[i].len
688                         )
689                                 continue;
690                         c = r->field[i].val;
691                         e = c + r->field[i].len - tlen;
692                         for ( ; c<=e; c++ )
693                                 if ( f == *c && !memcmp( c, txt, tlen ) ) {
694                                         found = !0;
695                                         break;
696                                 }
697                 }
698                 if ( found )
699                         return r;
700         }
701         return 0;
702 }       /* dScan */
703
704
705
706
707
708 Rec *rSplitf ( Rec *r, const Field* field )
709 {
710         int i;
711         int nfields = 0; /* number of subfields */
712         int size; /* byte length of field list */
713         const char *p, *e;
714
715         if ( ! field || 2 > field->len )
716                 return 0;
717         if ( '^' != field->val[0] ) /* initial anonymous subfield */
718                 nfields = 1;
719         /* go counting hats ... */
720         for ( e = (p=field->val) + field->len - 1; p<e; )
721                 if ( '^' == *p++ && '^' != *p ) /* ignore first of ^^ */
722                         nfields++;
723         if ( '^' == *p )
724                 sMsg( LOG_ERROR, "found trailing '^' in field '%.*s'",
725                         (int)field->len, field->val );
726
727         if ( r ) {
728                 if ( 1 > r->len )
729                         return 0;
730                 size = sizeof(Rec) + (r->len - 1) * sizeof(Field);
731                 if ( nfields > r->len )
732                         nfields = r->len;
733         } else {
734                 /* first field already counted in sizeof(Rec) */
735                 size = sizeof(Rec) + (nfields - 1) * sizeof(Field);
736                 r = (Rec *)mAlloc( size );
737                 if ( ! r )
738                         return 0;
739         }
740         memset( r, 0, size );
741         r->rowid = 0;
742         r->len   = nfields;
743
744         e = (p = field->val) + field->len;
745         if ( '^' == *p ) /* else initial anonymous subfield */
746                 p++;
747         for ( i=0; i<nfields; i++ ) {
748                 /* p is on hat or end ... skip all consecutive hats */
749                 while ( p<e && '^' == *p )
750                         p++;
751                 if ( p >= e ) {
752                         sMsg( LOG_ERROR,
753                                 "confused cats hound counting hats at %d in '%.*s'",
754                                 i, field->len, field->val );
755                         r->len = i;
756                         break;
757                 }
758                 /* p is after a hat */
759                 r->field[ i ].tag = p==field->val ? 0 : (unsigned char)*p++;
760                 r->field[ i ].val = p;
761                 /* advance in buf */
762                 while ( p<e && '^' != *p )
763                         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 ); */
767         }
768
769         return r;
770 }       /* rSplitf */
771
772
773
774
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 */
781 };
782
783
784 /**
785         read a record from a stream.
786         This may either
787         <ol>
788         <li> read a record and return 1
789         </li>
790         <li> not get enough bytes on a non-blocking stream and return 0
791         </li>
792         <li> have some error and return negative
793         </li>
794         </ol>
795 */
796 int sGetr ( OpenIsisRecStream *s )
797 {
798         Ios *lio = s->in;
799         int state = 3 & s->flg;
800         int emptyline = 0;
801         int ret = 0;
802         if ( ! s->rec
803                 && ! (s->rec = s->buf) /* have buffer ? */
804                 && ! (s->rec = rDup( 0, 0, 0 ))
805                 /* allocate and prepare an 8k standard record */
806         ) {
807                 ret = sMsg( LOG_ERROR, "could not get record" );
808                 goto fatal;
809         }
810         if ( RS_REC == state ) { /* initialize */
811                 s->rec->rowid = 0;
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 */
815                 state = RS_TAG;
816         }
817         if ( ! s->rec->len )
818                 state = RS_TAG;
819         for (;;) {
820                 unsigned char *b, *p, *e; /* begin, pointer, end */
821                 if ( lio->b.done >= lio->b.fill ) {
822         reload:
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 );
830                                 }
831                                 break;
832                         }
833                 }
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 */
838                 switch ( state ) {
839                 case RS_TAG: {
840                         int tag = -1;
841                         int recognized = 0;
842                         if ( LCS_ISFR(lcs_latin1_ct[*p]) ) { /* line starts with line/record sep */
843                                 if ( '\n' == *p && (RS_EOL & s->flg) ) { /* TODO */
844                                         s->flg &= ~RS_EOL;
845                                         continue;
846                                 }
847                                 emptyline = !0;
848                                 break;
849                         }
850                         for (;;p++) {
851                                 if ( p < e ) {
852                                         if ( LCS_ISWORD(lcs_latin1_ct[*p]) )
853                                                 continue; /* tight loop */
854                                         break;
855                                 }
856                                 if ( 4096 > p - b )
857                                         goto reload;
858                                 sMsg( ERR_TRASH, "tag too long" );
859                                 goto fatal;
860                         }
861                         /* identify tag between s and p */
862                         if ( p == b )
863                                 recognized = !0;/* empty tag */
864                         else if ( '0' <= *b && *b <= '9' ) {
865                                 unsigned char *d = b; /* digit ? */
866                                 tag = 0;
867                                 while ( d < p && '0' <= *d && *d <= '9' )
868                                         tag = 10*tag + *d++ - '0';
869                                 if ( !(recognized = d == p) ) /* all digits */
870                                         tag = -1;
871                         }
872                         if ( ! recognized && s->dict ) { /* dict lookup */
873                                 int i=s->dict->len, l = p-b;
874                                 Field *f = s->dict->field;
875                                 for ( ; i--; f++ )
876                                         if ( l == f->len && memcmp( b, f->val, l ) ) {
877                                                 tag = f->tag;
878                                                 break;
879                                         }
880                         }
881                         /* create new field unless it's a continuation line */
882                         if ( p == b && s->rec->len )
883                                 ;
884                         else if ( recognized )
885                                 RADD( s->rec, tag, 0, 0, s->rec != s->buf );
886                         else
887                                 RADD( s->rec, tag, b, p-b, s->rec != s->buf );
888                         if ( ! s->rec )
889                                 goto outamem;
890                         /* did that */
891                         lio->b.done = (b = p) - lio->b.c;
892                         if ( LCS_ISFR(lcs_latin1_ct[*p]) )
893                                 break;
894                         state = RS_SEP;
895                 } case RS_SEP:
896                         /* p = b is at beginning of separator */
897                         while ( p < e && ' ' == *p ) /* skip leading blanks */
898                                 p++;
899                         if ( p < e && LCS_ISCST(lcs_latin1_ct[*p]) && '\t' != *p++ )
900                                 while ( p < e && ' ' == *p ) /* skip trailing blanks */
901                                         p++;
902                         if ( e == p ) {
903                                 if ( 4096 > p - b )
904                                         goto reload;
905                                 sMsg( ERR_TRASH, "sep too long" );
906                                 goto fatal;
907                         }
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 );
911                         if ( ! s->rec )
912                                 goto outamem;
913                         lio->b.done = (b = p) - lio->b.c;
914                         if ( LCS_ISFR(lcs_latin1_ct[*p]) )
915                                 break;
916                         state = RS_VAL;
917                 case RS_VAL: default: /* ??? */
918                         while ( p < e && !LCS_ISFR(lcs_latin1_ct[*p]) )
919                                 p++;
920                         assert( s->rec->len );
921                         if ( p > b )
922                                 RCAT( s->rec, b, p-b, s->rec != s->buf );
923                         if ( ! s->rec )
924                                 goto outamem;
925                         lio->b.done = (b = p) - lio->b.c;
926                         if ( p == e )
927                                 goto reload;
928                         /* else we found line or record separator */
929                 } /* switch state */
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;
933                         if ( e-p < 2 )
934                                 s->flg |= RS_EOL; /* possibly check later */
935                         else if ( '\n' == p[1] )
936                                 p++;
937                 }
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))
941                 )
942                         break;
943                 state = RS_TAG;
944         }
945         /* got record */
946         state = RS_REC;
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 );
950                 ret = 1;
951         }
952         goto done;
953 outamem:
954 fatal:
955         if ( LIO_SISOPEN( s->in ) )
956                 LIO_CLOSE( s->in );
957 done:
958         s->flg = state | (~3 & s->flg); /* save state */
959         /* sMsg( LOG_ERROR, "fill %d done %d", lio->b.fill, lio->b.done ); */
960         return ret;
961 }       /* openIsisReadStream */
962
963
964 int rSer ( char *buf, OpenIsisRec *rec )
965 {
966         Field *f = rec->field;
967         int i = rec->len;
968         char *p = buf, *e;
969
970         for ( ; i--; f++ ) {
971                 p += i2a( p, f->tag );
972                 *p++ = 9; /* TAB */
973                 memcpy( p, f->val, f->len );
974                 for ( e = p + f->len; p<e; )
975                         if ( 10 == *p++ )
976                                 p[-1] = 11;
977                 *p++ = 10;
978         }
979         *p++ = 10;
980         return p - buf;
981 }       /* rSer */
982
983
984 /** binary serialize, turns newline into newline tab
985         buf must have len >= 2*rec->used
986 */
987 int rSerB ( char *buf, OpenIsisRec *rec )
988 {
989         Field *f = rec->field;
990         int i = rec->len;
991         char *p = buf;
992         const char *s, *e;
993
994         for ( ; i--; f++ ) {
995                 p += i2a( p, f->tag );
996                 *p++ = 9; /* TAB */
997                 for ( e = (s = f->val) + f->len; s<e; )
998                         if ( 10 == (*p++ = *s++) )
999                                 *p++ = 9;
1000                 *p++ = 10;
1001         }
1002         *p++ = 10;
1003         return p - buf;
1004 }       /* rSerB */
1005
1006
1007 char* rSerA (Rec *rec, char *buf, int *len) {
1008         if (!len) {
1009                 return 0;
1010         }
1011         if (!rec) {
1012                 *len = 0;
1013                 return buf;
1014         }
1015         if (*len <= rec->used) {
1016                 buf = (char*) mAlloc (1 + rec->used);
1017                 if (!buf) {
1018                         sMsg (ERR_NOMEM, "rSerA: cannot malloc %d", (1 + rec->used));
1019                         return 0;
1020                 }
1021         }
1022         *len = rSer (buf, rec);
1023         return buf;
1024 }
1025
1026
1027 int rDeser ( OpenIsisRec **rp, const char *buf, int len, int flg )
1028 {
1029         Rec *rec = *rp;
1030         Field *f;
1031         const char *p = buf, *e = buf+len;
1032         char *q;
1033         int dis = RDIS & flg;
1034         int tag;
1035         /* non-transparent mode - no continuation lines */
1036         for ( ; e > p; p++ ) {
1037                 if ( 10 == *p ) { /* blank line */
1038                         if ( OPENISIS_STOPONEMPTY & flg )
1039                                 break;
1040                         continue;
1041                 }
1042                 p += a2il( p, e-p, &tag );
1043                 if ( e > p && 9 == *p ) /* TAB */
1044                         p++;
1045                 RADD( rec, tag, 0, e-p, dis || rec!=*rp );
1046                 if ( !rec )
1047                         break;
1048                 f = rec->field + rec->len-1;
1049                 for ( q=(char*)f->val; e>p && 10 != *p; )
1050                         if ( 11 == (*q++ = *p++) )
1051                                 q[-1] = 10;
1052                 rec->used += f->len = q - f->val;
1053         }
1054         *rp = rec;
1055         return p-buf;
1056 }       /* rDeser */