2 * type=image is treated like submit
11 typedef struct Field Field;
12 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? */
59 void h_checkinput(Panel *, int, int);
60 void h_radioinput(Panel *, int, int);
61 void h_submitinput(Panel *, int);
62 void h_buttoninput(Panel *, int);
63 void h_submittype(Panel *, char *);
64 void h_submitindex(Panel *, char *);
65 void h_resetinput(Panel *, int);
66 void h_select(Panel *, int, int);
67 void h_cut(Panel *, int);
68 void h_paste(Panel *, int);
69 void h_snarf(Panel *, int);
71 char *selgen(Panel *, int);
72 char *nullgen(Panel *, int);
73 Field *newfield(Form *form){
75 f=emallocz(sizeof(Field), 1);
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=emallocz(sizeof(Form), 1);
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);
118 g->form->next = g->dst->form;
119 g->dst->form = g->form;
126 htmlerror(g->name, g->lineno, "<%s> not in form, ignored\n",
131 s=pl_getattr(g->attr, "name");
136 s=pl_getattr(g->attr, "value");
141 f->checked=pl_hasattr(g->attr, "checked");
142 s=pl_getattr(g->attr, "size");
147 s=pl_getattr(g->attr, "maxlength");
149 f->maxlength=0x3fffffff;
151 f->maxlength=atoi(s);
152 s=pl_getattr(g->attr, "type");
153 if(g->tag == Tag_button && (s==0 || cistrcmp(s, "reset") || cistrcmp(s, "button")))
155 if(s==0 || cistrcmp(s, "text")==0 ||
156 cistrcmp(s, "password")==0 || cistrcmp(s, "int")==0 ||
157 cistrcmp(s, "email")==0){
158 s=pl_getattr(g->attr, "name");
159 if(s!=0 && strcmp(s, "isindex")==0)
164 * If there's exactly one attribute, use its value as the name,
165 * regardless of the attribute name. This makes
166 * http://linus.att.com/ias/puborder.html work.
169 if(g->attr[0].name && g->attr[1].name==0)
170 f->name=strdup(g->attr[0].value);
172 f->name=strdup("no-name");
175 else if(cistrcmp(s, "checkbox")==0)
177 else if(cistrcmp(s, "radio")==0)
179 else if(cistrcmp(s, "submit")==0)
181 else if(cistrcmp(s, "button")==0)
183 else if(cistrcmp(s, "image")==0){
184 /* presotto's egregious hack to make image submits do something */
190 } else if(cistrcmp(s, "reset")==0)
192 else if(cistrcmp(s, "hidden")==0)
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 s=pl_getattr(g->attr, "size");
217 if(f->size<=0) f->size=1;
219 f->multiple=pl_hasattr(g->attr, "multiple");
227 if(g->form==0) goto BadTag;
228 if((f=g->form->efields)==0) goto BadTag;
229 o=emallocz(sizeof(Option), 1);
230 for(op=&f->options;*op;op=&(*op)->next);
235 g->etext=o->label+NLABEL;
236 memset(o->label, 0, NLABEL+1);
238 o->def=pl_hasattr(g->attr, "selected");
240 s=pl_getattr(g->attr, "value");
247 if(g->form==0) goto BadTag;
249 s=pl_getattr(g->attr, "name");
251 f->name=strdup("enter text");
252 htmlerror(g->name, g->lineno, "select has no name=\n");
256 s=pl_getattr(g->attr, "rows");
258 s=pl_getattr(g->attr, "cols");
259 f->cols=s?atoi(s):30;
261 /* suck up initial text */
262 pl_htmloutput(g, g->nsp, f->name, f);
266 * Make up a form with one tag, of type INDEX
267 * I have seen a page with <ISINDEX PROMPT="Enter a title here ">,
268 * which is nonstandard and not handled here.
270 form=emalloc(sizeof(Form));
273 s=pl_getattr(g->attr, "action");
274 form->action=strdup((s && s[0]) ? s : g->dst->url->fullname);
281 f->maxlength=0x3fffffff;
283 pl_htmloutput(g, g->nsp, f->value[0]?f->value:"blank field", f);
288 * Called by rdhtml on seeing a forms-related end tag
290 void endform(Hglob *g){
299 htmlerror(g->name, g->lineno, "</select> not in form, ignored\n");
300 else if((f=g->form->efields)==0)
301 htmlerror(g->name, g->lineno, "spurious </select>\n");
303 pl_htmloutput(g, g->nsp, f->name, f);
309 char *nullgen(Panel *, int ){
312 char *selgen(Panel *p, int index){
317 for(a=f->options;index!=0 && a!=0;--index,a=a->next);
319 a->label[0]=a->selected?'*':' ';
322 char *seloption(Field *f){
324 for(a=f->options;a!=0;a=a->next)
329 void mkfieldpanel(Rtext *t){
331 Panel *win, *scrl, *menu, *pop, *button;
334 if((a = t->user) == nil)
336 if((f = a->field) == nil)
342 f->p=plentry(0, 0, f->size*chrwidth, f->value, h_submittype);
345 f->p=plcheckbutton(0, 0, "", h_checkinput);
347 plsetbutton(f->p, f->checked);
350 f->p=plradiobutton(0, 0, "", h_radioinput);
352 plsetbutton(f->p, f->checked);
355 f->p=plbutton(0, 0, f->value[0]?f->value:"submit", h_submitinput);
358 f->p=plbutton(0, 0, f->value[0]?f->value:"reset", h_resetinput);
361 f->p=plbutton(0, 0, f->value[0]?f->value:"button", h_buttoninput);
364 f->pulldown=plgroup(0,0);
365 scrl=plscrollbar(f->pulldown, PACKW|FILLY);
366 win=pllist(f->pulldown, PACKN, nullgen, f->size, h_select);
368 plinitlist(win, PACKN, selgen, f->size, h_select);
369 plscroll(win, 0, scrl);
370 plpack(f->pulldown, Rect(0,0,1024,1024));
371 f->p=plpulldown(0, FIXEDX, seloption(f), f->pulldown, PACKS);
372 f->p->fixedsize.x=f->pulldown->r.max.x-f->pulldown->r.min.x;
377 pllabel(f->p, PACKN|FILLX, f->name);
378 scrl=plscrollbar(f->p, PACKW|FILLY);
379 pop=plpopup(f->p, PACKN|FILLX, 0, menu, 0);
380 f->textwin=pledit(pop, EXPAND, Pt(f->cols*chrwidth, f->rows*font->height),
383 button=plbutton(menu, PACKN|FILLX, "cut", h_cut);
384 button->userp=f->textwin;
385 button=plbutton(menu, PACKN|FILLX, "paste", h_paste);
386 button->userp=f->textwin;
387 button=plbutton(menu, PACKN|FILLX, "snarf", h_snarf);
388 button->userp=f->textwin;
389 plscroll(f->textwin, 0, scrl);
392 f->p=plentry(0, 0, f->size*chrwidth, f->value, h_submitindex);
403 void h_checkinput(Panel *p, int, int v){
404 ((Field *)p->userp)->state=v;
406 void h_radioinput(Panel *p, int, int v){
411 for(f=me->form->fields;f;f=f->next)
412 if(f->type==RADIO && f!=me && strcmp(f->name, me->name)==0){
413 plsetbutton(f->p, 0);
415 pldraw(f->p, screen);
419 void h_select(Panel *p, int, int index){
424 if(!f->multiple) for(a=f->options;a;a=a->next) a->selected=0;
425 for(a=f->options;index!=0 && a!=0;--index,a=a->next);
427 a->selected=!a->selected;
428 plinitpulldown(f->p, FIXEDX, seloption(f), f->pulldown, PACKS);
429 pldraw(f->p, screen);
431 void h_resetinput(Panel *p, int){
434 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);
442 plsetbutton(f->p, f->checked);
445 for(o=f->options;o;o=o->next)
449 pldraw(text, screen);
451 void h_buttoninput(Panel *p, int){
453 void h_edit(Panel *p){
458 void h_snarf(Panel *p, int){
462 plegetsel(p, &s0, &s1);
465 if(snarfbuf) free(snarfbuf);
467 snarfbuf=malloc(nsnarfbuf*sizeof(Rune));
469 fprint(2, "No mem\n");
472 memmove(snarfbuf, text+s0, nsnarfbuf*sizeof(Rune));
474 void h_cut(Panel *p, int b){
476 plepaste(p->userp, 0, 0);
478 void h_paste(Panel *p, int){
479 plepaste(p->userp, snarfbuf, nsnarfbuf);
485 if(strchr("/$-_@.!*'(), ", *s)
486 || 'a'<=*s && *s<='z'
487 || 'A'<=*s && *s<='Z'
488 || '0'<=*s && *s<='9')
496 return 0<=v && v<=9?'0'+v:'A'+v-10;
498 char *ucpy(char *buf, char *s){
500 if(strchr("/$-_@.!*'(),", *s)
501 || 'a'<=*s && *s<='z'
502 || 'A'<=*s && *s<='Z'
503 || '0'<=*s && *s<='9')
509 *buf++=hexdigit((*s>>4)&15);
510 *buf++=hexdigit(*s&15);
516 char *runetou(char *buf, Rune r){
526 * If there's exactly one button with type=text, then
527 * a CR in the button is supposed to submit the form.
529 void h_submittype(Panel *p, char *){
533 for(f=((Field *)p->userp)->form->fields;f;f=f->next) if(f->type==TYPEIN) ntype++;
534 if(ntype==1) h_submitinput(p, 0);
536 void h_submitindex(Panel *p, char *){
539 void h_submitinput(Panel *p, int){
542 char *buf, *bufp, sep;
546 form=((Field *)p->userp)->form;
547 if(form->method==GET) size=ulen(form->action)+1;
549 for(f=form->fields;f;f=f->next) switch(f->type){
552 size+=ulen(f->name)+1+ulen(plentryval(f->p))+1;
555 size+=ulen(plentryval(f->p))+1;
561 size+=ulen(f->name)+1+ulen(f->value)+1;
564 for(o=f->options;o;o=o->next)
566 size+=ulen(f->name)+1+ulen(o->value)+1;
569 size+=ulen(f->name)+1+plelen(f->textwin)*3+1;
573 if(form->method==GET){
574 strcpy(buf, form->action);
581 bufp=buf+strlen(buf);
582 if(form->method==GET && bufp!=buf && bufp[-1]=='?') *--bufp='\0'; /* spurious ? */
583 for(f=form->fields;f;f=f->next) switch(f->type){
588 bufp=ucpy(bufp, f->name);
590 bufp=ucpy(bufp, plentryval(f->p));
595 bufp=ucpy(bufp, plentryval(f->p));
603 bufp=ucpy(bufp, f->name);
605 bufp=ucpy(bufp, f->value);
608 for(o=f->options;o;o=o->next)
612 bufp=ucpy(bufp, f->name);
614 bufp=ucpy(bufp, o->value);
620 bufp=ucpy(bufp, f->name);
622 rp=pleget(f->textwin);
623 for(nrune=plelen(f->textwin);nrune!=0;--nrune)
624 bufp=runetou(bufp, *rp++);
628 if(form->method==GET){
629 if(debug)fprint(2, "GET %s\n", buf);
630 geturl(buf, GET, 0, 0, 0);
633 if(debug)fprint(2, "POST %s: %s\n", form->action, buf);
634 geturl(form->action, POST, buf, 0, 0);
639 void freeform(void *p)
648 while(f = form->fields){
649 form->fields = f->next;
657 while(o = f->options){
658 f->options = o->next;
659 if(o->value != o->label+1)