fixed parsing for science direct html with more than one <a href=> per one <tr>
[webpac] / openisis / lcli.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: lcli.c,v 1.13 2003/05/15 19:17:17 mawag Exp $
25         OpenIsis client side of communication
26 */
27
28 #ifndef WIN32
29 #include <sys/types.h>
30 #include <sys/socket.h> /* socket etc */
31 #include <netinet/in.h> /* IPPROTO_TCP, htons */
32 #include <netdb.h> /* gethostbyname */
33 #endif
34
35 #include <errno.h>
36 #include <memory.h>
37 #include <unistd.h>
38
39 #include "openisis.h"
40 #include "loi.h"
41 #include "lio.h"
42 #include "lcli.h"
43
44 /*
45 gcc -D_MAIN_ -I. -Lsto lcli.c -lopenisis && ./a.out 111 `find .`
46 gcc -D_MAIN_ -DWIN32 -I. lcli.c win/libopenisis.a /mingw/lib/libwsock32.a
47 */
48
49 #define LF 10 /* LineFeed a.k.a. newline - '\n' isn't really well defined */
50 #define TAB 9 /* horizontal, that is */
51 #define VT 11 /* vertical, used as newline replacement */
52
53 #define STAT_CONT  0x01  /* not at beginning of line */
54 #define STAT_BIN   0x10  /* binary mode */
55
56 /* plain protocol
57 *  @return done or -err
58 */
59 int cliPlain (Ios *s, int *stat, Rec **rec) {
60         Field *f;
61         int l = s->b.fill - s->b.done;
62         unsigned char *b = s->b.c + s->b.done;
63         unsigned char *end = b+l, *v, *p;
64         if ( ! l ) { /* EOF: done */
65                 if ( ! (STAT_CONT & *stat) ) /* ok */
66                         return 1;
67                 /* last field wasn't closed by LF */
68                 return sMsg (ERR_INVAL, "cliPlain(%d): no EOL", LIO_FD & s->file);
69         }
70         if ( STAT_CONT & *stat )
71                 RSPACE( *rec, l, !0 );
72         /* add text lines */
73         while ( b<end ) {
74                 int conti = 0;
75                 switch ( STAT_CONT & *stat ) {
76                 case 0: /* at beginning of line -- start new field */
77                         if ( LF == *b ) /* empty line */
78                                 return 1;
79                         if ( TAB != *b || ! *rec || ! (*rec)->len ) {
80                                 RADD( *rec, 0,0,end-b, !0 );
81                         }
82                         else { /* binary mode continuation line */
83                                 conti = 1;
84                                 if ( !(STAT_BIN & *stat)) {
85                                         sMsg( LOG_INFO, "cliPlain(%d): detected binary mode",
86                                                 LIO_FD & s->file);
87                                         *stat |= STAT_BIN;
88                                 }
89                                 RSPACE( *rec, end-b, !0 );
90                         }
91                         if ( ! *rec )
92                                 return -ERR_NOMEM;
93                         *stat |= STAT_CONT;
94                 case STAT_CONT: /* add to last field */
95                         f = (*rec)->field + (*rec)->len-1;
96                         v = (unsigned char*)f->val;
97                         p = v + f->len;
98                         if ( conti ) {
99                                 *p++ = LF;
100                                 b++;
101                         }
102                         if ( STAT_BIN & *stat ) {
103                                 for ( ; b<end && LF != (*p = *b++); p++ )
104                                         ;
105                         } else {
106                                 for ( ; b<end && LF != (*p = *b++); p++ )
107                                         if ( VT == *p ) /* convert VTABs */
108                                                 *p = LF; /* back to newlines */
109                         }
110                         (*rec)->used += (p - v) - f->len;
111                         f->len = p - v;
112                         if ( LF == b[-1] ) {
113                                 int ret = a2il( f->val, f->len, &f->tag );
114                                 if ( ret ) {
115                                         if ( ret < f->len && TAB == v[ret] )
116                                                 ret++;
117                                         if ( ret < f->len )
118                                                 memmove( v, v+ret, f->len - ret );
119                                         f->len -= ret;
120                                 }
121                                 *stat &= ~STAT_CONT;
122                         }
123                         sMsg (LOG_VERBOSE, "cliPlain(%d): fld[%2d] %3d = '%.*s'",
124                                 LIO_FD & s->file, (*rec)->len-1, f->tag, f->len, f->val);
125                 }
126         }
127         return 0;
128 }
129
130 Rec* cliRead (CliChnl *ch) {
131         Ios  str;
132         Rec *rec;
133         int  rt, stat;
134
135         if (!ch || 0 > ch->sd) {
136                 return 0;
137         }
138         memset (&str, 0, sizeof (Ios));
139         str.file = ch->sd | LIO_IN;
140         rec = 0;
141         stat = 0;
142         ch->err = errno = 0;
143
144         while (1) {
145                 str.b.done = str.b.fill;
146                 rt = ioStdio (&str, LIO_SFILL);
147                 if (0 > rt) {
148                         ch->err = errno;
149                         if (rec) {
150                                 mFree (rec);
151                         }
152                         if (!(LIO_IN & str.file)) {
153                                 ch->sd = -1;
154                         }
155                         return 0;
156                 }
157                 rt = cliPlain (&str, &stat, &rec);
158                 if (rt) {
159                         if (0 > rt) {
160                                 if (rec) {
161                                         mFree (rec);
162                                 }
163                                 return 0;
164                         }
165                         log_rec (LOG_VERBOSE, rec, "cliRead(%d): ", 0);
166                         sMsg (LOG_INFO, "cliRead(%d): read #flds %d",
167                                 ch->sd, (rec ? rec->len : -1));
168                         return rec;
169                 }
170         }
171 }
172
173 int cliWrite (CliChnl *ch, Rec *rec) {
174         char  buf [4096];
175         char *b;
176         int   fd, len, sl, rl, rt;
177         if (!ch || 0 > ch->sd) {
178                 return -ERR_BADF;
179         }
180         ch->err = 0;
181         if (!rec || !rec->len) {
182                 return 0;
183         }
184         len = sizeof (buf);
185         b = rSerA (rec, buf, &len);
186         if (!b) {
187                 return sMsg (ERR_NOMEM, "cliWrite(%d): write (%d)", ch->sd, rec->used);
188         }
189         fd = ch->sd | LIO_OUT;
190         for (sl = rt = errno = 0, rl = len; rl; sl += rt, rl -= rt) {
191                 rt = lio_write (&fd, b + sl, (unsigned)rl);
192                 ch->err = errno;
193                 if (0 > rt) {
194                         if (!(LIO_OUT & fd)) {
195                                 ch->sd = -1;
196                         }
197                         break;
198                 }
199         }
200         if (b != buf) {
201                 mFree (b);
202         }
203         if (0 > rt) {
204                 return sMsg (ERR_IO, "cliWrite(%d): write (%d) = %d(%d)",
205                         len, rt, ch->err);
206         }
207         sMsg (LOG_INFO, "cliWrite(%d): wrote %d,%d", ch->sd, rec->len, len);
208         return 0;
209 }
210
211 int cliConnect (CliChnl *ch, const char *hname, int port) {
212 #ifdef WIN32
213         return sMsg (ERR_TRASH, "cliConnect: operation not supported");
214 #else
215         struct sockaddr_in  addr;
216         struct hostent     *hostp;
217         int                 rt;
218         if (!ch) {
219                 return sMsg (ERR_IDIOT, "cliConnect: null channel");
220         }
221         memset (ch, 0, sizeof (CliChnl));
222         rt = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP);
223         if (0 > rt) {
224                 ch->err = errno;
225                 return sMsg (ERR_IO, "cliConnect: cannot create socket: %d", errno);
226         }
227         ch->sd = rt;
228         memset (&addr, 0, sizeof (addr));
229         if (hname) {
230                 hostp = gethostbyname (hname);
231                 if (! hostp) {
232                         ch->err = errno;
233                         return sMsg (ERR_INVAL, "cliConnect: unknown host <%s>: %d",
234                                 hname, errno);
235                 }
236                 addr.sin_family = hostp->h_addrtype; /* already network-byte-order */
237                 addr.sin_addr.s_addr = *(long*)(*(hostp->h_addr_list)); /* dto */
238         }
239         else {
240                 addr.sin_family = AF_INET;
241                 addr.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
242         }
243         addr.sin_port = htons (port);
244         rt = connect (ch->sd, &addr, sizeof (addr));
245         if (0 > rt) {
246                 ch->err = errno;
247                 return sMsg (ERR_IO, "cliConnect: cannot connect to %s:%d: %d",
248                         (hname ? hname : "localhost"), port, errno);
249         }
250         return 0;
251 #endif
252 }
253
254 void cliClose (CliChnl *ch) {
255         if (ch && 0 <= ch->sd) {
256                 close (ch->sd);
257                 ch->sd = -1;
258         }
259 }
260
261 /* -----------------------------------------------------------------------
262  * test
263  */
264 #ifdef _MAIN_
265
266 #include <stdio.h> /* printf */
267 #include <stdlib.h> /* atoi */
268 #include <string.h> /* strerror */
269 #include "luti.h" /* log_rec */
270
271 void cmp (Rec *in, Rec *out) {
272         Field *F1, *F2;
273         int l1 = in->len;
274         int l2 = out->len;
275         int j, k;
276         for (j = k = 0, F1 = in->field, F2 = out->field; l2 > k; ++k, ++F2) {
277                 if (0 > F2->tag) {
278                         continue;
279                 }
280                 if (l1 <= j) {
281                         printf ("ERR too many answers %d %d\n", j, k);
282                         log_rec (0, in, "ERR IN ", 0);
283                         log_rec (0, out, "ERR OUT ", 0);
284                         exit (1);
285                 }
286                 if (F1->tag != F2->tag) {
287                         printf ("ERR tag %d mismatch %d %d\n", j, F1->tag, F2->tag);
288                         log_rec (0, in, "ERR IN ", 0);
289                         log_rec (0, out, "ERR OUT ", 0);
290                         exit (1);
291                 }
292                 if (F1->len != F2->len) {
293                         printf ("ERR len %d mismatch %d %d\n", j, F1->tag, F2->tag);
294                         log_rec (0, in, "ERR IN ", 0);
295                         log_rec (0, out, "ERR OUT ", 0);
296                         exit (1);
297                 }
298                 if (memcmp (F1->val, F2->val, F1->len)) {
299                         printf ("ERR val  %d mismatch\n", j);
300                         log_rec (0, in, "ERR IN ", 0);
301                         log_rec (0, out, "ERR OUT ", 0);
302                         exit (1);
303                 }
304                 ++j;
305                 ++F1;
306         }
307 }
308
309 int loop (CliChnl *ch, Rec *demo, int fail) {
310         Rec *rsp;
311         int rt;
312         rt = cliWrite (ch, demo);
313         if (0 > rt) {
314                 printf ("%s write %d %s\n", (fail ? "ERR":"WARN"),
315                         ch->err, strerror (ch->err));
316                 if (!fail) {
317                         return -1;
318                 }
319                 exit (1);
320         }
321         rsp = cliRead (ch);
322         if (! rsp) {
323                 printf ("%s read %d %s\n", (fail ? "ERR":"WARN"),
324                         ch->err, strerror (ch->err));
325                 if (!fail) {
326                         return -1;
327                 }
328                 exit (1);
329         }
330         cmp (demo, rsp);
331         mFree (rsp);
332         return 0;
333 }
334
335 int main (int argc, char **argv) {
336         CliChnl ch;
337         char *hname = 0;
338         int port = 0;
339         Rec *demo = 0;
340         int  num = 0;
341         int  j;
342
343         for (j = 1; argc > j; ++j) {
344                 if (*argv[j] == '-') {
345                         if (argv[j][1] == 'h') {
346                                 hname = argv[j] + 2;
347                                 continue;
348                         }
349                         if (argv[j][1] == 'p') {
350                                 port = atoi (argv[j] + 2);
351                                 continue;
352                         }
353                 }
354                 num = atoi (argv[j]);
355                 break;
356         }
357
358         if (0 >= num) {
359                 num = 1;
360         }
361
362         for (j = argc; 0 <= --j;  ) {
363                 RADDS (demo, j, argv[j], !0);
364         }
365
366         if (0 >= port) {
367                 port = 8080;
368         }
369         if (cliConnect (&ch, hname, port)) {
370                 printf ("ERR connect %d %s\n", ch.err, strerror (ch.err));
371                 exit (1);
372         }
373
374         j = num;
375         while (1) {
376                 if (loop (&ch, demo, 0)) {
377                         sleep (15);
378                         if (cliConnect (&ch, hname, port)) {
379                                 printf ("ERR reconnect %d %s\n", ch.err, strerror (ch.err));
380                                 exit (1);
381                         }
382                         loop (&ch, demo, !0);
383                 }
384                 if (! --j) {
385                         break;
386                 }
387                 if (! (j % 10)) {
388                         printf ("loop %d ...\n", j);
389                 }
390         }
391
392         cliClose (&ch);
393         printf ("ok.\n");
394         return 0;
395 }
396
397 #endif /* _MAIN_ */
398