10 typedef struct Field Field;
11 typedef struct Option Option;
16 Field *fields, *efields;
25 int size; /* should be a point, but that feature is deprecated */
31 int state; /* is the button marked? */
61 #define BOUNDARY "nAboJ9uN6ZXsqoVGzLAdjKq97TWDTGjo"
63 void h_checkinput(Panel *, int, int);
64 void h_radioinput(Panel *, int, int);
65 void h_submitinput(Panel *, int);
66 void h_buttoninput(Panel *, int);
67 void h_fileinput(Panel *, int);
68 void h_submittype(Panel *, char *);
69 void h_submitindex(Panel *, char *);
70 void h_resetinput(Panel *, int);
71 void h_select(Panel *, int, int);
72 char *selgen(Panel *, int);
73 char *nullgen(Panel *, int);
74 Field *newfield(Form *form){
76 f=emalloc(sizeof(Field));
80 form->efields->next=f;
87 * Called by rdhtml on seeing a forms-related tag
89 void rdform(Hglob *g){
96 fprint(2, "Bad tag <%s> in rdform (Can't happen!)\n", g->token);
100 htmlerror(g->name, g->lineno, "nested forms illegal\n");
103 g->form=emalloc(sizeof(Form));
104 s=pl_getattr(g->attr, "action");
105 g->form->action=strdup((s && *s) ? s : g->dst->url->fullname);
106 s=pl_getattr(g->attr, "method");
109 else if(cistrcmp(s, "post")==0)
110 g->form->method=POST;
112 if(cistrcmp(s, "get")!=0)
113 htmlerror(g->name, g->lineno,
114 "unknown form method %s\n", s);
117 s=pl_getattr(g->attr, "enctype");
118 if(s && cistrcmp(s, "multipart/form-data")==0)
119 g->form->ctype = "multipart/form-data; boundary=" BOUNDARY;
122 g->form->next = g->dst->form;
123 g->dst->form = g->form;
128 /* no form, assume link button */
129 form = emalloc(sizeof(Form));
134 form->action = strdup(g->state->link);
135 form->next = g->dst->form;
140 s=pl_getattr(g->attr, "name");
145 s=pl_getattr(g->attr, "value");
150 f->checked=pl_hasattr(g->attr, "checked");
151 s=pl_getattr(g->attr, "size");
156 s=pl_getattr(g->attr, "maxlength");
158 f->maxlength=0x3fffffff;
160 f->maxlength=atoi(s);
161 s=pl_getattr(g->attr, "type");
162 if((g->tag == Tag_button) &&
163 (s==0 || cistrcmp(s, "reset") || cistrcmp(s, "button")))
167 if(cistrcmp(s, "checkbox")==0)
169 else if(cistrcmp(s, "radio")==0)
171 else if(cistrcmp(s, "submit")==0)
173 else if(cistrcmp(s, "image")==0){
175 s=pl_getattr(g->attr, "src");
177 free(g->state->image);
178 g->state->image = strdup(s);
180 s=pl_getattr(g->attr, "width");
182 g->state->width=strtolength(g, HORIZ, s);
183 s=pl_getattr(g->attr, "height");
185 g->state->height=strtolength(g, VERT, s);
186 s=pl_getattr(g->attr, "alt");
187 if(s==0 || *s == 0) s = f->value;
188 pl_htmloutput(g, g->nsp, s, f);
189 free(g->state->image);
195 else if(cistrcmp(s, "button")==0)
197 else if(cistrcmp(s, "file")==0)
199 else if(cistrcmp(s, "reset")==0)
201 else if(cistrcmp(s, "hidden")==0)
205 if(cistrcmp(s, "password")==0)
208 if(s && cistrcmp(s, "isindex")==0)
212 * If there's exactly one attribute, use its value as the name,
213 * regardless of the attribute name. This makes
214 * http://linus.att.com/ias/puborder.html work.
217 if(g->attr[0].name && g->attr[1].name==0)
218 f->name=strdup(g->attr[0].value);
220 f->name=strdup("no-name");
223 if((f->type==CHECK || f->type==RADIO) && !pl_hasattr(g->attr, "value")){
225 f->value=strdup("on");
228 pl_htmloutput(g, g->nsp, f->value[0]?f->value:"blank field", f);
233 htmlerror(g->name, g->lineno, "<%s> not in form, ignored\n",
238 s=pl_getattr(g->attr, "name");
240 f->name=strdup("select");
241 htmlerror(g->name, g->lineno, "select has no name=\n");
245 f->multiple=pl_hasattr(g->attr, "multiple");
254 if(g->form==0) goto BadTag;
255 if((f=g->form->efields)==0) goto BadTag;
258 o=emalloc(sizeof(Option));
259 for(op=&f->options;*op;op=&(*op)->next);
264 g->etext=o->label+NLABEL;
265 memset(o->label, 0, NLABEL+1);
267 o->def=pl_hasattr(g->attr, "selected");
269 if(pl_hasattr(g->attr, "disabled"))
271 s=pl_getattr(g->attr, "value");
278 if(g->form==0) goto BadTag;
280 s=pl_getattr(g->attr, "name");
282 f->name=strdup("enter text");
283 htmlerror(g->name, g->lineno, "select has no name=\n");
287 s=pl_getattr(g->attr, "rows");
288 f->rows=(s && *s)?atoi(s):8;
289 s=pl_getattr(g->attr, "cols");
290 f->cols=(s && *s)?atoi(s):30;
292 /* suck up initial text */
293 pl_htmloutput(g, g->nsp, f->name, f);
297 * Make up a form with one tag, of type INDEX
298 * I have seen a page with <ISINDEX PROMPT="Enter a title here ">,
299 * which is nonstandard and not handled here.
301 form=emalloc(sizeof(Form));
304 s=pl_getattr(g->attr, "action");
305 form->action=strdup((s && *s) ? s : g->dst->url->fullname);
312 f->maxlength=0x3fffffff;
314 pl_htmloutput(g, g->nsp, f->value[0]?f->value:"blank field", f);
319 * Called by rdhtml on seeing a forms-related end tag
321 void endform(Hglob *g){
330 htmlerror(g->name, g->lineno, "</select> not in form, ignored\n");
331 else if((f=g->form->efields)==0)
332 htmlerror(g->name, g->lineno, "spurious </select>\n");
334 pl_htmloutput(g, g->nsp, f->name, f);
340 char *nullgen(Panel *, int ){
343 char *selgen(Panel *p, int index){
348 for(a=f->options;index!=0 && a!=0;--index,a=a->next);
350 a->label[0]=a->selected?'*':' ';
353 char *seloption(Field *f){
355 for(a=f->options;a!=0;a=a->next)
360 void mkfieldpanel(Rtext *t){
365 if((a = t->user) == nil)
367 if((f = a->field) == nil)
373 f->p=plentry(0, 0, f->size*chrwidth, f->value, h_submittype);
376 f->p=plentry(0, USERFL, f->size*chrwidth, f->value, h_submittype);
379 f->p=plcheckbutton(0, 0, "", h_checkinput);
381 plsetbutton(f->p, f->checked);
384 f->p=plradiobutton(0, 0, "", h_radioinput);
386 plsetbutton(f->p, f->checked);
389 f->p=plbutton(0, 0, f->value[0]?f->value:"submit", h_submitinput);
392 f->p=plbutton(0, 0, f->value[0]?f->value:"reset", h_resetinput);
395 f->p=plbutton(0, 0, f->value[0]?f->value:"button", h_buttoninput);
398 f->p=plbutton(0, 0, f->value[0]?f->value:"file", h_fileinput);
403 f->pulldown=plgroup(0,0);
404 scrl=plscrollbar(f->pulldown, PACKW|FILLY);
405 win=pllist(f->pulldown, PACKN, nullgen, f->size, h_select);
407 plinitlist(win, PACKN, selgen, f->size, h_select);
408 plscroll(win, 0, scrl);
409 plpack(f->pulldown, Rect(0,0,1024,1024));
410 f->p=plpulldown(0, FIXEDX, seloption(f), f->pulldown, PACKS);
411 f->p->fixedsize.x=f->pulldown->r.max.x-f->pulldown->r.min.x;
415 pllabel(f->p, PACKN|FILLX, f->name);
416 scrl=plscrollbar(f->p, PACKW|FILLY);
417 f->textwin=pledit(f->p, EXPAND, Pt(f->cols*chrwidth, f->rows*font->height), 0, 0, 0);
419 plscroll(f->textwin, 0, scrl);
422 f->p=plentry(0, 0, f->size*chrwidth, f->value, h_submitindex);
433 void h_checkinput(Panel *p, int, int v){
434 ((Field *)p->userp)->state=v;
436 void h_radioinput(Panel *p, int, int v){
441 for(f=me->form->fields;f;f=f->next)
442 if(f->type==RADIO && f!=me && strcmp(f->name, me->name)==0){
443 plsetbutton(f->p, 0);
445 pldraw(f->p, screen);
449 void h_select(Panel *p, int, int index){
454 if(!f->multiple) for(a=f->options;a;a=a->next) a->selected=0;
455 for(a=f->options;index!=0 && a!=0;--index,a=a->next);
457 a->selected=!a->selected;
458 plinitpulldown(f->p, FIXEDX, seloption(f), f->pulldown, PACKS);
459 pldraw(f->p, screen);
461 void h_resetinput(Panel *p, int){
464 for(f=((Field *)p->userp)->form->fields;f;f=f->next) switch(f->type){
466 plinitentry(f->p, 0, f->size*chrwidth, f->value, 0);
469 plinitentry(f->p, USERFL, f->size*chrwidth, f->value, 0);
476 pldraw(f->p, screen);
481 plsetbutton(f->p, f->checked);
484 for(o=f->options;o;o=o->next)
488 pldraw(text, screen);
491 void h_buttoninput(Panel *p, int){
495 if(f && f->form && f->form->method != POST && f->form->action)
496 geturl(f->form->action, -1, 0, 0);
499 void h_fileinput(Panel *p, int){
504 nstrcpy(name, f->value, sizeof(name));
506 if(eenter("Upload file", name, sizeof(name), &mouse) <= 0)
508 if(access(name, AREAD) == 0)
512 f->value = strdup(name);
513 p->state = name[0] != 0;
514 pldraw(f->p, screen);
518 * If there's exactly one button with type=text, then
519 * a CR in the button is supposed to submit the form.
521 void h_submittype(Panel *p, char *){
525 for(f=((Field *)p->userp)->form->fields;f;f=f->next)
526 if(f->type==TYPEIN || f->type==PASSWD)
528 if(ntype==1) h_submitinput(p, 0);
530 void h_submitindex(Panel *p, char *){
534 void mencodeform(Form *form, int fd){
542 for(f=form->fields;f;f=f->next)switch(f->type){
545 fprint(fd, "%s\r\nContent-Disposition: form-data; name=\"%s\"\r\n\r\n%s",
546 sep, f->name, plentryval(f->p));
547 sep = "\r\n--" BOUNDARY;
554 if(f->name==0 || f->value==0)
556 fprint(fd, "%s\r\nContent-Disposition: form-data; name=\"%s\"\r\n\r\n%s",
557 sep, f->name, f->value);
558 sep = "\r\n--" BOUNDARY;
561 if(f->name==0) break;
562 for(o=f->options;o;o=o->next)
563 if(o->selected && o->value){
564 fprint(fd, "%s\r\nContent-Disposition: form-data; name=\"%s\"\r\n\r\n%s",
565 sep, f->name, o->value);
566 sep = "\r\n--" BOUNDARY;
570 if(f->name==0) break;
571 n=plelen(f->textwin);
572 rp=pleget(f->textwin);
573 p=b=malloc(UTFmax*n+1);
577 p += runetochar(p, rp);
582 fprint(fd, "%s\r\nContent-Disposition: form-data; name=\"%s\"\r\n\r\n%s",
584 sep = "\r\n--" BOUNDARY;
588 if(f->name==0 || f->value[0]==0)
590 if(p = strrchr(f->value, '/'))
592 if(p == 0 || *p == 0)
594 if((b = malloc(nb = 8192)) == nil)
596 if((ifd = open(f->value, OREAD)) >= 0){
597 if(filetype(ifd, b, nb) < 0)
598 strcpy(b, "application/octet-stream");
599 fprint(fd, "%s\r\nContent-Disposition: form-data; name=\"%s\"; filename=\"%s\""
600 "\r\nContent-Type: %s\r\n\r\n", sep, f->name, p, b);
601 sep = "\r\n--" BOUNDARY;
602 while((n = read(ifd, b, nb)) > 0)
603 if(write(fd, b, n) != n)
610 fprint(fd, "%s--\r\n", sep);
613 void uencodeform(Form *form, int fd){
621 for(f=form->fields;f;f=f->next) switch(f->type){
624 fprint(fd, "%s%U=%U", sep, f->name, plentryval(f->p));
628 fprint(fd, "%s%U", sep, plentryval(f->p));
636 if(f->name==0 || f->value==0)
638 fprint(fd, "%s%U=%U", sep, f->name, f->value);
642 if(f->name==0) break;
643 for(o=f->options;o;o=o->next)
644 if(o->selected && o->value){
645 fprint(fd, "%s%U=%U", sep, f->name, o->value);
650 if(f->name==0) break;
651 n=plelen(f->textwin);
652 rp=pleget(f->textwin);
653 p=b=malloc(UTFmax*n+1);
657 p += runetochar(p, rp);
662 fprint(fd, "%s%U=%U", sep, f->name, b);
669 void h_submitinput(Panel *p, int){
677 for(f=form->fields;f;f=f->next)
679 f->state = (f->p == p);
681 switch(form->method){
683 strcpy(buf, "/tmp/mfXXXXXXXXXXX");
684 fd = create(mktemp(buf), ORDWR|ORCLOSE, 0600);
687 fd = urlpost(selurl(form->action), form->ctype);
694 message("submit: %r");
697 if(form->method==GET){
698 fprint(fd, "%s?", form->action);
699 uencodeform(form, fd);
701 n = readn(fd, buf, sizeof(buf));
703 if(n < 0 || n >= sizeof(buf)){
704 message("submit: too large");
708 geturl(buf, -1, 0, 0);
710 /* only set for multipart/form-data */
712 mencodeform(form, fd);
714 uencodeform(form, fd);
715 geturl(form->action, fd, 0, 0);
719 void freeform(void *p)
728 while(f = form->fields){
729 form->fields = f->next;
737 while(o = f->options){
738 f->options = o->next;
739 if(o->value != o->label+1)
751 char *s = va_arg(f->args, char*);
753 if(strchr("/$-_@.!*'(),", *s)
754 || 'a'<=*s && *s<='z'
755 || 'A'<=*s && *s<='Z'
756 || '0'<=*s && *s<='9')
757 fmtprint(f, "%c", *s);
761 fmtprint(f, "%%%.2X", *s & 0xFF);