~ubuntu-branches/ubuntu/karmic/edbrowse/karmic

« back to all changes in this revision

Viewing changes to html.c

  • Committer: Bazaar Package Importer
  • Author(s): Kapil Hari Paranjape
  • Date: 2008-04-09 18:55:23 UTC
  • mfrom: (1.1.4 upstream) (3.1.1 lenny)
  • Revision ID: james.westby@ubuntu.com-20080409185523-dqokcloumyn1ibn4
Tags: 3.3.4-1
* New upstream version (3.3.4).
 - Convert between iso8859-1 and utf-8 on the fly.
 - Support reading of pdf using pdftohtml.
 - French translation of html documentation.
 - Old html documentation renamed to usersguide.
 - Additional documentation on philosophy.
* debian/control:
 - Changed homepage to sourcefource site.
 - Moved homepage from description to its own field.
 - Added "poppler-utils | xpdf-utils" to Recommends.
 - Added "www-browser", "mail-reader" and "editor" to Provides. 
 - Removed "XS-" from Vcs-Svn tag.
 - Standards-Version: 3.7.3
* debian/docs: Added new documentation files
  from "doc/" subdirectory.
* debian/watch: Updated to use sourceforge site.
* debian/edbrowse.doc-base:
  - Changed name of upstream provided html documentation from
    "ebdoc.html" to "usersguide.html".
  - Changed section from "net" to "Network/Web Browsing".
* debian/install: Compiled binary is now in "src/".

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* html.c
2
 
 * Parse html tags.
3
 
 * Copyright (c) Karl Dahlke, 2006
4
 
 * This file is part of the edbrowse project, released under GPL.
5
 
 */
6
 
 
7
 
#include "eb.h"
8
 
 
9
 
/* Close an open anchor when you see this tag. */
10
 
#define TAG_CLOSEA 1
11
 
/* You won't see the text between <foo> and </fooo> */
12
 
#define TAG_INVISIBLE 2
13
 
/* sometimes </foo> means nothing. */
14
 
#define TAG_NOSLASH 4
15
 
 
16
 
enum {
17
 
    INP_RESET, INP_BUTTON, INP_IMAGE, INP_SUBMIT,
18
 
    INP_HIDDEN,
19
 
    INP_TEXT, INP_PW, INP_NUMBER, INP_FILE,
20
 
    INP_SELECT, INP_TA, INP_RADIO, INP_CHECKBOX,
21
 
};
22
 
 
23
 
enum {
24
 
    TAGACT_ZERO, TAGACT_A, TAGACT_INPUT, TAGACT_TITLE, TAGACT_TA,
25
 
    TAGACT_BUTTON, TAGACT_SELECT, TAGACT_OPTION,
26
 
    TAGACT_NOP, TAGACT_JS, TAGACT_H, TAGACT_SUB, TAGACT_SUP,
27
 
    TAGACT_DW, TAGACT_BODY, TAGACT_HEAD,
28
 
    TAGACT_MUSIC, TAGACT_IMAGE, TAGACT_BR, TAGACT_IBR, TAGACT_P,
29
 
    TAGACT_BASE, TAGACT_META, TAGACT_PRE,
30
 
    TAGACT_DT, TAGACT_LI, TAGACT_HR, TAGACT_TABLE, TAGACT_TR, TAGACT_TD,
31
 
    TAGACT_DIV, TAGACT_SPAN,
32
 
    TAGACT_FORM, TAGACT_FRAME,
33
 
    TAGACT_MAP, TAGACT_AREA, TAGACT_SCRIPT, TAGACT_EMBED, TAGACT_OBJ,
34
 
};
35
 
 
36
 
static const char *const inp_types[] = {
37
 
    "reset", "button", "image", "submit",
38
 
    "hidden",
39
 
    "text", "password", "number", "file",
40
 
    "select", "textarea", "radio", "checkbox",
41
 
    0
42
 
};
43
 
 
44
 
static const char dfvl[] = "defaultValue";
45
 
static const char dfck[] = "defaultChecked";
46
 
 
47
 
static struct listHead htmlStack;
48
 
static struct htmlTag **tagArray, *topTag;
49
 
static int ntags;               /* number of tags in this page */
50
 
static char *topAttrib;
51
 
static char *basehref;
52
 
static struct htmlTag *currentForm;     /* the open form */
53
 
bool parsePage;                 /* parsing html */
54
 
int browseLine;                 /* for error reporting */
55
 
static char *radioChecked;
56
 
static int radioChecked_l;
57
 
static char *preamble;
58
 
static int preamble_l;
59
 
 
60
 
static void htmlName(void);
61
 
 
62
 
/* Switch from the linked list of tags to an array. */
63
 
static void
64
 
buildTagArray(void)
65
 
{
66
 
    int j = 0;
67
 
    struct htmlTag *t;
68
 
    if(!parsePage)
69
 
        return;
70
 
    if(tagArray)
71
 
        nzFree(tagArray);
72
 
    tagArray = allocMem(sizeof (struct htmlTag *) * ((ntags + 32) & ~31));
73
 
    cw->tags = tagArray;
74
 
    foreach(t, htmlStack)
75
 
       tagArray[j++] = t;
76
 
    tagArray[j] = 0;
77
 
}                               /* buildTagArray */
78
 
 
79
 
static bool
80
 
htmlAttrPresent(const char *e, const char *name)
81
 
{
82
 
    char *a;
83
 
    if(!(a = htmlAttrVal(e, name)))
84
 
        return false;
85
 
    nzFree(a);
86
 
    return true;
87
 
}                               /* htmlAttrPresent */
88
 
 
89
 
static char *
90
 
hrefVal(const char *e, const char *name)
91
 
{
92
 
    char *a;
93
 
    htmlAttrVal_nl = true;
94
 
    a = htmlAttrVal(e, name);
95
 
    htmlAttrVal_nl = false;     /* put it back */
96
 
    return a;
97
 
}                               /* hrefVal */
98
 
 
99
 
static char *
100
 
targetVal(const char *e)
101
 
{
102
 
    return htmlAttrVal(e, "target");
103
 
}                               /* targetVal */
104
 
 
105
 
static void
106
 
toPreamble(int tagno, const char *msg, const char *j, const char *h)
107
 
{
108
 
    char buf[120];
109
 
    sprintf(buf, "\r\200%d{%s", tagno, msg);
110
 
    if(h) {
111
 
        strcat(buf, ": ");
112
 
        strcat(buf, h);
113
 
    } else if(j) {
114
 
        char fn[40];
115
 
        char *s;
116
 
        skipWhite(&j);
117
 
        if(memEqualCI(j, "javascript:", 11))
118
 
            j += 11;
119
 
        skipWhite(&j);
120
 
        if(isalphaByte(*j) || *j == '_') {
121
 
            fn[0] = ':';
122
 
            fn[1] = ' ';
123
 
            for(s = fn + 2; isalnumByte(*j) || *j == '_'; ++j) {
124
 
                if(s < fn + sizeof (fn) - 3)
125
 
                    *s++ = *j;
126
 
            }
127
 
            strcpy(s, "()");
128
 
            skipWhite(&j);
129
 
            if(*j == '(')
130
 
                strcat(buf, fn);
131
 
        }
132
 
    }
133
 
    strcat(buf, "\2000}\r");
134
 
    if(!preamble)
135
 
        preamble = initString(&preamble_l);
136
 
    stringAndString(&preamble, &preamble_l, buf);
137
 
}                               /* toPreamble */
138
 
 
139
 
struct tagInfo {
140
 
    const char *name;
141
 
    const char *desc;
142
 
    int action;
143
 
    uchar nest;                 /* must nest, like parentheses */
144
 
    uchar para;                 /* paragraph and line breaks */
145
 
    ushort bits;                /* a bunch of boolean attributes */
146
 
};
147
 
 
148
 
static const struct tagInfo elements[] = {
149
 
    {"BASE", "base reference for relative URLs", TAGACT_BASE, 0, 0, 5},
150
 
    {"A", "an anchor", TAGACT_A, 1, 0, 1},
151
 
    {"INPUT", "an input item", TAGACT_INPUT, 0, 0, 5},
152
 
    {"TITLE", "the title", TAGACT_TITLE, 1, 0, 1},
153
 
    {"TEXTAREA", "an input text area", TAGACT_TA, 1, 0, 1},
154
 
    {"SELECT", "an option list", TAGACT_SELECT, 1, 0, 1},
155
 
    {"OPTION", "a select option", TAGACT_OPTION, 0, 0, 5},
156
 
    {"SUB", "a subscript", TAGACT_SUB, 3, 0, 0},
157
 
    {"SUP", "a superscript", TAGACT_SUP, 3, 0, 0},
158
 
    {"FONT", "a font", TAGACT_NOP, 3, 0, 0},
159
 
    {"CENTER", "centered text", TAGACT_NOP, 3, 0, 0},
160
 
    {"DOCWRITE", "document.write() text", TAGACT_DW, 0, 0, 0},
161
 
    {"CAPTION", "a caption", TAGACT_NOP, 1, 5, 0},
162
 
    {"HEAD", "the html header information", TAGACT_HEAD, 1, 0, 5},
163
 
    {"BODY", "the html body", TAGACT_BODY, 1, 0, 5},
164
 
    {"BGSOUND", "background music", TAGACT_MUSIC, 0, 0, 5},
165
 
    {"AUDIO", "audio passage", TAGACT_MUSIC, 0, 0, 5},
166
 
    {"META", "a meta tag", TAGACT_META, 0, 0, 4},
167
 
    {"IMG", "an image", TAGACT_IMAGE, 0, 0, 4},
168
 
    {"IMAGE", "an image", TAGACT_IMAGE, 0, 0, 4},
169
 
    {"BR", "a line break", TAGACT_BR, 0, 1, 4},
170
 
    {"P", "a paragraph", TAGACT_NOP, 0, 2, 5},
171
 
    {"DIV", "a divided section", TAGACT_DIV, 3, 5, 0},
172
 
    {"HTML", "html", TAGACT_NOP, 0, 0, 0},
173
 
    {"BLOCKQUOTE", "a quoted paragraph", TAGACT_NOP, 1, 10, 1},
174
 
    {"H1", "a level 1 header", TAGACT_NOP, 1, 10, 1},
175
 
    {"H2", "a level 2 header", TAGACT_NOP, 1, 10, 1},
176
 
    {"H3", "a level 3 header", TAGACT_NOP, 1, 10, 1},
177
 
    {"H4", "a level 4 header", TAGACT_NOP, 1, 10, 1},
178
 
    {"H5", "a level 5 header", TAGACT_NOP, 1, 10, 1},
179
 
    {"H6", "a level 6 header", TAGACT_NOP, 1, 10, 1},
180
 
    {"DT", "a term", TAGACT_DT, 0, 2, 5},
181
 
    {"DD", "a definition", TAGACT_DT, 0, 1, 5},
182
 
    {"LI", "a list item", TAGACT_LI, 0, 1, 5},
183
 
    {"UL", "a bullet list", TAGACT_NOP, 3, 5, 1},
184
 
    {"DIR", "a directory list", TAGACT_NOP, 3, 5, 1},
185
 
    {"MENU", "a menu", TAGACT_NOP, 3, 5, 1},
186
 
    {"OL", "a numbered list", TAGACT_NOP, 3, 5, 1},
187
 
    {"DL", "a definition list", TAGACT_NOP, 3, 5, 1},
188
 
    {"HR", "a horizontal line", TAGACT_HR, 0, 5, 5},
189
 
    {"FORM", "a form", TAGACT_FORM, 1, 0, 1},
190
 
    {"BUTTON", "a button", TAGACT_BUTTON, 0, 0, 5},
191
 
    {"FRAME", "a frame", TAGACT_FRAME, 0, 2, 5},
192
 
    {"IFRAME", "a frame", TAGACT_FRAME, 0, 2, 5},
193
 
    {"MAP", "an image map", TAGACT_MAP, 0, 2, 5},
194
 
    {"AREA", "an image map area", TAGACT_AREA, 0, 0, 1},
195
 
    {"TABLE", "a table", TAGACT_TABLE, 3, 10, 1},
196
 
    {"TR", "a table row", TAGACT_TR, 3, 5, 1},
197
 
    {"TD", "a table entry", TAGACT_TD, 3, 0, 1},
198
 
    {"TH", "a table heading", TAGACT_TD, 1, 0, 1},
199
 
    {"PRE", "a preformatted section", TAGACT_PRE, 1, 1, 0},
200
 
    {"LISTING", "a listing", TAGACT_PRE, 1, 1, 0},
201
 
    {"XMP", "an example", TAGACT_PRE, 1, 1, 0},
202
 
    {"FIXED", "a fixed presentation", TAGACT_NOP, 1, 1, 0},
203
 
    {"CODE", "a block of code", TAGACT_NOP, 1, 0, 0},
204
 
    {"SAMP", "a block of sample text", TAGACT_NOP, 1, 0, 0},
205
 
    {"ADDRESS", "an address block", TAGACT_NOP, 1, 1, 0},
206
 
    {"STYLE", "a style block", TAGACT_NOP, 1, 0, 2},
207
 
    {"SCRIPT", "a script", TAGACT_SCRIPT, 0, 0, 1},
208
 
    {"NOSCRIPT", "no script section", TAGACT_NOP, 1, 0, 3},
209
 
    {"NOFRAMES", "no frames section", TAGACT_NOP, 1, 0, 3},
210
 
    {"EMBED", "embedded html", TAGACT_MUSIC, 1, 0, 3},
211
 
    {"NOEMBED", "no embed section", TAGACT_NOP, 1, 0, 3},
212
 
    {"OBJECT", "an html object", TAGACT_OBJ, 0, 0, 3},
213
 
    {"EM", "emphasized text", TAGACT_JS, 1, 0, 0},
214
 
    {"LABEL", "a label", TAGACT_JS, 1, 0, 0},
215
 
    {"STRIKE", "emphasized text", TAGACT_JS, 1, 0, 0},
216
 
    {"S", "emphasized text", TAGACT_JS, 1, 0, 0},
217
 
    {"STRONG", "emphasized text", TAGACT_JS, 1, 0, 0},
218
 
    {"B", "bold text", TAGACT_JS, 1, 0, 0},
219
 
    {"I", "italicized text", TAGACT_JS, 1, 0, 0},
220
 
    {"U", "underlined text", TAGACT_JS, 1, 0, 0},
221
 
    {"DFN", "definition text", TAGACT_JS, 1, 0, 0},
222
 
    {"Q", "quoted text", TAGACT_JS, 1, 0, 0},
223
 
    {"ABBR", "an abbreviation", TAGACT_JS, 1, 0, 0},
224
 
    {"SPAN", "an html span", TAGACT_SPAN, 1, 0, 0},
225
 
    {"FRAMESET", "a frame set", TAGACT_JS, 3, 0, 1},
226
 
    {NULL, NULL, 0}
227
 
};
228
 
 
229
 
struct htmlTag {
230
 
    struct htmlTag *next, *prev;
231
 
    void *jv;                   /* corresponding java variable */
232
 
    int seqno;
233
 
    int ln;                     /* line number */
234
 
    int lic;                    /* list item count, highly overloaded */
235
 
    int action;
236
 
    const struct tagInfo *info;
237
 
/* the form that owns this input tag, etc */
238
 
    struct htmlTag *controller;
239
 
    bool slash:1;               /* as in </A> */
240
 
    bool balanced:1;            /* <foo> and </foo> */
241
 
    bool retain:1;
242
 
    bool multiple:1;
243
 
    bool rdonly:1;
244
 
    bool clickable:1;           /* but not an input field */
245
 
    bool secure:1;
246
 
    bool checked:1;
247
 
    bool rchecked:1;            /* for reset */
248
 
    bool post:1;                /* post, rather than get */
249
 
    bool javapost:1;            /* post by calling javascript */
250
 
    bool mime:1;                /* encode as mime, rather than url encode */
251
 
    bool bymail:1;              /* send by mail, rather than http */
252
 
    bool submitted:1;
253
 
    bool handler:1;
254
 
    uchar itype;                /* input type = */
255
 
    short ninp;                 /* number of nonhidden inputs */
256
 
    char *attrib;
257
 
    char *name, *id, *value, *href;
258
 
    const char *inner;          /* for inner html */
259
 
};
260
 
 
261
 
static const char *const handlers[] = {
262
 
    "onmousemove", "onmouseover", "onmouseout", "onmouseup", "onmousedown",
263
 
    "onclick", "ondblclick", "onblur", "onfocus",
264
 
    "onchange", "onsubmit", "onreset",
265
 
    "onload", "onunload",
266
 
    "onkeypress", "onkeyup", "onkeydown",
267
 
    0
268
 
};
269
 
 
270
 
static void
271
 
freeTag(struct htmlTag *e)
272
 
{
273
 
    nzFree(e->attrib);
274
 
    nzFree(e->name);
275
 
    nzFree(e->id);
276
 
    nzFree(e->value);
277
 
    nzFree(e->href);
278
 
    free(e);
279
 
}                               /* freeTag */
280
 
 
281
 
void
282
 
freeTags(void *a)
283
 
{
284
 
    int n;
285
 
    struct ebWindow *w;
286
 
    struct htmlTag *t, **e;
287
 
 
288
 
    if(!(e = a))
289
 
        return;                 /* no tags */
290
 
 
291
 
/* drop empty textarea buffers created by this session */
292
 
    for(e = a; t = *e; ++e) {
293
 
        if(t->action != TAGACT_INPUT)
294
 
            continue;
295
 
        if(t->itype != INP_TA)
296
 
            continue;
297
 
        if(!(n = t->lic))
298
 
            continue;
299
 
        if(!(w = sessionList[n].lw))
300
 
            continue;
301
 
        if(w->fileName)
302
 
            continue;
303
 
        if(w->dol)
304
 
            continue;
305
 
        if(w != sessionList[n].fw)
306
 
            continue;
307
 
/* We could have added a line, then deleted it */
308
 
        w->changeMode = false;
309
 
        cxQuit(n, 2);
310
 
    }                           /* loop over tags */
311
 
 
312
 
    for(e = a; t = *e; ++e)
313
 
        freeTag(t);
314
 
    free(a);
315
 
}                               /* freeTags */
316
 
 
317
 
static void
318
 
get_js_event(const char *name)
319
 
{
320
 
    void *ev = topTag->jv;
321
 
    char *s;
322
 
 
323
 
    int action = topTag->action;
324
 
    int itype = topTag->itype;
325
 
    if(!(s = htmlAttrVal(topAttrib, name)))
326
 
        return;                 /* not there */
327
 
    if(ev)
328
 
        handlerSet(ev, name, s);
329
 
    nzFree(s);
330
 
 
331
 
/* This code is only here to print warnings if js is disabled
332
 
 * or otherwise broken.  Record the fact that handlers exist,
333
 
 * outside the context of js. */
334
 
    if(stringEqual(name, "onclick")) {
335
 
        if(action == TAGACT_A || action == TAGACT_AREA || action == TAGACT_FRAME
336
 
           || action == TAGACT_INPUT && (itype >= INP_RADIO ||
337
 
           itype <= INP_SUBMIT) || action == TAGACT_OPTION) {
338
 
            topTag->handler = true;
339
 
            if(currentForm && action == TAGACT_INPUT && itype == INP_BUTTON)
340
 
                currentForm->submitted = true;
341
 
        }
342
 
    }
343
 
    if(stringEqual(name, "onsubmit") || stringEqual(name, "onreset")) {
344
 
        if(action == TAGACT_FORM)
345
 
            topTag->handler = true;
346
 
    }
347
 
    if(stringEqual(name, "onchange")) {
348
 
        if(action == TAGACT_INPUT || action == TAGACT_SELECT) {
349
 
            if(itype == INP_TA)
350
 
                runningError(MSG_OnchangeText);
351
 
            else if(itype > INP_HIDDEN && itype <= INP_SELECT) {
352
 
                topTag->handler = true;
353
 
                if(currentForm)
354
 
                    currentForm->submitted = true;
355
 
            }
356
 
        }
357
 
    }
358
 
}                               /* get_js_event */
359
 
 
360
 
static bool strayClick;
361
 
 
362
 
static void
363
 
get_js_events(void)
364
 
{
365
 
    void *ev = topTag->jv;
366
 
    int j;
367
 
    const char *t;
368
 
    int action = topTag->action;
369
 
    int itype = topTag->itype;
370
 
 
371
 
    for(j = 0; t = handlers[j]; ++j)
372
 
        get_js_event(t);
373
 
 
374
 
    if(!ev)
375
 
        return;
376
 
 
377
 
/* Some warnings about some handlers that we just don't "handle" */
378
 
    if(handlerPresent(ev, "onkeypress") ||
379
 
       handlerPresent(ev, "onkeyup") || handlerPresent(ev, "onkeydown"))
380
 
        browseError(MSG_JSKeystroke);
381
 
    if(handlerPresent(ev, "onfocus") || handlerPresent(ev, "onblur"))
382
 
        browseError(MSG_JSFocus);
383
 
    if(handlerPresent(ev, "ondblclick"))
384
 
        runningError(MSG_Doubleclick);
385
 
    if(handlerPresent(ev, "onclick")) {
386
 
        if((action == TAGACT_A || action == TAGACT_AREA || action == TAGACT_FRAME) && topTag->href || action == TAGACT_INPUT && (itype <= INP_SUBMIT || itype >= INP_RADIO)) ;  /* ok */
387
 
        else
388
 
            strayClick = true;
389
 
    }
390
 
    if(handlerPresent(ev, "onchange")) {
391
 
        if(action != TAGACT_INPUT && action != TAGACT_SELECT || itype == INP_TA)
392
 
            browseError(MSG_StrayOnchange);
393
 
    }
394
 
/* Other warnings might be appropriate, but I'm going to assume this
395
 
 * is valid javascript, and you won't put an onsubmit function on <P> etc */
396
 
}                               /* get_js_events */
397
 
 
398
 
bool
399
 
tagHandler(int seqno, const char *name)
400
 
{
401
 
    const struct htmlTag **list = cw->tags;
402
 
    const struct htmlTag *t = list[seqno];
403
 
    return t->handler | handlerPresent(t->jv, name);
404
 
}                               /* tagHandler */
405
 
 
406
 
static char *
407
 
getBaseHref(int n)
408
 
{
409
 
    const struct htmlTag *t, **list;
410
 
    if(parsePage)
411
 
        return basehref;
412
 
    list = cw->tags;
413
 
    if(n < 0) {
414
 
        for(n = 0; list[n]; ++n) ;
415
 
    }
416
 
    list += n;
417
 
    do
418
 
        --list;
419
 
    while((t = *list)->action != TAGACT_BASE);
420
 
    return t->href;
421
 
}                               /* getBaseHref */
422
 
 
423
 
static void
424
 
htmlMeta(void)
425
 
{
426
 
    char *name, *content, *heq;
427
 
    char **ptr;
428
 
    void *e;
429
 
 
430
 
    htmlName();
431
 
    topTag->jv = e =
432
 
       domLink("Meta", topTag->name, topTag->id, 0, 0, "metas", jdoc, false);
433
 
    name = topTag->name;
434
 
    content = htmlAttrVal(topAttrib, "content");
435
 
    if(content == EMPTYSTRING)
436
 
        content = 0;
437
 
    if(e)
438
 
        establish_property_string(e, "content", content, true);
439
 
    heq = htmlAttrVal(topAttrib, "http-equiv");
440
 
    if(heq == EMPTYSTRING)
441
 
        heq = 0;
442
 
 
443
 
    if(heq && content) {
444
 
        bool rc;
445
 
        int delay;
446
 
/* It's not clear if we should process the http refresh command
447
 
 * immediately, the moment we spot it, or if we finish parsing
448
 
 * all the html first.
449
 
 * Does it matter?  It might.
450
 
 * A subsequent meta tag could use http-equiv to set a cooky,
451
 
 * and we won't see that cooky if we jump to the new page right now.
452
 
 * And there's no telling what subsequent javascript might do.
453
 
 * So - I'm going to postpone the refresh, until everything is parsed.
454
 
 * Bear in mind, we really don't want to refresh if we're working
455
 
 * on a local file. */
456
 
        if(stringEqualCI(heq, "Set-Cookie")) {
457
 
            rc = receiveCookie(cw->fileName, content);
458
 
            debugPrint(3, rc ? "jar" : "rejected");
459
 
        }
460
 
 
461
 
        if(allowRedirection && !browseLocal && stringEqualCI(heq, "Refresh")) {
462
 
            if(parseRefresh(content, &delay)) {
463
 
                char *newcontent;
464
 
                unpercentURL(content);
465
 
                newcontent = resolveURL(basehref, content);
466
 
                gotoLocation(newcontent, delay, true);
467
 
            }
468
 
        }
469
 
    }
470
 
 
471
 
    if(name) {
472
 
        ptr = 0;
473
 
        if(stringEqualCI(name, "description"))
474
 
            ptr = &cw->fd;
475
 
        if(stringEqualCI(name, "keywords"))
476
 
            ptr = &cw->fk;
477
 
        if(ptr && !*ptr && content) {
478
 
            stripWhite(content);
479
 
            *ptr = content;
480
 
            content = 0;
481
 
        }
482
 
    }
483
 
    /* name */
484
 
    nzFree(content);
485
 
    nzFree(heq);
486
 
}                               /* htmlMeta */
487
 
 
488
 
static void
489
 
htmlName(void)
490
 
{
491
 
    char *name = htmlAttrVal(topAttrib, "name");
492
 
    char *id = htmlAttrVal(topAttrib, "id");
493
 
    if(name == EMPTYSTRING)
494
 
        name = 0;
495
 
    topTag->name = name;
496
 
    if(id == EMPTYSTRING)
497
 
        id = 0;
498
 
    topTag->id = id;
499
 
}                               /* htmlName */
500
 
 
501
 
static void
502
 
htmlHref(const char *desc)
503
 
{
504
 
    char *h = hrefVal(topAttrib, desc);
505
 
    if(h == EMPTYSTRING) {
506
 
        h = 0;
507
 
        if(topTag->action == TAGACT_A)
508
 
            h = cloneString("#");
509
 
    }
510
 
    if(h) {
511
 
        unpercentURL(h);
512
 
        topTag->href = resolveURL(basehref, h);
513
 
        free(h);
514
 
    }
515
 
}                               /* htmlHref */
516
 
 
517
 
static void
518
 
formControl(bool namecheck)
519
 
{
520
 
    void *fo = 0;               /* form object */
521
 
    void *e;                    /* the new element */
522
 
    const char *typedesc;
523
 
    int itype = topTag->itype;
524
 
    int isradio = itype == INP_RADIO;
525
 
    int isselect = (itype == INP_SELECT) * 2;
526
 
 
527
 
    if(currentForm) {
528
 
        topTag->controller = currentForm;
529
 
        fo = currentForm->jv;
530
 
    } else if(itype != INP_BUTTON)
531
 
        browseError(MSG_NotInForm2, topTag->info->desc);
532
 
 
533
 
    htmlName();
534
 
    if(namecheck && !topTag->name)
535
 
        browseError(MSG_FieldNoName, topTag->info->desc);
536
 
 
537
 
    if(fo) {
538
 
        topTag->jv = e =
539
 
           domLink("Element", topTag->name, topTag->id, 0, 0,
540
 
           "elements", fo, isradio | isselect);
541
 
    } else {
542
 
        topTag->jv = e =
543
 
           domLink("Element", topTag->name, topTag->id, 0, 0, 0,
544
 
           jdoc, isradio | isselect);
545
 
    }
546
 
    get_js_events();
547
 
    if(!e)
548
 
        return;
549
 
 
550
 
    if(itype <= INP_RADIO) {
551
 
        establish_property_string(e, "value", topTag->value, false);
552
 
        if(itype != INP_FILE) {
553
 
/* No default value on file, for security reasons */
554
 
            establish_property_string(e, dfvl, topTag->value, true);
555
 
        }                       /* not file */
556
 
    }
557
 
 
558
 
    if(isselect)
559
 
        typedesc = topTag->multiple ? "select-multiple" : "select-one";
560
 
    else
561
 
        typedesc = inp_types[itype];
562
 
    establish_property_string(e, "type", typedesc, true);
563
 
 
564
 
    if(itype >= INP_RADIO) {
565
 
        establish_property_bool(e, "checked", topTag->checked, false);
566
 
        establish_property_bool(e, dfck, topTag->checked, true);
567
 
    }
568
 
}                               /* formControl */
569
 
 
570
 
static void
571
 
htmlImage(void)
572
 
{
573
 
    char *a;
574
 
    htmlName();
575
 
    htmlHref("src");
576
 
    topTag->jv =
577
 
       domLink("Image", topTag->name, topTag->id, "src", topTag->href,
578
 
       "images", jdoc, false);
579
 
    get_js_events();
580
 
/* don't know if javascript ever looks at alt.  Probably not. */
581
 
    if(!topTag->jv)
582
 
        return;
583
 
    a = htmlAttrVal(topAttrib, "alt");
584
 
    if(a)
585
 
        establish_property_string(topTag->jv, "alt", a, true);
586
 
}                               /* htmlImage */
587
 
 
588
 
static void
589
 
htmlForm(void)
590
 
{
591
 
    char *a;
592
 
    void *fv;                   /* form variable in javascript */
593
 
 
594
 
    if(topTag->slash)
595
 
        return;
596
 
    currentForm = topTag;
597
 
    htmlName();
598
 
    htmlHref("action");
599
 
 
600
 
    a = htmlAttrVal(topAttrib, "method");
601
 
    if(a) {
602
 
        if(stringEqualCI(a, "post"))
603
 
            topTag->post = true;
604
 
        else if(!stringEqualCI(a, "get"))
605
 
            browseError(MSG_GetPost);
606
 
        nzFree(a);
607
 
    }
608
 
 
609
 
    a = htmlAttrVal(topAttrib, "enctype");
610
 
    if(a) {
611
 
        if(stringEqualCI(a, "multipart/form-data"))
612
 
            topTag->mime = true;
613
 
        else if(!stringEqualCI(a, "application/x-www-form-urlencoded"))
614
 
            browseError(MSG_Enctype);
615
 
        nzFree(a);
616
 
    }
617
 
 
618
 
    if(a = topTag->href) {
619
 
        const char *prot = getProtURL(a);
620
 
        if(prot) {
621
 
            if(stringEqualCI(prot, "mailto"))
622
 
                topTag->bymail = true;
623
 
            else if(stringEqualCI(prot, "javascript"))
624
 
                topTag->javapost = true;
625
 
            else if(stringEqualCI(prot, "https"))
626
 
                topTag->secure = true;
627
 
            else if(!stringEqualCI(prot, "http"))
628
 
                browseError(MSG_FormProtBad, prot);
629
 
        }
630
 
    }
631
 
 
632
 
    nzFree(radioChecked);
633
 
    radioChecked = 0;
634
 
 
635
 
    topTag->jv = fv =
636
 
       domLink("Form", topTag->name, topTag->id, "action", topTag->href,
637
 
       "forms", jdoc, false);
638
 
    if(!fv)
639
 
        return;
640
 
    get_js_events();
641
 
    establish_property_array(fv, "elements");
642
 
}                               /* htmlForm */
643
 
 
644
 
void
645
 
jsdw(void)
646
 
{
647
 
    int side;
648
 
    if(!cw->dw)
649
 
        return;
650
 
    memcpy(cw->dw + 3, "<html>\n", 7);
651
 
    side = sideBuffer(0, cw->dw + 10, -1, cw->fileName, true);
652
 
    if(side) {
653
 
        i_printf(MSG_SideBufferX, side);
654
 
    } else {
655
 
        i_puts(MSG_NoSideBuffer);
656
 
        printf("%s\n", cw->dw + 10);
657
 
    }
658
 
    nzFree(cw->dw);
659
 
    cw->dw = 0;
660
 
    cw->dw_l = 0;
661
 
}                               /* jsdw */
662
 
 
663
 
static void
664
 
htmlInput(void)
665
 
{
666
 
    int n = INP_TEXT;
667
 
    int len;
668
 
    char *s = htmlAttrVal(topAttrib, "type");
669
 
    if(s && *s) {
670
 
        caseShift(s, 'l');
671
 
        n = stringInList(inp_types, s);
672
 
        if(n < 0) {
673
 
            browseError(MSG_InputType, s);
674
 
            n = INP_TEXT;
675
 
        }
676
 
    }
677
 
    topTag->itype = n;
678
 
    if(htmlAttrPresent(topAttrib, "readonly"))
679
 
        topTag->rdonly = true;
680
 
    s = htmlAttrVal(topAttrib, "maxlength");
681
 
    len = 0;
682
 
    if(s)
683
 
        len = stringIsNum(s);
684
 
    nzFree(s);
685
 
    if(len > 0)
686
 
        topTag->lic = len;
687
 
/* store the original text in value. */
688
 
/* This makes it easy to push the reset button. */
689
 
    s = htmlAttrVal(topAttrib, "value");
690
 
    if(!s)
691
 
        s = EMPTYSTRING;
692
 
    topTag->value = s;
693
 
    if(n >= INP_RADIO && htmlAttrPresent(topAttrib, "checked")) {
694
 
        char namebuf[200];
695
 
        if(n == INP_RADIO && topTag->name &&
696
 
           strlen(topTag->name) < sizeof (namebuf) - 3) {
697
 
            if(!radioChecked) {
698
 
                radioChecked = initString(&radioChecked_l);
699
 
                stringAndChar(&radioChecked, &radioChecked_l, '|');
700
 
            }
701
 
            sprintf(namebuf, "|%s|", topTag->name);
702
 
            if(strstr(radioChecked, namebuf)) {
703
 
                browseError(MSG_RadioMany);
704
 
                return;
705
 
            }
706
 
            stringAndString(&radioChecked, &radioChecked_l, namebuf + 1);
707
 
        }                       /* radio name */
708
 
        topTag->rchecked = true;
709
 
        topTag->checked = true;
710
 
    }
711
 
 
712
 
    /* Even the submit fields can have a name, but they don't have to */
713
 
    formControl(n > INP_SUBMIT);
714
 
}                               /* htmlInput */
715
 
 
716
 
static void
717
 
makeButton(void)
718
 
{
719
 
    struct htmlTag *t = allocZeroMem(sizeof (struct htmlTag));
720
 
    addToListBack(&htmlStack, t);
721
 
    t->seqno = ntags++;
722
 
    t->info = elements + 2;
723
 
    t->action = TAGACT_INPUT;
724
 
    t->controller = currentForm;
725
 
    t->itype = INP_SUBMIT;
726
 
}                               /* makeButton */
727
 
 
728
 
/* display the checked options */
729
 
static char *
730
 
displayOptions(const struct htmlTag *sel)
731
 
{
732
 
    const struct htmlTag *t;
733
 
    char *new;
734
 
    int l;
735
 
    new = initString(&l);
736
 
    foreach(t, htmlStack) {
737
 
        if(t->controller != sel)
738
 
            continue;
739
 
        if(!t->checked)
740
 
            continue;
741
 
        if(l)
742
 
            stringAndChar(&new, &l, ',');
743
 
        stringAndString(&new, &l, t->name);
744
 
    }
745
 
    return new;
746
 
}                               /* displayOptions */
747
 
 
748
 
static struct htmlTag *
749
 
locateOptionByName(const struct htmlTag *sel, const char *name, int *pmc,
750
 
   bool exact)
751
 
{
752
 
    struct htmlTag **list = cw->tags, *t, *em = 0, *pm = 0;
753
 
    int pmcount = 0;            /* partial match count */
754
 
    const char *s;
755
 
    while(t = *list++) {
756
 
        if(t->controller != sel)
757
 
            continue;
758
 
        if(!(s = t->name))
759
 
            continue;
760
 
        if(stringEqualCI(s, name)) {
761
 
            em = t;
762
 
            continue;
763
 
        }
764
 
        if(exact)
765
 
            continue;
766
 
        if(strstrCI(s, name)) {
767
 
            pm = t;
768
 
            ++pmcount;
769
 
        }
770
 
    }
771
 
    if(em)
772
 
        return em;
773
 
    if(pmcount == 1)
774
 
        return pm;
775
 
    *pmc = pmcount;
776
 
    return 0;
777
 
}                               /* locateOptionByName */
778
 
 
779
 
static struct htmlTag *
780
 
locateOptionByNum(const struct htmlTag *sel, int n)
781
 
{
782
 
    struct htmlTag **list = cw->tags, *t;
783
 
    int cnt = 0;
784
 
    while(t = *list++) {
785
 
        if(t->controller != sel)
786
 
            continue;
787
 
        if(!t->name)
788
 
            continue;
789
 
        ++cnt;
790
 
        if(cnt == n)
791
 
            return t;
792
 
    }
793
 
    return 0;
794
 
}                               /* locateOptionByNum */
795
 
 
796
 
static bool
797
 
locateOptions(const struct htmlTag *sel, const char *input,
798
 
   char **disp_p, char **val_p, bool setcheck)
799
 
{
800
 
    struct htmlTag *t, **list;
801
 
    char *display = 0, *value = 0;
802
 
    int disp_l, val_l;
803
 
    int len = strlen(input);
804
 
    void *ev = sel->jv;
805
 
    int n, pmc, cnt;
806
 
    const char *s, *e;          /* start and end of an option */
807
 
    char *iopt;                 /* individual option */
808
 
 
809
 
    if(disp_p)
810
 
        display = initString(&disp_l);
811
 
    if(val_p)
812
 
        value = initString(&val_l);
813
 
    iopt = allocMem(len + 1);
814
 
 
815
 
    if(setcheck) {
816
 
/* Uncheck all existing options, then check the ones selected. */
817
 
        if(ev)
818
 
            set_property_number(ev, "selectedIndex", -1);
819
 
        list = cw->tags;
820
 
        while(t = *list++)
821
 
            if(t->controller == sel && t->name) {
822
 
                t->checked = false;
823
 
                if(t->jv)
824
 
                    set_property_bool(t->jv, "selected", false);
825
 
            }
826
 
    }
827
 
 
828
 
    s = input;
829
 
    while(*s) {
830
 
        e = 0;
831
 
        if(sel->multiple)
832
 
            e = strchr(s, ',');
833
 
        if(!e)
834
 
            e = s + strlen(s);
835
 
        len = e - s;
836
 
        strncpy(iopt, s, len);
837
 
        iopt[len] = 0;
838
 
        s = e;
839
 
        if(*s == ',')
840
 
            ++s;
841
 
 
842
 
        t = locateOptionByName(sel, iopt, &pmc, true);
843
 
        if(!t) {
844
 
            n = stringIsNum(iopt);
845
 
            if(n >= 0)
846
 
                t = locateOptionByNum(sel, n);
847
 
        }
848
 
        if(!t)
849
 
            t = locateOptionByName(sel, iopt, &pmc, false);
850
 
        if(!t) {
851
 
            if(n >= 0)
852
 
                setError(MSG_XOutOfRange, n);
853
 
            else
854
 
                setError(pmc + MSG_OptMatchNone, iopt);
855
 
/* This should never happen when we're doing a set check */
856
 
            if(setcheck) {
857
 
                runningError(MSG_OptionSync, iopt);
858
 
                continue;
859
 
            }
860
 
            goto fail;
861
 
        }
862
 
 
863
 
        if(value) {
864
 
            if(val_l)
865
 
                stringAndChar(&value, &val_l, '\1');
866
 
            stringAndString(&value, &val_l, t->value);
867
 
        }
868
 
        if(display) {
869
 
            if(disp_l)
870
 
                stringAndChar(&display, &disp_l, ',');
871
 
            stringAndString(&display, &disp_l, t->name);
872
 
        }
873
 
        if(setcheck) {
874
 
            t->checked = true;
875
 
            if(t->jv) {
876
 
                set_property_bool(t->jv, "selected", true);
877
 
                if(ev)
878
 
                    set_property_number(ev, "selectedIndex", t->lic);
879
 
            }
880
 
        }
881
 
    }                           /* loop over multiple options */
882
 
 
883
 
    if(value)
884
 
        *val_p = value;
885
 
    if(display)
886
 
        *disp_p = display;
887
 
    free(iopt);
888
 
    return true;
889
 
 
890
 
  fail:
891
 
    free(iopt);
892
 
    nzFree(value);
893
 
    nzFree(display);
894
 
    return false;
895
 
}                               /* locateOptions */
896
 
 
897
 
 
898
 
/*********************************************************************
899
 
Sync up the javascript variables with the input fields.
900
 
This is required before running any javascript, e.g. an onclick function.
901
 
After all, the input fields may have changed.
902
 
You may have changed the last name from Flintstone to Rubble.
903
 
This has to propagate down to the javascript strings in the DOM.
904
 
This is quick and stupid; I just update everything.
905
 
Most of the time I'm setting the strings to what they were before;
906
 
that's the way it goes.
907
 
*********************************************************************/
908
 
 
909
 
void
910
 
jSyncup(void)
911
 
{
912
 
    const struct htmlTag *t, **list;
913
 
    void *eo;                   /* element object */
914
 
    int itype, j, cx;
915
 
    char *value, *cxbuf;
916
 
 
917
 
    if(parsePage)
918
 
        return;                 /* not necessary */
919
 
    if(cw->jsdead)
920
 
        return;
921
 
    debugPrint(5, "jSyncup starts");
922
 
 
923
 
    jMyContext();
924
 
    buildTagArray();
925
 
 
926
 
    list = cw->tags;
927
 
    while(t = *list++) {
928
 
        if(t->action != TAGACT_INPUT)
929
 
            continue;
930
 
        if(!(eo = t->jv))
931
 
            continue;
932
 
        itype = t->itype;
933
 
        if(itype <= INP_HIDDEN)
934
 
            continue;
935
 
 
936
 
        if(itype >= INP_RADIO) {
937
 
            int checked = fieldIsChecked(t->seqno);
938
 
            if(checked < 0)
939
 
                checked = t->rchecked;
940
 
            set_property_bool(eo, "checked", checked);
941
 
            continue;
942
 
        }
943
 
 
944
 
        value = getFieldFromBuffer(t->seqno);
945
 
/* If that line has been deleted from the user's buffer,
946
 
 * indicated by value = 0,
947
 
 * revert back to the original (reset) value. */
948
 
 
949
 
        if(itype == INP_SELECT) {
950
 
            locateOptions(t, (value ? value : t->value), 0, 0, true);
951
 
            if(!t->multiple)
952
 
                value = cloneString( get_property_option(eo));
953
 
        }
954
 
 
955
 
        if(itype == INP_TA) {
956
 
            if(!value) {
957
 
                set_property_string(eo, "value", 0);
958
 
                continue;
959
 
            }
960
 
/* Now value is just <buffer 3>, which is meaningless. */
961
 
            nzFree(value);
962
 
            cx = t->lic;
963
 
            if(!cx)
964
 
                continue;
965
 
/* The unfold command should never fail */
966
 
            if(!unfoldBuffer(cx, false, &cxbuf, &j))
967
 
                continue;
968
 
            set_property_string(eo, "value", cxbuf);
969
 
            nzFree(cxbuf);
970
 
            continue;
971
 
        }
972
 
 
973
 
        if(value) {
974
 
            set_property_string(eo, "value", value);
975
 
            nzFree(value);
976
 
        } else
977
 
            set_property_string(eo, "value", t->value);
978
 
    }                           /* loop over tags */
979
 
 
980
 
    debugPrint(5, "jSyncup ends");
981
 
}                               /* jSyncup */
982
 
 
983
 
 
984
 
/* Find the <foo> tag to match </foo> */
985
 
static struct htmlTag *
986
 
findOpenTag(const char *name)
987
 
{
988
 
    struct htmlTag *t;
989
 
    bool closing = topTag->slash;
990
 
    bool match;
991
 
    const char *desc = topTag->info->desc;
992
 
    foreachback(t, htmlStack) {
993
 
        if(t == topTag)
994
 
            continue;           /* last one doesn't count */
995
 
        if(t->balanced)
996
 
            continue;
997
 
        if(!t->info->nest)
998
 
            continue;
999
 
        if(t->slash)
1000
 
            continue;           /* unbalanced slash, should never happen */
1001
 
/* Now we have an unbalanced open tag */
1002
 
        match = stringEqualCI(t->info->name, name);
1003
 
/* I expect tags to nest perfectly, like labeled parentheses */
1004
 
        if(closing) {
1005
 
            if(match)
1006
 
                return t;
1007
 
            browseError(MSG_TagNest, desc, t->info->desc);
1008
 
            continue;
1009
 
        }
1010
 
        if(!match)
1011
 
            continue;
1012
 
        if(!(t->info->nest & 2))
1013
 
            browseError(MSG_TagInTag, desc, desc);
1014
 
        return t;
1015
 
    }                           /* loop */
1016
 
 
1017
 
    if(closing)
1018
 
        browseError(MSG_TagClose, desc);
1019
 
    return NULL;
1020
 
}                               /* findOpenTag */
1021
 
 
1022
 
static struct htmlTag *
1023
 
newTag(const char *name)
1024
 
{
1025
 
    struct htmlTag *t;
1026
 
    const struct tagInfo *ti;
1027
 
    int action;
1028
 
    for(ti = elements; ti->name; ++ti)
1029
 
        if(stringEqualCI(ti->name, name))
1030
 
            break;
1031
 
    if(!ti->name)
1032
 
        return 0;
1033
 
    action = ti->action;
1034
 
    t = allocZeroMem(sizeof (struct htmlTag));
1035
 
    t->action = action;
1036
 
    t->info = ti;
1037
 
    t->seqno = ntags++;
1038
 
    t->balanced = true;
1039
 
    if(stringEqual(name, "a"))
1040
 
        t->clickable = true;
1041
 
    addToListBack(&htmlStack, t);
1042
 
    return t;
1043
 
}                               /* newTag */
1044
 
 
1045
 
static void
1046
 
onloadGo(void *obj, const char *jsrc, const char *tagname)
1047
 
{
1048
 
    struct htmlTag *t;
1049
 
    char buf[32];
1050
 
 
1051
 
/* The first one is easy - one line of code. */
1052
 
    handlerGo(obj, "onload");
1053
 
 
1054
 
    if(!handlerPresent(obj, "onunload"))
1055
 
        return;
1056
 
    if(handlerPresent(obj, "onclick")) {
1057
 
        runningError(MSG_UnloadClick);
1058
 
        return;
1059
 
    }
1060
 
 
1061
 
    t = newTag("a");
1062
 
    t->jv = obj;
1063
 
    t->href = cloneString("#");
1064
 
    link_onunload_onclick(obj);
1065
 
    sprintf(buf, "on close %s", tagname);
1066
 
    caseShift(buf, 'm');
1067
 
    toPreamble(t->seqno, buf, jsrc, 0);
1068
 
}                               /* onloadGo */
1069
 
 
1070
 
/* Always returns a string, even if errors were found.
1071
 
 * Internet web pages often contain errors!
1072
 
 * This routine mucks with the passed-in string, and frees it
1073
 
 * when finished.  Thus the argument is not const.
1074
 
 * The produced string contains only these tags.
1075
 
 * Input field open and close.
1076
 
 * Hyperlink open and close.
1077
 
 * Internal anchor.
1078
 
 * Preformat open and close.
1079
 
 * Try to keep this list up to date.
1080
 
 * We want a handle on what tags are hanging around during formatting. */
1081
 
static char *
1082
 
encodeTags(char *html)
1083
 
{
1084
 
    const struct tagInfo *ti;
1085
 
    struct htmlTag *t, *open, *v;
1086
 
    char *save_h;
1087
 
    char *h = html;
1088
 
    char *a;                    /* for a specific attribute */
1089
 
    char *new;                  /* the new string */
1090
 
    char *javatext;
1091
 
    int js_nl;                  /* number of newlines in javascript */
1092
 
    const char *name, *attrib, *end;
1093
 
    char tagname[12];
1094
 
    const char *s;
1095
 
    int j, l, namelen, lns;
1096
 
    int dw_line, dw_nest = 0;
1097
 
    char hnum[40];              /* hidden number */
1098
 
    char c;
1099
 
    bool retainTag, onload_done = false;
1100
 
    bool a_text;                /* visible text within the hyperlink */
1101
 
    bool slash, a_href, rc;
1102
 
    bool premode = false, invisible = false;
1103
 
/* Tags that cannot nest, one open at a time. */
1104
 
    struct htmlTag *currentA;   /* the open anchor */
1105
 
    struct htmlTag *currentSel; /* the open select */
1106
 
    struct htmlTag *currentOpt; /* the current option */
1107
 
    struct htmlTag *currentTitle;
1108
 
    struct htmlTag *currentTA;  /* text area */
1109
 
    int offset;                 /* where the current x starts */
1110
 
    int ln = 1;                 /* line number */
1111
 
    int action;                 /* action of the tag */
1112
 
    int lastact = 0;
1113
 
    int nopt;                   /* number of options */
1114
 
    int intable = 0, inrow = 0;
1115
 
    bool tdfirst;
1116
 
    void *to;                   /* table object */
1117
 
    void *ev;                   /* generic event variable */
1118
 
 
1119
 
    currentA = currentForm = currentSel = currentOpt = currentTitle =
1120
 
       currentTA = 0;
1121
 
    initList(&htmlStack);
1122
 
    tagArray = 0;
1123
 
    new = initString(&l);
1124
 
    preamble = 0;
1125
 
    ntags = 0;
1126
 
 
1127
 
/* first tag is a base tag, from the filename */
1128
 
    t = newTag("base");
1129
 
    t->href = cloneString(cw->fileName);
1130
 
    basehref = t->href;
1131
 
 
1132
 
  top:
1133
 
    while(c = *h) {
1134
 
        if(c != '<') {
1135
 
            if(c == '\n')
1136
 
                ++ln;           /* keep track of line numbers */
1137
 
          putc:
1138
 
            if(!invisible) {
1139
 
                if(strchr("\r\n\f", c) && !currentTA) {
1140
 
                    if(!premode || c == '\r' && h[1] == '\n')
1141
 
                        c = ' ';
1142
 
                    else if(c == '\r')
1143
 
                        c = '\n';
1144
 
                }
1145
 
                if(lastact == TAGACT_TD && c == ' ')
1146
 
                    goto nextchar;
1147
 
                stringAndChar(&new, &l, c);
1148
 
                if(!isspaceByte(c)) {
1149
 
                    a_text = true;
1150
 
                    lastact = 0;
1151
 
                }
1152
 
            }
1153
 
          nextchar:
1154
 
            ++h;
1155
 
            continue;
1156
 
        }
1157
 
 
1158
 
        if(h[1] == '!' || h[1] == '?') {
1159
 
            h = (char *)skipHtmlComment(h, &lns);
1160
 
            ln += lns;
1161
 
            continue;
1162
 
        }
1163
 
 
1164
 
        if(!parseTag(h, &name, &namelen, &attrib, &end, &lns))
1165
 
            goto putc;
1166
 
 
1167
 
/* html tag found */
1168
 
        save_h = h;
1169
 
        h = (char *)end;        /* skip past tag */
1170
 
        if(!dw_nest)
1171
 
            browseLine = ln;
1172
 
        ln += lns;
1173
 
        slash = false;
1174
 
        if(*name == '/')
1175
 
            slash = true, ++name, --namelen;
1176
 
        if(namelen > sizeof (tagname) - 1)
1177
 
            namelen = sizeof (tagname) - 1;
1178
 
        strncpy(tagname, name, namelen);
1179
 
        tagname[namelen] = 0;
1180
 
 
1181
 
        for(ti = elements; ti->name; ++ti)
1182
 
            if(stringEqualCI(ti->name, tagname))
1183
 
                break;
1184
 
        action = ti->action;
1185
 
        debugPrint(7, "tag %s %d %d %d", tagname, ntags, ln, action);
1186
 
 
1187
 
        if(currentTA) {
1188
 
/* Sometimes a textarea is used to present a chunk of html code.
1189
 
 * "Cut and paste this into your web page."
1190
 
 * So it may contain tags.  Ignore them!
1191
 
 * All except the textarea tag. */
1192
 
            if(action != TAGACT_TA) {
1193
 
                ln = browseLine, h = save_h;    /* back up */
1194
 
                goto putc;
1195
 
            }
1196
 
/* Close it off */
1197
 
            currentTA->action = TAGACT_INPUT;
1198
 
            currentTA->itype = INP_TA;
1199
 
            currentTA->balanced = true;
1200
 
            s = currentTA->value = andTranslate(new + offset, true);
1201
 
/* Text starts at the next line boundary */
1202
 
            while(*s == '\t' || *s == ' ')
1203
 
                ++s;
1204
 
            if(*s == '\r')
1205
 
                ++s;
1206
 
            if(*s == '\n')
1207
 
                ++s;
1208
 
            if(s > currentTA->value)
1209
 
                strcpy(currentTA->value, s);
1210
 
            a = currentTA->value;
1211
 
            a += strlen(a);
1212
 
            while(a > currentTA->value && (a[-1] == ' ' || a[-1] == '\t'))
1213
 
                --a;
1214
 
            *a = 0;
1215
 
            if(currentTA->jv) {
1216
 
                establish_innerHTML(currentTA->jv, currentTA->inner, save_h,
1217
 
                   true);
1218
 
                establish_property_string(currentTA->jv, "value",
1219
 
                   currentTA->value, false);
1220
 
                establish_property_string(currentTA->jv, dfvl, currentTA->value,
1221
 
                   true);
1222
 
            }
1223
 
            l -= strlen(new + offset);
1224
 
            new[offset] = 0;
1225
 
            j = sideBuffer(0, currentTA->value, -1, 0, false);
1226
 
            if(j) {
1227
 
                currentTA->lic = j;
1228
 
                sprintf(hnum, "%c%d<buffer %d%c0>",
1229
 
                   InternalCodeChar, currentTA->seqno, j, InternalCodeChar);
1230
 
                stringAndString(&new, &l, hnum);
1231
 
            } else
1232
 
                stringAndString(&new, &l, "<buffer ?>");
1233
 
            currentTA = 0;
1234
 
            if(slash)
1235
 
                continue;
1236
 
            browseError(MSG_TextareaNest);
1237
 
        }
1238
 
 
1239
 
        if(!action)
1240
 
            continue;           /* tag not recognized */
1241
 
 
1242
 
        topTag = t = allocZeroMem(sizeof (struct htmlTag));
1243
 
        addToListBack(&htmlStack, t);
1244
 
        t->seqno = ntags;
1245
 
        sprintf(hnum, "%c%d", InternalCodeChar, ntags);
1246
 
        ++ntags;
1247
 
        t->info = ti;
1248
 
        t->slash = slash;
1249
 
        if(!slash)
1250
 
            t->inner = end;
1251
 
        t->ln = browseLine;
1252
 
        t->action = action;     /* we might change this later */
1253
 
        j = end - attrib;
1254
 
        topAttrib = t->attrib = j ? pullString(attrib, j) : EMPTYSTRING;
1255
 
 
1256
 
        open = 0;
1257
 
        if(ti->nest) {
1258
 
            open = findOpenTag(ti->name);
1259
 
            if(slash) {
1260
 
                if(!open)
1261
 
                    continue;   /* unbalanced </ul> means nothing */
1262
 
                open->balanced = t->balanced = true;
1263
 
                if(open->jv)
1264
 
                    establish_innerHTML(open->jv, open->inner, save_h, false);
1265
 
            } else
1266
 
                open = 0;
1267
 
        }
1268
 
 
1269
 
        if(slash && ti->bits & TAG_NOSLASH)
1270
 
            continue;           /* negated version means nothing */
1271
 
 
1272
 
/* Does this tag force the closure of an anchor? */
1273
 
        if(currentA && (action != TAGACT_A || !slash)) {
1274
 
            if(open && open->clickable)
1275
 
                goto forceCloseAnchor;
1276
 
            rc = htmlAttrPresent(topAttrib, "onclick");
1277
 
            if(rc ||
1278
 
               ti->bits & TAG_CLOSEA && (a_text || action <= TAGACT_OPTION)) {
1279
 
                browseError(MSG_InAnchor, ti->desc);
1280
 
              forceCloseAnchor:
1281
 
                stringAndString(&new, &l, "\2000}");
1282
 
                currentA->balanced = true;
1283
 
                currentA = 0;
1284
 
/* if/when the </a> comes along, it will be unbalanced, and we'll ignore it. */
1285
 
            }
1286
 
        }
1287
 
 
1288
 
        retainTag = true;
1289
 
        if(ti->bits & TAG_INVISIBLE)
1290
 
            retainTag = false;
1291
 
        if(invisible)
1292
 
            retainTag = false;
1293
 
        if(ti->bits & TAG_INVISIBLE)
1294
 
            invisible = !slash;
1295
 
 
1296
 
        strayClick = false;
1297
 
 
1298
 
/* Are we gathering text to build title or option? */
1299
 
        if(currentTitle || currentOpt) {
1300
 
            char **ptr;
1301
 
            v = (currentTitle ? currentTitle : currentOpt);
1302
 
/* Should we print an error message? */
1303
 
            if(v->action != action &&
1304
 
               !(v->action == TAGACT_OPTION && action == TAGACT_SELECT) ||
1305
 
               action != TAGACT_OPTION && !slash)
1306
 
                browseError(MSG_HasTags, v->info->desc);
1307
 
            if(!(ti->bits & TAG_CLOSEA))
1308
 
                continue;
1309
 
/* close off the title or option */
1310
 
            v->balanced = true;
1311
 
            ptr = 0;
1312
 
            if(currentTitle && !cw->ft)
1313
 
                ptr = &cw->ft;
1314
 
            if(currentOpt)
1315
 
                ptr = &v->name;
1316
 
 
1317
 
            if(ptr) {
1318
 
                a = andTranslate(new + offset, true);
1319
 
                stripWhite(a);
1320
 
                if(currentOpt && strchr(a, ',') && currentSel->multiple) {
1321
 
                    char *y;
1322
 
                    for(y = a; *y; ++y)
1323
 
                        if(*y == ',')
1324
 
                            *y = ' ';
1325
 
                    browseError(MSG_OptionComma);
1326
 
                }
1327
 
                spaceCrunch(a, true, true);
1328
 
                *ptr = a;
1329
 
 
1330
 
                if(currentTitle) {
1331
 
                    if(!cw->jsdead)
1332
 
                        establish_property_string(jdoc, "title", a, true);
1333
 
                }
1334
 
 
1335
 
                if(currentOpt) {
1336
 
                    if(!*a) {
1337
 
                        browseError(MSG_OptionEmpty);
1338
 
                    } else {
1339
 
                        if(!v->value)
1340
 
                            v->value = cloneString(a);
1341
 
                    }
1342
 
 
1343
 
                    if(ev = currentSel->jv) {   /* element variable */
1344
 
                        void *ov = establish_js_option(ev, v->lic);
1345
 
                        v->jv = ov;
1346
 
                        establish_property_string(ov, "text", v->name, true);
1347
 
                        establish_property_string(ov, "value", v->value, true);
1348
 
                        establish_property_bool(ov, "selected", v->checked,
1349
 
                           false);
1350
 
                        establish_property_bool(ov, "defaultSelected",
1351
 
                           v->checked, true);
1352
 
                    }           /* select has corresponding java variables */
1353
 
                }               /* option */
1354
 
            }
1355
 
            /* ptr is nonzero */
1356
 
            l -= strlen(new + offset);
1357
 
            new[offset] = 0;
1358
 
            currentTitle = currentOpt = 0;
1359
 
        }
1360
 
 
1361
 
        switch (action) {
1362
 
        case TAGACT_INPUT:
1363
 
            htmlInput();
1364
 
            if(t->itype == INP_HIDDEN)
1365
 
                continue;
1366
 
            if(!retainTag)
1367
 
                continue;
1368
 
            t->retain = true;
1369
 
            if(currentForm) {
1370
 
                ++currentForm->ninp;
1371
 
                if(t->itype == INP_SUBMIT || t->itype == INP_IMAGE)
1372
 
                    currentForm->submitted = true;
1373
 
            }
1374
 
            strcat(hnum, "<");
1375
 
            stringAndString(&new, &l, hnum);
1376
 
            if(t->itype < INP_RADIO) {
1377
 
                if(t->value[0])
1378
 
                    stringAndString(&new, &l, t->value);
1379
 
                else if(t->itype == INP_SUBMIT || t->itype == INP_IMAGE)
1380
 
                    stringAndString(&new, &l, "Go");
1381
 
                else if(t->itype == INP_RESET)
1382
 
                    stringAndString(&new, &l, "Reset");
1383
 
            } else
1384
 
                stringAndChar(&new, &l, t->checked ? '+' : '-');
1385
 
            if(currentForm && (t->itype == INP_SUBMIT || t->itype == INP_IMAGE)) {
1386
 
                if(currentForm->secure)
1387
 
                    stringAndString(&new, &l, " secure");
1388
 
                if(currentForm->bymail)
1389
 
                    stringAndString(&new, &l, " bymail");
1390
 
            }
1391
 
            stringAndString(&new, &l, "\2000>");
1392
 
            goto endtag;
1393
 
 
1394
 
        case TAGACT_TITLE:
1395
 
            if(slash)
1396
 
                continue;
1397
 
            if(cw->ft)
1398
 
                browseError(MSG_ManyTitles);
1399
 
            offset = l;
1400
 
            currentTitle = t;
1401
 
            continue;
1402
 
 
1403
 
        case TAGACT_A:
1404
 
            a_href = false;
1405
 
            if(slash) {
1406
 
                if(open->href)
1407
 
                    a_href = true;
1408
 
                currentA = 0;
1409
 
            } else {
1410
 
                htmlName();
1411
 
                htmlHref("href");
1412
 
                topTag->jv =
1413
 
                   domLink("Anchor", topTag->name, topTag->id, "href",
1414
 
                   topTag->href, "links", jdoc, false);
1415
 
                get_js_events();
1416
 
                if(t->href) {
1417
 
                    a_href = true;
1418
 
                    topTag->clickable = true;
1419
 
                }
1420
 
                a_text = false;
1421
 
            }
1422
 
            if(a_href) {
1423
 
                if(slash) {
1424
 
                    strcpy(hnum, "\2000}");
1425
 
                } else {
1426
 
                    strcat(hnum, "{");
1427
 
                    currentA = t;
1428
 
                }
1429
 
            } else {
1430
 
                if(!t->name)
1431
 
                    retainTag = false;  /* no need to keep this anchor */
1432
 
            }                   /* href or not */
1433
 
            break;
1434
 
 
1435
 
        case TAGACT_PRE:
1436
 
            premode = !slash;
1437
 
            break;
1438
 
 
1439
 
        case TAGACT_TA:
1440
 
            currentTA = t;
1441
 
            offset = l;
1442
 
            t->itype = INP_TA;
1443
 
            formControl(true);
1444
 
            continue;
1445
 
 
1446
 
        case TAGACT_HEAD:
1447
 
            htmlName();
1448
 
            topTag->jv =
1449
 
               domLink("Head", topTag->name, topTag->id, 0, 0,
1450
 
               "heads", jdoc, false);
1451
 
            goto plainWithElements;
1452
 
 
1453
 
        case TAGACT_BODY:
1454
 
            htmlName();
1455
 
            topTag->jv =
1456
 
               domLink("Body", topTag->name, topTag->id, 0, 0,
1457
 
               "bodies", jdoc, false);
1458
 
          plainWithElements:
1459
 
            if(t->jv)
1460
 
                establish_property_array(t->jv, "elements");
1461
 
/* fall through */
1462
 
 
1463
 
        case TAGACT_JS:
1464
 
          plainTag:
1465
 
/* check for javascript events, that's it */
1466
 
            if(!slash)
1467
 
                get_js_events();
1468
 
/* no need to keep these tags in the output */
1469
 
            continue;
1470
 
 
1471
 
        case TAGACT_META:
1472
 
            htmlMeta();
1473
 
            continue;
1474
 
 
1475
 
        case TAGACT_LI:
1476
 
/* Look for the open UL or OL */
1477
 
            j = -1;
1478
 
            foreachback(v, htmlStack) {
1479
 
                if(v->balanced || !v->info->nest)
1480
 
                    continue;
1481
 
                if(v->slash)
1482
 
                    continue;   /* should never happen */
1483
 
                s = v->info->name;
1484
 
                if(stringEqual(s, "OL") ||
1485
 
                   stringEqual(s, "UL") ||
1486
 
                   stringEqual(s, "MENU") || stringEqual(s, "DIR")) {
1487
 
                    j = 0;
1488
 
                    if(*s == 'O')
1489
 
                        j = ++v->lic;
1490
 
                }
1491
 
            }
1492
 
            if(j < 0)
1493
 
                browseError(MSG_NotInList, ti->desc);
1494
 
            if(!retainTag)
1495
 
                continue;
1496
 
            hnum[0] = '\r';
1497
 
            hnum[1] = 0;
1498
 
            if(j == 0)
1499
 
                strcat(hnum, "* ");
1500
 
            if(j > 0)
1501
 
                sprintf(hnum + 1, "%d. ", j);
1502
 
            stringAndString(&new, &l, hnum);
1503
 
            continue;
1504
 
 
1505
 
        case TAGACT_DT:
1506
 
        foreachback(v, htmlStack) {
1507
 
            if(v->balanced || !v->info->nest)
1508
 
                continue;
1509
 
            if(v->slash)
1510
 
                continue;       /* should never happen */
1511
 
            s = v->info->name;
1512
 
            if(stringEqual(s, "DL"))
1513
 
                break;
1514
 
        }
1515
 
            if(v == (struct htmlTag *)&htmlStack)
1516
 
                browseError(MSG_NotInList, ti->desc);
1517
 
            goto nop;
1518
 
 
1519
 
        case TAGACT_TABLE:
1520
 
            if(!slash) {
1521
 
                htmlName();
1522
 
                topTag->jv = to =
1523
 
                   domLink("Table", topTag->name, topTag->id, 0, 0,
1524
 
                   "tables", jdoc, false);
1525
 
                get_js_events();
1526
 
/* create the array of rows under the table */
1527
 
                if(to)
1528
 
                    establish_property_array(to, "rows");
1529
 
            }
1530
 
            if(slash)
1531
 
                --intable;
1532
 
            else
1533
 
                ++intable;
1534
 
            goto nop;
1535
 
 
1536
 
        case TAGACT_TR:
1537
 
            if(!intable) {
1538
 
                browseError(MSG_NotInTable, ti->desc);
1539
 
                continue;
1540
 
            }
1541
 
            if(slash)
1542
 
                --inrow;
1543
 
            else
1544
 
                ++inrow;
1545
 
            tdfirst = true;
1546
 
            if((!slash) && (open = findOpenTag("table")) && open->jv) {
1547
 
                htmlName();
1548
 
                topTag->jv = to =
1549
 
                   domLink("Trow", topTag->name, topTag->id, 0, 0,
1550
 
                   "rows", open->jv, false);
1551
 
                get_js_events();
1552
 
                establish_property_array(to, "cells");
1553
 
            }
1554
 
            goto nop;
1555
 
 
1556
 
        case TAGACT_TD:
1557
 
            if(!inrow) {
1558
 
                browseError(MSG_NotInRow, ti->desc);
1559
 
                continue;
1560
 
            }
1561
 
            if(slash)
1562
 
                continue;
1563
 
            if(tdfirst)
1564
 
                tdfirst = false;
1565
 
            else if(retainTag) {
1566
 
                while(l && new[l - 1] == ' ')
1567
 
                    --l;
1568
 
                new[l] = 0;
1569
 
                stringAndChar(&new, &l, '|');
1570
 
            }
1571
 
            if((open = findOpenTag("tr")) && open->jv) {
1572
 
                htmlName();
1573
 
                topTag->jv = to =
1574
 
                   domLink("Cell", topTag->name, topTag->id, 0, 0,
1575
 
                   "cells", open->jv, false);
1576
 
                get_js_events();
1577
 
            }
1578
 
            goto endtag;
1579
 
 
1580
 
        case TAGACT_DIV:
1581
 
            if(!slash) {
1582
 
                htmlName();
1583
 
                topTag->jv =
1584
 
                   domLink("Div", topTag->name, topTag->id, 0, 0,
1585
 
                   "divs", jdoc, false);
1586
 
                get_js_events();
1587
 
            }
1588
 
            goto nop;
1589
 
 
1590
 
        case TAGACT_SPAN:
1591
 
            if(!slash) {
1592
 
                htmlName();
1593
 
                topTag->jv =
1594
 
                   domLink("Span", topTag->name, topTag->id, 0, 0,
1595
 
                   "spans", jdoc, false);
1596
 
                get_js_events();
1597
 
            }
1598
 
            goto nop;
1599
 
 
1600
 
        case TAGACT_BR:
1601
 
            if(lastact == TAGACT_TD)
1602
 
                continue;
1603
 
 
1604
 
        case TAGACT_NOP:
1605
 
          nop:
1606
 
            if(!retainTag)
1607
 
                continue;
1608
 
            j = ti->para;
1609
 
            if(slash)
1610
 
                j >>= 2;
1611
 
            else
1612
 
                j &= 3;
1613
 
            if(!j)
1614
 
                goto endtag;
1615
 
            c = '\f';
1616
 
            if(j == 1) {
1617
 
                c = '\r';
1618
 
                if(action == TAGACT_BR)
1619
 
                    c = '\n';
1620
 
            }
1621
 
            if(currentA)
1622
 
                c = ' ';
1623
 
            stringAndChar(&new, &l, c);
1624
 
            goto endtag;
1625
 
 
1626
 
        case TAGACT_FORM:
1627
 
            htmlForm();
1628
 
            if(currentSel) {
1629
 
              doneSelect:
1630
 
                currentSel->action = TAGACT_INPUT;
1631
 
                if(currentSel->controller)
1632
 
                    ++currentSel->controller->ninp;
1633
 
                currentSel->value = a = displayOptions(currentSel);
1634
 
                if(retainTag) {
1635
 
                    currentSel->retain = true;
1636
 
/* Crank out the input tag */
1637
 
                    sprintf(hnum, "%c%d<", InternalCodeChar, currentSel->seqno);
1638
 
                    stringAndString(&new, &l, hnum);
1639
 
                    stringAndString(&new, &l, a);
1640
 
                    stringAndString(&new, &l, "\2000>");
1641
 
                }
1642
 
                currentSel = 0;
1643
 
            }
1644
 
            if(action == TAGACT_FORM && slash && currentForm) {
1645
 
                if(retainTag && currentForm->href && !currentForm->submitted) {
1646
 
                    makeButton();
1647
 
                    sprintf(hnum, " %c%d<Go", InternalCodeChar, ntags - 1);
1648
 
                    stringAndString(&new, &l, hnum);
1649
 
                    if(currentForm->secure)
1650
 
                        stringAndString(&new, &l, " secure");
1651
 
                    if(currentForm->bymail)
1652
 
                        stringAndString(&new, &l, " bymail");
1653
 
                    stringAndString(&new, &l, " implicit\2000>");
1654
 
                }
1655
 
                currentForm = 0;
1656
 
            }
1657
 
            continue;
1658
 
 
1659
 
        case TAGACT_SELECT:
1660
 
            if(slash) {
1661
 
                if(currentSel)
1662
 
                    goto doneSelect;
1663
 
                continue;
1664
 
            }
1665
 
            currentSel = t;
1666
 
            nopt = 0;
1667
 
            t->itype = INP_SELECT;
1668
 
            if(htmlAttrPresent(topAttrib, "readonly"))
1669
 
                t->rdonly = true;
1670
 
            if(htmlAttrPresent(topAttrib, "multiple"))
1671
 
                t->multiple = true;
1672
 
            formControl(true);
1673
 
            continue;
1674
 
 
1675
 
        case TAGACT_OPTION:
1676
 
            if(slash)
1677
 
                continue;
1678
 
            if(!currentSel) {
1679
 
                browseError(MSG_NotInSelect);
1680
 
                continue;
1681
 
            }
1682
 
            currentOpt = t;
1683
 
            offset = l;
1684
 
            t->controller = currentSel;
1685
 
            t->lic = nopt++;
1686
 
            t->value = htmlAttrVal(topAttrib, "value");
1687
 
            if(htmlAttrPresent(topAttrib, "selected")) {
1688
 
                if(currentSel->lic && !currentSel->multiple)
1689
 
                    browseError(MSG_ManyOptSelected);
1690
 
                else
1691
 
                    t->checked = t->rchecked = true, ++currentSel->lic;
1692
 
            }
1693
 
            continue;
1694
 
 
1695
 
        case TAGACT_HR:
1696
 
            if(!retainTag)
1697
 
                continue;
1698
 
            stringAndString(&new, &l,
1699
 
               "\r--------------------------------------------------------------------------------\r");
1700
 
            continue;
1701
 
 
1702
 
        case TAGACT_SUP:
1703
 
        case TAGACT_SUB:
1704
 
            if(!retainTag)
1705
 
                continue;
1706
 
            t->retain = true;
1707
 
            j = (action == TAGACT_SUP ? 2 : 1);
1708
 
            if(!slash) {
1709
 
                t->lic = l;
1710
 
                stringAndString(&new, &l, (j == 2 ? "^(" : "["));
1711
 
                continue;
1712
 
            }
1713
 
/* backup, and see if we can get rid of the parentheses or brackets */
1714
 
            a = new + open->lic + j;
1715
 
            if(j == 2 && isalphaByte(*a) && !a[1])
1716
 
                goto unparen;
1717
 
            if(j == 2 && isalnumByte(a[-3]) && (stringEqual(a, "th") ||
1718
 
               stringEqual(a, "rd") ||
1719
 
               stringEqual(a, "nd") || stringEqual(a, "st"))) {
1720
 
                a -= 2, l -= 2;
1721
 
                strcpy(a, a + 2);
1722
 
                continue;
1723
 
            }
1724
 
            while(isdigitByte(*a))
1725
 
                ++a;
1726
 
            if(!*a)
1727
 
                goto unparen;
1728
 
            stringAndChar(&new, &l, (j == 2 ? ')' : ']'));
1729
 
            continue;
1730
 
/* ok, we can trash the original ( or [ */
1731
 
          unparen:
1732
 
            a = new + open->lic + j - 1;
1733
 
            strcpy(a, a + 1);
1734
 
            --l;
1735
 
            if(j == 2)
1736
 
                stringAndChar(&new, &l, ' ');
1737
 
            continue;
1738
 
 
1739
 
        case TAGACT_AREA:
1740
 
        case TAGACT_FRAME:
1741
 
            htmlName();
1742
 
            if(action == TAGACT_FRAME) {
1743
 
                htmlHref("src");
1744
 
                topTag->jv =
1745
 
                   domLink("Frame", topTag->name, 0, "src",
1746
 
                   topTag->href, "frames", jwin, false);
1747
 
            } else {
1748
 
                htmlHref("href");
1749
 
                topTag->jv =
1750
 
                   domLink("Area", topTag->name, topTag->id, "href",
1751
 
                   topTag->href, "areas", jdoc, false);
1752
 
            }
1753
 
            topTag->clickable = true;
1754
 
            get_js_events();
1755
 
            if(!retainTag)
1756
 
                continue;
1757
 
            stringAndString(&new, &l,
1758
 
               action == TAGACT_FRAME ? "\rFrame " : "\r");
1759
 
            name = t->name;
1760
 
            if(!name)
1761
 
                name = altText(t->href);
1762
 
            if(!name)
1763
 
                name = (action == TAGACT_FRAME ? "???" : "area");
1764
 
            if(t->href) {
1765
 
                strcat(hnum, "{");
1766
 
                stringAndString(&new, &l, hnum);
1767
 
                t->action = TAGACT_A;
1768
 
                t->balanced = true;
1769
 
            }
1770
 
            if(t->href || action == TAGACT_FRAME)
1771
 
                stringAndString(&new, &l, name);
1772
 
            if(t->href)
1773
 
                stringAndString(&new, &l, "\2000}");
1774
 
            stringAndChar(&new, &l, '\r');
1775
 
            continue;
1776
 
 
1777
 
        case TAGACT_MUSIC:
1778
 
            if(!retainTag)
1779
 
                continue;
1780
 
            htmlHref("src");
1781
 
            if(!t->href)
1782
 
                continue;
1783
 
            toPreamble(t->seqno,
1784
 
               (ti->name[0] == 'A' ? "Audio passage" : "Background Music"),
1785
 
               0, 0);
1786
 
            t->action = TAGACT_A;
1787
 
            continue;
1788
 
 
1789
 
        case TAGACT_BASE:
1790
 
            htmlHref("href");
1791
 
            if(!t->href)
1792
 
                continue;
1793
 
            basehref = t->href;
1794
 
            debugPrint(3, "base href %s", basehref);
1795
 
            continue;
1796
 
 
1797
 
        case TAGACT_IMAGE:
1798
 
            htmlImage();
1799
 
            if(!currentA) {
1800
 
/* I'm going to assume that if the web designer took the time
1801
 
 * to put in an alt tag, then it's worth reading.
1802
 
 * You can turn this feature off, but I don't think you'd want to. */
1803
 
                if(a = htmlAttrVal(topAttrib, "alt"))
1804
 
                    stringAndString(&new, &l, a);
1805
 
                continue;
1806
 
            }
1807
 
            if(!retainTag)
1808
 
                continue;
1809
 
            if(a_text)
1810
 
                continue;
1811
 
            s = 0;
1812
 
            a = htmlAttrVal(topAttrib, "alt");
1813
 
            if(a) {
1814
 
                s = altText(a);
1815
 
                nzFree(a);
1816
 
            }
1817
 
            if(!s)
1818
 
                s = altText(t->name);
1819
 
            if(!s)
1820
 
                s = altText(currentA->href);
1821
 
            if(!s)
1822
 
                s = altText(t->href);
1823
 
            if(!s)
1824
 
                s = "image";
1825
 
            stringAndString(&new, &l, s);
1826
 
            a_text = true;
1827
 
            continue;
1828
 
 
1829
 
        case TAGACT_SCRIPT:
1830
 
            if(slash)
1831
 
                continue;
1832
 
            rc = findEndScript(h, ti->name,
1833
 
               (ti->action == TAGACT_SCRIPT), &h, &javatext, &js_nl);
1834
 
            if(*h)
1835
 
                h = strchr(h, '>') + 1;
1836
 
            ln += js_nl;
1837
 
/* I'm not going to process an open ended script. */
1838
 
            if(!rc) {
1839
 
                nzFree(javatext);
1840
 
                runningError(MSG_ScriptNotClosed);
1841
 
                cw->jsdead = true;
1842
 
                continue;
1843
 
            }
1844
 
            htmlHref("src");
1845
 
            a = htmlAttrVal(topAttrib, "language");
1846
 
/* If no language is specified, javascript is default. */
1847
 
            if(!cw->jsdead &&
1848
 
               (!a || !*a || !intFlag &&
1849
 
               a && memEqualCI(a, "javascript", 10) && !isalphaByte(a[10]))) {
1850
 
/* It's javascript, run with the source, or the inline text. */
1851
 
                int js_line = browseLine;
1852
 
                char *js_file = cw->fileName;
1853
 
                if(t->href) {   /* fetch the javascript page */
1854
 
                    nzFree(javatext);
1855
 
                    javatext = 0;
1856
 
                    if(javaOK(t->href)) {
1857
 
                        debugPrint(2, "java source %s", t->href);
1858
 
                        if(browseLocal && !isURL(t->href)) {
1859
 
                            if(!fileIntoMemory(t->href, &serverData,
1860
 
                               &serverDataLen)) {
1861
 
                                runningError(MSG_GetLocalJS, errorMsg);
1862
 
                            } else {
1863
 
                                javatext = serverData;
1864
 
                                prepareForBrowse(javatext, serverDataLen);
1865
 
                            }
1866
 
                        } else if(httpConnect(basehref, t->href)) {
1867
 
                            if(hcode == 200) {
1868
 
                                javatext = serverData;
1869
 
                                prepareForBrowse(javatext, serverDataLen);
1870
 
                            } else {
1871
 
                                nzFree(serverData);
1872
 
                                runningError(MSG_GetJS, t->href, hcode);
1873
 
                            }
1874
 
                        } else {
1875
 
                            runningError(MSG_GetJS2, errorMsg);
1876
 
                        }
1877
 
                        js_line = 1;
1878
 
                        js_file = t->href;
1879
 
                    }
1880
 
                }               /* fetch from the net */
1881
 
                if(javatext) {
1882
 
                    char *w = 0;
1883
 
                    if(js_file)
1884
 
                        w = strrchr(js_file, '/');
1885
 
                    if(w) {
1886
 
/* Trailing slash doesn't count */
1887
 
                        if(w[1] == 0 && w > js_file)
1888
 
                            for(--w; w >= js_file && *w != '/'; --w) ;
1889
 
                        js_file = w + 1;
1890
 
                    }
1891
 
                    debugPrint(3, "execute %s at %d", js_file, js_line);
1892
 
                    javaParseExecute(jwin, javatext, js_file, js_line);
1893
 
                    debugPrint(3, "execution complete");
1894
 
 
1895
 
/* See if the script has produced html, via document.write() */
1896
 
                    if(cw->dw) {
1897
 
                        int afterlen;   /* after we fold in this string */
1898
 
                        char *after;
1899
 
                        struct htmlTag *z;
1900
 
                        debugPrint(3, "docwrite %d bytes", cw->dw_l);
1901
 
                        debugPrint(4, "<<\n%s\n>>", cw->dw + 10);
1902
 
                        stringAndString(&cw->dw, &cw->dw_l, "</docwrite>");
1903
 
                        afterlen = strlen(h) + strlen(cw->dw);
1904
 
                        after = allocMem(afterlen + 1);
1905
 
                        strcpy(after, cw->dw);
1906
 
                        strcat(after, h);
1907
 
                        nzFree(cw->dw);
1908
 
                        cw->dw = 0;
1909
 
                        cw->dw_l = 0;
1910
 
                        nzFree(html);
1911
 
                        html = h = after;
1912
 
/* After the realloc, the inner pointers are no longer valid. */
1913
 
                        foreach(z, htmlStack) z->inner = 0;
1914
 
                    }           /* document.write */
1915
 
                }
1916
 
            }
1917
 
            nzFree(javatext);
1918
 
            nzFree(a);
1919
 
            continue;
1920
 
 
1921
 
        case TAGACT_OBJ:
1922
 
/* no clue what to do here */
1923
 
            continue;
1924
 
 
1925
 
        case TAGACT_DW:
1926
 
            if(slash) {
1927
 
                if(!--dw_nest)
1928
 
                    browseLine = ln = dw_line;
1929
 
            } else {
1930
 
                if(!dw_nest++)
1931
 
                    dw_line = ln;
1932
 
                browseLine = ln = 0;
1933
 
            }
1934
 
            continue;
1935
 
 
1936
 
        default:
1937
 
            browseError(MSG_BadTag, action);
1938
 
            continue;
1939
 
        }                       /* switch */
1940
 
 
1941
 
        if(!retainTag)
1942
 
            continue;
1943
 
        t->retain = true;
1944
 
        if(!strpbrk(hnum, "{}")) {
1945
 
            strcat(hnum, "*");
1946
 
/* Leave the meaningless tags out. */
1947
 
            if(action == TAGACT_PRE || action == TAGACT_A && topTag->name) ;    /* ok */
1948
 
            else
1949
 
                hnum[0] = 0;
1950
 
        }
1951
 
        stringAndString(&new, &l, hnum);
1952
 
      endtag:
1953
 
        lastact = action;
1954
 
        if(strayClick) {
1955
 
            topTag->clickable = true;
1956
 
            a_text = false;
1957
 
            topTag->href = cloneString("#");
1958
 
            currentA = topTag;
1959
 
            sprintf(hnum, "%c%d{", InternalCodeChar, topTag->seqno);
1960
 
            stringAndString(&new, &l, hnum);
1961
 
        }
1962
 
    }                           /* loop over html string */
1963
 
 
1964
 
    if(currentA) {
1965
 
        stringAndString(&new, &l, "\2000}");
1966
 
        currentA = 0;
1967
 
    }
1968
 
 
1969
 
/* Run the various onload functions */
1970
 
/* Turn the onunload functions into hyperlinks */
1971
 
    if(!cw->jsdead && !onload_done) {
1972
 
        const struct htmlTag *lasttag;
1973
 
        onloadGo(jwin, 0, "window");
1974
 
        onloadGo(jdoc, 0, "document");
1975
 
        lasttag = htmlStack.prev;
1976
 
        foreach(t, htmlStack) {
1977
 
            char *jsrc;
1978
 
            ev = t->jv;
1979
 
/* in case the option has disappeared */
1980
 
            if(t->action == TAGACT_OPTION)
1981
 
                goto next_onload;
1982
 
            if(!ev)
1983
 
                goto next_onload;
1984
 
            if(t->slash)
1985
 
                goto next_onload;
1986
 
            jsrc = htmlAttrVal(t->attrib, "onunload");
1987
 
            onloadGo(ev, jsrc, t->info->name);
1988
 
 
1989
 
          next_onload:
1990
 
            if(t == lasttag)
1991
 
                break;
1992
 
        }                       /* loop over tags */
1993
 
    }
1994
 
    onload_done = true;
1995
 
 
1996
 
/* The onload function can, and often does, invoke document.write() */
1997
 
    if(cw->dw) {
1998
 
        nzFree(html);
1999
 
        html = h = cw->dw;
2000
 
        cw->dw = 0;
2001
 
        cw->dw_l = 0;
2002
 
        goto top;
2003
 
    }
2004
 
 
2005
 
    if(browseLocal == 1) {      /* no errors yet */
2006
 
        foreach(t, htmlStack) {
2007
 
            browseLine = t->ln;
2008
 
            if(t->info->nest && !t->slash && !t->balanced) {
2009
 
                browseError(MSG_TagNotClosed, t->info->desc);
2010
 
                break;
2011
 
            }
2012
 
 
2013
 
/* Make sure the internal links are defined. */
2014
 
            if(t->action != TAGACT_A)
2015
 
                continue;       /* not anchor */
2016
 
            h = t->href;
2017
 
            if(!h)
2018
 
                continue;
2019
 
            if(h[0] != '#')
2020
 
                continue;
2021
 
            if(h[1] == 0)
2022
 
                continue;
2023
 
            a = h + 1;          /* this is what we're looking for */
2024
 
            foreach(v, htmlStack) {
2025
 
                if(v->action != TAGACT_A)
2026
 
                    continue;   /* not achor */
2027
 
                if(!v->name)
2028
 
                    continue;   /* no name */
2029
 
                if(stringEqual(a, v->name))
2030
 
                    break;
2031
 
            }
2032
 
            if(v == (struct htmlTag *)&htmlStack) {     /* end of list */
2033
 
                browseError(MSG_NoLable2, a);
2034
 
                break;
2035
 
            }
2036
 
        }                       /* loop over all tags */
2037
 
    }
2038
 
 
2039
 
    /* clean up */
2040
 
    browseLine = 0;
2041
 
    nzFree(html);
2042
 
    nzFree(radioChecked);
2043
 
    radioChecked = 0;
2044
 
    basehref = 0;
2045
 
    if(preamble) {
2046
 
        h = allocMem(preamble_l + l + 2);
2047
 
        strcpy(h, preamble);
2048
 
        h[preamble_l] = '\f';
2049
 
        strcpy(h + preamble_l + 1, new);
2050
 
        nzFree(preamble);
2051
 
        preamble = 0;
2052
 
        nzFree(new);
2053
 
        new = h;
2054
 
    }
2055
 
 
2056
 
    return new;
2057
 
}                               /* encodeTags */
2058
 
 
2059
 
void
2060
 
preFormatCheck(int tagno, bool * pretag, bool * slash)
2061
 
{
2062
 
    const struct htmlTag *t;
2063
 
    if(!parsePage)
2064
 
        i_printfExit(MSG_ErrCallPreFormat);
2065
 
    *pretag = *slash = false;
2066
 
    if(tagno >= 0 && tagno < ntags) {
2067
 
        t = tagArray[tagno];
2068
 
        *pretag = (t->action == TAGACT_PRE);
2069
 
        *slash = t->slash;
2070
 
    }
2071
 
}                               /* preFormatCheck */
2072
 
 
2073
 
char *
2074
 
htmlParse(char *buf, int remote)
2075
 
{
2076
 
    char *newbuf;
2077
 
 
2078
 
    if(parsePage)
2079
 
        i_printfExit(MSG_HtmlNotreentrant);
2080
 
    parsePage = true;
2081
 
    if(remote >= 0)
2082
 
        browseLocal = !remote;
2083
 
    buf = encodeTags(buf);
2084
 
    debugPrint(7, "%s", buf);
2085
 
 
2086
 
    buildTagArray();
2087
 
 
2088
 
    newbuf = andTranslate(buf, false);
2089
 
    nzFree(buf);
2090
 
    buf = newbuf;
2091
 
    anchorSwap(buf);
2092
 
    debugPrint(7, "%s", buf);
2093
 
 
2094
 
    newbuf = htmlReformat(buf);
2095
 
    nzFree(buf);
2096
 
    buf = newbuf;
2097
 
 
2098
 
    parsePage = false;
2099
 
 
2100
 
/* In case one of the onload functions called document.write() */
2101
 
    jsdw();
2102
 
 
2103
 
    return buf;
2104
 
}                               /* htmlParse */
2105
 
 
2106
 
void
2107
 
findField(const char *line, int ftype, int n,
2108
 
   int *total, int *realtotal, int *tagno, char **href, void **evp)
2109
 
{
2110
 
    const struct htmlTag *t, **list = cw->tags;
2111
 
    int nt = 0;                 /* number of fields total */
2112
 
    int nrt = 0;                /* the real total, for input fields */
2113
 
    int nm = 0;                 /* number match */
2114
 
    int j;
2115
 
    const char *s, *ss;
2116
 
    char *h, *nmh;
2117
 
    char c;
2118
 
    static const char urlok[] =
2119
 
       "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890,./?@#%&-_+=:~";
2120
 
 
2121
 
    if(href)
2122
 
        *href = 0;
2123
 
    if(evp)
2124
 
        *evp = 0;
2125
 
 
2126
 
    if(cw->browseMode) {
2127
 
        s = line;
2128
 
        while((c = *s) != '\n') {
2129
 
            ++s;
2130
 
            if(c != (char)InternalCodeChar)
2131
 
                continue;
2132
 
            j = strtol(s, (char **)&s, 10);
2133
 
            if(!ftype) {
2134
 
                if(*s != '{')
2135
 
                    continue;
2136
 
                ++nt, ++nrt;
2137
 
                if(n == nt)
2138
 
                    nm = j;
2139
 
                if(!n) {
2140
 
                    if(!nm)
2141
 
                        nm = j;
2142
 
                    else
2143
 
                        nm = -1;
2144
 
                }
2145
 
            } else {
2146
 
                if(*s != '<')
2147
 
                    continue;
2148
 
                if(n) {
2149
 
                    ++nt, ++nrt;
2150
 
                    if(n == nt)
2151
 
                        nm = j;
2152
 
                    continue;
2153
 
                }
2154
 
                t = list[j];
2155
 
                ++nrt;
2156
 
                if(ftype == 1 && t->itype <= INP_SUBMIT)
2157
 
                    continue;
2158
 
                if(ftype == 2 && t->itype > INP_SUBMIT)
2159
 
                    continue;
2160
 
                ++nt;
2161
 
                if(!nm)
2162
 
                    nm = j;
2163
 
                else
2164
 
                    nm = -1;
2165
 
            }
2166
 
        }                       /* loop over line */
2167
 
    }
2168
 
 
2169
 
    if(nm < 0)
2170
 
        nm = 0;
2171
 
    if(total)
2172
 
        *total = nrt;
2173
 
    if(realtotal)
2174
 
        *realtotal = nt;
2175
 
    if(tagno)
2176
 
        *tagno = nm;
2177
 
    if(!ftype && nm) {
2178
 
        t = list[nm];
2179
 
        if(href)
2180
 
            *href = cloneString(t->href);
2181
 
        if(evp)
2182
 
            *evp = t->jv;
2183
 
        if(href && t->jv) {
2184
 
/* defer to the java variable for the reference */
2185
 
            const char *jh = get_property_url(t->jv, false);
2186
 
            if(jh) {
2187
 
                if(!*href || !stringEqual(*href, jh)) {
2188
 
                    nzFree(*href);
2189
 
                    *href = cloneString(jh);
2190
 
                }
2191
 
            }
2192
 
        }
2193
 
    }
2194
 
 
2195
 
    if(nt || ftype)
2196
 
        return;
2197
 
 
2198
 
/* Second time through, maybe the url is in plain text. */
2199
 
    nmh = 0;
2200
 
    s = line;
2201
 
    while(true) {
2202
 
/* skip past weird characters */
2203
 
        while((c = *s) != '\n') {
2204
 
            if(strchr(urlok, c))
2205
 
                break;
2206
 
            ++s;
2207
 
        }
2208
 
        if(c == '\n')
2209
 
            break;
2210
 
        ss = s;
2211
 
        while(strchr(urlok, *s))
2212
 
            ++s;
2213
 
        h = pullString1(ss, s);
2214
 
        unpercentURL(h);
2215
 
        if(!isURL(h)) {
2216
 
            free(h);
2217
 
            continue;
2218
 
        }
2219
 
        ++nt;
2220
 
        if(n == nt) {
2221
 
            nm = nt;
2222
 
            nmh = h;
2223
 
            continue;
2224
 
        }
2225
 
        if(n) {
2226
 
            free(h);
2227
 
            continue;
2228
 
        }
2229
 
        if(!nm) {
2230
 
            nm = nt;
2231
 
            nmh = h;
2232
 
            continue;
2233
 
        }
2234
 
        free(h);
2235
 
        nm = -1;
2236
 
        free(nmh);
2237
 
        nmh = 0;
2238
 
    }                           /* loop over line */
2239
 
 
2240
 
    if(nm < 0)
2241
 
        nm = 0;
2242
 
    if(total)
2243
 
        *total = nt;
2244
 
    if(realtotal)
2245
 
        *realtotal = nt;
2246
 
    if(href)
2247
 
        *href = nmh;
2248
 
    else
2249
 
        nzFree(nmh);
2250
 
}                               /* findField */
2251
 
 
2252
 
void
2253
 
findInputField(const char *line, int ftype, int n, int *total, int *realtotal,
2254
 
   int *tagno)
2255
 
{
2256
 
    findField(line, ftype, n, total, realtotal, tagno, 0, 0);
2257
 
}                               /* findInputField */
2258
 
 
2259
 
bool
2260
 
lineHasTag(const char *p, const char *s)
2261
 
{
2262
 
    const struct htmlTag *t, **list = cw->tags;
2263
 
    char c;
2264
 
    int j;
2265
 
    while((c = *p++) != '\n') {
2266
 
        if(c != (char)InternalCodeChar)
2267
 
            continue;
2268
 
        j = strtol(p, (char **)&p, 10);
2269
 
        if(*p != '*')
2270
 
            continue;
2271
 
        t = list[j];
2272
 
        if(t->action != TAGACT_A)
2273
 
            continue;
2274
 
        if(!t->name)
2275
 
            continue;
2276
 
        if(stringEqual(t->name, s))
2277
 
            return true;
2278
 
    }
2279
 
    return false;
2280
 
}                               /* lineHasTag */
2281
 
 
2282
 
/* See if there are simple tags like <p> or </font> */
2283
 
bool
2284
 
htmlTest(void)
2285
 
{
2286
 
    int j, ln;
2287
 
    int cnt = 0;
2288
 
    int fsize = 0;              /* file size */
2289
 
    char look[12];
2290
 
    bool firstline = true;
2291
 
 
2292
 
    for(ln = 1; ln <= cw->dol; ++ln) {
2293
 
        char *p = (char *)fetchLine(ln, -1);
2294
 
        char c;
2295
 
        int state = 0;
2296
 
 
2297
 
        while(isspaceByte(*p) && *p != '\n')
2298
 
            ++p;
2299
 
        if(*p == '\n')
2300
 
            continue;           /* skip blank line */
2301
 
        if(firstline && *p == '<') {
2302
 
/* check for <!doctype */
2303
 
            if(memEqualCI(p + 1, "!doctype", 8))
2304
 
                return true;
2305
 
/* If it starts with <tag, for any tag we recognize,
2306
 
 * we'll call it good. */
2307
 
            for(j = 1; j < 10; ++j) {
2308
 
                if(!isalnumByte(p[j]))
2309
 
                    break;
2310
 
                look[j - 1] = p[j];
2311
 
            }
2312
 
            look[j - 1] = 0;
2313
 
            if(j > 1 && (p[j] == '>' || isspaceByte(p[j]))) {
2314
 
/* something we recognize? */
2315
 
                const struct tagInfo *ti;
2316
 
                for(ti = elements; ti->name; ++ti)
2317
 
                    if(stringEqualCI(ti->name, look))
2318
 
                        return true;
2319
 
            }                   /* leading tag */
2320
 
        }                       /* leading < */
2321
 
        firstline = false;
2322
 
 
2323
 
/* count tags through the buffer */
2324
 
        for(j = 0; (c = p[j]) != '\n'; ++j) {
2325
 
            if(state == 0) {
2326
 
                if(c == '<')
2327
 
                    state = 1;
2328
 
                continue;
2329
 
            }
2330
 
            if(state == 1) {
2331
 
                if(c == '/')
2332
 
                    state = 2;
2333
 
                else if(isalphaByte(c))
2334
 
                    state = 3;
2335
 
                else
2336
 
                    state = 0;
2337
 
                continue;
2338
 
            }
2339
 
            if(state == 2) {
2340
 
                if(isalphaByte(c))
2341
 
                    state = 3;
2342
 
                else
2343
 
                    state = 0;
2344
 
                continue;
2345
 
            }
2346
 
            if(isalphaByte(c))
2347
 
                continue;
2348
 
            if(c == '>')
2349
 
                ++cnt;
2350
 
            state = 0;
2351
 
        }
2352
 
        fsize += j;
2353
 
    }                           /* loop over lines */
2354
 
 
2355
 
/* we need at least one of these tags every 300 characters.
2356
 
 * And we need at least 4 such tags.
2357
 
 * Remember, you can always override by putting <html> at the top. */
2358
 
    return (cnt >= 4 && cnt * 300 >= fsize);
2359
 
}                               /* htmlTest */
2360
 
 
2361
 
/* Show an input field */
2362
 
void
2363
 
infShow(int tagno, const char *search)
2364
 
{
2365
 
    const struct htmlTag **list = cw->tags;
2366
 
    const struct htmlTag *t = list[tagno], *v;
2367
 
    const char *s;
2368
 
    int j, cnt;
2369
 
    bool show;
2370
 
 
2371
 
    s = inp_types[t->itype];
2372
 
    if(*s == ' ')
2373
 
        ++s;
2374
 
    printf("%s", s);
2375
 
    if(t->multiple)
2376
 
        i_printf(MSG_Many);
2377
 
    if(t->itype >= INP_TEXT && t->itype <= INP_NUMBER && t->lic)
2378
 
        printf("[%d]", t->lic);
2379
 
    if(t->itype == INP_TA) {
2380
 
        char *rows = htmlAttrVal(t->attrib, "rows");
2381
 
        char *cols = htmlAttrVal(t->attrib, "cols");
2382
 
        char *wrap = htmlAttrVal(t->attrib, "wrap");
2383
 
        if(rows && cols) {
2384
 
            printf("[%sx%s", rows, cols);
2385
 
            if(wrap && stringEqualCI(wrap, "virtual"))
2386
 
                i_printf(MSG_Recommended);
2387
 
            i_printf(MSG_Close);
2388
 
        }
2389
 
        nzFree(rows);
2390
 
        nzFree(cols);
2391
 
        nzFree(wrap);
2392
 
    }                           /* text area */
2393
 
    if(t->name)
2394
 
        printf(" %s", t->name);
2395
 
    nl();
2396
 
    if(t->itype != INP_SELECT)
2397
 
        return;
2398
 
 
2399
 
/* display the options in a pick list */
2400
 
/* If a search string is given, display the options containing that string. */
2401
 
    cnt = 0;
2402
 
    show = false;
2403
 
    for(j = 0; v = list[j]; ++j) {
2404
 
        if(v->controller != t)
2405
 
            continue;
2406
 
        if(!v->name)
2407
 
            continue;
2408
 
        ++cnt;
2409
 
        if(*search && !strstrCI(v->name, search))
2410
 
            continue;
2411
 
        show = true;
2412
 
        printf("%3d %s\n", cnt, v->name);
2413
 
    }
2414
 
    if(!show) {
2415
 
        if(!search)
2416
 
            i_puts(MSG_NoOptions);
2417
 
        else
2418
 
            i_printf(MSG_NoOptionsMatch, search);
2419
 
    }
2420
 
}                               /* infShow */
2421
 
 
2422
 
/* Update an input field. */
2423
 
bool
2424
 
infReplace(int tagno, const char *newtext, int notify)
2425
 
{
2426
 
    const struct htmlTag **list = cw->tags;
2427
 
    const struct htmlTag *t = list[tagno], *v;
2428
 
    const struct htmlTag *form = t->controller;
2429
 
    char *display;
2430
 
    int itype = t->itype;
2431
 
    int newlen = strlen(newtext);
2432
 
 
2433
 
/* sanity checks on the input */
2434
 
    if(itype <= INP_SUBMIT) {
2435
 
        int b = MSG_IsButton;
2436
 
        if(itype == INP_SUBMIT || itype == INP_IMAGE)
2437
 
            b = MSG_SubmitButton;
2438
 
        if(itype == INP_RESET)
2439
 
            b = MSG_ResetButton;
2440
 
        setError(b);
2441
 
        return false;
2442
 
    }
2443
 
 
2444
 
    if(itype == INP_TA) {
2445
 
        setError(MSG_Textarea, t->lic);
2446
 
        return false;
2447
 
    }
2448
 
 
2449
 
    if(t->rdonly) {
2450
 
        setError(MSG_Readonly);
2451
 
        return false;
2452
 
    }
2453
 
 
2454
 
    if(strchr(newtext, '\n')) {
2455
 
        setError(MSG_InputNewline);
2456
 
        return false;
2457
 
    }
2458
 
 
2459
 
    if(itype >= INP_TEXT && itype <= INP_NUMBER && t->lic && newlen > t->lic) {
2460
 
        setError(MSG_InputLong, t->lic);
2461
 
        return false;
2462
 
    }
2463
 
 
2464
 
    if(itype >= INP_RADIO) {
2465
 
        if(newtext[0] != '+' && newtext[0] != '-' || newtext[1]) {
2466
 
            setError(MSG_InputRadio);
2467
 
            return false;
2468
 
        }
2469
 
        if(itype == INP_RADIO && newtext[0] == '-') {
2470
 
            setError(MSG_ClearRadio);
2471
 
            return false;
2472
 
        }
2473
 
    }
2474
 
 
2475
 
/* Two lines, clear the "other" radio button, and set this one. */
2476
 
    if(!linesComing(2))
2477
 
        return false;
2478
 
 
2479
 
    jMyContext();
2480
 
 
2481
 
    if(itype == INP_SELECT) {
2482
 
        if(!locateOptions(t, newtext, 0, 0, false))
2483
 
            return false;
2484
 
        locateOptions(t, newtext, &display, 0, false);
2485
 
        updateFieldInBuffer(tagno, display, notify, true);
2486
 
        nzFree(display);
2487
 
    }
2488
 
 
2489
 
    if(itype == INP_FILE) {
2490
 
        if(!envFile(newtext, &newtext))
2491
 
            return false;
2492
 
        if(newtext[0] && access(newtext, 4)) {
2493
 
            setError(MSG_FileAccess, newtext);
2494
 
            return false;
2495
 
        }
2496
 
    }
2497
 
 
2498
 
    if(itype == INP_NUMBER) {
2499
 
        if(*newtext && stringIsNum(newtext) < 0) {
2500
 
            setError(MSG_NumberExpected);
2501
 
            return false;
2502
 
        }
2503
 
    }
2504
 
 
2505
 
    if(itype == INP_RADIO && form && t->name && *newtext == '+') {
2506
 
/* clear the other radio button */
2507
 
        while(v = *list++) {
2508
 
            if(v->controller != form)
2509
 
                continue;
2510
 
            if(v->itype != INP_RADIO)
2511
 
                continue;
2512
 
            if(!v->name)
2513
 
                continue;
2514
 
            if(!stringEqual(v->name, t->name))
2515
 
                continue;
2516
 
            if(fieldIsChecked(v->seqno) == true)
2517
 
                updateFieldInBuffer(v->seqno, "-", 0, false);
2518
 
        }
2519
 
    }
2520
 
 
2521
 
    if(itype != INP_SELECT) {
2522
 
        updateFieldInBuffer(tagno, newtext, notify, true);
2523
 
    }
2524
 
 
2525
 
    if(itype >= INP_RADIO && tagHandler(t->seqno, "onclick")) {
2526
 
        if(cw->jsdead)
2527
 
            runningError(MSG_NJNoOnclick);
2528
 
        else {
2529
 
            jSyncup();
2530
 
            handlerGo(t->jv, "onclick");
2531
 
            jsdw();
2532
 
            if(js_redirects)
2533
 
                return true;
2534
 
        }
2535
 
    }
2536
 
 
2537
 
    if(itype >= INP_TEXT && itype <= INP_SELECT &&
2538
 
       tagHandler(t->seqno, "onchange")) {
2539
 
        if(cw->jsdead)
2540
 
            runningError(MSG_NJNoOnchange);
2541
 
        else {
2542
 
            jSyncup();
2543
 
            handlerGo(t->jv, "onchange");
2544
 
            jsdw();
2545
 
            if(js_redirects)
2546
 
                return true;
2547
 
        }
2548
 
    }
2549
 
 
2550
 
    return true;
2551
 
}                               /* infReplace */
2552
 
 
2553
 
/*********************************************************************
2554
 
Reset or submit a form.
2555
 
This function could be called by javascript, as well as a human.
2556
 
It must therefore update the js variables and the text simultaneously.
2557
 
Most of this work is done by resetVar().
2558
 
To reset a variable, copy its original value, in the html tag,
2559
 
back to the text buffer, and over to javascript.
2560
 
*********************************************************************/
2561
 
 
2562
 
static void
2563
 
resetVar(struct htmlTag *t)
2564
 
{
2565
 
    int itype = t->itype;
2566
 
    const char *w = t->value;
2567
 
    bool bval;
2568
 
    void *jv = t->jv;
2569
 
 
2570
 
/* This is a kludge - option looks like INP_SELECT */
2571
 
    if(t->action == TAGACT_OPTION)
2572
 
        itype = INP_SELECT;
2573
 
 
2574
 
    if(itype <= INP_SUBMIT)
2575
 
        return;
2576
 
 
2577
 
    if(itype >= INP_SELECT) {
2578
 
        bval = t->rchecked;
2579
 
        t->checked = bval;
2580
 
        w = bval ? "+" : "-";
2581
 
    }
2582
 
 
2583
 
    if(itype == INP_TA) {
2584
 
        int cx = t->lic;
2585
 
        if(cx)
2586
 
            sideBuffer(cx, t->value, -1, 0, false);
2587
 
    } else if(itype != INP_HIDDEN && itype != INP_SELECT)
2588
 
        updateFieldInBuffer(t->seqno, w, 0, false);
2589
 
 
2590
 
    if(jv) {
2591
 
        if(itype >= INP_RADIO) {
2592
 
            set_property_bool(jv, "checked", bval);
2593
 
        } else if(itype == INP_SELECT) {
2594
 
            set_property_bool(jv, "selected", bval);
2595
 
            if(bval && !t->controller->multiple)
2596
 
                set_property_number(t->controller->jv, "selectedIndex", t->lic);
2597
 
        } else
2598
 
            set_property_string(jv, "value", w);
2599
 
    }
2600
 
}                               /* resetVar */
2601
 
 
2602
 
static void
2603
 
formReset(const struct htmlTag *form)
2604
 
{
2605
 
    struct htmlTag **list = cw->tags, *t, *sel = 0;
2606
 
    int itype;
2607
 
 
2608
 
    while(t = *list++) {
2609
 
        if(t->action == TAGACT_OPTION) {
2610
 
            if(!sel)
2611
 
                continue;
2612
 
            if(t->controller != sel)
2613
 
                continue;
2614
 
            resetVar(t);
2615
 
            continue;
2616
 
        }
2617
 
        /* option */
2618
 
        if(t->action != TAGACT_INPUT)
2619
 
            continue;
2620
 
 
2621
 
        if(sel) {
2622
 
            char *display = displayOptions(sel);
2623
 
            updateFieldInBuffer(sel->seqno, display, 0, false);
2624
 
            nzFree(display);
2625
 
            sel = 0;
2626
 
        }
2627
 
 
2628
 
        if(t->controller != form)
2629
 
            continue;
2630
 
        itype = t->itype;
2631
 
        if(itype != INP_SELECT) {
2632
 
            resetVar(t);
2633
 
            continue;
2634
 
        }
2635
 
        sel = t;
2636
 
        if(t->jv)
2637
 
            set_property_number(t->jv, "selectedIndex", -1);
2638
 
    }                           /* loop over tags */
2639
 
 
2640
 
    i_puts(MSG_FormReset);
2641
 
}                               /* formReset */
2642
 
 
2643
 
/* Fetch a field value (from a form) to post. */
2644
 
/* The result is allocated */
2645
 
static char *
2646
 
fetchTextVar(const struct htmlTag *t)
2647
 
{
2648
 
    void *jv = t->jv;
2649
 
    char *v;
2650
 
    if(jv) {
2651
 
        return cloneString(get_property_string(jv, "value"));
2652
 
    }
2653
 
    if(t->itype > INP_HIDDEN) {
2654
 
        v = getFieldFromBuffer(t->seqno);
2655
 
        if(v)
2656
 
            return v;
2657
 
    }
2658
 
/* Revert to the default value */
2659
 
    return cloneString(t->value);
2660
 
}                               /* fetchTextVar */
2661
 
 
2662
 
static bool
2663
 
fetchBoolVar(const struct htmlTag *t)
2664
 
{
2665
 
    void *jv = t->jv;
2666
 
    int checked;
2667
 
    if(jv) {
2668
 
        return get_property_bool(jv,
2669
 
           (t->action == TAGACT_OPTION ? "selected" : "checked"));
2670
 
    }
2671
 
    checked = fieldIsChecked(t->seqno);
2672
 
    if(checked < 0)
2673
 
        checked = t->rchecked;
2674
 
    return checked;
2675
 
}                               /* fetchBoolVar */
2676
 
 
2677
 
/* Some information on posting forms can be found here.
2678
 
 * http://www.w3.org/TR/REC-html40/interact/forms.html */
2679
 
 
2680
 
static void
2681
 
postDelimiter(char fsep, const char *boundary, char **post, int *l)
2682
 
{
2683
 
    char *p = *post;
2684
 
    char c = p[*l - 1];
2685
 
    if(c == '?' || c == '\1')
2686
 
        return;
2687
 
    if(fsep == '-') {
2688
 
        stringAndString(post, l, "--");
2689
 
        stringAndString(post, l, boundary);
2690
 
        stringAndChar(post, l, '\r');
2691
 
        fsep = '\n';
2692
 
    }
2693
 
    stringAndChar(post, l, fsep);
2694
 
}                               /* postDelimiter */
2695
 
 
2696
 
static bool
2697
 
postNameVal(const char *name, const char *val,
2698
 
   char fsep, uchar isfile, const char *boundary, char **post, int *l)
2699
 
{
2700
 
    char *enc;
2701
 
    const char *ct, *ce;        /* content type, content encoding */
2702
 
 
2703
 
    if(name && !*name)
2704
 
        name = 0;
2705
 
    if(val && !*val)
2706
 
        val = 0;
2707
 
    if(!name && !val)
2708
 
        return true;
2709
 
 
2710
 
    if(name) {
2711
 
        postDelimiter(fsep, boundary, post, l);
2712
 
        switch (fsep) {
2713
 
        case '&':
2714
 
            enc = encodePostData(name);
2715
 
            stringAndString(post, l, enc);
2716
 
            stringAndChar(post, l, '=');
2717
 
            nzFree(enc);
2718
 
            break;
2719
 
        case '\n':
2720
 
            stringAndString(post, l, name);
2721
 
            stringAndString(post, l, "=\r\n");
2722
 
            break;
2723
 
        case '-':
2724
 
            stringAndString(post, l, "Content-Disposition: form-data; name=\"");
2725
 
            stringAndString(post, l, name);
2726
 
            stringAndChar(post, l, '"');
2727
 
/* I'm leaving nl off, in case we need ; filename */
2728
 
            break;
2729
 
        }                       /* switch */
2730
 
    }
2731
 
    /* name */
2732
 
    if(!val) {
2733
 
        if(fsep == '&')
2734
 
            return true;
2735
 
        val = EMPTYSTRING;
2736
 
    }
2737
 
    /* no value */
2738
 
    switch (fsep) {
2739
 
    case '&':
2740
 
        enc = encodePostData(val);
2741
 
        stringAndString(post, l, enc);
2742
 
        nzFree(enc);
2743
 
        break;
2744
 
    case '\n':
2745
 
        stringAndString(post, l, val);
2746
 
        stringAndString(post, l, eol);
2747
 
        break;
2748
 
    case '-':
2749
 
        if(isfile) {
2750
 
            if(isfile & 2) {
2751
 
                stringAndString(post, l, "; filename=\"");
2752
 
                stringAndString(post, l, val);
2753
 
                stringAndChar(post, l, '"');
2754
 
            }
2755
 
            if(!encodeAttachment(val, 0, &ct, &ce, &enc))
2756
 
                return false;
2757
 
            val = enc;
2758
 
/* remember to free val in this case */
2759
 
        } else {
2760
 
            const char *s;
2761
 
            ct = "text/plain";
2762
 
/* Anything nonascii makes it 8bit */
2763
 
            ce = "7bit";
2764
 
            for(s = val; *s; ++s)
2765
 
                if(*s < 0) {
2766
 
                    ce = "8bit";
2767
 
                    break;
2768
 
                }
2769
 
        }
2770
 
        stringAndString(post, l, "\r\nContent-Type: ");
2771
 
        stringAndString(post, l, ct);
2772
 
        stringAndString(post, l, "\r\nContent-Transfer-Encoding: ");
2773
 
        stringAndString(post, l, ce);
2774
 
        stringAndString(post, l, "\r\n\r\n");
2775
 
        stringAndString(post, l, val);
2776
 
        stringAndString(post, l, eol);
2777
 
        if(isfile)
2778
 
            nzFree(enc);
2779
 
        break;
2780
 
    }                           /* switch */
2781
 
 
2782
 
    return true;
2783
 
}                               /* postNameVal */
2784
 
 
2785
 
static bool
2786
 
formSubmit(const struct htmlTag *form, const struct htmlTag *submit,
2787
 
   char **post, int *l)
2788
 
{
2789
 
    const struct htmlTag **list = cw->tags, *t;
2790
 
    int itype;
2791
 
    int j;
2792
 
    char *name, *value;
2793
 
    const char *boundary;
2794
 
    char fsep = '&';            /* field separator */
2795
 
    bool noname = false, rc;
2796
 
    bool bval;
2797
 
 
2798
 
    if(form->bymail)
2799
 
        fsep = '\n';
2800
 
    if(form->mime) {
2801
 
        fsep = '-';
2802
 
        boundary = makeBoundary();
2803
 
        stringAndString(post, l, "`mfd~");
2804
 
        stringAndString(post, l, boundary);
2805
 
        stringAndString(post, l, eol);
2806
 
    }
2807
 
 
2808
 
    while(t = *list++) {
2809
 
        if(t->action != TAGACT_INPUT)
2810
 
            continue;
2811
 
        if(t->controller != form)
2812
 
            continue;
2813
 
        itype = t->itype;
2814
 
        if(itype <= INP_SUBMIT && t != submit)
2815
 
            continue;
2816
 
        name = t->name;
2817
 
 
2818
 
        if(t == submit) {       /* the submit button you pushed */
2819
 
            int namelen;
2820
 
            char *nx;
2821
 
            if(!name)
2822
 
                continue;
2823
 
            value = t->value;
2824
 
            if(!value)
2825
 
                value = "Submit";
2826
 
            if(t->itype != INP_IMAGE)
2827
 
                goto success;
2828
 
            namelen = strlen(name);
2829
 
            nx = allocMem(namelen + 3);
2830
 
            strcpy(nx, name);
2831
 
            strcpy(nx + namelen, ".x");
2832
 
            postNameVal(nx, "0", fsep, false, boundary, post, l);
2833
 
            nx[namelen + 1] = 'y';
2834
 
            postNameVal(nx, "0", fsep, false, boundary, post, l);
2835
 
            nzFree(nx);
2836
 
            goto success;
2837
 
        }
2838
 
 
2839
 
        if(itype >= INP_RADIO) {
2840
 
            value = t->value;
2841
 
            bval = fetchBoolVar(t);
2842
 
            if(!bval)
2843
 
                continue;
2844
 
            if(!name) {
2845
 
                noname = true;
2846
 
                continue;
2847
 
            }
2848
 
            if(value && !*value)
2849
 
                value = 0;
2850
 
            if(itype == INP_CHECKBOX && value == 0)
2851
 
                value = "on";
2852
 
            goto success;
2853
 
        }
2854
 
 
2855
 
        if(itype < INP_FILE) {
2856
 
/* Even a hidden variable can be adjusted by js.
2857
 
 * fetchTextVar allows for this possibility.
2858
 
 * I didn't allow for it in the above, the value of a radio button;
2859
 
 * hope that's not a problem. */
2860
 
            value = fetchTextVar(t);
2861
 
            postNameVal(name, value, fsep, false, boundary, post, l);
2862
 
            nzFree(value);
2863
 
            continue;
2864
 
        }
2865
 
 
2866
 
        if(itype == INP_TA) {
2867
 
            int cx = t->lic;
2868
 
            char *cxbuf;
2869
 
            int cxlen;
2870
 
            if(!name) {
2871
 
                noname = true;
2872
 
                continue;
2873
 
            }
2874
 
            if(cx) {
2875
 
                if(fsep == '-') {
2876
 
                    char cxstring[12];
2877
 
/* do this as an attachment */
2878
 
                    sprintf(cxstring, "%d", cx);
2879
 
                    if(!postNameVal(name, cxstring, fsep, 1, boundary, post, l))
2880
 
                        goto fail;
2881
 
                    continue;
2882
 
                }               /* attach */
2883
 
                if(!unfoldBuffer(cx, textAreaDosNewlines, &cxbuf, &cxlen))
2884
 
                    goto fail;
2885
 
                for(j = 0; j < cxlen; ++j)
2886
 
                    if(cxbuf[j] == 0) {
2887
 
                        setError(MSG_SessionNull, cx);
2888
 
                        nzFree(cxbuf);
2889
 
                        goto fail;
2890
 
                    }
2891
 
                if(j && cxbuf[j - 1] == '\n')
2892
 
                    --j;
2893
 
                if(j && cxbuf[j - 1] == '\r')
2894
 
                    --j;
2895
 
                cxbuf[j] = 0;
2896
 
                rc = postNameVal(name, cxbuf, fsep, false, boundary, post, l);
2897
 
                nzFree(cxbuf);
2898
 
                if(rc)
2899
 
                    continue;
2900
 
                goto fail;
2901
 
            }
2902
 
            /* valid context */
2903
 
            /* Just an empty string */
2904
 
            postNameVal(name, 0, fsep, false, boundary, post, l);
2905
 
            continue;
2906
 
        }
2907
 
 
2908
 
        if(itype == INP_SELECT) {
2909
 
            char *display = getFieldFromBuffer(t->seqno);
2910
 
            char *s, *e;
2911
 
            if(!display) {      /* off the air */
2912
 
                struct htmlTag *v, **vl = cw->tags;
2913
 
/* revert back to reset state */
2914
 
                while(v = *vl++)
2915
 
                    if(v->controller == t)
2916
 
                        v->checked = v->rchecked;
2917
 
                display = displayOptions(t);
2918
 
            }
2919
 
            rc = locateOptions(t, display, 0, &value, false);
2920
 
            nzFree(display);
2921
 
            if(!rc)
2922
 
                goto fail;      /* this should never happen */
2923
 
/* option could have an empty value, usually the null choice,
2924
 
 * before you have made a selection. */
2925
 
            if(!*value) {
2926
 
                postNameVal(name, value, fsep, false, boundary, post, l);
2927
 
                continue;
2928
 
            }
2929
 
/* Step through the options */
2930
 
            for(s = value; *s; s = e) {
2931
 
                char more;
2932
 
                e = 0;
2933
 
                if(t->multiple)
2934
 
                    e = strchr(s, '\1');
2935
 
                if(!e)
2936
 
                    e = s + strlen(s);
2937
 
                more = *e, *e = 0;
2938
 
                postNameVal(name, s, fsep, false, boundary, post, l);
2939
 
                if(more)
2940
 
                    ++e;
2941
 
            }
2942
 
            nzFree(value);
2943
 
            continue;
2944
 
        }
2945
 
 
2946
 
        if(itype == INP_FILE) { /* the only one left */
2947
 
            value = fetchTextVar(t);
2948
 
            if(!value)
2949
 
                continue;
2950
 
            if(!*value)
2951
 
                continue;
2952
 
            if(!(form->post & form->mime)) {
2953
 
                setError(MSG_FilePost);
2954
 
                nzFree(value);
2955
 
                goto fail;
2956
 
            }
2957
 
            rc = postNameVal(name, value, fsep, 3, boundary, post, l);
2958
 
            nzFree(value);
2959
 
            if(rc)
2960
 
                continue;
2961
 
            goto fail;
2962
 
        }
2963
 
        /* file */
2964
 
        i_printfExit(MSG_UnexSubmitForm);
2965
 
 
2966
 
      success:
2967
 
        postNameVal(name, value, fsep, false, boundary, post, l);
2968
 
    }                           /* loop over tags */
2969
 
 
2970
 
    if(form->mime) {            /* the last boundary */
2971
 
        stringAndString(post, l, "--");
2972
 
        stringAndString(post, l, boundary);
2973
 
        stringAndString(post, l, "--\r\n");
2974
 
    }
2975
 
 
2976
 
    if(noname)
2977
 
        i_puts(MSG_UnnamedFields);
2978
 
    i_puts(MSG_FormSubmit);
2979
 
    return true;
2980
 
 
2981
 
  fail:
2982
 
    return false;
2983
 
}                               /* formSubmit */
2984
 
 
2985
 
/*********************************************************************
2986
 
Push the reset or submit button.
2987
 
This routine must be reentrant.
2988
 
You push submit, which calls this routine, which runs the onsubmit code,
2989
 
which checks the fields and calls form.submit(),
2990
 
which calls this routine.  Happens all the time.
2991
 
*********************************************************************/
2992
 
 
2993
 
bool
2994
 
infPush(int tagno, char **post_string)
2995
 
{
2996
 
    struct htmlTag **list = cw->tags;
2997
 
    struct htmlTag *t = list[tagno];
2998
 
    struct htmlTag *form;
2999
 
    int itype;
3000
 
    char *post, *section;
3001
 
    int l, actlen;
3002
 
    const char *action = 0;
3003
 
    const char *prot;
3004
 
    bool rc;
3005
 
 
3006
 
    *post_string = 0;
3007
 
 
3008
 
/* If the tag is actually a form, then infPush() was invoked
3009
 
 * by form.submit().
3010
 
 * Revert t back to 0, since there may be multiple submit buttons
3011
 
 * on the form, and we don't know which one was pushed. */
3012
 
    if(t->action == TAGACT_FORM) {
3013
 
        form = t;
3014
 
        t = 0;
3015
 
        itype = INP_SUBMIT;
3016
 
    } else {
3017
 
        form = t->controller;
3018
 
        itype = t->itype;
3019
 
    }
3020
 
 
3021
 
    if(itype > INP_SUBMIT) {
3022
 
        setError(MSG_NoButton);
3023
 
        return false;
3024
 
    }
3025
 
 
3026
 
    if(!form && itype != INP_BUTTON) {
3027
 
        setError(MSG_NotInForm);
3028
 
        return false;
3029
 
    }
3030
 
 
3031
 
    if(t && tagHandler(t->seqno, "onclick")) {
3032
 
        if(cw->jsdead)
3033
 
            runningError(itype ==
3034
 
               INP_BUTTON ? MSG_NJNoAction : MSG_NJNoOnclick);
3035
 
        else {
3036
 
            rc = handlerGo(t->jv, "onclick");
3037
 
            jsdw();
3038
 
            if(!rc)
3039
 
                return true;
3040
 
            if(js_redirects)
3041
 
                return true;
3042
 
        }
3043
 
    }
3044
 
 
3045
 
    if(itype == INP_BUTTON) {
3046
 
        if(!handlerPresent(t->jv, "onclick")) {
3047
 
            setError(MSG_ButtonNoJS);
3048
 
            return false;
3049
 
        }
3050
 
        return true;
3051
 
    }
3052
 
 
3053
 
    if(itype == INP_RESET) {
3054
 
/* Before we reset, run the onreset code */
3055
 
        if(t && tagHandler(form->seqno, "onreset")) {
3056
 
            if(cw->jsdead)
3057
 
                runningError(MSG_NJNoReset);
3058
 
            else {
3059
 
                rc = handlerGo(form->jv, "onreset");
3060
 
                jsdw();
3061
 
                if(!rc)
3062
 
                    return true;
3063
 
                if(js_redirects)
3064
 
                    return true;
3065
 
            }
3066
 
        }                       /* onreset */
3067
 
        formReset(form);
3068
 
        return true;
3069
 
    }
3070
 
 
3071
 
    /* Before we submit, run the onsubmit code */
3072
 
    if(t && tagHandler(form->seqno, "onsubmit")) {
3073
 
        if(cw->jsdead)
3074
 
            runningError(MSG_NJNoSubmit);
3075
 
        else {
3076
 
            rc = handlerGo(form->jv, "onsubmit");
3077
 
            jsdw();
3078
 
            if(!rc)
3079
 
                return true;
3080
 
            if(js_redirects)
3081
 
                return true;
3082
 
        }
3083
 
    }
3084
 
 
3085
 
    action = form->href;
3086
 
/* But we defer to the java variable */
3087
 
    if(form->jv) {
3088
 
        const char *jh = get_property_url(form->jv, true);
3089
 
        if(jh && (!action || !stringEqual(jh, action))) {
3090
 
/* Tie action to the form tag, to plug a small memory leak */
3091
 
            nzFree(form->href);
3092
 
            form->href = resolveURL(getBaseHref(form->seqno), jh);
3093
 
            action = form->href;
3094
 
        }
3095
 
    }
3096
 
 
3097
 
/* if no action, the default is the current location */
3098
 
    if(!action) {
3099
 
        action = getBaseHref(form->seqno);
3100
 
    }
3101
 
 
3102
 
    if(!action) {
3103
 
        setError(MSG_FormNoURL);
3104
 
        return false;
3105
 
    }
3106
 
 
3107
 
    debugPrint(2, "* %s", action);
3108
 
 
3109
 
    prot = getProtURL(action);
3110
 
    if(!prot) {
3111
 
        setError(MSG_FormBadURL);
3112
 
        return false;
3113
 
    }
3114
 
 
3115
 
    if(stringEqualCI(prot, "javascript")) {
3116
 
        if(cw->jsdead) {
3117
 
            setError(MSG_NJNoForm);
3118
 
            return false;
3119
 
        }
3120
 
        javaParseExecute(form->jv, action, 0, 0);
3121
 
        jsdw();
3122
 
        return true;
3123
 
    }
3124
 
 
3125
 
    form->bymail = false;
3126
 
    if(stringEqualCI(prot, "mailto")) {
3127
 
        if(!validAccount(localAccount))
3128
 
            return false;
3129
 
        form->bymail = true;
3130
 
    } else if(stringEqualCI(prot, "http")) {
3131
 
        if(form->secure) {
3132
 
            setError(MSG_BecameInsecure);
3133
 
            return false;
3134
 
        }
3135
 
    } else if(!stringEqualCI(prot, "https")) {
3136
 
        setError(MSG_SubmitProtBad, prot);
3137
 
        return false;
3138
 
    }
3139
 
 
3140
 
    post = initString(&l);
3141
 
    stringAndString(&post, &l, action);
3142
 
    section = strchr(post, '#');
3143
 
    if(section) {
3144
 
        i_printf(MSG_SectionIgnored, section);
3145
 
        *section = 0;
3146
 
        l = strlen(post);
3147
 
    }
3148
 
    section = strpbrk(post, "?\1");
3149
 
    if(section) {
3150
 
        if(*section == '\1' || !(form->bymail | form->post)) {
3151
 
            debugPrint(3,
3152
 
               "the url already specifies some data, which will be overwritten by the data in this form");
3153
 
            *section = 0;
3154
 
            l = strlen(post);
3155
 
        }
3156
 
    }
3157
 
 
3158
 
    stringAndChar(&post, &l, (form->post ? '\1' : '?'));
3159
 
    actlen = l;
3160
 
 
3161
 
    if(!formSubmit(form, t, &post, &l)) {
3162
 
        nzFree(post);
3163
 
        return false;
3164
 
    }
3165
 
 
3166
 
    debugPrint(3, "%s %s", form->post ? "post" : "get", post + actlen);
3167
 
 
3168
 
/* Handle the mail method here and now. */
3169
 
    if(form->bymail) {
3170
 
        char *addr, *subj, *q;
3171
 
        const char *tolist[2], *atlist[2];
3172
 
        const char *name = form->name;
3173
 
        int newlen = l - actlen;        /* the new string could be longer than post */
3174
 
        decodeMailURL(action, &addr, &subj, 0);
3175
 
        tolist[0] = addr;
3176
 
        tolist[1] = 0;
3177
 
        atlist[0] = 0;
3178
 
        newlen += 9;            /* subject: \n */
3179
 
        if(subj)
3180
 
            newlen += strlen(subj);
3181
 
        else
3182
 
            newlen += 11 + (name ? strlen(name) : 1);
3183
 
        ++newlen;               /* null */
3184
 
        ++newlen;               /* encodeAttachment might append another nl */
3185
 
        q = allocMem(newlen);
3186
 
        if(subj)
3187
 
            sprintf(q, "subject:%s\n", subj);
3188
 
        else
3189
 
            sprintf(q, "subject:html form(%s)\n", name ? name : "?");
3190
 
        strcpy(q + strlen(q), post + actlen);
3191
 
        nzFree(post);
3192
 
        i_printf(MSG_MailSending, addr);
3193
 
        sleep(1);
3194
 
        rc = sendMail(localAccount, tolist, q, -1, atlist, 0, false);
3195
 
        if(rc)
3196
 
            i_puts(MSG_MailSent);
3197
 
        nzFree(addr);
3198
 
        nzFree(subj);
3199
 
        nzFree(q);
3200
 
        *post_string = 0;
3201
 
        return rc;
3202
 
    }
3203
 
 
3204
 
    *post_string = post;
3205
 
    return true;
3206
 
}                               /* infPush */
3207
 
 
3208
 
/* I don't have any reverse pointers, so I'm just going to scan the list */
3209
 
static struct htmlTag *
3210
 
tagFromJavaVar(void *v)
3211
 
{
3212
 
    struct htmlTag **list = cw->tags;
3213
 
    struct htmlTag *t;
3214
 
    if(!list)
3215
 
        i_printfExit(MSG_NullListInform);
3216
 
    while(t = *list++)
3217
 
        if(t->jv == v)
3218
 
            break;
3219
 
    if(!t)
3220
 
        runningError(MSG_LostTag);
3221
 
    return t;
3222
 
}                               /* tagFromJavaVar */
3223
 
 
3224
 
/* Javascript has changed an input field */
3225
 
void
3226
 
javaSetsTagVar(void *v, const char *val)
3227
 
{
3228
 
    struct htmlTag *t;
3229
 
    buildTagArray();
3230
 
    t = tagFromJavaVar(v);
3231
 
    if(!t)
3232
 
        return;
3233
 
/* ok, we found it */
3234
 
    if(t->itype == INP_HIDDEN || t->itype == INP_RADIO) {
3235
 
        return;
3236
 
    }
3237
 
    if(t->itype == INP_TA) {
3238
 
        runningError(MSG_JSTextarea);
3239
 
        return;
3240
 
    }
3241
 
    updateFieldInBuffer(t->seqno, val, parsePage ? 0 : 2, false);
3242
 
}                               /* javaSetsTagVar */
3243
 
 
3244
 
/* Return false to stop javascript, due to a url redirect */
3245
 
void
3246
 
javaSubmitsForm(void *v, bool reset)
3247
 
{
3248
 
    char *post;
3249
 
    bool rc;
3250
 
    struct htmlTag *t;
3251
 
 
3252
 
    buildTagArray();
3253
 
    t = tagFromJavaVar(v);
3254
 
    if(!t)
3255
 
        return;
3256
 
 
3257
 
    if(reset) {
3258
 
        formReset(t);
3259
 
        return;
3260
 
    }
3261
 
 
3262
 
    rc = infPush(t->seqno, &post);
3263
 
    if(!rc) {
3264
 
        showError();
3265
 
        return;
3266
 
    }
3267
 
    gotoLocation(post, 0, false);
3268
 
}                               /* javaSubmitsForm */
3269
 
 
3270
 
void
3271
 
javaOpensWindow(const char *href, const char *name)
3272
 
{
3273
 
    struct htmlTag *t;
3274
 
    char *copy, *r;
3275
 
    const char *a;
3276
 
 
3277
 
    if(!href || !*href) {
3278
 
        browseError(MSG_JSBlankWindow);
3279
 
        return;
3280
 
    }
3281
 
 
3282
 
    copy = cloneString(href);
3283
 
    unpercentURL(copy);
3284
 
    r = resolveURL(getBaseHref(-1), copy);
3285
 
    nzFree(copy);
3286
 
    if(!parsePage) {
3287
 
        gotoLocation(r, 0, false);
3288
 
        return;
3289
 
    }
3290
 
 
3291
 
    t = newTag("a");
3292
 
    t->href = r;
3293
 
    a = altText(r);
3294
 
/* I'll assume this is more helpful than the name of the window */
3295
 
    if(a)
3296
 
        name = a;
3297
 
    toPreamble(t->seqno, "Popup", 0, name);
3298
 
}                               /* javaOpensWindow */
3299
 
 
3300
 
void
3301
 
javaSetsTimeout(int n, const char *jsrc, void *to, bool isInterval)
3302
 
{
3303
 
    struct htmlTag *t = newTag("a");
3304
 
    char timedesc[48];
3305
 
    int l;
3306
 
 
3307
 
    strcpy(timedesc, (isInterval ? "Interval" : "Timer"));
3308
 
    l = strlen(timedesc);
3309
 
    if(n > 1000)
3310
 
        sprintf(timedesc + l, " %d", n / 1000);
3311
 
    else
3312
 
        sprintf(timedesc + l, " %dms", n);
3313
 
 
3314
 
    t->jv = to;
3315
 
    t->href = cloneString("#");
3316
 
    toPreamble(t->seqno, timedesc, jsrc, 0);
3317
 
}                               /* javaSetsTimeout */