w3m

Unnamed repository; edit this file to name it for gitweb.
git clone https://logand.com/git/w3m.git/
Log | Files | Refs | README

form.c (24701B)


      1 /* $Id$ */
      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     line->size = len;
    407 
    408     return pos;
    409 }
    410 
    411 void
    412 formUpdateBuffer(Anchor *a, Buffer *buf, FormItemList *form)
    413 {
    414     Buffer save;
    415     char *p;
    416     int spos, epos, rows, c_rows, pos, col = 0;
    417     Line *l;
    418 
    419     copyBuffer(&save, buf);
    420     gotoLine(buf, a->start.line);
    421     switch (form->type) {
    422     case FORM_TEXTAREA:
    423     case FORM_INPUT_TEXT:
    424     case FORM_INPUT_FILE:
    425     case FORM_INPUT_PASSWORD:
    426     case FORM_INPUT_CHECKBOX:
    427     case FORM_INPUT_RADIO:
    428 #ifdef MENU_SELECT
    429     case FORM_SELECT:
    430 #endif				/* MENU_SELECT */
    431 	spos = a->start.pos;
    432 	epos = a->end.pos;
    433 	break;
    434     default:
    435 	spos = a->start.pos + 1;
    436 	epos = a->end.pos - 1;
    437     }
    438     switch (form->type) {
    439     case FORM_INPUT_CHECKBOX:
    440     case FORM_INPUT_RADIO:
    441 	if (form->checked)
    442 	    buf->currentLine->lineBuf[spos] = '*';
    443 	else
    444 	    buf->currentLine->lineBuf[spos] = ' ';
    445 	break;
    446     case FORM_INPUT_TEXT:
    447     case FORM_INPUT_FILE:
    448     case FORM_INPUT_PASSWORD:
    449     case FORM_TEXTAREA:
    450 #ifdef MENU_SELECT
    451     case FORM_SELECT:
    452 	if (form->type == FORM_SELECT) {
    453 	    p = form->label->ptr;
    454 	    updateSelectOption(form, form->select_option);
    455 	}
    456 	else
    457 #endif				/* MENU_SELECT */
    458 	    p = form->value->ptr;
    459 	l = buf->currentLine;
    460 	if (form->type == FORM_TEXTAREA) {
    461 	    int n = a->y - buf->currentLine->linenumber;
    462 	    if (n > 0)
    463 		for (; l && n; l = l->prev, n--) ;
    464 	    else if (n < 0)
    465 		for (; l && n; l = l->prev, n++) ;
    466 	    if (!l)
    467 		break;
    468 	}
    469 	rows = form->rows ? form->rows : 1;
    470 	col = COLPOS(l, a->start.pos);
    471 	for (c_rows = 0; c_rows < rows; c_rows++, l = l->next) {
    472 	    if (rows > 1) {
    473 		pos = columnPos(l, col);
    474 		a = retrieveAnchor(buf->formitem, l->linenumber, pos);
    475 		if (a == NULL)
    476 		    break;
    477 		spos = a->start.pos;
    478 		epos = a->end.pos;
    479 	    }
    480 	    pos = form_update_line(l, &p, spos, epos, COLPOS(l, epos) - col,
    481 				   rows > 1,
    482 				   form->type == FORM_INPUT_PASSWORD);
    483 	    if (pos != epos) {
    484 		shiftAnchorPosition(buf->href, buf->hmarklist,
    485 				    a->start.line, spos, pos - epos);
    486 		shiftAnchorPosition(buf->name, buf->hmarklist,
    487 				    a->start.line, spos, pos - epos);
    488 		shiftAnchorPosition(buf->img, buf->hmarklist,
    489 				    a->start.line, spos, pos - epos);
    490 		shiftAnchorPosition(buf->formitem, buf->hmarklist,
    491 				    a->start.line, spos, pos - epos);
    492 	    }
    493 	}
    494 	break;
    495     }
    496     copyBuffer(buf, &save);
    497     arrangeLine(buf);
    498 }
    499 
    500 
    501 Str
    502 textfieldrep(Str s, int width)
    503 {
    504     Lineprop c_type;
    505     Str n = Strnew_size(width + 2);
    506     int i, j, k, c_len;
    507 
    508     j = 0;
    509     for (i = 0; i < s->length; i += c_len) {
    510 	c_type = get_mctype((unsigned char *)&s->ptr[i]);
    511 	c_len = get_mclen(&s->ptr[i]);
    512 	if (s->ptr[i] == '\r')
    513 	    continue;
    514 	k = j + get_mcwidth(&s->ptr[i]);
    515 	if (k > width)
    516 	    break;
    517 	if (c_type == PC_CTRL)
    518 	    Strcat_char(n, ' ');
    519 #ifdef USE_M17N
    520 	else if (c_type & PC_UNKNOWN)
    521 	    Strcat_char(n, ' ');
    522 #endif
    523 	else if (s->ptr[i] == '&')
    524 	    Strcat_charp(n, "&amp;");
    525 	else if (s->ptr[i] == '<')
    526 	    Strcat_charp(n, "&lt;");
    527 	else if (s->ptr[i] == '>')
    528 	    Strcat_charp(n, "&gt;");
    529 	else
    530 	    Strcat_charp_n(n, &s->ptr[i], c_len);
    531 	j = k;
    532     }
    533     for (; j < width; j++)
    534 	Strcat_char(n, ' ');
    535     return n;
    536 }
    537 
    538 static void
    539 form_fputs_decode(Str s, FILE * f)
    540 {
    541     char *p;
    542     Str z = Strnew();
    543 
    544     for (p = s->ptr; *p;) {
    545 	switch (*p) {
    546 #if !defined( __CYGWIN__ ) && !defined( __EMX__ )
    547 	case '\r':
    548 	    if (*(p + 1) == '\n')
    549 		p++;
    550 	    /* continue to the next label */
    551 #endif				/* !defined( __CYGWIN__ ) && !defined( __EMX__ 
    552 				 * ) */
    553 	default:
    554 	    Strcat_char(z, *p);
    555 	    p++;
    556 	    break;
    557 	}
    558     }
    559 #ifdef USE_M17N
    560     z = wc_Str_conv_strict(z, InnerCharset, DisplayCharset);
    561 #endif
    562     Strfputs(z, f);
    563 }
    564 
    565 
    566 void
    567 input_textarea(FormItemList *fi)
    568 {
    569     char *tmpf = tmpfname(TMPF_DFL, NULL)->ptr;
    570     Str tmp;
    571     FILE *f;
    572 #ifdef USE_M17N
    573     wc_ces charset = DisplayCharset;
    574     wc_uint8 auto_detect;
    575 #endif
    576 
    577     f = fopen(tmpf, "w");
    578     if (f == NULL) {
    579 	/* FIXME: gettextize? */
    580 	disp_err_message("Can't open temporary file", FALSE);
    581 	return;
    582     }
    583     if (fi->value)
    584 	form_fputs_decode(fi->value, f);
    585     fclose(f);
    586 
    587     fmTerm();
    588     system(myEditor(Editor, tmpf, 1)->ptr);
    589     fmInit();
    590 
    591     if (fi->readonly)
    592 	goto input_end;
    593     f = fopen(tmpf, "r");
    594     if (f == NULL) {
    595 	/* FIXME: gettextize? */
    596 	disp_err_message("Can't open temporary file", FALSE);
    597 	goto input_end;
    598     }
    599     fi->value = Strnew();
    600 #ifdef USE_M17N
    601     auto_detect = WcOption.auto_detect;
    602     WcOption.auto_detect = WC_OPT_DETECT_ON;
    603 #endif
    604     while (tmp = Strfgets(f), tmp->length > 0) {
    605 	if (tmp->length == 1 && tmp->ptr[tmp->length - 1] == '\n') {
    606 	    /* null line with bare LF */
    607 	    tmp = Strnew_charp("\r\n");
    608 	}
    609 	else if (tmp->length > 1 && tmp->ptr[tmp->length - 1] == '\n' &&
    610 		 tmp->ptr[tmp->length - 2] != '\r') {
    611 	    Strshrink(tmp, 1);
    612 	    Strcat_charp(tmp, "\r\n");
    613 	}
    614 	tmp = convertLine(NULL, tmp, RAW_MODE, &charset, DisplayCharset);
    615 	Strcat(fi->value, tmp);
    616     }
    617 #ifdef USE_M17N
    618     WcOption.auto_detect = auto_detect;
    619 #endif
    620     fclose(f);
    621   input_end:
    622     unlink(tmpf);
    623 }
    624 
    625 void
    626 do_internal(char *action, char *data)
    627 {
    628     int i;
    629 
    630     for (i = 0; internal_action[i].action; i++) {
    631 	if (strcasecmp(internal_action[i].action, action) == 0) {
    632 	    if (internal_action[i].rout)
    633 		internal_action[i].rout(cgistr2tagarg(data));
    634 	    return;
    635 	}
    636     }
    637 }
    638 
    639 #ifdef MENU_SELECT
    640 void
    641 addSelectOption(FormSelectOption *fso, Str value, Str label, int chk)
    642 {
    643     FormSelectOptionItem *o;
    644     o = New(FormSelectOptionItem);
    645     if (value == NULL)
    646 	value = label;
    647     o->value = value;
    648     Strremovefirstspaces(label);
    649     Strremovetrailingspaces(label);
    650     o->label = label;
    651     o->checked = chk;
    652     o->next = NULL;
    653     if (fso->first == NULL)
    654 	fso->first = fso->last = o;
    655     else {
    656 	fso->last->next = o;
    657 	fso->last = o;
    658     }
    659 }
    660 
    661 void
    662 chooseSelectOption(FormItemList *fi, FormSelectOptionItem *item)
    663 {
    664     FormSelectOptionItem *opt;
    665     int i;
    666 
    667     fi->selected = 0;
    668     if (item == NULL) {
    669 	fi->value = Strnew_size(0);
    670 	fi->label = Strnew_size(0);
    671 	return;
    672     }
    673     fi->value = item->value;
    674     fi->label = item->label;
    675     for (i = 0, opt = item; opt != NULL; i++, opt = opt->next) {
    676 	if (opt->checked) {
    677 	    fi->value = opt->value;
    678 	    fi->label = opt->label;
    679 	    fi->selected = i;
    680 	    break;
    681 	}
    682     }
    683     updateSelectOption(fi, item);
    684 }
    685 
    686 void
    687 updateSelectOption(FormItemList *fi, FormSelectOptionItem *item)
    688 {
    689     int i;
    690 
    691     if (fi == NULL || item == NULL)
    692 	return;
    693     for (i = 0; item != NULL; i++, item = item->next) {
    694 	if (i == fi->selected)
    695 	    item->checked = TRUE;
    696 	else
    697 	    item->checked = FALSE;
    698     }
    699 }
    700 
    701 int
    702 formChooseOptionByMenu(struct form_item_list *fi, int x, int y)
    703 {
    704     int i, n, selected = -1, init_select = fi->selected;
    705     FormSelectOptionItem *opt;
    706     char **label;
    707 
    708     for (n = 0, opt = fi->select_option; opt != NULL; n++, opt = opt->next) ;
    709     label = New_N(char *, n + 1);
    710     for (i = 0, opt = fi->select_option; opt != NULL; i++, opt = opt->next)
    711 	label[i] = opt->label->ptr;
    712     label[n] = NULL;
    713 
    714     optionMenu(x, y, label, &selected, init_select, NULL);
    715 
    716     if (selected < 0)
    717 	return 0;
    718     for (i = 0, opt = fi->select_option; opt != NULL; i++, opt = opt->next) {
    719 	if (i == selected) {
    720 	    fi->selected = selected;
    721 	    fi->value = opt->value;
    722 	    fi->label = opt->label;
    723 	    break;
    724 	}
    725     }
    726     updateSelectOption(fi, fi->select_option);
    727     return 1;
    728 }
    729 #endif				/* MENU_SELECT */
    730 
    731 void
    732 form_write_data(FILE * f, char *boundary, char *name, char *value)
    733 {
    734     fprintf(f, "--%s\r\n", boundary);
    735     fprintf(f, "Content-Disposition: form-data; name=\"%s\"\r\n\r\n", name);
    736     fprintf(f, "%s\r\n", value);
    737 }
    738 
    739 void
    740 form_write_from_file(FILE * f, char *boundary, char *name, char *filename,
    741 		     char *file)
    742 {
    743     FILE *fd;
    744     struct stat st;
    745     int c;
    746     char *type;
    747 
    748     fprintf(f, "--%s\r\n", boundary);
    749     fprintf(f,
    750 	    "Content-Disposition: form-data; name=\"%s\"; filename=\"%s\"\r\n",
    751 	    name, mybasename(filename));
    752     type = guessContentType(file);
    753     fprintf(f, "Content-Type: %s\r\n\r\n",
    754 	    type ? type : "application/octet-stream");
    755 
    756     if (lstat(file, &st) < 0)
    757 	goto write_end;
    758     if (S_ISDIR(st.st_mode))
    759 	goto write_end;
    760     fd = fopen(file, "r");
    761     if (fd != NULL) {
    762 	while ((c = fgetc(fd)) != EOF)
    763 	    fputc(c, f);
    764 	fclose(fd);
    765     }
    766   write_end:
    767     fprintf(f, "\r\n");
    768 }
    769 
    770 struct pre_form_item {
    771     int type;
    772     char *name;
    773     char *value;
    774     int checked;
    775     struct pre_form_item *next;
    776 };
    777 
    778 struct pre_form {
    779     char *url;
    780     Regex *re_url;
    781     char *name;
    782     char *action;
    783     struct pre_form_item *item;
    784     struct pre_form *next;
    785 };
    786 
    787 static struct pre_form *PreForm = NULL;
    788 
    789 static struct pre_form *
    790 add_pre_form(struct pre_form *prev, char *url, char *name, char *action)
    791 {
    792     ParsedURL pu;
    793     struct pre_form *new;
    794 
    795     if (prev)
    796 	new = prev->next = New(struct pre_form);
    797     else
    798 	new = PreForm = New(struct pre_form);
    799     if (url && *url == '/') {
    800 	int l = strlen(url);
    801 	if (l > 1 && url[l - 1] == '/')
    802 	    new->url = allocStr(url + 1, l - 2);
    803 	else
    804 	    new->url = url + 1;
    805 	new->re_url = newRegex(new->url, FALSE, NULL, NULL);
    806 	if (!new->re_url)
    807 	    new->url = NULL;
    808     }
    809     else if (url) {
    810 	parseURL2(url, &pu, NULL);
    811 	new->url = parsedURL2Str(&pu)->ptr;
    812 	new->re_url = NULL;
    813     }
    814     new->name = (name && *name) ? name : NULL;
    815     new->action = (action && *action) ? action : NULL;
    816     new->item = NULL;
    817     new->next = NULL;
    818     return new;
    819 }
    820 
    821 static struct pre_form_item *
    822 add_pre_form_item(struct pre_form *pf, struct pre_form_item *prev, int type,
    823 		  char *name, char *value, char *checked)
    824 {
    825     struct pre_form_item *new;
    826 
    827     if (!pf)
    828 	return NULL;
    829     if (prev)
    830 	new = prev->next = New(struct pre_form_item);
    831     else
    832 	new = pf->item = New(struct pre_form_item);
    833     new->type = type;
    834     new->name = name;
    835     new->value = value;
    836     if (checked && *checked && (!strcmp(checked, "0") ||
    837 				strcasecmp(checked, "off")
    838 				|| !strcasecmp(checked, "no")))
    839 	new->checked = 0;
    840     else
    841 	new->checked = 1;
    842     new->next = NULL;
    843     return new;
    844 }
    845 
    846 /*
    847  * url <url>|/<re-url>/
    848  * form [<name>] <action>
    849  * text <name> <value>
    850  * file <name> <value>
    851  * passwd <name> <value>
    852  * checkbox <name> <value> [<checked>]
    853  * radio <name> <value>
    854  * select <name> <value>
    855  * submit [<name> [<value>]]
    856  * image [<name> [<value>]]
    857  * textarea <name>
    858  * <value>
    859  * /textarea
    860  */
    861 
    862 void
    863 loadPreForm(void)
    864 {
    865     FILE *fp;
    866     Str line = NULL, textarea = NULL;
    867     struct pre_form *pf = NULL;
    868     struct pre_form_item *pi = NULL;
    869     int type = -1;
    870     char *name = NULL;
    871 
    872     PreForm = NULL;
    873     fp = openSecretFile(pre_form_file);
    874     if (fp == NULL)
    875 	return;
    876     while (1) {
    877 	char *p, *s, *arg;
    878 
    879 	line = Strfgets(fp);
    880 	if (line->length == 0)
    881 	    break;
    882 	if (textarea && !(!strncmp(line->ptr, "/textarea", 9) &&
    883 			  IS_SPACE(line->ptr[9]))) {
    884 	    Strcat(textarea, line);
    885 	    continue;
    886 	}
    887 	Strchop(line);
    888 	Strremovefirstspaces(line);
    889 	p = line->ptr;
    890 	if (*p == '#' || *p == '\0')
    891 	    continue;		/* comment or empty line */
    892 	s = getWord(&p);
    893 	arg = getWord(&p);
    894 
    895 	if (!strcmp(s, "url")) {
    896 	    if (!arg || !*arg)
    897 		continue;
    898 	    p = getQWord(&p);
    899 	    pf = add_pre_form(pf, arg, NULL, p);
    900 	    pi = pf->item;
    901 	    continue;
    902 	}
    903 	if (!pf)
    904 	    continue;
    905 	if (!strcmp(s, "form")) {
    906 	    if (!arg || !*arg)
    907 		continue;
    908 	    s = getQWord(&p);
    909 	    p = getQWord(&p);
    910 	    if (!p || !*p) {
    911 		p = s;
    912 		s = NULL;
    913 	    }
    914 	    if (pf->item) {
    915 		struct pre_form *prev = pf;
    916 		pf = add_pre_form(prev, "", s, p);
    917 		/* copy previous URL */
    918 		pf->url = prev->url;
    919 		pf->re_url = prev->re_url;
    920 	    }
    921 	    else {
    922 		pf->name = s;
    923 		pf->action = (p && *p) ? p : NULL;
    924 	    }
    925 	    pi = pf->item;
    926 	    continue;
    927 	}
    928 	if (!strcmp(s, "text"))
    929 	    type = FORM_INPUT_TEXT;
    930 	else if (!strcmp(s, "file"))
    931 	    type = FORM_INPUT_FILE;
    932 	else if (!strcmp(s, "passwd") || !strcmp(s, "password"))
    933 	    type = FORM_INPUT_PASSWORD;
    934 	else if (!strcmp(s, "checkbox"))
    935 	    type = FORM_INPUT_CHECKBOX;
    936 	else if (!strcmp(s, "radio"))
    937 	    type = FORM_INPUT_RADIO;
    938 	else if (!strcmp(s, "submit"))
    939 	    type = FORM_INPUT_SUBMIT;
    940 	else if (!strcmp(s, "image"))
    941 	    type = FORM_INPUT_IMAGE;
    942 	else if (!strcmp(s, "select"))
    943 	    type = FORM_SELECT;
    944 	else if (!strcmp(s, "textarea")) {
    945 	    type = FORM_TEXTAREA;
    946 	    name = Strnew_charp(arg)->ptr;
    947 	    textarea = Strnew();
    948 	    continue;
    949 	}
    950 	else if (textarea && name && !strcmp(s, "/textarea")) {
    951 	    pi = add_pre_form_item(pf, pi, type, name, textarea->ptr, NULL);
    952 	    textarea = NULL;
    953 	    name = NULL;
    954 	    continue;
    955 	}
    956 	else
    957 	    continue;
    958 	s = getQWord(&p);
    959 	pi = add_pre_form_item(pf, pi, type, arg, s, getQWord(&p));
    960     }
    961     fclose(fp);
    962 }
    963 
    964 void
    965 preFormUpdateBuffer(Buffer *buf)
    966 {
    967     struct pre_form *pf;
    968     struct pre_form_item *pi;
    969     int i;
    970     Anchor *a;
    971     FormList *fl;
    972     FormItemList *fi;
    973 #ifdef MENU_SELECT
    974     FormSelectOptionItem *opt;
    975     int j;
    976 #endif
    977 
    978     if (!buf || !buf->formitem || !PreForm)
    979 	return;
    980 
    981     for (pf = PreForm; pf; pf = pf->next) {
    982 	if (pf->re_url) {
    983 	    Str url = parsedURL2Str(&buf->currentURL);
    984 	    if (!RegexMatch(pf->re_url, url->ptr, url->length, 1))
    985 		continue;
    986 	}
    987 	else if (pf->url) {
    988 	    if (Strcmp_charp(parsedURL2Str(&buf->currentURL), pf->url))
    989 		continue;
    990 	}
    991 	else
    992 	    continue;
    993 	for (i = 0; i < buf->formitem->nanchor; i++) {
    994 	    a = &buf->formitem->anchors[i];
    995 	    fi = (FormItemList *)a->url;
    996 	    fl = fi->parent;
    997 	    if (pf->name && (!fl->name || strcmp(fl->name, pf->name)))
    998 		continue;
    999 	    if (pf->action
   1000 		&& (!fl->action || Strcmp_charp(fl->action, pf->action)))
   1001 		continue;
   1002 	    for (pi = pf->item; pi; pi = pi->next) {
   1003 		if (pi->type != fi->type)
   1004 		    continue;
   1005 		if (pi->type == FORM_INPUT_SUBMIT ||
   1006 		    pi->type == FORM_INPUT_IMAGE) {
   1007 		    if ((!pi->name || !*pi->name ||
   1008 			 (fi->name && !Strcmp_charp(fi->name, pi->name))) &&
   1009 			(!pi->value || !*pi->value ||
   1010 			 (fi->value && !Strcmp_charp(fi->value, pi->value))))
   1011 			buf->submit = a;
   1012 		    continue;
   1013 		}
   1014 		if (!pi->name || !fi->name || Strcmp_charp(fi->name, pi->name))
   1015 		    continue;
   1016 		switch (pi->type) {
   1017 		case FORM_INPUT_TEXT:
   1018 		case FORM_INPUT_FILE:
   1019 		case FORM_INPUT_PASSWORD:
   1020 		case FORM_TEXTAREA:
   1021 		    fi->value = Strnew_charp(pi->value);
   1022 		    formUpdateBuffer(a, buf, fi);
   1023 		    break;
   1024 		case FORM_INPUT_CHECKBOX:
   1025 		    if (pi->value && fi->value &&
   1026 			!Strcmp_charp(fi->value, pi->value)) {
   1027 			fi->checked = pi->checked;
   1028 			formUpdateBuffer(a, buf, fi);
   1029 		    }
   1030 		    break;
   1031 		case FORM_INPUT_RADIO:
   1032 		    if (pi->value && fi->value &&
   1033 			!Strcmp_charp(fi->value, pi->value))
   1034 			formRecheckRadio(a, buf, fi);
   1035 		    break;
   1036 #ifdef MENU_SELECT
   1037 		case FORM_SELECT:
   1038 		    for (j = 0, opt = fi->select_option; opt != NULL;
   1039 			 j++, opt = opt->next) {
   1040 			if (pi->value && opt->value &&
   1041 			    !Strcmp_charp(opt->value, pi->value)) {
   1042 			    fi->selected = j;
   1043 			    fi->value = opt->value;
   1044 			    fi->label = opt->label;
   1045 			    updateSelectOption(fi, fi->select_option);
   1046 			    formUpdateBuffer(a, buf, fi);
   1047 			    break;
   1048 			}
   1049 		    }
   1050 		    break;
   1051 #endif
   1052 		}
   1053 	    }
   1054 	}
   1055     }
   1056 }