2 * type=image is treated like submit
12 typedef struct Field Field;
13 typedef struct Option Option;
18 Field *fields, *efields;
27 int size; /* should be a point, but that feature is deprecated */
33 int state; /* is the button marked? */
62 #define BOUNDARY "hjdicksHjDiCkSHJDICKS"
64 void h_checkinput(Panel *, int, int);
65 void h_radioinput(Panel *, int, int);
66 void h_submitinput(Panel *, int);
67 void h_buttoninput(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=emallocz(sizeof(Field), 1);
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=emallocz(sizeof(Form), 1);
104 s=pl_getattr(g->attr, "action");
105 g->form->action=strdup((s && s[0]) ? 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;
130 htmlerror(g->name, g->lineno, "<%s> not in form, ignored\n",
135 s=pl_getattr(g->attr, "name");
140 s=pl_getattr(g->attr, "value");
145 f->checked=pl_hasattr(g->attr, "checked");
146 s=pl_getattr(g->attr, "size");
151 s=pl_getattr(g->attr, "maxlength");
153 f->maxlength=0x3fffffff;
155 f->maxlength=atoi(s);
156 s=pl_getattr(g->attr, "type");
157 if((g->tag == Tag_button) &&
158 (s==0 || cistrcmp(s, "reset") || cistrcmp(s, "button")))
162 if(cistrcmp(s, "checkbox")==0)
164 else if(cistrcmp(s, "radio")==0)
166 else if(cistrcmp(s, "submit")==0)
168 else if(cistrcmp(s, "button")==0)
170 else if(cistrcmp(s, "image")==0){
171 /* presotto's egregious hack to make image submits do something */
177 } else if(cistrcmp(s, "reset")==0)
179 else if(cistrcmp(s, "hidden")==0)
183 if(cistrcmp(s, "password")==0)
187 if(s && cistrcmp(s, "isindex")==0)
191 * If there's exactly one attribute, use its value as the name,
192 * regardless of the attribute name. This makes
193 * http://linus.att.com/ias/puborder.html work.
196 if(g->attr[0].name && g->attr[1].name==0)
197 f->name=strdup(g->attr[0].value);
199 f->name=strdup("no-name");
202 if((f->type==CHECK || f->type==RADIO) && !pl_hasattr(g->attr, "value")){
204 f->value=strdup("on");
207 pl_htmloutput(g, g->nsp, f->value[0]?f->value:"blank field", f);
210 if(g->form==0) goto BadTag;
212 s=pl_getattr(g->attr, "name");
214 f->name=strdup("select");
215 htmlerror(g->name, g->lineno, "select has no name=\n");
219 s=pl_getattr(g->attr, "size");
223 if(f->size<=0) f->size=1;
225 f->multiple=pl_hasattr(g->attr, "multiple");
233 if(g->form==0) goto BadTag;
234 if((f=g->form->efields)==0) goto BadTag;
235 o=emallocz(sizeof(Option), 1);
236 for(op=&f->options;*op;op=&(*op)->next);
241 g->etext=o->label+NLABEL;
242 memset(o->label, 0, NLABEL+1);
244 o->def=pl_hasattr(g->attr, "selected");
246 s=pl_getattr(g->attr, "value");
253 if(g->form==0) goto BadTag;
255 s=pl_getattr(g->attr, "name");
257 f->name=strdup("enter text");
258 htmlerror(g->name, g->lineno, "select has no name=\n");
262 s=pl_getattr(g->attr, "rows");
264 s=pl_getattr(g->attr, "cols");
265 f->cols=s?atoi(s):30;
267 /* suck up initial text */
268 pl_htmloutput(g, g->nsp, f->name, f);
272 * Make up a form with one tag, of type INDEX
273 * I have seen a page with <ISINDEX PROMPT="Enter a title here ">,
274 * which is nonstandard and not handled here.
276 form=emalloc(sizeof(Form));
279 s=pl_getattr(g->attr, "action");
280 form->action=strdup((s && s[0]) ? s : g->dst->url->fullname);
287 f->maxlength=0x3fffffff;
289 pl_htmloutput(g, g->nsp, f->value[0]?f->value:"blank field", f);
294 * Called by rdhtml on seeing a forms-related end tag
296 void endform(Hglob *g){
305 htmlerror(g->name, g->lineno, "</select> not in form, ignored\n");
306 else if((f=g->form->efields)==0)
307 htmlerror(g->name, g->lineno, "spurious </select>\n");
309 pl_htmloutput(g, g->nsp, f->name, f);
315 char *nullgen(Panel *, int ){
318 char *selgen(Panel *p, int index){
323 for(a=f->options;index!=0 && a!=0;--index,a=a->next);
325 a->label[0]=a->selected?'*':' ';
328 char *seloption(Field *f){
330 for(a=f->options;a!=0;a=a->next)
335 void mkfieldpanel(Rtext *t){
337 Panel *win, *scrl, *menu, *pop, *button;
340 if((a = t->user) == nil)
342 if((f = a->field) == nil)
348 f->p=plentry(0, 0, f->size*chrwidth, f->value, h_submittype);
351 f->p=plentry(0, USERFL, f->size*chrwidth, f->value, h_submittype);
354 f->p=plcheckbutton(0, 0, "", h_checkinput);
356 plsetbutton(f->p, f->checked);
359 f->p=plradiobutton(0, 0, "", h_radioinput);
361 plsetbutton(f->p, f->checked);
364 f->p=plbutton(0, 0, f->value[0]?f->value:"submit", h_submitinput);
367 f->p=plbutton(0, 0, f->value[0]?f->value:"reset", h_resetinput);
370 f->p=plbutton(0, 0, f->value[0]?f->value:"button", h_buttoninput);
373 f->pulldown=plgroup(0,0);
374 scrl=plscrollbar(f->pulldown, PACKW|FILLY);
375 win=pllist(f->pulldown, PACKN, nullgen, f->size, h_select);
377 plinitlist(win, PACKN, selgen, f->size, h_select);
378 plscroll(win, 0, scrl);
379 plpack(f->pulldown, Rect(0,0,1024,1024));
380 f->p=plpulldown(0, FIXEDX, seloption(f), f->pulldown, PACKS);
381 f->p->fixedsize.x=f->pulldown->r.max.x-f->pulldown->r.min.x;
385 pllabel(f->p, PACKN|FILLX, f->name);
386 scrl=plscrollbar(f->p, PACKW|FILLY);
387 f->textwin=pledit(f->p, EXPAND, Pt(f->cols*chrwidth, f->rows*font->height),
390 plscroll(f->textwin, 0, scrl);
393 f->p=plentry(0, 0, f->size*chrwidth, f->value, h_submitindex);
404 void h_checkinput(Panel *p, int, int v){
405 ((Field *)p->userp)->state=v;
407 void h_radioinput(Panel *p, int, int v){
412 for(f=me->form->fields;f;f=f->next)
413 if(f->type==RADIO && f!=me && strcmp(f->name, me->name)==0){
414 plsetbutton(f->p, 0);
416 pldraw(f->p, screen);
420 void h_select(Panel *p, int, int index){
425 if(!f->multiple) for(a=f->options;a;a=a->next) a->selected=0;
426 for(a=f->options;index!=0 && a!=0;--index,a=a->next);
428 a->selected=!a->selected;
429 plinitpulldown(f->p, FIXEDX, seloption(f), f->pulldown, PACKS);
430 pldraw(f->p, screen);
432 void h_resetinput(Panel *p, int){
435 for(f=((Field *)p->userp)->form->fields;f;f=f->next) switch(f->type){
437 plinitentry(f->p, 0, f->size*chrwidth, f->value, 0);
440 plinitentry(f->p, USERFL, f->size*chrwidth, f->value, 0);
445 plsetbutton(f->p, f->checked);
448 for(o=f->options;o;o=o->next)
452 pldraw(text, screen);
454 void h_buttoninput(Panel *p, int){
458 * If there's exactly one button with type=text, then
459 * a CR in the button is supposed to submit the form.
461 void h_submittype(Panel *p, char *){
465 for(f=((Field *)p->userp)->form->fields;f;f=f->next)
466 if(f->type==TYPEIN || f->type==PASSWD)
468 if(ntype==1) h_submitinput(p, 0);
470 void h_submitindex(Panel *p, char *){
474 void mencodeform(Form *form, int fd){
481 #define SEPS "-----------------------------" BOUNDARY
482 #define NEXT "\r\n" SEPS
485 for(f=form->fields;f;f=f->next){
489 fprint(fd, "%s\r\nContent-Disposition: form-data; name=\"%s\"\r\n\r\n%s",
490 sep, f->name, plentryval(f->p));
497 fprint(fd, "%s\r\nContent-Disposition: form-data; name=\"%s\"\r\n\r\n%s",
498 sep, f->name, f->value);
504 for(o=f->options;o;o=o->next)
505 if(o->selected && o->value){
506 fprint(fd, "%s\r\nContent-Disposition: form-data; name=\"%s\"\r\n\r\n%s",
507 sep, f->name, o->value);
514 n=plelen(f->textwin);
515 rp=pleget(f->textwin);
516 p=b=malloc(UTFmax*n+1);
520 p += runetochar(p, rp);
525 fprint(fd, "%s\r\nContent-Disposition: form-data; name=\"%s\"\r\n\r\n%s",
532 fprint(fd, "%s--\r\n", sep);
535 void uencodeform(Form *form, int fd){
543 for(f=form->fields;f;f=f->next) switch(f->type){
546 fprint(fd, "%s%U=%U", sep, f->name, plentryval(f->p));
550 fprint(fd, "%s%U", sep, plentryval(f->p));
557 if(f->name==0 || f->value==0)
559 fprint(fd, "%s%U=%U", sep, f->name, f->value);
565 for(o=f->options;o;o=o->next)
566 if(o->selected && o->value){
567 fprint(fd, "%s%U=%U", sep, f->name, o->value);
574 n=plelen(f->textwin);
575 rp=pleget(f->textwin);
576 p=b=malloc(UTFmax*n+1);
580 p += runetochar(p, rp);
585 fprint(fd, "%s%U=%U", sep, f->name, b);
592 void h_submitinput(Panel *p, int){
597 form=((Field *)p->userp)->form;
598 if(form->method==GET){
599 strcpy(buf, "/tmp/mfXXXXXXXXXXX");
600 fd = create(mktemp(buf), ORDWR|ORCLOSE, 0600);
602 fd = urlpost(selurl(form->action), form->ctype);
604 message("submit: %r");
607 if(form->method==GET){
608 fprint(fd, "%s?", form->action);
609 uencodeform(form, fd);
611 n = readn(fd, buf, sizeof(buf));
613 if(n < 0 || n >= sizeof(buf)){
614 message("submit: too large");
618 if(debug)fprint(2, "GET %s\n", buf);
619 geturl(buf, -1, 0, 0);
621 /* only set for multipart/form-data */
623 mencodeform(form, fd);
625 uencodeform(form, fd);
626 if(debug)fprint(2, "POST %s\n", form->action);
627 geturl(form->action, fd, 0, 0);
631 void freeform(void *p)
640 while(f = form->fields){
641 form->fields = f->next;
649 while(o = f->options){
650 f->options = o->next;
651 if(o->value != o->label+1)
663 char *s = va_arg(f->args, char*);
665 if(strchr("/$-_@.!*'(),", *s)
666 || 'a'<=*s && *s<='z'
667 || 'A'<=*s && *s<='Z'
668 || '0'<=*s && *s<='9')
669 fmtprint(f, "%c", *s);
673 fmtprint(f, "%%%.2X", (unsigned int)*s);