5728d569a592526a1ef0f2e43c7dadab01368ed7
[egate.git] / www / wtcl.c
1 /*
2  * Copyright (c) 1995, the EUROPAGATE consortium (see below).
3  *
4  * The EUROPAGATE consortium members are:
5  *
6  *    University College Dublin
7  *    Danmarks Teknologiske Videnscenter
8  *    An Chomhairle Leabharlanna
9  *    Consejo Superior de Investigaciones Cientificas
10  *
11  * Permission to use, copy, modify, distribute, and sell this software and
12  * its documentation, in whole or in part, for any purpose, is hereby granted,
13  * provided that:
14  *
15  * 1. This copyright and permission notice appear in all copies of the
16  * software and its documentation. Notices of copyright or attribution
17  * which appear at the beginning of any file must remain unchanged.
18  *
19  * 2. The names of EUROPAGATE or the project partners may not be used to
20  * endorse or promote products derived from this software without specific
21  * prior written permission.
22  *
23  * 3. Users of this software (implementors and gateway operators) agree to
24  * inform the EUROPAGATE consortium of their use of the software. This
25  * information will be used to evaluate the EUROPAGATE project and the
26  * software, and to plan further developments. The consortium may use
27  * the information in later publications.
28  * 
29  * 4. Users of this software agree to make their best efforts, when
30  * documenting their use of the software, to acknowledge the EUROPAGATE
31  * consortium, and the role played by the software in their work.
32  *
33  * THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT WARRANTY OF ANY KIND,
34  * EXPRESS, IMPLIED, OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
35  * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
36  * IN NO EVENT SHALL THE EUROPAGATE CONSORTIUM OR ITS MEMBERS BE LIABLE
37  * FOR ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF
38  * ANY KIND, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
39  * OR PROFITS, WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND
40  * ON ANY THEORY OF LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE
41  * USE OR PERFORMANCE OF THIS SOFTWARE.
42  *
43  * $Log: wtcl.c,v $
44  * Revision 1.16  1996/05/23 15:53:12  adam
45  * Bug fix: egw_enc failed on 8-bit chars.
46  * New command: egw_parms.
47  *
48  * Revision 1.15  1996/05/22  16:50:27  adam
49  * Bug fix.
50  *
51  * Revision 1.14  1996/05/21  14:53:04  adam
52  * Tcl command wform extented; options -raw and -exists added.
53  *
54  * Revision 1.13  1996/03/14  11:48:40  adam
55  * New function egw_prog that returns name of shell.
56  *
57  * Revision 1.12  1996/03/07  12:45:34  adam
58  * New Tcl calls egw_enc/egw_dec to encode/decode binary URL info.
59  *
60  * Revision 1.11  1996/01/24  08:26:56  adam
61  * All tcl commands prefixed with egw_ (except the html command).
62  *
63  * Revision 1.10  1995/11/08  16:14:35  adam
64  * Many improvements and bug fixes.
65  * First version that ran on dtbsun.
66  *
67  * Revision 1.9  1995/11/07  14:57:00  adam
68  * Work on search in multiple targets.
69  * New wtcl command: wlog.
70  * Optional timeout parameter to zwait.
71  *
72  * Revision 1.8  1995/11/06  17:44:23  adam
73  * State reestablised when shell restarts. History of previous
74  * result sets.
75  *
76  * Revision 1.7  1995/10/31  16:56:25  adam
77  * Record presentation.
78  *
79  * Revision 1.6  1995/10/31  10:03:54  adam
80  * Work on queries.
81  * New command implemented - aborts script.
82  *
83  * Revision 1.5  1995/10/30  17:35:18  adam
84  * New function zwait that waits for a variable change - due to i/o events
85  * that invoke callback routines.
86  *
87  * Revision 1.4  1995/10/27  17:30:16  adam
88  * First search request/response that works.
89  *
90  * Revision 1.3  1995/10/27  15:12:14  adam
91  * IrTcl incorporated in the gateway.
92  * Better separation of script types.
93  * Z39.50 gateway scripts entered.
94  *
95  * Revision 1.2  1995/10/23  16:55:43  adam
96  * A lot of changes - really.
97  *
98  * Revision 1.1  1995/10/20  14:02:42  adam
99  * First version of WWW gateway with embedded Tcl.
100  *
101  */
102
103 #include <stdio.h>
104 #include <stdlib.h>
105 #include <string.h>
106 #include <assert.h>
107 #include <ctype.h>
108
109 #include "wtcl.h"
110
111 static void *do_create (WCLIENT wcl, void *args);
112 static int do_exec (const char *fname, char *parms, void *mydata);
113 static int do_load (char *parms, void *mydata);
114 static int do_save (char *parms, void *mydata);
115
116 static struct w_interp_type w_interp_t = {
117     "tcl",
118     do_create,
119     do_exec,
120     do_load,
121     do_save
122 };
123
124 W_Interp_Type w_interp_tcl = &w_interp_t;
125
126
127 static char *mod = "wtcl";
128
129 struct tcl_info {
130     Tcl_Interp *interp;
131     char  *fbuf;
132     int    fbuf_size;
133     int    fbuf_ptr;
134     int    wabort;
135     WCLIENT wcl;
136 };
137
138 Tcl_Interp *w_interp_tcl_get (W_Interp w_interp)
139 {
140     struct tcl_info *p;
141
142     if (strcmp (w_interp->ctrl->name, "tcl"))
143     {
144         gw_log (GW_LOG_FATAL, mod, "Internal failure");
145         assert (0);
146     }
147     p = (struct tcl_info*) w_interp->mydata;
148     return p->interp;
149 }
150
151 static int proc_wabort_invoke (ClientData clientData, Tcl_Interp *interp,
152                                int argc, char **argv)
153 {
154     struct tcl_info *p = (struct tcl_info*) clientData;
155
156     p->wabort = 1;
157     if (argc > 1)
158         Tcl_AppendResult (interp, argv[1], NULL);
159     return TCL_RETURN;
160 }
161
162 static int proc_wflush_invoke (ClientData clientData, Tcl_Interp *interp,
163                                int argc, char **argv)
164 {
165     struct tcl_info *p = (struct tcl_info*) clientData;
166
167     wo_flush (p->wcl);
168     return TCL_OK;
169 }
170
171 static int proc_html_invoke (ClientData clientData, Tcl_Interp *interp,
172                              int argc, char **argv)
173 {
174     struct tcl_info *p = (struct tcl_info*) clientData;
175     int i;
176
177     for (i = 1; i<argc; i++)
178         wo_write (p->wcl, argv[i], strlen(argv[i]));
179     return TCL_OK;
180 }
181
182 static int proc_form_invoke (struct tcl_info *p, wform_data *wfdata,
183                              Tcl_Interp *interp,
184                              int argc, char **argv)
185 {
186     const char *arg = NULL;
187     int failFlag = 0;
188     int i;
189
190     if (argc == 3)
191     {
192         if (!strcmp (argv[1], "-raw"))
193         {
194             interp->result = p->wcl->raw_data;
195             return TCL_OK;
196         }
197         else if (!strcmp (argv[1], "-exists"))
198         {
199             failFlag = 1;
200             arg = argv[2];
201         }
202         else
203         {
204             Tcl_AppendResult (p->interp, "bad option to ", argv[0],
205                               " \"", argv[1], "\"", NULL);
206             return TCL_ERROR;
207         }
208     }
209     else if (argc == 2)
210         arg = argv[1];
211     if (arg)
212     {
213         for (i = 0; *wfdata[i].name; i++)
214             if (!strcmp (arg, wfdata[i].name))
215             {
216                 failFlag = 0;
217                 if (*wfdata[i].value)
218                     Tcl_AppendElement (p->interp, wfdata[i].value);
219             }
220         if (failFlag)
221         {
222             Tcl_AppendResult (p->interp, arg, " doesn't exist", NULL);
223             return TCL_ERROR;
224         }
225         return TCL_OK;
226     }    
227     for (i = 0; *wfdata[i].name; i++)
228     {
229         Tcl_AppendResult (p->interp, "{ ", NULL);
230         Tcl_AppendElement (p->interp, wfdata[i].name);
231         Tcl_AppendElement (p->interp, wfdata[i].value);
232         Tcl_AppendResult (p->interp, " }\n", NULL);
233     }
234     return TCL_OK;
235 }
236
237 static int proc_wform_invoke (ClientData clientData, Tcl_Interp *interp,
238                               int argc, char **argv)
239 {
240     struct tcl_info *p = (struct tcl_info*) clientData;
241     wform_data *wfdata = p->wcl->wf_data;
242     return proc_form_invoke (p, wfdata, interp, argc, argv);
243 }
244
245 static int proc_parms_invoke (ClientData clientData, Tcl_Interp *interp,
246                               int argc, char **argv)
247 {
248     struct tcl_info *p = (struct tcl_info*) clientData;
249     wform_data *wfdata = p->wcl->wf_parms_var;
250     return proc_form_invoke (p, wfdata, interp, argc, argv);
251 }
252
253
254 static int proc_wlog_invoke (ClientData clientData, Tcl_Interp *interp,
255                              int argc, char **argv)
256 {
257     unsigned mask;
258
259     if (argc < 3)
260         return TCL_OK;
261     if (!strcmp (argv[1], "debug"))
262         mask = GW_LOG_DEBUG;
263     else if (!strcmp (argv[1], "fatal"))
264         mask = GW_LOG_FATAL;
265     else if (!strcmp (argv[1], "warn"))
266         mask = GW_LOG_WARN;
267     else if (!strcmp (argv[1], "acct"))
268         mask = GW_LOG_ACCT;
269     else
270         mask = GW_LOG_DEBUG;
271     switch (argc)
272     {
273     case 3:
274         gw_log (mask, mod, "%s", argv[2]);
275         break;
276     case 4:
277         gw_log (mask, mod, "%s %s", argv[2], argv[3]);
278         break;
279     case 5:
280         gw_log (mask, mod, "%s %s %s", argv[2], argv[3], argv[4]);
281         break;
282     case 6:
283         gw_log (mask, mod, "%s %s %s %s", argv[2], argv[3], argv[4], argv[5]);
284         break;
285     }
286     return TCL_OK;
287 }
288
289 static int proc_enc (ClientData clientData, Tcl_Interp *interp,
290                      int argc, char **argv)
291 {
292     int i;
293     char buf1[6];
294     char buf2[2];
295     
296     buf1[0] = '%';
297     buf2[1] = '\0';
298     for (i = 1; i<argc; i++)
299     {
300         const char *cp = argv[i];
301         while (*cp)
302         {
303             if (*cp <= ' ' || *cp >= 127 || *cp == '/' || *cp == ' ' ||
304                 *cp == '&' || *cp == ':' || *cp == '%')
305             {
306                 sprintf (buf1+1, "%02X", *cp & 0xff);
307                 Tcl_AppendResult (interp, buf1, NULL);
308             }
309             else
310             {
311                 buf2[0] = *cp;
312                 Tcl_AppendResult (interp, buf2, NULL);
313             }
314             cp++;
315         }
316     }
317     return TCL_OK;
318 }
319
320 static int proc_dec (ClientData clientData, Tcl_Interp *interp,
321                      int argc, char **argv)
322 {
323     int i;
324     unsigned val;
325     char buf[2];
326     
327     buf[1] = '\0';
328     for (i = 1; i<argc; i++)
329     {
330         const char *cp = argv[i];
331         while (*cp)
332         {
333             if (*cp == '%' && cp[1] && cp[2])
334             {
335                 if (cp[1] >= 'A')
336                     val = cp[1] - 'A'+10;
337                 else
338                     val = cp[1] - '0';
339
340                 
341                 if (cp[2] >= 'A')
342                     val = val*16 + (cp[2] - 'A'+10);
343                 else
344                     val = val*16 + (cp[2] - '0');
345                 buf[0] = val;
346                 cp += 3;
347             }
348             else
349                 buf[0] = *cp++;
350             Tcl_AppendResult (interp, buf, NULL);
351         }
352     }
353     return TCL_OK;
354 }
355
356 static int proc_prog (ClientData clientData, Tcl_Interp *interp,
357                       int argc, char **argv)
358 {
359     struct tcl_info *p = (struct tcl_info*) clientData;
360
361     Tcl_AppendResult (p->interp, p->wcl->prog, NULL);
362     return TCL_OK;
363 }
364
365 int Tcl_AppInit (Tcl_Interp *interp)
366 {
367     if (Tcl_Init (interp) == TCL_ERROR)
368         return TCL_ERROR;
369     return TCL_OK;
370 }
371
372 static void *do_create (WCLIENT wcl, void *args)
373 {
374     struct tcl_info *p;
375     char tmp_str[256];
376
377     if (!(p = malloc (sizeof(*p))))
378     {
379         gw_log (GW_LOG_FATAL|GW_LOG_ERRNO, mod, "malloc: tcl_info");
380         exit (1);
381     }
382     if (!(p->interp = Tcl_CreateInterp ()))
383     {
384         gw_log (GW_LOG_FATAL, mod, "Cannot make Tcl_Interp");
385         exit (1);
386     }
387     p->wcl = wcl;
388     p->fbuf_size = 1024;
389     if (!(p->fbuf = malloc (p->fbuf_size)))
390     {
391         gw_log (GW_LOG_FATAL|GW_LOG_ERRNO, mod, "malloc: tcl_info fbuf");
392         exit (1);
393     }
394     Tcl_AppInit (p->interp);
395     Tcl_CreateCommand (p->interp, "html", proc_html_invoke, p, NULL);
396     Tcl_CreateCommand (p->interp, "egw_form", proc_wform_invoke, p, NULL);
397     Tcl_CreateCommand (p->interp, "egw_parms", proc_parms_invoke, p, NULL);
398     Tcl_CreateCommand (p->interp, "egw_abort", proc_wabort_invoke, p, NULL);
399     Tcl_CreateCommand (p->interp, "egw_flush", proc_wflush_invoke, p, NULL);
400     Tcl_CreateCommand (p->interp, "egw_log", proc_wlog_invoke, p, NULL);
401     Tcl_CreateCommand (p->interp, "egw_enc", proc_enc, p, NULL);
402     Tcl_CreateCommand (p->interp, "egw_dec", proc_dec, p, NULL);
403     Tcl_CreateCommand (p->interp, "egw_prog", proc_prog, p, NULL);
404     sprintf (tmp_str, "%d", wcl->id);
405     Tcl_SetVar (p->interp, "sessionId", tmp_str, TCL_GLOBAL_ONLY);
406     return p;
407 }
408
409 static void report_error (struct tcl_info *p, int errorLine,
410                           const char *pre, const char *msg)
411 {
412     if (!msg)
413         msg = "";
414     gw_log (GW_LOG_WARN, mod, "%s %d %s", pre, errorLine, msg);
415     wo_printf (p->wcl, "\n<br><hr>\n<strong>"
416                "%s %d</strong><br>\n", pre, errorLine);
417     wo_printf (p->wcl, "<xmp>\n%s</xmp>\n<hr>\n", msg);
418 }
419
420 static int tcl_exec (const char *fname, char *parms,
421                      struct tcl_info *p, FILE *inf, int *lineno)
422 {
423     int c, escape = 0, level = 0;
424     int r, fbuf_ptr = 0;
425     int local_line = 0;
426
427     while (1)
428     {
429         if (fbuf_ptr == p->fbuf_size-1)
430         {
431             char *newb;
432
433             if (!(newb = malloc (p->fbuf_size += 16384)))
434             {
435                 gw_log (GW_LOG_FATAL|GW_LOG_ERRNO, mod, "malloc: fbuf");
436                 exit (1);
437             }
438             memcpy (newb, p->fbuf, fbuf_ptr);
439             free (p->fbuf);
440             p->fbuf = newb;
441         }
442         c = getc (inf);
443         if (c == EOF)
444         {
445             report_error (p, *lineno, "Error in Tcl script starting at line",
446                                       "Unexpected EOF (missing right brace)");
447             return TCL_ERROR;
448         }
449         if (c == '\\')
450             escape = 1;
451         else if (c == '{' && !escape)
452         {
453             level++;
454             escape = 0;
455         }
456         else if (c == '}' && !escape)
457         {
458             if (--level < 0)
459                 break;
460             escape = 0;
461         }
462         else
463         {
464             if (c == '\n')
465                 local_line++;
466             escape = 0;
467         }
468         p->fbuf[fbuf_ptr++] = c;
469     }
470     p->fbuf[fbuf_ptr] = '\0';
471     p->wabort = 0;
472     r = Tcl_Eval (p->interp, p->fbuf);
473     if (r == TCL_ERROR)
474         report_error (p, p->interp->errorLine + *lineno - 1, 
475                       "Error in Tcl script in line", 
476                       Tcl_GetVar (p->interp, "errorInfo", 0));
477     (*lineno) += local_line;
478     if (p->wabort)
479         return TCL_RETURN;
480     return r;
481 }
482
483 static int do_exec (const char *fname, char *parms, void *mydata)
484 {
485     struct tcl_info *p = mydata;
486     int c, escape = 0;
487     int lineno = 1;
488     FILE *inf = fopen (fname, "r");
489
490     gw_log (GW_LOG_DEBUG, mod, "Executing %s", fname);
491     if (!inf)
492     {
493         gw_log (GW_LOG_WARN|GW_LOG_ERRNO, mod, "open %s", fname);
494         return -1;
495     }
496     Tcl_SetVar (p->interp, "sessionParms", parms, TCL_GLOBAL_ONLY);
497     while ((c = getc(inf)) != EOF)
498     {
499         if (c == '\\')
500             escape = 1;
501         else if (c == '{')
502         {
503             if (escape)
504                 wo_putc (p->wcl, c);
505             else
506             {
507                 int r = tcl_exec (fname, parms, p, inf, &lineno);
508                 if (r == TCL_RETURN)
509                 {
510                     fclose (inf);
511                     return 0;
512                 }
513                 else if (r == TCL_ERROR)
514                 {
515                     fclose (inf);
516                     return -2;
517                 }
518             }
519             escape = 0;
520         }
521         else
522         {
523             if (c == '\n')
524                 lineno++;
525             escape = 0;
526             wo_putc (p->wcl, c);
527         }
528     }
529     fclose (inf);
530     return 0;
531 }
532
533
534 static int do_load (char *parms, void *mydata)
535 {
536     struct tcl_info *p = mydata;
537     char fname[80];
538     int r;
539
540     sprintf (fname, "tcl.state.%d", p->wcl->id);
541     r = Tcl_EvalFile (p->interp, fname);
542     if (r == TCL_ERROR)
543         gw_log (GW_LOG_WARN, mod, "Cannot load Tcl state" );
544     return 0;
545 }
546
547 static int do_save (char *parms, void *mydata)
548 {
549     struct tcl_info *p = mydata;
550     struct Tcl_CmdInfo cinfo;
551
552     if (Tcl_GetCommandInfo(p->interp, "saveState", &cinfo))
553     {
554         int r;
555
556         gw_log (GW_LOG_DEBUG, mod, "saveState");
557         r = Tcl_Eval (p->interp, "saveState\n");
558         if (r == TCL_ERROR)
559             report_error (p, p->interp->errorLine, 
560                           "Error in Tcl saveState in line", 
561                           Tcl_GetVar (p->interp, "errorInfo", 0));
562     }
563     return 0;
564 }
565