fixed parsing for science direct html with more than one <a href=> per one <tr>
[webpac] / openisis / lses.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: lses.c,v 1.18 2003/05/26 10:55:10 kripke Exp $
25         implementation of general db access functions.
26 */
27
28 #include <stdlib.h>
29 #include <string.h> /* memset */
30
31 #ifdef _REENTRANT
32 #ifdef WIN32
33 #       define WIN32_LEAN_AND_MEAN
34 #       define NONAMELESSUNION
35 #       include <windows.h>
36 /* they call it "thread local storage" */
37 #       define pthread_key_t int
38 # define pthread_key_create( pkey, func ) *(pkey) = TlsAlloc()
39 # define pthread_setspecific( key, val ) TlsSetValue( key, val )
40 # define pthread_getspecific( key ) TlsGetValue( key )
41 #else
42 #       include <pthread.h>
43 #endif
44 #endif
45
46 #include "lses.h"
47
48
49 /* ************************************************************
50         private types
51 */
52 typedef struct {
53         Ses h;
54         /* int          brk; */
55 } SesI;
56
57 enum {
58         LSES_MAX = 0x1000,
59         LSES_MSK = 0x0fff
60 };
61
62 /* ************************************************************
63         package data
64 */
65
66 /* ************************************************************
67         private data
68 */
69 static Ios stdio[3] = {
70         LIO_STDINITIALIZER( "&0", LIO_IN ),
71         LIO_STDINITIALIZER( "&1", LIO_OUT|1 ),
72         LIO_STDINITIALIZER( "&2", LIO_OUT|2 )
73 };
74 static SesI cses = { /* the default session */
75 {
76         0,
77         "",
78         { stdio, stdio+1, stdio+2 },
79         -1,
80         0, 0, /* nxt, accnt */
81         {0},{0},{0}, /* times */
82         0,0,0,0 /* state */
83 }
84 };
85
86 static SesI *tab[ LSES_MAX ] = {
87         &cses
88 };
89 static Ses *htab[ LSES_MAX ];
90 static int maxid;
91 static const char oops[] = "out of memory\n";
92
93 #ifndef _REENTRANT
94 Session ses = &cses.h;
95 #else
96 int openisis_threaded = 42;
97 static pthread_key_t seskey;
98 #endif
99
100 /* ************************************************************
101         private functions
102 */
103
104 static void *memordie ( int size )
105 {
106         void *p = malloc( size );
107         if ( p )
108                 return p;
109         /* TODO: ask btree to release some cache */
110         lio_write( &lio_err, oops, sizeof(oops)-1 );
111         exit( 71/*EX_OSERR*/ ); /* exit handlers MUST NOT alloc memory */
112         return 0;
113 }
114
115
116 /* ************************************************************
117         package functions
118 */
119
120 void lses_init () /* called from openIsisInit */
121 {
122 #ifdef _REENTRANT
123         pthread_key_create( &seskey, 0 ); /* TODO: use ses destr func */
124         pthread_setspecific( seskey, &cses.h );
125 #endif
126 }       /* lses_init */
127
128
129 void lses_fini () /* called from openIsisFini */
130 {
131         int i, s = LSES_MAX;
132         /* lio_write( &lio_out, "fini time\n", 9 ); */
133         while ( s-- )
134                 if ( tab[s] )
135                         for ( i=0; i<3; i++ )
136                                 if ( tab[s]->h.io[i] )
137                                         LIO_CLOSE( tab[s]->h.io[i] );
138 }       /* lses_fini */
139
140
141 int lses_open ( Session s, const char *name, int flags, SFunc *type )
142 {
143         int fd = 0;
144         const char *p = name;
145         lio_sfunc *func = type ? (lio_sfunc*)type : lio_stdio;
146
147         /* check for i<> */
148         while ( '0' <= *p && *p <= '9' )
149                 fd = 10*fd + *p++ - '0';
150         if ( '<' != *p && '>' != *p )
151                 p = name; /* forget it */
152         else {
153                 if ( name == p && '>' == *p ) /* no fd given, > defaults to 1 */
154                         fd = 1;
155                 flags |= '<' == *p ? LIO_RD : LIO_WR;
156                 p++;
157         }
158         if ( p == name ) { /* no fd given, find one */
159                 for ( fd=0; s->io[fd]; )
160                         if ( LSES_FILE_MAX == ++fd )
161                                 return -ERR_NOMEM;
162         } else if ( s->io[fd] ) { /* close existing */
163                 func( s->io[fd], LIO_SCLOSE );
164                 /* don't kill the statics :) */
165                 if ( s->io[fd] < stdio || s->io[fd] > stdio+2 )
166                         mFree( s->io[fd] );
167         }
168         s->io[fd] = (Ios*)mAlloc( func( 0, LIO_SSIZE ) );
169         if ( ! s->io[fd] )
170                 return -ERR_NOMEM;
171         s->io[fd]->func = func;
172         s->io[fd]->name = mDup( p, -1 );
173         s->io[fd]->file = flags;
174         return func( s->io[fd], LIO_SOPEN );
175 }       /* lses_open */
176
177
178
179 /* ************************************************************
180         public functions
181 */
182
183 extern Ses *sGet ()
184 {
185 #ifdef _REENTRANT
186         Ses *ses;
187         if ( ! (ses = (Ses*)pthread_getspecific( seskey )) )
188                 pthread_setspecific( seskey, ses = cSession( 0 ) );
189 #endif
190         return ses;
191 }
192
193 extern void sSet ( Ses *s )
194 {
195 #ifndef _REENTRANT
196         ses = s;
197 #else
198         pthread_setspecific( seskey, s );
199 #endif
200 }
201
202
203 Ses *cOpen ( Rec *args )
204 {
205         extern void linit();
206
207         linit();
208         if ( args ) { /* add args */
209                 /* TODO: merge args */
210                 cses.h.prop = rDup( args, -1, 0 );
211         }
212         return &cses.h;
213 }       /* cOpen */
214
215
216 Ses *cSession ( Rec *args )
217 {
218         SesI *sesi = 0;
219         Ses *s = 0;
220         int id = 1;
221         /** XXX unless we actually free sessions,
222                 we should start checking with maxid
223                 or check for expiry
224         */
225         while ( tab[id] )
226                 if ( LSES_MAX == ++id )
227                         return 0;
228         if ( maxid < id )
229                 maxid = id;
230         sesi = tab[id] = (SesI*)malloc( sizeof(SesI) );
231         memset( sesi, 0, sizeof(SesI) );
232         s = &sesi->h;
233         s->id = id;
234         s->hash = -1; /* not hashed (hash is non-negative) */
235         s->prop = args ? rDup( args, -1, 0 ) : 0;
236         /* check props */
237         /* open files */
238         lses_open( s, "2>&2", 0, lio_stdio );
239         return s;
240 }       /* cSession */
241
242
243 void *mAlloc ( int size )
244 {
245         void *p = memordie( size );
246         memset(p, 0, size);
247         return p;
248 }       /* mAlloc */
249
250
251 void  mFree ( void *mem )
252 {
253         if ( mem )
254                 free( mem );
255 }       /* mFree */
256
257
258 void *mDup ( const void *str, int sz )
259 {
260         void *m = memordie( 0<=sz ? sz : (sz = strlen( str ) + 1) );
261         memcpy( m, str, sz );
262         return m;
263 }       /* mDup */
264
265
266 int sOpen ( const char *name, int flags, SFunc *type )
267 {
268         SESDECL
269         return lses_open( ses, name, flags, type );
270 }       /* sOpen */
271
272
273 Ses *openIsisId2Session (int sid) {
274         if (0 > sid || LSES_MAX <= sid || 0 == tab[sid]) {
275                 return (Ses*)0;
276         }
277         return &(tab[sid]->h);
278 }       /* openIsisId2Session */
279
280
281 int openIsisSession2Id (Ses *s) {
282         if (0 == s) {
283                 return -1;
284         }
285         return s->id;
286 }       /* openIsisSession2Id */
287
288
289 Ses *cSesByName ( char *name, int nlen, Tm *now, Tm *expire )
290 {
291         char tbuf[20];
292         int h,i;
293         Ses *s;
294         if ( nlen < 0 )
295                 nlen = strlen(name);
296         if ( nlen > 63 )
297                 nlen = 63;
298         for ( i=nlen; i--; )
299                 if ( !(0xe0 & name[i]) )
300                         name[i] = '_';
301         h = lhash( name, nlen );
302         for ( s = htab[h & LSES_MSK]; s; s = s->nxt )
303                 if ( h == s->hash
304                         && !memcmp( name, s->name, nlen )
305                         && !s->name[nlen]
306                 ) {     /* match */
307                         if ( !expire || s->atime.millis >= expire->millis )
308                                 goto gotit; /* valid */
309                         if ( s->cur || s->que ) {
310                                 log_msg( LOG_WARN, "expired session %d %s still busy!",
311                                         s->id, s->name );
312                                 goto gotit; /* valid anyway */
313                         }
314                         goto refresh;
315                 }
316         /* didn't find by name */
317         if ( ! expire ) /* bad thing -- we're going to run out of sessions ... */
318                 i = maxid+1;
319         else /* check other expired */
320                 for ( i=1; i<=maxid; i++ ) {
321                         s = &tab[i]->h; /* sind wir heute defensiv ... */
322                         if ( s && s->atime.millis < expire->millis
323                                 && s->atime.millis && 0 <= s->hash /* else is none of our biz */
324                                 && !s->cur && !s->que /* else is busy !? */
325                         )
326                                 break;
327                 }
328         if ( i > maxid ) {
329                 s = cSession( 0 );
330                 if ( ! s )
331                         return 0;
332         } else /* got one */
333                 log_msg( LOG_INFO, "reusing session %d '%s' atime %s",
334                         s->id, s->name, s->atime.millis ? timeGtf(tbuf,&s->atime) : "-" );
335         if ( 0 <= s->hash ) { /* was hashed -- unlink */
336                 Ses **spp = &htab[s->hash & LSES_MSK];
337                 for ( ; *spp; spp = &(*spp)->nxt )
338                         if ( s == *spp ) {
339                                 *spp = s->nxt;
340                                 break;
341                         }
342         }
343         memset( s->name, 0, sizeof(s->name) );
344         memcpy( s->name, name, nlen );
345         s->hash = h;
346         s->nxt = htab[h & LSES_MSK];
347         htab[h & LSES_MSK] = s;
348 refresh:
349         s->ctime.millis = s->mtime.millis = now ? now->millis : 0;
350         s->accnt = 0;
351         if ( s->prop )
352                 CLRREC( s->prop );
353         if ( s->res )
354                 CLRREC( s->res );
355 gotit:
356         s->atime.millis = now ? now->millis : 0;
357         return s;
358 }       /* cSesByName */