Updated version to 0.2.1.
[tclrobot.git] / hswitch.c
1 /*
2  * $Id: hswitch.c,v 1.5 2001/11/08 10:23:02 adam Exp $
3  */
4 #include <assert.h>
5 #include <string.h>
6 #include <stdlib.h>
7 #include <ctype.h>
8
9 #include "tclrobot.h"
10
11 #define TAG_MAX_LEN 64
12
13 #define SPACECHR " \t\r\n\f"
14
15 #define DEBUG(x)
16
17 static int skipSpace (const char *cp)
18 {
19     int i = 0;
20     while (cp[i] && strchr (SPACECHR, cp[i]))
21         i++;
22     return i;
23 }
24
25 static int skipTag (const char *cp, char *dst)
26 {
27     int i;
28     int j = 0;
29
30     for (i=0; cp[i] && !strchr (SPACECHR "/>=", cp[i]); i++)
31         if (j < TAG_MAX_LEN-1)
32         {
33             dst[j] = tolower(cp[j]);
34             j++;
35         }
36     dst[j] = '\0';
37     return i;
38 }
39
40 static int skipParm (const char *cp, char *name, char **value)
41 {
42     int i = skipTag (cp, name);   
43     *value = NULL;
44     if (!i)
45         return skipSpace (cp);
46     i += skipSpace (cp + i);
47     if (cp[i] == '=')
48     {
49         int v0, v1;
50         i++;
51         i += skipSpace (cp + i);
52         if (cp[i] == '\"')
53         {
54             v0 = ++i;
55             while (cp[i] != '\"' && cp[i])
56                 i++; 
57             v1 = i;
58             if (cp[i])
59                 i++;
60         }
61         else
62         {
63             v0 = i;
64             while (cp[i] && !strchr (SPACECHR ">", cp[i]))
65                 i++;
66             v1 = i;
67         }
68         *value = malloc (v1 - v0 + 1);
69         memcpy (*value, cp + v0, v1-v0);
70         (*value)[v1-v0] = '\0';
71     }
72     i += skipSpace (cp + i);
73     return i;
74 }
75
76 struct tagParm {
77     char name[TAG_MAX_LEN];
78     char *value;
79     struct tagParm *next;
80 };
81
82 struct tagInfo {
83     int level;
84     int nest;
85     char *pattern;
86     char *code;
87
88     char name[TAG_MAX_LEN];
89     const char *body_start;
90     struct tagParm *tagParms;
91 };
92
93 static int tagLookup (struct tagInfo *tags, int tagNo, const char *tagString)
94 {
95     int i;
96     for (i = 0; i<tagNo; i++)
97         if (!strcmp (tags[i].pattern, tagString))
98              return i;
99     return -1;
100 }
101
102 static int tagStart (struct tagInfo *tag, const char *tagString,
103                      const char *cp)
104 {
105     int i;
106     char parm_name[TAG_MAX_LEN];
107     char *parm_value;
108     struct tagParm **nParms;
109
110     if (tag && !tag->level)
111     {
112         strcpy (tag->name, tagString);
113         DEBUG(printf ("------ consuming this %s\n", tag->name));
114         tag->tagParms = NULL;
115         nParms = &tag->tagParms;
116     }
117
118     i = skipSpace (cp);
119     while (cp[i] && cp[i] != '>')
120     {
121         int nor = skipParm (cp+i, parm_name, &parm_value);
122         i += nor;
123         if (nor && tag)
124         {
125             DEBUG(printf ("parm_name=%s parm_value=%s\n", parm_name, parm_value));
126         }
127         if (nor && tag && !tag->level)
128         {
129             *nParms = malloc (sizeof(**nParms));
130             assert (*nParms);
131             strcpy ((*nParms)->name, parm_name);
132             (*nParms)->value = parm_value;
133             (*nParms)->next = NULL;
134             nParms = &(*nParms)->next;
135         }
136         else
137         {
138             if (!nor)
139                 i++;
140             free (parm_value);
141         }
142     }
143     if (cp[i])
144         i++;
145     if (tag)
146     {
147         if (!tag->level)
148             tag->body_start = cp+i;
149         ++(tag->level);
150     }
151     return i;
152 }
153
154 static int tagEnd (Tcl_Interp *interp, struct tagInfo *tag,
155                    const char *tagString, const char *cp, const char *body_end)
156 {
157     int i = 0;
158
159     if (cp[i] == '>')
160         i++;
161
162     if (tag && tag->level)
163     {
164         -- (tag->level);
165         if (!tag->level)
166         {
167             int tcl_err;
168             struct tagParm *tp = tag->tagParms;
169             char *value = malloc (body_end - tag->body_start + 1);
170
171             assert (value);
172             memcpy (value, tag->body_start, body_end - tag->body_start);
173             value[body_end - tag->body_start] = '\0';
174             Tcl_SetVar (interp, "body", value, 0);
175             while (tp)
176             {
177                 char vname[TAG_MAX_LEN+30];
178                 struct tagParm *tp0 = tp;
179                 
180                 sprintf (vname, "parm(%s)", tp->name);
181                 DEBUG(printf ("vname=%s\n", vname));
182
183                 Tcl_SetVar (interp, vname, tp->value ? tp->value : "",0);
184                 tp = tp->next;
185                 free (tp0);
186             }
187             tcl_err = Tcl_Eval (interp, tag->code);
188             free (value);
189             if (tcl_err == TCL_ERROR)
190             {
191                 printf ("Error: code=%d %s\n", tcl_err, interp->result);
192                 exit (1);
193             }
194         }
195     }
196     return i;
197 }
198
199 int htmlSwitch (ClientData clientData, Tcl_Interp *interp,
200                 int argc, char **argv)
201 {
202     struct tagInfo *tags;
203     int noTags;
204     const char *cp;
205     int i = 0, argi = 1;
206
207     cp = argv[argi++];
208     noTags = (argc - argi)/2;
209     if (noTags < 1)
210     {
211         interp->result =
212             "wrong # args: should be ?switches? string pattern body ...";
213         return TCL_ERROR;
214     }
215     tags = malloc (sizeof(*tags) * noTags);
216     assert (tags);
217     while (argi < argc-1)
218     {
219         tags[i].level = 0;
220         tags[i].nest = 1;
221         if (!strcmp(argv[argi], "-nonest"))
222         {
223             argi++;
224             tags[i].nest = 0;
225         }
226         else if (!strcmp(argv[argi], "-nest"))
227         {
228             argi++;
229             tags[i].nest = 1;
230         }
231         tags[i].pattern = argv[argi++];
232         tags[i].code = argv[argi++];
233         i++;
234     }
235     noTags = i;
236     while (*cp)
237     {
238         if (cp[0] == '<' && cp[1] != '/')     /* start tag */
239         {
240             char tagStr[TAG_MAX_LEN];
241             int tagI;
242             const char *body_start = cp;
243
244             cp++;
245             cp += skipTag (cp, tagStr);
246             tagI = tagLookup (tags, noTags, tagStr);
247             DEBUG(printf ("tagStr = %s tagI = %d\n", tagStr, tagI));
248             cp += tagStart (tagI >= 0 ? tags+tagI : NULL, tagStr, cp);
249             if (tagI >= 0 && tags[tagI].nest == 0)
250             {
251                 cp += tagEnd (interp, tags+tagI, tagStr, body_start, cp);
252             }
253         }
254         else if (cp[0] == '<' && cp[1] == '/')/* end tag */
255         {
256             char tagStr[TAG_MAX_LEN];
257             const char *body_end = cp;
258             int tagI;
259
260             cp += 2;
261             cp += skipTag (cp, tagStr);
262             tagI = tagLookup (tags, noTags, tagStr);
263             cp += tagEnd (interp, tagI >= 0 ? tags+tagI : NULL,
264                           tagStr, cp, body_end);
265         }
266         else                                  /* no tag */
267             cp++;
268     }
269     free (tags);
270     return TCL_OK;
271 }
272
273