Intial commit
[profile/ivi/w3m.git] / form.c
1 /* $Id: form.c,v 1.34 2004/02/05 17:23:07 ukai Exp $ */
2 /* 
3  * HTML forms
4  */
5 #include "fm.h"
6 #include "parsetag.h"
7 #include "parsetagx.h"
8 #include "myctype.h"
9 #include "local.h"
10 #include "regex.h"
11
12 extern Str *textarea_str;
13 #ifdef MENU_SELECT
14 extern FormSelectOption *select_option;
15 #include "menu.h"
16 #endif                          /* MENU_SELECT */
17
18 /* *INDENT-OFF* */
19 struct {
20     char *action;
21     void (*rout)(struct parsed_tagarg *);
22 } internal_action[] = {
23     {"map", follow_map}, 
24     {"option", panel_set_option},
25 #ifdef USE_COOKIE
26     {"cookie", set_cookie_flag},
27 #endif                          /* USE_COOKIE */
28     {"download", download_action},
29 #ifdef USE_M17N
30     { "charset", change_charset },
31 #endif
32     {"none", NULL},
33     {NULL, NULL},
34 };
35 /* *INDENT-ON* */
36
37 struct form_list *
38 newFormList(char *action, char *method, char *charset, char *enctype,
39             char *target, char *name, struct form_list *_next)
40 {
41     struct form_list *l;
42     Str a = Strnew_charp(action);
43     int m = FORM_METHOD_GET;
44     int e = FORM_ENCTYPE_URLENCODED;
45 #ifdef USE_M17N
46     wc_ces c = 0;
47 #endif
48
49     if (method == NULL || !strcasecmp(method, "get"))
50         m = FORM_METHOD_GET;
51     else if (!strcasecmp(method, "post"))
52         m = FORM_METHOD_POST;
53     else if (!strcasecmp(method, "internal"))
54         m = FORM_METHOD_INTERNAL;
55     /* unknown method is regarded as 'get' */
56
57     if (enctype != NULL && !strcasecmp(enctype, "multipart/form-data")) {
58         e = FORM_ENCTYPE_MULTIPART;
59         if (m == FORM_METHOD_GET)
60             m = FORM_METHOD_POST;
61     }
62
63 #ifdef USE_M17N
64     if (charset != NULL)
65         c = wc_guess_charset(charset, 0);
66 #endif
67
68     l = New(struct form_list);
69     l->item = l->lastitem = NULL;
70     l->action = a;
71     l->method = m;
72 #ifdef USE_M17N
73     l->charset = c;
74 #endif
75     l->enctype = e;
76     l->target = target;
77     l->name = name;
78     l->next = _next;
79     l->nitems = 0;
80     l->body = NULL;
81     l->length = 0;
82     return l;
83 }
84
85 /* 
86  * add <input> element to form_list
87  */
88 struct form_item_list *
89 formList_addInput(struct form_list *fl, struct parsed_tag *tag)
90 {
91     struct form_item_list *item;
92     char *p;
93     int i;
94
95     /* if not in <form>..</form> environment, just ignore <input> tag */
96     if (fl == NULL)
97         return NULL;
98
99     item = New(struct form_item_list);
100     item->type = FORM_UNKNOWN;
101     item->size = -1;
102     item->rows = 0;
103     item->checked = item->init_checked = 0;
104     item->accept = 0;
105     item->name = NULL;
106     item->value = item->init_value = NULL;
107     item->readonly = 0;
108     if (parsedtag_get_value(tag, ATTR_TYPE, &p)) {
109         item->type = formtype(p);
110         if (item->size < 0 &&
111             (item->type == FORM_INPUT_TEXT ||
112              item->type == FORM_INPUT_FILE ||
113              item->type == FORM_INPUT_PASSWORD))
114             item->size = FORM_I_TEXT_DEFAULT_SIZE;
115     }
116     if (parsedtag_get_value(tag, ATTR_NAME, &p))
117         item->name = Strnew_charp(p);
118     if (parsedtag_get_value(tag, ATTR_VALUE, &p))
119         item->value = item->init_value = Strnew_charp(p);
120     item->checked = item->init_checked = parsedtag_exists(tag, ATTR_CHECKED);
121     item->accept = parsedtag_exists(tag, ATTR_ACCEPT);
122     parsedtag_get_value(tag, ATTR_SIZE, &item->size);
123     parsedtag_get_value(tag, ATTR_MAXLENGTH, &item->maxlength);
124     item->readonly = parsedtag_exists(tag, ATTR_READONLY);
125     if (parsedtag_get_value(tag, ATTR_TEXTAREANUMBER, &i))
126         item->value = item->init_value = textarea_str[i];
127 #ifdef MENU_SELECT
128     if (parsedtag_get_value(tag, ATTR_SELECTNUMBER, &i))
129         item->select_option = select_option[i].first;
130 #endif                          /* MENU_SELECT */
131     if (parsedtag_get_value(tag, ATTR_ROWS, &p))
132         item->rows = atoi(p);
133     if (item->type == FORM_UNKNOWN) {
134         /* type attribute is missing. Ignore the tag. */
135         return NULL;
136     }
137 #ifdef MENU_SELECT
138     if (item->type == FORM_SELECT) {
139         chooseSelectOption(item, item->select_option);
140         item->init_selected = item->selected;
141         item->init_value = item->value;
142         item->init_label = item->label;
143     }
144 #endif                          /* MENU_SELECT */
145     if (item->type == FORM_INPUT_FILE && item->value && item->value->length) {
146         /* security hole ! */
147         return NULL;
148     }
149     item->parent = fl;
150     item->next = NULL;
151     if (fl->item == NULL) {
152         fl->item = fl->lastitem = item;
153     }
154     else {
155         fl->lastitem->next = item;
156         fl->lastitem = item;
157     }
158     if (item->type == FORM_INPUT_HIDDEN)
159         return NULL;
160     fl->nitems++;
161     return item;
162 }
163
164 static char *_formtypetbl[] = {
165     "text", "password", "checkbox", "radio", "submit", "reset", "hidden",
166     "image", "select", "textarea", "button", "file", NULL
167 };
168
169 static char *_formmethodtbl[] = {
170     "GET", "POST", "INTERNAL", "HEAD"
171 };
172
173 char *
174 form2str(FormItemList *fi)
175 {
176     Str tmp = Strnew();
177
178     if (fi->type != FORM_SELECT && fi->type != FORM_TEXTAREA)
179         Strcat_charp(tmp, "input type=");
180     Strcat_charp(tmp, _formtypetbl[fi->type]);
181     if (fi->name && fi->name->length)
182         Strcat_m_charp(tmp, " name=\"", fi->name->ptr, "\"", NULL);
183     if ((fi->type == FORM_INPUT_RADIO || fi->type == FORM_INPUT_CHECKBOX ||
184          fi->type == FORM_SELECT) && fi->value)
185         Strcat_m_charp(tmp, " value=\"", fi->value->ptr, "\"", NULL);
186     Strcat_m_charp(tmp, " (", _formmethodtbl[fi->parent->method], " ",
187                    fi->parent->action->ptr, ")", NULL);
188     return tmp->ptr;
189 }
190
191 int
192 formtype(char *typestr)
193 {
194     int i;
195     for (i = 0; _formtypetbl[i]; i++) {
196         if (!strcasecmp(typestr, _formtypetbl[i]))
197             return i;
198     }
199     return FORM_UNKNOWN;
200 }
201
202 void
203 formRecheckRadio(Anchor *a, Buffer *buf, FormItemList *fi)
204 {
205     int i;
206     Anchor *a2;
207     FormItemList *f2;
208
209     for (i = 0; i < buf->formitem->nanchor; i++) {
210         a2 = &buf->formitem->anchors[i];
211         f2 = (FormItemList *)a2->url;
212         if (f2->parent == fi->parent && f2 != fi &&
213             f2->type == FORM_INPUT_RADIO && Strcmp(f2->name, fi->name) == 0) {
214             f2->checked = 0;
215             formUpdateBuffer(a2, buf, f2);
216         }
217     }
218     fi->checked = 1;
219     formUpdateBuffer(a, buf, fi);
220 }
221
222 void
223 formResetBuffer(Buffer *buf, AnchorList *formitem)
224 {
225     int i;
226     Anchor *a;
227     FormItemList *f1, *f2;
228
229     if (buf == NULL || buf->formitem == NULL || formitem == NULL)
230         return;
231     for (i = 0; i < buf->formitem->nanchor && i < formitem->nanchor; i++) {
232         a = &buf->formitem->anchors[i];
233         if (a->y != a->start.line)
234             continue;
235         f1 = (FormItemList *)a->url;
236         f2 = (FormItemList *)formitem->anchors[i].url;
237         if (f1->type != f2->type ||
238             strcmp(((f1->name == NULL) ? "" : f1->name->ptr),
239                    ((f2->name == NULL) ? "" : f2->name->ptr)))
240             break;              /* What's happening */
241         switch (f1->type) {
242         case FORM_INPUT_TEXT:
243         case FORM_INPUT_PASSWORD:
244         case FORM_INPUT_FILE:
245         case FORM_TEXTAREA:
246             f1->value = f2->value;
247             f1->init_value = f2->init_value;
248             break;
249         case FORM_INPUT_CHECKBOX:
250         case FORM_INPUT_RADIO:
251             f1->checked = f2->checked;
252             f1->init_checked = f2->init_checked;
253             break;
254         case FORM_SELECT:
255 #ifdef MENU_SELECT
256             f1->select_option = f2->select_option;
257             f1->value = f2->value;
258             f1->label = f2->label;
259             f1->selected = f2->selected;
260             f1->init_value = f2->init_value;
261             f1->init_label = f2->init_label;
262             f1->init_selected = f2->init_selected;
263 #endif                          /* MENU_SELECT */
264             break;
265         default:
266             continue;
267         }
268         formUpdateBuffer(a, buf, f1);
269     }
270 }
271
272 static int
273 form_update_line(Line *line, char **str, int spos, int epos, int width,
274                  int newline, int password)
275 {
276     int c_len = 1, c_width = 1, w, i, len, pos;
277     char *p, *buf;
278     Lineprop c_type, effect, *prop;
279
280     for (p = *str, w = 0, pos = 0; *p && w < width;) {
281         c_type = get_mctype((unsigned char *)p);
282 #ifdef USE_M17N
283         c_len = get_mclen(p);
284         c_width = get_mcwidth(p);
285 #endif
286         if (c_type == PC_CTRL) {
287             if (newline && *p == '\n')
288                 break;
289             if (*p != '\r') {
290                 w++;
291                 pos++;
292             }
293         }
294         else if (password) {
295 #ifdef USE_M17N
296             if (w + c_width > width)
297                 break;
298 #endif
299             w += c_width;
300             pos += c_width;
301 #ifdef USE_M17N
302         }
303         else if (c_type & PC_UNKNOWN) {
304             w++;
305             pos++;
306         }
307         else {
308             if (w + c_width > width)
309                 break;
310 #endif
311             w += c_width;
312             pos += c_len;
313         }
314         p += c_len;
315     }
316     pos += width - w;
317
318     len = line->len + pos + spos - epos;
319     buf = New_N(char, len);
320     prop = New_N(Lineprop, len);
321     bcopy((void *)line->lineBuf, (void *)buf, spos * sizeof(char));
322     bcopy((void *)line->propBuf, (void *)prop, spos * sizeof(Lineprop));
323
324     effect = CharEffect(line->propBuf[spos]);
325     for (p = *str, w = 0, pos = spos; *p && w < width;) {
326         c_type = get_mctype((unsigned char *)p);
327 #ifdef USE_M17N
328         c_len = get_mclen(p);
329         c_width = get_mcwidth(p);
330 #endif
331         if (c_type == PC_CTRL) {
332             if (newline && *p == '\n')
333                 break;
334             if (*p != '\r') {
335                 buf[pos] = password ? '*' : ' ';
336                 prop[pos] = effect | PC_ASCII;
337                 pos++;
338                 w++;
339             }
340         }
341         else if (password) {
342 #ifdef USE_M17N
343             if (w + c_width > width)
344                 break;
345 #endif
346             for (i = 0; i < c_width; i++) {
347                 buf[pos] = '*';
348                 prop[pos] = effect | PC_ASCII;
349                 pos++;
350                 w++;
351             }
352 #ifdef USE_M17N
353         }
354         else if (c_type & PC_UNKNOWN) {
355             buf[pos] = ' ';
356             prop[pos] = effect | PC_ASCII;
357             pos++;
358             w++;
359         }
360         else {
361             if (w + c_width > width)
362                 break;
363 #else
364         }
365         else {
366 #endif
367             buf[pos] = *p;
368             prop[pos] = effect | c_type;
369             pos++;
370 #ifdef USE_M17N
371             c_type = (c_type & ~PC_WCHAR1) | PC_WCHAR2;
372             for (i = 1; i < c_len; i++) {
373                 buf[pos] = p[i];
374                 prop[pos] = effect | c_type;
375                 pos++;
376             }
377 #endif
378             w += c_width;
379         }
380         p += c_len;
381     }
382     for (; w < width; w++) {
383         buf[pos] = ' ';
384         prop[pos] = effect | PC_ASCII;
385         pos++;
386     }
387     if (newline) {
388         if (!FoldTextarea) {
389             while (*p && *p != '\r' && *p != '\n')
390                 p++;
391         }
392         if (*p == '\r')
393             p++;
394         if (*p == '\n')
395             p++;
396     }
397     *str = p;
398
399     bcopy((void *)&line->lineBuf[epos], (void *)&buf[pos],
400           (line->len - epos) * sizeof(char));
401     bcopy((void *)&line->propBuf[epos], (void *)&prop[pos],
402           (line->len - epos) * sizeof(Lineprop));
403     line->lineBuf = buf;
404     line->propBuf = prop;
405     line->len = len;
406
407     return pos;
408 }
409
410 void
411 formUpdateBuffer(Anchor *a, Buffer *buf, FormItemList *form)
412 {
413     Buffer save;
414     char *p;
415     int spos, epos, rows, c_rows, pos, col = 0;
416     Line *l;
417
418     copyBuffer(&save, buf);
419     gotoLine(buf, a->start.line);
420     switch (form->type) {
421     case FORM_TEXTAREA:
422     case FORM_INPUT_TEXT:
423     case FORM_INPUT_FILE:
424     case FORM_INPUT_PASSWORD:
425     case FORM_INPUT_CHECKBOX:
426     case FORM_INPUT_RADIO:
427 #ifdef MENU_SELECT
428     case FORM_SELECT:
429 #endif                          /* MENU_SELECT */
430         spos = a->start.pos;
431         epos = a->end.pos;
432         break;
433     default:
434         spos = a->start.pos + 1;
435         epos = a->end.pos - 1;
436     }
437     switch (form->type) {
438     case FORM_INPUT_CHECKBOX:
439     case FORM_INPUT_RADIO:
440         if (form->checked)
441             buf->currentLine->lineBuf[spos] = '*';
442         else
443             buf->currentLine->lineBuf[spos] = ' ';
444         break;
445     case FORM_INPUT_TEXT:
446     case FORM_INPUT_FILE:
447     case FORM_INPUT_PASSWORD:
448     case FORM_TEXTAREA:
449 #ifdef MENU_SELECT
450     case FORM_SELECT:
451         if (form->type == FORM_SELECT) {
452             p = form->label->ptr;
453             updateSelectOption(form, form->select_option);
454         }
455         else
456 #endif                          /* MENU_SELECT */
457             p = form->value->ptr;
458         l = buf->currentLine;
459         if (form->type == FORM_TEXTAREA) {
460             int n = a->y - buf->currentLine->linenumber;
461             if (n > 0)
462                 for (; l && n; l = l->prev, n--) ;
463             else if (n < 0)
464                 for (; l && n; l = l->prev, n++) ;
465             if (!l)
466                 break;
467         }
468         rows = form->rows ? form->rows : 1;
469         col = COLPOS(l, a->start.pos);
470         for (c_rows = 0; c_rows < rows; c_rows++, l = l->next) {
471             if (rows > 1) {
472                 pos = columnPos(l, col);
473                 a = retrieveAnchor(buf->formitem, l->linenumber, pos);
474                 if (a == NULL)
475                     break;
476                 spos = a->start.pos;
477                 epos = a->end.pos;
478             }
479             pos = form_update_line(l, &p, spos, epos, COLPOS(l, epos) - col,
480                                    rows > 1,
481                                    form->type == FORM_INPUT_PASSWORD);
482             if (pos != epos) {
483                 shiftAnchorPosition(buf->href, buf->hmarklist,
484                                     a->start.line, spos, pos - epos);
485                 shiftAnchorPosition(buf->name, buf->hmarklist,
486                                     a->start.line, spos, pos - epos);
487                 shiftAnchorPosition(buf->img, buf->hmarklist,
488                                     a->start.line, spos, pos - epos);
489                 shiftAnchorPosition(buf->formitem, buf->hmarklist,
490                                     a->start.line, spos, pos - epos);
491             }
492         }
493         break;
494     }
495     copyBuffer(buf, &save);
496     arrangeLine(buf);
497 }
498
499
500 Str
501 textfieldrep(Str s, int width)
502 {
503     Lineprop c_type;
504     Str n = Strnew_size(width + 2);
505     int i, j, k, c_len;
506
507     j = 0;
508     for (i = 0; i < s->length; i += c_len) {
509         c_type = get_mctype((unsigned char *)&s->ptr[i]);
510         c_len = get_mclen(&s->ptr[i]);
511         if (s->ptr[i] == '\r')
512             continue;
513         k = j + get_mcwidth(&s->ptr[i]);
514         if (k > width)
515             break;
516         if (c_type == PC_CTRL)
517             Strcat_char(n, ' ');
518 #ifdef USE_M17N
519         else if (c_type & PC_UNKNOWN)
520             Strcat_char(n, ' ');
521 #endif
522         else if (s->ptr[i] == '&')
523             Strcat_charp(n, "&amp;");
524         else if (s->ptr[i] == '<')
525             Strcat_charp(n, "&lt;");
526         else if (s->ptr[i] == '>')
527             Strcat_charp(n, "&gt;");
528         else
529             Strcat_charp_n(n, &s->ptr[i], c_len);
530         j = k;
531     }
532     for (; j < width; j++)
533         Strcat_char(n, ' ');
534     return n;
535 }
536
537 static void
538 form_fputs_decode(Str s, FILE * f)
539 {
540     char *p;
541     Str z = Strnew();
542
543     for (p = s->ptr; *p;) {
544         switch (*p) {
545 #if !defined( __CYGWIN__ ) && !defined( __EMX__ )
546         case '\r':
547             if (*(p + 1) == '\n')
548                 p++;
549             /* continue to the next label */
550 #endif                          /* !defined( __CYGWIN__ ) && !defined( __EMX__ 
551                                  * ) */
552         default:
553             Strcat_char(z, *p);
554             p++;
555             break;
556         }
557     }
558 #ifdef USE_M17N
559     z = wc_Str_conv_strict(z, InnerCharset, DisplayCharset);
560 #endif
561     Strfputs(z, f);
562 }
563
564
565 void
566 input_textarea(FormItemList *fi)
567 {
568     char *tmpf = tmpfname(TMPF_DFL, NULL)->ptr;
569     Str tmp;
570     FILE *f;
571 #ifdef USE_M17N
572     wc_ces charset = DisplayCharset;
573     wc_uint8 auto_detect;
574 #endif
575
576     f = fopen(tmpf, "w");
577     if (f == NULL) {
578         /* FIXME: gettextize? */
579         disp_err_message("Can't open temporary file", FALSE);
580         return;
581     }
582     if (fi->value)
583         form_fputs_decode(fi->value, f);
584     fclose(f);
585
586     fmTerm();
587     system(myEditor(Editor, tmpf, 1)->ptr);
588     fmInit();
589
590     if (fi->readonly)
591         goto input_end;
592     f = fopen(tmpf, "r");
593     if (f == NULL) {
594         /* FIXME: gettextize? */
595         disp_err_message("Can't open temporary file", FALSE);
596         goto input_end;
597     }
598     fi->value = Strnew();
599 #ifdef USE_M17N
600     auto_detect = WcOption.auto_detect;
601     WcOption.auto_detect = WC_OPT_DETECT_ON;
602 #endif
603     while (tmp = Strfgets(f), tmp->length > 0) {
604         if (tmp->length == 1 && tmp->ptr[tmp->length - 1] == '\n') {
605             /* null line with bare LF */
606             tmp = Strnew_charp("\r\n");
607         }
608         else if (tmp->length > 1 && tmp->ptr[tmp->length - 1] == '\n' &&
609                  tmp->ptr[tmp->length - 2] != '\r') {
610             Strshrink(tmp, 1);
611             Strcat_charp(tmp, "\r\n");
612         }
613         tmp = convertLine(NULL, tmp, RAW_MODE, &charset, DisplayCharset);
614         Strcat(fi->value, tmp);
615     }
616 #ifdef USE_M17N
617     WcOption.auto_detect = auto_detect;
618 #endif
619     fclose(f);
620   input_end:
621     unlink(tmpf);
622 }
623
624 void
625 do_internal(char *action, char *data)
626 {
627     int i;
628
629     for (i = 0; internal_action[i].action; i++) {
630         if (strcasecmp(internal_action[i].action, action) == 0) {
631             if (internal_action[i].rout)
632                 internal_action[i].rout(cgistr2tagarg(data));
633             return;
634         }
635     }
636 }
637
638 #ifdef MENU_SELECT
639 void
640 addSelectOption(FormSelectOption *fso, Str value, Str label, int chk)
641 {
642     FormSelectOptionItem *o;
643     o = New(FormSelectOptionItem);
644     if (value == NULL)
645         value = label;
646     o->value = value;
647     Strremovefirstspaces(label);
648     Strremovetrailingspaces(label);
649     o->label = label;
650     o->checked = chk;
651     o->next = NULL;
652     if (fso->first == NULL)
653         fso->first = fso->last = o;
654     else {
655         fso->last->next = o;
656         fso->last = o;
657     }
658 }
659
660 void
661 chooseSelectOption(FormItemList *fi, FormSelectOptionItem *item)
662 {
663     FormSelectOptionItem *opt;
664     int i;
665
666     fi->selected = 0;
667     if (item == NULL) {
668         fi->value = Strnew_size(0);
669         fi->label = Strnew_size(0);
670         return;
671     }
672     fi->value = item->value;
673     fi->label = item->label;
674     for (i = 0, opt = item; opt != NULL; i++, opt = opt->next) {
675         if (opt->checked) {
676             fi->value = opt->value;
677             fi->label = opt->label;
678             fi->selected = i;
679             break;
680         }
681     }
682     updateSelectOption(fi, item);
683 }
684
685 void
686 updateSelectOption(FormItemList *fi, FormSelectOptionItem *item)
687 {
688     int i;
689
690     if (fi == NULL || item == NULL)
691         return;
692     for (i = 0; item != NULL; i++, item = item->next) {
693         if (i == fi->selected)
694             item->checked = TRUE;
695         else
696             item->checked = FALSE;
697     }
698 }
699
700 int
701 formChooseOptionByMenu(struct form_item_list *fi, int x, int y)
702 {
703     int i, n, selected = -1, init_select = fi->selected;
704     FormSelectOptionItem *opt;
705     char **label;
706
707     for (n = 0, opt = fi->select_option; opt != NULL; n++, opt = opt->next) ;
708     label = New_N(char *, n + 1);
709     for (i = 0, opt = fi->select_option; opt != NULL; i++, opt = opt->next)
710         label[i] = opt->label->ptr;
711     label[n] = NULL;
712
713     optionMenu(x, y, label, &selected, init_select, NULL);
714
715     if (selected < 0)
716         return 0;
717     for (i = 0, opt = fi->select_option; opt != NULL; i++, opt = opt->next) {
718         if (i == selected) {
719             fi->selected = selected;
720             fi->value = opt->value;
721             fi->label = opt->label;
722             break;
723         }
724     }
725     updateSelectOption(fi, fi->select_option);
726     return 1;
727 }
728 #endif                          /* MENU_SELECT */
729
730 void
731 form_write_data(FILE * f, char *boundary, char *name, char *value)
732 {
733     fprintf(f, "--%s\r\n", boundary);
734     fprintf(f, "Content-Disposition: form-data; name=\"%s\"\r\n\r\n", name);
735     fprintf(f, "%s\r\n", value);
736 }
737
738 void
739 form_write_from_file(FILE * f, char *boundary, char *name, char *filename,
740                      char *file)
741 {
742     FILE *fd;
743     struct stat st;
744     int c;
745     char *type;
746
747     fprintf(f, "--%s\r\n", boundary);
748     fprintf(f,
749             "Content-Disposition: form-data; name=\"%s\"; filename=\"%s\"\r\n",
750             name, mybasename(filename));
751     type = guessContentType(file);
752     fprintf(f, "Content-Type: %s\r\n\r\n",
753             type ? type : "application/octet-stream");
754
755     if (lstat(file, &st) < 0)
756         goto write_end;
757     if (S_ISDIR(st.st_mode))
758         goto write_end;
759     fd = fopen(file, "r");
760     if (fd != NULL) {
761         while ((c = fgetc(fd)) != EOF)
762             fputc(c, f);
763         fclose(fd);
764     }
765   write_end:
766     fprintf(f, "\r\n");
767 }
768
769 struct pre_form_item {
770     int type;
771     char *name;
772     char *value;
773     int checked;
774     struct pre_form_item *next;
775 };
776
777 struct pre_form {
778     char *url;
779     Regex *re_url;
780     char *name;
781     char *action;
782     struct pre_form_item *item;
783     struct pre_form *next;
784 };
785
786 static struct pre_form *PreForm = NULL;
787
788 static struct pre_form *
789 add_pre_form(struct pre_form *prev, char *url, char *name, char *action)
790 {
791     ParsedURL pu;
792     struct pre_form *new;
793
794     if (prev)
795         new = prev->next = New(struct pre_form);
796     else
797         new = PreForm = New(struct pre_form);
798     if (url && *url == '/') {
799         int l = strlen(url);
800         if (l > 1 && url[l - 1] == '/')
801             new->url = allocStr(url + 1, l - 2);
802         else
803             new->url = url + 1;
804         new->re_url = newRegex(new->url, FALSE, NULL, NULL);
805         if (!new->re_url)
806             new->url = NULL;
807     }
808     else if (url) {
809         parseURL2(url, &pu, NULL);
810         new->url = parsedURL2Str(&pu)->ptr;
811         new->re_url = NULL;
812     }
813     new->name = (name && *name) ? name : NULL;
814     new->action = (action && *action) ? action : NULL;
815     new->item = NULL;
816     new->next = NULL;
817     return new;
818 }
819
820 static struct pre_form_item *
821 add_pre_form_item(struct pre_form *pf, struct pre_form_item *prev, int type,
822                   char *name, char *value, char *checked)
823 {
824     struct pre_form_item *new;
825
826     if (!pf)
827         return NULL;
828     if (prev)
829         new = prev->next = New(struct pre_form_item);
830     else
831         new = pf->item = New(struct pre_form_item);
832     new->type = type;
833     new->name = name;
834     new->value = value;
835     if (checked && *checked && (!strcmp(checked, "0") ||
836                                 strcasecmp(checked, "off")
837                                 || !strcasecmp(checked, "no")))
838         new->checked = 0;
839     else
840         new->checked = 1;
841     new->next = NULL;
842     return new;
843 }
844
845 /*
846  * url <url>|/<re-url>/
847  * form [<name>] <action>
848  * text <name> <value>
849  * file <name> <value>
850  * passwd <name> <value>
851  * checkbox <name> <value> [<checked>]
852  * radio <name> <value>
853  * select <name> <value>
854  * submit [<name> [<value>]]
855  * image [<name> [<value>]]
856  * textarea <name>
857  * <value>
858  * /textarea
859  */
860
861 void
862 loadPreForm(void)
863 {
864     FILE *fp;
865     Str line = NULL, textarea = NULL;
866     struct pre_form *pf = NULL;
867     struct pre_form_item *pi = NULL;
868     int type = -1;
869     char *name = NULL;
870
871     PreForm = NULL;
872     fp = openSecretFile(pre_form_file);
873     if (fp == NULL)
874         return;
875     while (1) {
876         char *p, *s, *arg;
877
878         line = Strfgets(fp);
879         if (line->length == 0)
880             break;
881         if (textarea && !(!strncmp(line->ptr, "/textarea", 9) &&
882                           IS_SPACE(line->ptr[9]))) {
883             Strcat(textarea, line);
884             continue;
885         }
886         Strchop(line);
887         Strremovefirstspaces(line);
888         p = line->ptr;
889         if (*p == '#' || *p == '\0')
890             continue;           /* comment or empty line */
891         s = getWord(&p);
892         arg = getWord(&p);
893
894         if (!strcmp(s, "url")) {
895             if (!arg || !*arg)
896                 continue;
897             p = getQWord(&p);
898             pf = add_pre_form(pf, arg, NULL, p);
899             pi = pf->item;
900             continue;
901         }
902         if (!pf)
903             continue;
904         if (!strcmp(s, "form")) {
905             if (!arg || !*arg)
906                 continue;
907             s = getQWord(&p);
908             p = getQWord(&p);
909             if (!p || !*p) {
910                 p = s;
911                 s = NULL;
912             }
913             if (pf->item) {
914                 struct pre_form *prev = pf;
915                 pf = add_pre_form(prev, "", s, p);
916                 /* copy previous URL */
917                 pf->url = prev->url;
918                 pf->re_url = prev->re_url;
919             }
920             else {
921                 pf->name = s;
922                 pf->action = (p && *p) ? p : NULL;
923             }
924             pi = pf->item;
925             continue;
926         }
927         if (!strcmp(s, "text"))
928             type = FORM_INPUT_TEXT;
929         else if (!strcmp(s, "file"))
930             type = FORM_INPUT_FILE;
931         else if (!strcmp(s, "passwd") || !strcmp(s, "password"))
932             type = FORM_INPUT_PASSWORD;
933         else if (!strcmp(s, "checkbox"))
934             type = FORM_INPUT_CHECKBOX;
935         else if (!strcmp(s, "radio"))
936             type = FORM_INPUT_RADIO;
937         else if (!strcmp(s, "submit"))
938             type = FORM_INPUT_SUBMIT;
939         else if (!strcmp(s, "image"))
940             type = FORM_INPUT_IMAGE;
941         else if (!strcmp(s, "select"))
942             type = FORM_SELECT;
943         else if (!strcmp(s, "textarea")) {
944             type = FORM_TEXTAREA;
945             name = Strnew_charp(arg)->ptr;
946             textarea = Strnew();
947             continue;
948         }
949         else if (textarea && name && !strcmp(s, "/textarea")) {
950             pi = add_pre_form_item(pf, pi, type, name, textarea->ptr, NULL);
951             textarea = NULL;
952             name = NULL;
953             continue;
954         }
955         else
956             continue;
957         s = getQWord(&p);
958         pi = add_pre_form_item(pf, pi, type, arg, s, getQWord(&p));
959     }
960     fclose(fp);
961 }
962
963 void
964 preFormUpdateBuffer(Buffer *buf)
965 {
966     struct pre_form *pf;
967     struct pre_form_item *pi;
968     int i;
969     Anchor *a;
970     FormList *fl;
971     FormItemList *fi;
972 #ifdef MENU_SELECT
973     FormSelectOptionItem *opt;
974     int j;
975 #endif
976
977     if (!buf || !buf->formitem || !PreForm)
978         return;
979
980     for (pf = PreForm; pf; pf = pf->next) {
981         if (pf->re_url) {
982             Str url = parsedURL2Str(&buf->currentURL);
983             if (!RegexMatch(pf->re_url, url->ptr, url->length, 1))
984                 continue;
985         }
986         else if (pf->url) {
987             if (Strcmp_charp(parsedURL2Str(&buf->currentURL), pf->url))
988                 continue;
989         }
990         else
991             continue;
992         for (i = 0; i < buf->formitem->nanchor; i++) {
993             a = &buf->formitem->anchors[i];
994             fi = (FormItemList *)a->url;
995             fl = fi->parent;
996             if (pf->name && (!fl->name || strcmp(fl->name, pf->name)))
997                 continue;
998             if (pf->action
999                 && (!fl->action || Strcmp_charp(fl->action, pf->action)))
1000                 continue;
1001             for (pi = pf->item; pi; pi = pi->next) {
1002                 if (pi->type != fi->type)
1003                     continue;
1004                 if (pi->type == FORM_INPUT_SUBMIT ||
1005                     pi->type == FORM_INPUT_IMAGE) {
1006                     if ((!pi->name || !*pi->name ||
1007                          (fi->name && !Strcmp_charp(fi->name, pi->name))) &&
1008                         (!pi->value || !*pi->value ||
1009                          (fi->value && !Strcmp_charp(fi->value, pi->value))))
1010                         buf->submit = a;
1011                     continue;
1012                 }
1013                 if (!pi->name || !fi->name || Strcmp_charp(fi->name, pi->name))
1014                     continue;
1015                 switch (pi->type) {
1016                 case FORM_INPUT_TEXT:
1017                 case FORM_INPUT_FILE:
1018                 case FORM_INPUT_PASSWORD:
1019                 case FORM_TEXTAREA:
1020                     fi->value = Strnew_charp(pi->value);
1021                     formUpdateBuffer(a, buf, fi);
1022                     break;
1023                 case FORM_INPUT_CHECKBOX:
1024                     if (pi->value && fi->value &&
1025                         !Strcmp_charp(fi->value, pi->value)) {
1026                         fi->checked = pi->checked;
1027                         formUpdateBuffer(a, buf, fi);
1028                     }
1029                     break;
1030                 case FORM_INPUT_RADIO:
1031                     if (pi->value && fi->value &&
1032                         !Strcmp_charp(fi->value, pi->value))
1033                         formRecheckRadio(a, buf, fi);
1034                     break;
1035 #ifdef MENU_SELECT
1036                 case FORM_SELECT:
1037                     for (j = 0, opt = fi->select_option; opt != NULL;
1038                          j++, opt = opt->next) {
1039                         if (pi->value && opt->value &&
1040                             !Strcmp_charp(opt->value, pi->value)) {
1041                             fi->selected = j;
1042                             fi->value = opt->value;
1043                             fi->label = opt->label;
1044                             updateSelectOption(fi, fi->select_option);
1045                             formUpdateBuffer(a, buf, fi);
1046                             break;
1047                         }
1048                     }
1049                     break;
1050 #endif
1051                 }
1052             }
1053         }
1054     }
1055 }