abf579d004a3d70b37d47b38aea61a528781769d
[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.11  1996/01/24 08:26:56  adam
45  * All tcl commands prefixed with egw_ (except the html command).
46  *
47  * Revision 1.10  1995/11/08  16:14:35  adam
48  * Many improvements and bug fixes.
49  * First version that ran on dtbsun.
50  *
51  * Revision 1.9  1995/11/07  14:57:00  adam
52  * Work on search in multiple targets.
53  * New wtcl command: wlog.
54  * Optional timeout parameter to zwait.
55  *
56  * Revision 1.8  1995/11/06  17:44:23  adam
57  * State reestablised when shell restarts. History of previous
58  * result sets.
59  *
60  * Revision 1.7  1995/10/31  16:56:25  adam
61  * Record presentation.
62  *
63  * Revision 1.6  1995/10/31  10:03:54  adam
64  * Work on queries.
65  * New command implemented - aborts script.
66  *
67  * Revision 1.5  1995/10/30  17:35:18  adam
68  * New function zwait that waits for a variable change - due to i/o events
69  * that invoke callback routines.
70  *
71  * Revision 1.4  1995/10/27  17:30:16  adam
72  * First search request/response that works.
73  *
74  * Revision 1.3  1995/10/27  15:12:14  adam
75  * IrTcl incorporated in the gateway.
76  * Better separation of script types.
77  * Z39.50 gateway scripts entered.
78  *
79  * Revision 1.2  1995/10/23  16:55:43  adam
80  * A lot of changes - really.
81  *
82  * Revision 1.1  1995/10/20  14:02:42  adam
83  * First version of WWW gateway with embedded Tcl.
84  *
85  */
86
87 #include <stdio.h>
88 #include <stdlib.h>
89 #include <string.h>
90 #include <assert.h>
91 #include <ctype.h>
92
93 #include "wtcl.h"
94
95 static void *do_create (WCLIENT wcl, void *args);
96 static int do_exec (const char *fname, char *parms, void *mydata);
97 static int do_load (char *parms, void *mydata);
98 static int do_save (char *parms, void *mydata);
99
100 static struct w_interp_type w_interp_t = {
101     "tcl",
102     do_create,
103     do_exec,
104     do_load,
105     do_save
106 };
107
108 W_Interp_Type w_interp_tcl = &w_interp_t;
109
110
111 static char *mod = "wtcl";
112
113 struct tcl_info {
114     Tcl_Interp *interp;
115     char  *fbuf;
116     int    fbuf_size;
117     int    fbuf_ptr;
118     int    wabort;
119     WCLIENT wcl;
120 };
121
122 Tcl_Interp *w_interp_tcl_get (W_Interp w_interp)
123 {
124     struct tcl_info *p;
125
126     if (strcmp (w_interp->ctrl->name, "tcl"))
127     {
128         gw_log (GW_LOG_FATAL, mod, "Internal failure");
129         assert (0);
130     }
131     p = (struct tcl_info*) w_interp->mydata;
132     return p->interp;
133 }
134
135 static int proc_wabort_invoke (ClientData clientData, Tcl_Interp *interp,
136                                int argc, char **argv)
137 {
138     struct tcl_info *p = (struct tcl_info*) clientData;
139
140     p->wabort = 1;
141     if (argc > 1)
142         Tcl_AppendResult (interp, argv[1], NULL);
143     return TCL_RETURN;
144 }
145
146 static int proc_wflush_invoke (ClientData clientData, Tcl_Interp *interp,
147                                int argc, char **argv)
148 {
149     struct tcl_info *p = (struct tcl_info*) clientData;
150
151     wo_flush (p->wcl);
152     return TCL_OK;
153 }
154
155 static int proc_html_invoke (ClientData clientData, Tcl_Interp *interp,
156                              int argc, char **argv)
157 {
158     struct tcl_info *p = (struct tcl_info*) clientData;
159     int i;
160
161     for (i = 1; i<argc; i++)
162         wo_write (p->wcl, argv[i], strlen(argv[i]));
163     return TCL_OK;
164 }
165
166 static int proc_wform_invoke (ClientData clientData, Tcl_Interp *interp,
167                               int argc, char **argv)
168 {
169     struct tcl_info *p = (struct tcl_info*) clientData;
170     int i;
171     if (argc == 2)
172     {
173         for (i = 0; *p->wcl->wf_data[i].name; i++)
174             if (!strcmp (argv[1], p->wcl->wf_data[i].name) && 
175                 *p->wcl->wf_data[i].value)
176                 Tcl_AppendElement (p->interp, p->wcl->wf_data[i].value);
177         return TCL_OK;
178     }    
179     for (i = 0; *p->wcl->wf_data[i].name; i++)
180     { 
181         Tcl_AppendResult (p->interp, "{ ", NULL);
182         Tcl_AppendElement (p->interp, p->wcl->wf_data[i].name);
183         Tcl_AppendElement (p->interp, p->wcl->wf_data[i].value);
184         Tcl_AppendResult (p->interp, " }\n", NULL);
185     }
186     return TCL_OK;
187 }
188
189 static int proc_wlog_invoke (ClientData clientData, Tcl_Interp *interp,
190                              int argc, char **argv)
191 {
192     unsigned mask;
193
194     if (argc < 3)
195         return TCL_OK;
196     if (!strcmp (argv[1], "debug"))
197         mask = GW_LOG_DEBUG;
198     else if (!strcmp (argv[1], "fatal"))
199         mask = GW_LOG_FATAL;
200     else if (!strcmp (argv[1], "warn"))
201         mask = GW_LOG_WARN;
202     else if (!strcmp (argv[1], "acct"))
203         mask = GW_LOG_ACCT;
204     else
205         mask = GW_LOG_DEBUG;
206     switch (argc)
207     {
208     case 3:
209         gw_log (mask, mod, "%s", argv[2]);
210         break;
211     case 4:
212         gw_log (mask, mod, "%s %s", argv[2], argv[3]);
213         break;
214     case 5:
215         gw_log (mask, mod, "%s %s %s", argv[2], argv[3], argv[4]);
216         break;
217     case 6:
218         gw_log (mask, mod, "%s %s %s %s", argv[2], argv[3], argv[4], argv[5]);
219         break;
220     }
221     return TCL_OK;
222 }
223
224
225 int Tcl_AppInit (Tcl_Interp *interp)
226 {
227     if (Tcl_Init (interp) == TCL_ERROR)
228         return TCL_ERROR;
229     return TCL_OK;
230 }
231
232 static void *do_create (WCLIENT wcl, void *args)
233 {
234     struct tcl_info *p;
235     char tmp_str[256];
236
237     if (!(p = malloc (sizeof(*p))))
238     {
239         gw_log (GW_LOG_FATAL|GW_LOG_ERRNO, mod, "malloc: tcl_info");
240         exit (1);
241     }
242     if (!(p->interp = Tcl_CreateInterp ()))
243     {
244         gw_log (GW_LOG_FATAL, mod, "Cannot make Tcl_Interp");
245         exit (1);
246     }
247     p->wcl = wcl;
248     p->fbuf_size = 1024;
249     if (!(p->fbuf = malloc (p->fbuf_size)))
250     {
251         gw_log (GW_LOG_FATAL|GW_LOG_ERRNO, mod, "malloc: tcl_info fbuf");
252         exit (1);
253     }
254     Tcl_AppInit (p->interp);
255     Tcl_CreateCommand (p->interp, "html", proc_html_invoke, p, NULL);
256     Tcl_CreateCommand (p->interp, "egw_form", proc_wform_invoke, p, NULL);
257     Tcl_CreateCommand (p->interp, "egw_abort", proc_wabort_invoke, p, NULL);
258     Tcl_CreateCommand (p->interp, "egw_flush", proc_wflush_invoke, p, NULL);
259     Tcl_CreateCommand (p->interp, "egw_log", proc_wlog_invoke, p, NULL);
260     sprintf (tmp_str, "%d", wcl->id);
261     Tcl_SetVar (p->interp, "sessionId", tmp_str, TCL_GLOBAL_ONLY);
262     return p;
263 }
264
265 static void report_error (struct tcl_info *p, int errorLine,
266                           const char *pre, const char *msg)
267 {
268     if (!msg)
269         msg = "";
270     gw_log (GW_LOG_WARN, mod, "%s %d %s", pre, errorLine, msg);
271     wo_printf (p->wcl, "\n<br><hr>\n<strong>"
272                "%s %d</strong><br>\n", pre, errorLine);
273     wo_printf (p->wcl, "<xmp>\n%s</xmp>\n<hr>\n", msg);
274 }
275
276 static int tcl_exec (const char *fname, char *parms,
277                      struct tcl_info *p, FILE *inf, int *lineno)
278 {
279     int c, escape = 0, level = 0;
280     int r, fbuf_ptr = 0;
281     int local_line = 0;
282
283     while (1)
284     {
285         if (fbuf_ptr == p->fbuf_size-1)
286         {
287             char *newb;
288
289             if (!(newb = malloc (p->fbuf_size += 16384)))
290             {
291                 gw_log (GW_LOG_FATAL|GW_LOG_ERRNO, mod, "malloc: fbuf");
292                 exit (1);
293             }
294             memcpy (newb, p->fbuf, fbuf_ptr);
295             free (p->fbuf);
296             p->fbuf = newb;
297         }
298         c = getc (inf);
299         if (c == EOF)
300         {
301             report_error (p, *lineno, "Error in Tcl script starting at line",
302                                       "Unexpected EOF (missing right brace)");
303             return TCL_ERROR;
304         }
305         if (c == '\\')
306             escape = 1;
307         else if (c == '{' && !escape)
308         {
309             level++;
310             escape = 0;
311         }
312         else if (c == '}' && !escape)
313         {
314             if (--level < 0)
315                 break;
316             escape = 0;
317         }
318         else
319         {
320             if (c == '\n')
321                 local_line++;
322             escape = 0;
323         }
324         p->fbuf[fbuf_ptr++] = c;
325     }
326     p->fbuf[fbuf_ptr] = '\0';
327     p->wabort = 0;
328     r = Tcl_Eval (p->interp, p->fbuf);
329     if (r == TCL_ERROR)
330         report_error (p, p->interp->errorLine + *lineno - 1, 
331                       "Error in Tcl script in line", 
332                       Tcl_GetVar (p->interp, "errorInfo", 0));
333     (*lineno) += local_line;
334     if (p->wabort)
335         return TCL_RETURN;
336     return r;
337 }
338
339 static int do_exec (const char *fname, char *parms, void *mydata)
340 {
341     struct tcl_info *p = mydata;
342     int c, escape = 0;
343     int lineno = 1;
344     FILE *inf = fopen (fname, "r");
345
346     gw_log (GW_LOG_DEBUG, mod, "Executing %s", fname);
347     if (!inf)
348     {
349         gw_log (GW_LOG_WARN|GW_LOG_ERRNO, mod, "open %s", fname);
350         return -1;
351     }
352     Tcl_SetVar (p->interp, "sessionParms", parms, TCL_GLOBAL_ONLY);
353     while ((c = getc(inf)) != EOF)
354     {
355         if (c == '\\')
356             escape = 1;
357         else if (c == '{')
358         {
359             if (escape)
360                 wo_putc (p->wcl, c);
361             else
362             {
363                 int r = tcl_exec (fname, parms, p, inf, &lineno);
364                 if (r == TCL_RETURN)
365                 {
366                     fclose (inf);
367                     return 0;
368                 }
369                 else if (r == TCL_ERROR)
370                 {
371                     fclose (inf);
372                     return -2;
373                 }
374             }
375             escape = 0;
376         }
377         else
378         {
379             if (c == '\n')
380                 lineno++;
381             escape = 0;
382             wo_putc (p->wcl, c);
383         }
384     }
385     fclose (inf);
386     return 0;
387 }
388
389
390 static int do_load (char *parms, void *mydata)
391 {
392     struct tcl_info *p = mydata;
393     char fname[80];
394     int r;
395
396     sprintf (fname, "tcl.state.%d", p->wcl->id);
397     r = Tcl_EvalFile (p->interp, fname);
398     if (r == TCL_ERROR)
399         gw_log (GW_LOG_WARN, mod, "Cannot load Tcl state" );
400     return 0;
401 }
402
403 static int do_save (char *parms, void *mydata)
404 {
405     struct tcl_info *p = mydata;
406     struct Tcl_CmdInfo cinfo;
407
408     if (Tcl_GetCommandInfo(p->interp, "saveState", &cinfo))
409     {
410         int r;
411
412         gw_log (GW_LOG_DEBUG, mod, "saveState");
413         r = Tcl_Eval (p->interp, "saveState\n");
414         if (r == TCL_ERROR)
415             report_error (p, p->interp->errorLine, 
416                           "Error in Tcl saveState in line", 
417                           Tcl_GetVar (p->interp, "errorInfo", 0));
418     }
419     return 0;
420 }
421