fixed parsing for science direct html with more than one <a href=> per one <tr>
[webpac] / openisis / luti.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: luti.c,v 1.48 2003/06/23 14:47:35 kripke Exp $
25         utilities
26 */
27
28 #include <string.h> /* strlen */
29
30 #include "openisis.h"
31 #include "luti.h"
32
33
34 /* ************************************************************
35         private types
36 */
37
38 typedef struct {
39         char *name;
40         int   id;
41 } LutiLTEnt;
42
43 typedef struct OpenIsisLT {
44         LutiLTEnt *arr;
45         int        siz; /* malloc'ed */
46         int        num; /* used */
47 } LutiLTNod;
48
49 #define LT_NUM   27
50 #define LT_SIZE (LT_NUM * sizeof (LutiLTNod))
51
52 #define LTIDX(name) (  \
53         ('a' <= *(name) && 'z' >= *(name)) ? *(name) - 'a' :  ( \
54         ('A' <= *(name) && 'Z' >= *(name)) ? *(name) - 'A' : 26 \
55 ))
56
57 #define LT_INCR 2
58
59 /* ************************************************************
60         private data
61 */
62
63
64
65 /* ************************************************************
66         private functions
67 */
68
69
70 /* ************************************************************
71         package data
72 */
73 const char luti_hex[16] = {
74         '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'
75 };
76
77 /* ************************************************************
78         package functions
79 */
80
81 void log_rec (
82         int level, Rec *rec, const char *msg, const char *delim
83 ) {
84         Field *F;
85         int ch = 2;
86         if (0 > level) {
87                 level = -level;
88                 ch = 1;
89         }
90         if ((unsigned)level > log_lev) {
91                 return;
92         }
93         if (msg && *msg) {
94                 sMsg (ch, msg);
95         }
96         sMsg (ch, "%p", rec);
97         if (rec) {
98                 if (! delim) {
99                         delim = "; ";
100                 }
101                 sMsg (ch, " #f=%d", (int)rec->len);
102                 for (F = rec->field; rec->len > F - rec->field; ++F) {
103                         sMsg (ch, delim);
104                         sMsg (ch, "%d", (int)F->tag);
105                         sMsg (ch, " %.*s", (int)F->len, F->val);
106                 }
107         }
108         sMsg (ch, "\n");
109 }
110
111
112 int lprint ( void *buf, int i )
113 {
114         char *c = buf;
115         if ( 0 > i ) {
116                 *c++ = '-';
117                 i = -i;
118         }
119         if ( ! i )
120                 *c++ = '0';
121         else {
122                 int dig = 0, j=i;
123                 char *p;
124                 for ( ; j; j /= 10 )
125                         dig++;
126                 p = c += dig;
127                 for ( ; i; i /= 10 )
128                         *--p = '0' + i % 10;
129         }
130         *c = 0;
131         return c - (char*)buf;
132 }       /* lprint */
133
134
135 int a2il ( const char *p, int l, int *res )
136 {
137         const char *s = p;
138         const char *e = p + (0<=l ? l : (int)strlen(p));
139         char op = 0;
140         int ret = 0;
141         while ( p < e && ' ' >= (unsigned char)*p ) /* skip white */
142                 p++;
143         if ( p < e )
144                 switch ( *p ) {
145                 case '-':
146                         if ( p+1 == e ) ret = 1; /* so sole '-' is -1 */
147                 case '~': /* sole ~ is ~0 */
148                 case '+':
149                         op = *p++;
150                 }
151         if ( p < e && '0' == *p && ++p < e && 'x' == *p ) {
152                 while ( ++p < e ) {
153                         int v = *p;
154                         if ( '0' <= v && v <= '9' )
155                                 v -= '0';
156                         else if ( 'a' <= v && v <= 'f' )
157                                 v -= 'a'-10;
158                         else if ( 'A' <= v && v <= 'F' )
159                                 v -= 'A'-10;
160                         else
161                                 break;
162                         ret = (ret<<4) + v;
163                 }
164         } else {
165                 while ( p < e && '0' <= *p && *p <= '9' )
166                         ret = 10*ret + *p++ - '0';
167         }
168         if (0 != res)
169                 switch (op) {
170                 case '-': *res = -ret; break;
171                 case '~': *res = ~ret; break;
172                 default: *res = ret;
173                 }
174         return p - s;
175 }       /* a2il */
176
177 int a2i ( const char *p, int l ) {
178         int res;
179         a2il (p, l, &res);
180         return res;
181 }
182
183 int a2id ( const char *p, int l, int dflt ) {
184         int res;
185         if (0 > l) {
186                 l = (int) strlen (p);
187         }
188         if (l != a2il (p, l, &res)) {
189                 return dflt;
190         }
191         return res;
192 }
193
194
195 int i2a ( char *p, int i )
196 {
197         if ( i ) {
198                 int min, dig = 0, j;
199                 if ( 0 < i ) 
200                         min = 0;
201                 else {
202                         min = 1;
203                         *p++ = '-';
204                         i = -i;
205                 }
206                 for ( j=i; j; j/=10 )
207                         dig++;
208                 for ( *(p+=dig)=0; i; i/=10 )
209                         *--p = '0' + (i % 10);
210                 return min+dig;
211         }
212         *p++ = '0';
213         *p = 0;
214         return 1;
215 }       /* i2a */
216
217
218 int u2a ( char *p, unsigned u )
219 {
220         if ( u ) {
221                 int dig = 0, j;
222                 for ( j=u; j; j/=10 )
223                         dig++;
224                 for ( *(p+=dig)=0; u; u/=10 )
225                         *--p = '0' + (u % 10);
226                 return dig;
227         }
228         *p++ = '0';
229         *p = 0;
230         return 1;
231 }       /* u2a */
232
233
234 LutiLT luti_ltnew () {
235         LutiLT res = (LutiLT) mAlloc (LT_SIZE);
236         return res;
237 }
238
239 void luti_ltdel (LutiLT lt) {
240         LutiLTNod *N;
241         LutiLTEnt *E;
242         if (lt) {
243                 for (N = lt + LT_NUM; --N >= lt;  ) {
244                         if (N->arr) {
245                                 for (E = N->arr + N->num; --E >= N->arr;  ) {
246                                         if (E->name) {
247                                                 mFree (E->name);
248                                         }
249                                 }
250                                 mFree (N->arr);
251                         }
252                 }
253                 mFree (lt);
254         }
255 }
256
257 void luti_ltadd (LutiLT lt, const char *name, int id) {
258         if (0 != lt) {
259                 LutiLTNod *N;
260                 LutiLTEnt *E;
261                 int idx;
262                 if (!name) {
263                         name = "";
264                 }
265                 idx = LTIDX (name);
266                 N = lt + idx;
267                 if (N->siz == N->num) {
268                         E = mAlloc ( (LT_INCR + N->siz) * sizeof (LutiLTEnt));
269                         if (N->siz) {
270                                 memcpy (E, N->arr, N->siz * sizeof (LutiLTEnt));
271                                 mFree (N->arr);
272                         }
273                         N->arr = E;
274                         N->siz += LT_INCR;
275                 }
276                 N->arr[N->num].name = mDup (name, -1);
277                 N->arr[N->num].id = id;
278                 ++N->num;
279         }
280 }
281
282 int luti_ltget (LutiLT lt, const char *name) {
283         if (0 != lt) {
284                 LutiLTNod *N;
285                 LutiLTEnt *E;
286                 int idx;
287                 if (!name) {
288                         name = "";
289                 }
290                 idx = LTIDX (name);
291                 N = lt + idx;
292                 if (N->num) {
293                         for (E = N->arr + N->num; --E >= N->arr; ) {
294                                 if (! strcmp (E->name, name)) {
295                                         return E->id;
296                                 }
297                         }
298                 }
299         }
300         return -1;
301 }
302
303 void luti_ltrmv (LutiLT lt, const char *name) {
304         if (0 != lt) {
305                 LutiLTNod *N;
306                 LutiLTEnt *E;
307                 int idx;
308                 if (!name) {
309                         name = "";
310                 }
311                 idx = LTIDX (name);
312                 N = lt + idx;
313                 if (N->num) {
314                         for (E = N->arr + N->num; --E >= N->arr; ) {
315                                 if (! strcmp (E->name, name)) {
316                                         int len = N->num - (E - N->arr) - 1;
317                                         mFree (E->name);
318                                         if (len) {
319                                                 memmove (E, E + 1, len * sizeof (LutiLTEnt));
320                                         }
321                                         --N->num;
322                                         return;
323                                 }
324                         }
325                 }
326                 log_msg (LOG_WARN, "luti_ltrmv: no such entry <%s>", name);
327         }
328 }
329
330 static Rec *_unwrap ( Rec *env, int *pos, int tag, int dbid, int rdonly )
331 {
332         Field *F;
333         Rec   *rec = 0;
334         int    rlen, np;
335         if (! env ) {
336                 return 0;
337         }
338         if (! pos) {
339                 np = 0;
340                 pos = &np;
341         }
342         F = rGet (env, tag, pos);
343         if (!F) {
344                 return 0;
345         }
346         rlen = a2id (F->val, (int)F->len, -1);
347         if (0 > rlen) {
348                 sMsg (LOG_ERROR,
349                         "luti_unwrap: illegal reclen %d, pos %d, tag %d",
350                         rlen, *pos, F->tag);
351                 return 0;
352         }
353         ++F;
354         if (env->len < *pos + rlen) {
355                 sMsg (LOG_ERROR,
356                         "luti_unwrap: illegal reclen %d, pos %d, tag %d, env %d",
357                         rlen, *pos, F->tag, env->len);
358                 return 0;
359         }
360         if (! rlen && ! rdonly) {
361                 OPENISIS_RSPACE (rec, 0, 0);
362                 if (0 == rec) {
363                         return 0;
364                 }
365         }
366         while (0 <= --rlen) {
367                 if (! rdonly) {
368                         RADD (rec, F->tag, F->val, F->len, !0);
369                         if (0 == rec) {
370                                 return 0;
371                         }
372                 }
373                 ++F;
374                 ++(*pos);
375         }
376         if (rdonly) {
377                 return (Rec*)1;
378         }
379         rec->dbid = dbid;
380         return rec;
381 }
382
383 Rec *luti_unwrap ( Rec *env, int *pos, int tag, int dbid)
384 {
385         return _unwrap (env, pos, tag, dbid, 0);
386 }
387
388 Rec* luti_append (Rec *tgt, Rec *src) {
389         Field *F, *E;
390         if (!src) {
391                 return tgt;
392         }
393         E = src->field + src->len;
394         for (F = src->field; tgt && E > F; ++F) {
395                 RADD (tgt, F->tag, F->val, F->len, !0);
396         }
397         return tgt;
398 }
399
400 Rec *luti_wrap (Rec *env, Rec *rec, int tag) {
401         if (!rec) {
402                 return env;
403         }
404         env = rAddI (env, tag, rec->len, !0);
405         return luti_append (env, rec);
406 }
407
408 int luti_ptrincr (
409         void *start, int *num, int incr, int siz, int maxn
410 ) {
411         char **base = (char**)start;
412         char  *arr;
413         int    oldn = *num;
414         if (0 < maxn && oldn >= maxn) {
415                 return -1;
416         }
417         arr = mAlloc ((oldn + incr) * siz);
418         if (!arr) {
419                 return -1;
420         }
421         if (oldn) {
422                 memcpy (arr, *base, oldn * siz);
423                 mFree (*base);
424         }
425         memset (arr + oldn * siz, 0, incr * siz);
426         *num += incr;
427         *base = arr;
428         return oldn;
429 }
430
431 void luti_free (void **arr, int num) {
432         if (arr) {
433                 while (0 <= --num) {
434                         if (arr[num]) {
435                                 mFree (arr[num]);
436                         }
437                 }
438                 mFree (arr);
439         }
440 }
441
442 int luti_true (const char *str, int len) {
443         if (! str || ! *str) {
444                 return -1;
445         }
446         if (0 > len) {
447                 len = strlen (str);
448         }
449         if (1 == len) {
450                 if ('0' == *str ||
451                         'f' == *str ||
452                         'F' == *str ||
453                         'n' == *str ||
454                         'N' == *str
455                 ) {
456                         return 0;
457                 }
458                 if ('1' == *str ||
459                         'y' == *str ||
460                         'Y' == *str ||
461                         't' == *str ||
462                         'T' == *str
463                 ) {
464                         return 1;
465                 }
466                 return -1;
467         }
468         if (! strncmp ("false", str, len) ||
469                 ! strncmp ("no", str, len)) {
470                 return 0;
471         }
472         if (! strncmp ("true", str, len) ||
473                 ! strncmp ("yes", str, len)) {
474                 return 1;
475         }
476         return -1;
477 }
478
479 /*      ------------------------------------------------------------------------
480  *      unwrap by path
481  */
482
483 Fdt* luti_fdt_from_rec (Rec *rec) {
484         Db *dbh;
485         if (rec &&
486                 0 <= rec->dbid &&
487                 (dbh = nDbById (rec->dbid))
488         ) {
489                 return dbh->fdt;
490         }
491         return 0;
492 }
493
494 const char* luti_parse_path (
495         const char *path, const Fdt *fdt, Fd **fd, int *tag, int *occ
496 ) {
497         Fd  *_f = 0;
498         int  _t = -1;
499         int  _o = -1;
500         int  num, qsub;
501
502         if (fd) {
503                 *fd = 0;
504         }
505         if (tag) {
506                 *tag = -1;
507         }
508         if (occ) {
509                 *occ = -1;
510         }
511         if (! path) {
512                 return 0;
513         }
514
515         if ('.' == *path
516                 || ('-' == *path && (path[1]<'0' || '9'<path[1]))
517         ) {  /* path or option style */
518                 ++path;
519         }
520
521         num = a2il (path, -1, &_t);
522         if (num) {
523                 if (fdt) {
524                         _f = fById (fdt, _t, 0);
525                 }
526         }
527         else {
528                 char name[OPENISIS_FD_NAMELEN];
529                 if (! fdt) {
530                         return 0;
531                 }
532                 for (num = 0;
533                         path[num] && '[' != path[num] && '(' != path[num];
534                         ++num)
535                         ;
536                 if (num > OPENISIS_FD_NAMELEN - 1) {
537                         return 0;
538                 }
539                 strncpy (name, path, num) [num] = 0;
540                 _f = fByName ( fdt, name );
541                 if (! _f) {
542                         return 0;
543                 }
544                 _t = _f->id;
545         }
546         path += num;
547
548         if (tag) {
549                 *tag = _t;
550         }
551         if (fd) {
552                 *fd = _f;
553         }
554
555         if ('[' != *path && '(' != *path) {
556                 return path;
557         }
558         qsub = *path;
559
560         ++path;
561         num = a2il (path, -1, &_o);
562         if (! num || 0 > _o) {
563                 return 0;
564         }
565         path += num;
566         if ((']' != *path && '[' == qsub) || (')' != *path && '(' == qsub)) {
567                 return 0;
568         }
569
570         if (occ) {
571                 *occ = _o;
572         }
573         return path + 1;
574 }
575
576 Rec* luti_getembed ( Rec *env, const char *path, const Fdt *fdt)
577 {
578         Rec *res;
579         int  tag, occ, idx, pos;
580         if ( ! env || ! path || ! *path) {
581                 return 0;
582         }
583         if (! fdt) {
584                 fdt = luti_fdt_from_rec (env);
585         }
586         path = luti_parse_path (path, fdt, 0, &tag, &occ);
587         if (! path) {
588                 return 0;
589         }
590         res = 0;
591         idx = pos = 0;
592         while (1) {
593                 res = _unwrap (env, &pos, tag, -1, idx < occ);
594                 if (! res) {
595                         return 0;
596                 }
597                 if (idx >= occ) {
598                         break;
599                 }
600                 ++idx;
601         }
602         if (*path) {
603                 Rec *r2 = luti_getembed (res, path, 0);
604                 mFree (res);
605                 return r2;
606         }
607         return res;
608 }
609
610
611 int lhash ( const char *str, int len )
612 {
613         int h = 0;
614         while ( len-- )
615                 h = 31*h + *str++;
616         return 0<=h ? h : -h;
617 }
618
619
620 /* ************************************************************
621         public functions
622 */
623
624 char *toHtml ( const char *str, int len )
625 {
626         int l;
627         char *p, *e, *ret;
628         if ( ! str )
629                 return 0;
630         if ( 0 > len )
631                 len = strlen(str);
632         l = len + 1;
633         for ( e = (p=(char*)str) + len; p<e; p++ )
634                 switch ( *p ) {
635                 case '<': case '>': l += 3; break;
636                 case '&': l += 4; break;
637                 case '"': l += 5; break;
638                 /* do not replace ' w/ &apos; -- many clients don't grok it */
639                 }
640         ret = mAlloc( l );
641         if ( ! ret )
642                 return 0;
643         for ( p=ret; str<e; str++ )
644                 switch ( *str ) {
645                 case '<': *p='&'; p[1]='l'; p[2]='t'; p[3]=';'; p+=4; break;
646                 case '>': *p='&'; p[1]='g'; p[2]='t'; p[3]=';'; p+=4; break;
647                 case '&': *p='&'; p[1]='a'; p[2]='m'; p[3]='p'; p[4]=';'; p+=5; break;
648                 case '"': *p='&';p[1]='q';p[2]='u';p[3]='o';p[4]='t';p[5]=';';p+=6;break;
649                 default: *p++ = *str;
650                 }
651         *p = 0;
652         return ret;
653 }       /* toHtml */
654
655
656 int utf8Chk ( void *mem, int len, int *tof )
657 {
658         unsigned char *c = (unsigned char *)mem;
659         int f = 0; /* expected followers */
660         int l = len;
661         if ( tof )
662                 f = *tof;
663         for ( ; l--; c++ ) {
664                 unsigned char u = *c;
665                 int tofollow = u < 128 ? 0 : u < 192 ? -1 : u < 224 ? 1 : 2;
666                 if ( f ) {
667                         if ( 0 > tofollow ) { f--; continue; }
668                         break;
669                 }
670                 if ( 0 <= tofollow ) { f = tofollow; continue; }
671                 goto croak;
672         }
673         if ( tof )
674                 *tof = f;
675         if ( ! f )
676                 return 0;
677         if ( 0 > l && tof ) /* exhausted buf, want follower */
678                 return 0;
679         sMsg( LOG_ERROR, "expected follower at %d got %x", len-l-1, (int)*c );
680         return len-l;
681 croak:
682         sMsg( LOG_ERROR, "expected no follower at %d got %x", len-l-1, (int)*c );
683         return len-l;
684 }       /* utf8Chk */