9 typedef struct Field Field;
10 typedef struct Option Option;
15 Field *fields, *efields;
24 int size; /* should be a point, but that feature is deprecated */
30 int state; /* is the button marked? */
60 #define BOUNDARY "nAboJ9uN6ZXsqoVGzLAdjKq97TWDTGjo"
62 void h_checkinput(Panel *, int, int);
63 void h_radioinput(Panel *, int, int);
64 void h_submitinput(Panel *, int);
65 void h_buttoninput(Panel *, int);
66 void h_fileinput(Panel *, int);
67 void h_submittype(Panel *, char *);
68 void h_submitindex(Panel *, char *);
69 void h_resetinput(Panel *, int);
70 void h_select(Panel *, int, int);
71 char *selgen(Panel *, int);
72 char *nullgen(Panel *, int);
73 Field *newfield(Form *form){
75 f=emalloc(sizeof(Field));
79 form->efields->next=f;
86 * Called by rdhtml on seeing a forms-related tag
88 void rdform(Hglob *g){
95 fprint(2, "Bad tag <%s> in rdform (Can't happen!)\n", g->token);
99 htmlerror(g->name, g->lineno, "nested forms illegal\n");
102 g->form=emalloc(sizeof(Form));
103 s=pl_getattr(g->attr, "action");
104 g->form->action=strdup((s && s[0]) ? s : g->dst->url->fullname);
105 s=pl_getattr(g->attr, "method");
108 else if(cistrcmp(s, "post")==0)
109 g->form->method=POST;
111 if(cistrcmp(s, "get")!=0)
112 htmlerror(g->name, g->lineno,
113 "unknown form method %s\n", s);
116 s=pl_getattr(g->attr, "enctype");
117 if(s && cistrcmp(s, "multipart/form-data")==0)
118 g->form->ctype = "multipart/form-data; boundary=" BOUNDARY;
121 g->form->next = g->dst->form;
122 g->dst->form = g->form;
128 htmlerror(g->name, g->lineno, "<%s> not in form, ignored\n",
133 s=pl_getattr(g->attr, "name");
138 s=pl_getattr(g->attr, "value");
143 f->checked=pl_hasattr(g->attr, "checked");
144 s=pl_getattr(g->attr, "size");
149 s=pl_getattr(g->attr, "maxlength");
151 f->maxlength=0x3fffffff;
153 f->maxlength=atoi(s);
154 s=pl_getattr(g->attr, "type");
155 if((g->tag == Tag_button) &&
156 (s==0 || cistrcmp(s, "reset") || cistrcmp(s, "button")))
160 if(cistrcmp(s, "checkbox")==0)
162 else if(cistrcmp(s, "radio")==0)
164 else if(cistrcmp(s, "submit")==0)
166 else if(cistrcmp(s, "button")==0)
168 else if(cistrcmp(s, "image")==0)
170 else if(cistrcmp(s, "file")==0)
172 else if(cistrcmp(s, "reset")==0)
174 else if(cistrcmp(s, "hidden")==0)
178 if(cistrcmp(s, "password")==0)
181 if(s && cistrcmp(s, "isindex")==0)
185 * If there's exactly one attribute, use its value as the name,
186 * regardless of the attribute name. This makes
187 * http://linus.att.com/ias/puborder.html work.
190 if(g->attr[0].name && g->attr[1].name==0)
191 f->name=strdup(g->attr[0].value);
193 f->name=strdup("no-name");
196 if((f->type==CHECK || f->type==RADIO) && !pl_hasattr(g->attr, "value")){
198 f->value=strdup("on");
201 pl_htmloutput(g, g->nsp, f->value[0]?f->value:"blank field", f);
204 if(g->form==0) goto BadTag;
206 s=pl_getattr(g->attr, "name");
208 f->name=strdup("select");
209 htmlerror(g->name, g->lineno, "select has no name=\n");
213 f->multiple=pl_hasattr(g->attr, "multiple");
222 if(g->form==0) goto BadTag;
223 if((f=g->form->efields)==0) goto BadTag;
226 o=emalloc(sizeof(Option));
227 for(op=&f->options;*op;op=&(*op)->next);
232 g->etext=o->label+NLABEL;
233 memset(o->label, 0, NLABEL+1);
235 o->def=pl_hasattr(g->attr, "selected");
237 if(pl_hasattr(g->attr, "disabled"))
239 s=pl_getattr(g->attr, "value");
246 if(g->form==0) goto BadTag;
248 s=pl_getattr(g->attr, "name");
250 f->name=strdup("enter text");
251 htmlerror(g->name, g->lineno, "select has no name=\n");
255 s=pl_getattr(g->attr, "rows");
257 s=pl_getattr(g->attr, "cols");
258 f->cols=s?atoi(s):30;
260 /* suck up initial text */
261 pl_htmloutput(g, g->nsp, f->name, f);
265 * Make up a form with one tag, of type INDEX
266 * I have seen a page with <ISINDEX PROMPT="Enter a title here ">,
267 * which is nonstandard and not handled here.
269 form=emalloc(sizeof(Form));
272 s=pl_getattr(g->attr, "action");
273 form->action=strdup((s && s[0]) ? s : g->dst->url->fullname);
280 f->maxlength=0x3fffffff;
282 pl_htmloutput(g, g->nsp, f->value[0]?f->value:"blank field", f);
287 * Called by rdhtml on seeing a forms-related end tag
289 void endform(Hglob *g){
298 htmlerror(g->name, g->lineno, "</select> not in form, ignored\n");
299 else if((f=g->form->efields)==0)
300 htmlerror(g->name, g->lineno, "spurious </select>\n");
302 pl_htmloutput(g, g->nsp, f->name, f);
308 char *nullgen(Panel *, int ){
311 char *selgen(Panel *p, int index){
316 for(a=f->options;index!=0 && a!=0;--index,a=a->next);
318 a->label[0]=a->selected?'*':' ';
321 char *seloption(Field *f){
323 for(a=f->options;a!=0;a=a->next)
328 void mkfieldpanel(Rtext *t){
330 Panel *win, *scrl, *menu, *pop, *button;
333 if((a = t->user) == nil)
335 if((f = a->field) == nil)
341 f->p=plentry(0, 0, f->size*chrwidth, f->value, h_submittype);
344 f->p=plentry(0, USERFL, f->size*chrwidth, f->value, h_submittype);
347 f->p=plcheckbutton(0, 0, "", h_checkinput);
349 plsetbutton(f->p, f->checked);
352 f->p=plradiobutton(0, 0, "", h_radioinput);
354 plsetbutton(f->p, f->checked);
357 f->p=plbutton(0, 0, f->value[0]?f->value:"submit", h_submitinput);
360 f->p=plbutton(0, 0, f->value[0]?f->value:"reset", h_resetinput);
363 f->p=plbutton(0, 0, f->value[0]?f->value:"button", h_buttoninput);
366 f->p=plbutton(0, 0, f->value[0]?f->value:"file", h_fileinput);
371 f->pulldown=plgroup(0,0);
372 scrl=plscrollbar(f->pulldown, PACKW|FILLY);
373 win=pllist(f->pulldown, PACKN, nullgen, f->size, h_select);
375 plinitlist(win, PACKN, selgen, f->size, h_select);
376 plscroll(win, 0, scrl);
377 plpack(f->pulldown, Rect(0,0,1024,1024));
378 f->p=plpulldown(0, FIXEDX, seloption(f), f->pulldown, PACKS);
379 f->p->fixedsize.x=f->pulldown->r.max.x-f->pulldown->r.min.x;
383 pllabel(f->p, PACKN|FILLX, f->name);
384 scrl=plscrollbar(f->p, PACKW|FILLY);
385 f->textwin=pledit(f->p, EXPAND, Pt(f->cols*chrwidth, f->rows*font->height), 0, 0, 0);
387 plscroll(f->textwin, 0, scrl);
390 f->p=plentry(0, 0, f->size*chrwidth, f->value, h_submitindex);
401 void h_checkinput(Panel *p, int, int v){
402 ((Field *)p->userp)->state=v;
404 void h_radioinput(Panel *p, int, int v){
409 for(f=me->form->fields;f;f=f->next)
410 if(f->type==RADIO && f!=me && strcmp(f->name, me->name)==0){
411 plsetbutton(f->p, 0);
413 pldraw(f->p, screen);
417 void h_select(Panel *p, int, int index){
422 if(!f->multiple) for(a=f->options;a;a=a->next) a->selected=0;
423 for(a=f->options;index!=0 && a!=0;--index,a=a->next);
425 a->selected=!a->selected;
426 plinitpulldown(f->p, FIXEDX, seloption(f), f->pulldown, PACKS);
427 pldraw(f->p, screen);
429 void h_resetinput(Panel *p, int){
432 for(f=((Field *)p->userp)->form->fields;f;f=f->next) switch(f->type){
434 plinitentry(f->p, 0, f->size*chrwidth, f->value, 0);
437 plinitentry(f->p, USERFL, f->size*chrwidth, f->value, 0);
444 pldraw(f->p, screen);
449 plsetbutton(f->p, f->checked);
452 for(o=f->options;o;o=o->next)
456 pldraw(text, screen);
458 void h_buttoninput(Panel *p, int){
460 void h_fileinput(Panel *p, int){
465 nstrcpy(name, f->value, sizeof(name));
467 if(eenter("Upload file", name, sizeof(name), &mouse) <= 0)
469 if(access(name, AREAD) == 0)
473 f->value = strdup(name);
474 p->state = name[0] != 0;
475 pldraw(f->p, screen);
479 * If there's exactly one button with type=text, then
480 * a CR in the button is supposed to submit the form.
482 void h_submittype(Panel *p, char *){
486 for(f=((Field *)p->userp)->form->fields;f;f=f->next)
487 if(f->type==TYPEIN || f->type==PASSWD)
489 if(ntype==1) h_submitinput(p, 0);
491 void h_submitindex(Panel *p, char *){
495 void mencodeform(Form *form, int fd){
503 for(f=form->fields;f;f=f->next)switch(f->type){
506 fprint(fd, "%s\r\nContent-Disposition: form-data; name=\"%s\"\r\n\r\n%s",
507 sep, f->name, plentryval(f->p));
508 sep = "\r\n--" BOUNDARY;
515 if(f->name==0 || f->value==0)
517 fprint(fd, "%s\r\nContent-Disposition: form-data; name=\"%s\"\r\n\r\n%s",
518 sep, f->name, f->value);
519 sep = "\r\n--" BOUNDARY;
522 if(f->name==0) break;
523 for(o=f->options;o;o=o->next)
524 if(o->selected && o->value){
525 fprint(fd, "%s\r\nContent-Disposition: form-data; name=\"%s\"\r\n\r\n%s",
526 sep, f->name, o->value);
527 sep = "\r\n--" BOUNDARY;
531 if(f->name==0) break;
532 n=plelen(f->textwin);
533 rp=pleget(f->textwin);
534 p=b=malloc(UTFmax*n+1);
538 p += runetochar(p, rp);
543 fprint(fd, "%s\r\nContent-Disposition: form-data; name=\"%s\"\r\n\r\n%s",
545 sep = "\r\n--" BOUNDARY;
549 if(f->name==0 || f->value[0]==0)
551 if(p = strrchr(f->value, '/'))
553 if(p == 0 || *p == 0)
555 if((b = malloc(nb = 8192)) == nil)
557 if((ifd = open(f->value, OREAD)) >= 0){
558 if(filetype(ifd, b, nb) < 0)
559 strcpy(b, "application/octet-stream");
560 fprint(fd, "%s\r\nContent-Disposition: form-data; name=\"%s\"; filename=\"%s\""
561 "\r\nContent-Type: %s\r\n\r\n", sep, f->name, p, b);
562 sep = "\r\n--" BOUNDARY;
563 while((n = read(ifd, b, nb)) > 0)
564 if(write(fd, b, n) != n)
571 fprint(fd, "%s--\r\n", sep);
574 void uencodeform(Form *form, int fd){
582 for(f=form->fields;f;f=f->next) switch(f->type){
585 fprint(fd, "%s%U=%U", sep, f->name, plentryval(f->p));
589 fprint(fd, "%s%U", sep, plentryval(f->p));
597 if(f->name==0 || f->value==0)
599 fprint(fd, "%s%U=%U", sep, f->name, f->value);
603 if(f->name==0) break;
604 for(o=f->options;o;o=o->next)
605 if(o->selected && o->value){
606 fprint(fd, "%s%U=%U", sep, f->name, o->value);
611 if(f->name==0) break;
612 n=plelen(f->textwin);
613 rp=pleget(f->textwin);
614 p=b=malloc(UTFmax*n+1);
618 p += runetochar(p, rp);
623 fprint(fd, "%s%U=%U", sep, f->name, b);
630 void h_submitinput(Panel *p, int){
638 for(f=form->fields;f;f=f->next)
640 f->state = (f->p == p);
641 if(form->method==GET){
642 strcpy(buf, "/tmp/mfXXXXXXXXXXX");
643 fd = create(mktemp(buf), ORDWR|ORCLOSE, 0600);
645 fd = urlpost(selurl(form->action), form->ctype);
647 message("submit: %r");
650 if(form->method==GET){
651 fprint(fd, "%s?", form->action);
652 uencodeform(form, fd);
654 n = readn(fd, buf, sizeof(buf));
656 if(n < 0 || n >= sizeof(buf)){
657 message("submit: too large");
661 if(debug)fprint(2, "GET %s\n", buf);
662 geturl(buf, -1, 0, 0);
664 /* only set for multipart/form-data */
666 mencodeform(form, fd);
668 uencodeform(form, fd);
669 if(debug)fprint(2, "POST %s\n", form->action);
670 geturl(form->action, fd, 0, 0);
674 void freeform(void *p)
683 while(f = form->fields){
684 form->fields = f->next;
692 while(o = f->options){
693 f->options = o->next;
694 if(o->value != o->label+1)
706 char *s = va_arg(f->args, char*);
708 if(strchr("/$-_@.!*'(),", *s)
709 || 'a'<=*s && *s<='z'
710 || 'A'<=*s && *s<='Z'
711 || '0'<=*s && *s<='9')
712 fmtprint(f, "%c", *s);
716 fmtprint(f, "%%%.2X", *s & 0xFF);