henesyinit from p9fce6d0435 years agoHistory
496 lines · 10.4 KB · C
RawDownload
/* * Doesn't do type=image, isindex */#include <u.h>#include <libc.h>#include <libg.h>#include <panel.h>#include "mothra.h"#include "html.h"typedef struct Field Field;typedef struct Option Option;struct Form{	int method;	Url *action;	Field *fields;};struct Field{	Field *next;	Form *form;	char *name;	char *value;	int checked;	int size;		/* should be a point, but that feature is deprecated */	int maxlength;	int type;	int rows, cols;	Option *options;	int multiple;	int state;		/* is the button marked? */	Panel *p;	Panel *textwin;};/* * Field types */#define	TYPE	1#define	CHECK	2#define	PASSWD	3#define	RADIO	4#define	SUBMIT	5#define	RESET	6#define	SELECT	7#define	TEXTWIN	8#define	HIDDEN	9#define	INDEX	10#define	NLABEL	50		/* Maximum length of option name */struct Option{	int selected;	int def;	char label[NLABEL+1];	Option *next;};void h_checkinput(Panel *, int, int);void h_radioinput(Panel *, int, int);void h_submitinput(Panel *, int);void h_resetinput(Panel *, int);void h_select(Panel *, int, int);void h_cut(Panel *, int);void h_paste(Panel *, int);void h_snarf(Panel *, int);void h_edit(Panel *);char *selgen(Panel *, int);/* * Called by rdhtml on seeing a forms-related tag */void rdform(Hglob *g){	char *s;	Field *f;	Option *o;	switch(g->tag){	default:		fprint(2, "Bad tag <%s> in rdform (Can't happen!)\n", g->token);		return;	case Tag_form:		if(g->form){			htmlerror(g->name, g->lineno, "nested forms illegal\n");			break;		}		g->form=emalloc(sizeof(Form));		g->form->fields=0;		s=pl_getattr(g->attr, "action");		if(s==0)			htmlerror(g->name, g->lineno, "form has no action!\n");		else{			g->form->action=emalloc(sizeof(Url));			crackurl(g->form->action, s, g->dst->url);		}		s=pl_getattr(g->attr, "method");		if(s==0)			g->form->method=GET;		else if(cistrcmp(s, "post")==0)			g->form->method=POST;		else{			if(cistrcmp(s, "get")!=0)				htmlerror(g->name, g->lineno,					"unknown form method %s\n", s);			g->form->method=GET;		}		g->form->fields=0;		break;	case Tag_input:		f=emalloc(sizeof(Field));		f->next=g->form->fields;		g->form->fields=f;		f->form=g->form;		s=pl_getattr(g->attr, "name");		if(s==0)			f->name=0;		else			f->name=strdup(s);		s=pl_getattr(g->attr, "value");		if(s==0)			f->value=strdup("");		else			f->value=strdup(s);		f->checked=pl_hasattr(g->attr, "checked");		s=pl_getattr(g->attr, "size");		if(s==0)			f->size=20;		else			f->size=atoi(s);		s=pl_getattr(g->attr, "maxlength");		if(s==0)			f->maxlength=0x3fffffff;		else			f->maxlength=atoi(s);		s=pl_getattr(g->attr, "type");		/* bug -- password treated as text */		if(s==0 || cistrcmp(s, "text")==0 || cistrcmp(s, "password")==0)			f->type=TYPE;		else if(cistrcmp(s, "checkbox")==0)			f->type=CHECK;		else if(cistrcmp(s, "radio")==0)			f->type=RADIO;		else if(cistrcmp(s, "submit")==0)			f->type=SUBMIT;		else if(cistrcmp(s, "reset")==0)			f->type=RESET;		else if(cistrcmp(s, "hidden")==0)			f->type=HIDDEN;		else{			fprint(2, "bad field type %s, ignored\n", s);			break;		}		if(f->type!=HIDDEN)			pl_htmloutput(g, g->nsp, f->value[0]?f->value:"blank field", f);		break;	case Tag_select:		f=emalloc(sizeof(Field));		f->next=g->form->fields;		g->form->fields=f;		f->form=g->form;		s=pl_getattr(g->attr, "name");		if(s==0){			f->name=strdup("select");			htmlerror(g->name, g->lineno, "select has no name=\n");		}		else			f->name=strdup(s);		s=pl_getattr(g->attr, "size");		if(s==0) f->size=4;		else{			f->size=atoi(s);			if(f->size<=0) f->size=1;		}		f->multiple=pl_hasattr(g->attr, "multiple");		f->type=SELECT;		f->options=0;		g->text=g->token;		g->tp=g->text;		g->etext=g->text;		break;	case Tag_option:		f=g->form->fields;		o=emalloc(sizeof(Option));		o->next=f->options;		f->options=o;		g->text=o->label;		g->tp=o->label;		g->etext=o->label+NLABEL;		memset(o->label, 0, NLABEL+1);		*g->tp++=' ';		o->def=pl_hasattr(g->attr, "selected");		o->selected=o->def;		break;	case Tag_textarea:		f=emalloc(sizeof(Field));		f->next=g->form->fields;		g->form->fields=f;		f->form=g->form;		s=pl_getattr(g->attr, "name");		if(s==0){			f->name=strdup("enter text");			htmlerror(g->name, g->lineno, "select has no name=\n");		}		else			f->name=strdup(s);		s=pl_getattr(g->attr, "rows");		f->rows=s?atoi(s):8;		s=pl_getattr(g->attr, "cols");		f->cols=s?atoi(s):30;		f->type=TEXTWIN;		/* suck up initial text */		pl_htmloutput(g, g->nsp, f->name, f);		break;	case Tag_isindex:		break;	}}/* * Called by rdhtml on seeing a forms-related end tag */void endform(Hglob *g){	switch(g->tag){	case Tag_form:		g->form=0;		break;	case Tag_select:		pl_htmloutput(g, g->nsp, g->form->fields->name, g->form->fields);		break;	case Tag_textarea:		break;	}}char *selgen(Panel *p, int index){	Option *a;	Field *f;	f=p->userp;	if(f==0) return 0;	for(a=f->options;index!=0 && a!=0;--index,a=a->next);	if(a==0) return 0;	a->label[0]=a->selected?'*':' ';	return a->label;}void mkfieldpanel(Rtext *t){	Panel *win, *scrl, *menu, *pop, *button;	Field *f;	f=((Action *)t->user)->field;	f->p=0;	switch(f->type){	case TYPE:		f->p=plentry(0, 0, f->size*chrwidth, f->value, 0);		break;	case CHECK:		f->p=plcheckbutton(0, 0, f->value, h_checkinput);		f->state=f->checked;		plsetbutton(f->p, f->checked);		break;	case RADIO:		f->p=plradiobutton(0, 0, f->value, h_radioinput);		f->state=f->checked;		plsetbutton(f->p, f->checked);		break;	case SUBMIT:		f->p=plbutton(0, 0, f->value[0]?f->value:"submit", h_submitinput);		break;	case RESET:		f->p=plbutton(0, 0, f->value[0]?f->value:"reset", h_resetinput);		break;	case SELECT:		f->p=plgroup(0,0);		pllabel(f->p, PACKN|FILLX, f->name);		scrl=plscrollbar(f->p, PACKW|FILLY);		win=pllist(f->p, PACKN|FILLX, selgen, f->size, h_select);		win->userp=f;		plinitlist(win, PACKN|FILLX, selgen, f->size, h_select);		plscroll(win, 0, scrl);		break;	case TEXTWIN:		menu=plgroup(0,0);		f->p=plframe(0,0);		pllabel(f->p, PACKN|FILLX, f->name);		scrl=plscrollbar(f->p, PACKW|FILLY);		pop=plpopup(f->p, PACKN|FILLX, 0, menu, 0);		f->textwin=pledit(pop, EXPAND, Pt(f->cols*chrwidth, f->rows*font->height),			0, 0, h_edit);		f->textwin->userp=f;		button=plbutton(menu, PACKN|FILLX, "cut", h_cut);		button->userp=f->textwin;		button=plbutton(menu, PACKN|FILLX, "paste", h_paste);		button->userp=f->textwin;		button=plbutton(menu, PACKN|FILLX, "snarf", h_snarf);		button->userp=f->textwin;		plscroll(f->textwin, 0, scrl);		break;	}	if(f->p){		f->p->userp=f;		free(t->text);		t->text=0;		t->p=f->p;		t->hot=1;	}}void h_checkinput(Panel *p, int, int v){	((Field *)p->userp)->state=v;}void h_radioinput(Panel *p, int, int v){	Field *f, *me;	me=p->userp;	me->state=v;	if(v){		for(f=me->form->fields;f;f=f->next)			if(f->type==RADIO && f!=me && strcmp(f->name, me->name)==0){				plsetbutton(f->p, 0);				f->state=0;				pldraw(f->p, &screen);			}	}}void h_select(Panel *p, int, int index){	Option *a;	Field *f;	f=p->userp;	if(f==0) return;	if(!f->multiple) for(a=f->options;a;a=a->next) a->selected=0;	for(a=f->options;index!=0 && a!=0;--index,a=a->next);	if(a==0) return;	a->selected=!a->selected;	pldraw(p, &screen);}void h_resetinput(Panel *p, int){	Field *f;	Option *o;	for(f=((Field *)p->userp)->form->fields;f;f=f->next) switch(f->type){	case TYPE:	case PASSWD:		plinitentry(f->p, 0, f->size*chrwidth, f->value, 0);		break;	case CHECK:	case RADIO:		f->state=f->checked;		plsetbutton(f->p, f->checked);		break;	case SELECT:		for(o=f->options;o;o=o->next)			o->selected=o->def;		break;	}	pldraw(text, &screen);}void h_edit(Panel *p){	plgrabkb(p);}Rune *snarfbuf=0;int nsnarfbuf=0;void h_snarf(Panel *p, int){	int s0, s1;	Rune *text;	p=p->userp;	plegetsel(p, &s0, &s1);	if(s0==s1) return;	text=pleget(p);	if(snarfbuf) free(snarfbuf);	nsnarfbuf=s1-s0;	snarfbuf=malloc(nsnarfbuf*sizeof(Rune));	if(snarfbuf==0){		fprint(2, "No mem\n");		exits("no mem");	}	memmove(snarfbuf, text+s0, nsnarfbuf*sizeof(Rune));}void h_cut(Panel *p, int b){	h_snarf(p, b);	plepaste(p->userp, 0, 0);}void h_paste(Panel *p, int){	plepaste(p->userp, snarfbuf, nsnarfbuf);}int ulen(char *s){	int len;	len=0;	for(;*s;s++){		if(strchr("/$-_@.&!*'(),", *s)		|| 'a'<=*s && *s<='z'		|| 'A'<=*s && *s<='Z'		|| '0'<=*s && *s<='9')			len++;		else			len+=3;	}	return len;}int hexdigit(int v){	return 0<=v && v<=9?'0'+v:'A'+v-10;}char *ucpy(char *buf, char *s){	for(;*s;s++){		if(strchr("/$-_@.&!*'(),", *s)		|| 'a'<=*s && *s<='z'		|| 'A'<=*s && *s<='Z'		|| '0'<=*s && *s<='9')			*buf++=*s;		else{			*buf++='%';			*buf++=hexdigit((*s>>4)&15);			*buf++=hexdigit(*s&15);		}	}	*buf='\0';	return buf;}char *runetou(char *buf, Rune r){	char rbuf[2];	if(r<=255){		rbuf[0]=r;		rbuf[1]='\0';		buf=ucpy(buf, rbuf);	}	return buf;}void h_submitinput(Panel *p, int){	Form *form;	int size, nrune;	char *buf, *bufp, sep;	Rune *rp;	Field *f;	Option *o;	form=((Field *)p->userp)->form;	if(form->method==GET) size=ulen(form->action->reltext)+1;	else size=1;	for(f=form->fields;f;f=f->next) switch(f->type){	case TYPE:	case PASSWD:		size+=ulen(f->name)+1+ulen(plentryval(f->p))+1;		break;	case CHECK:	case RADIO:	case HIDDEN:		if(f->state) size+=ulen(f->name)+1+ulen(f->value)+1;		break;	case SELECT:		for(o=f->options;o;o=o->next)			if(o->selected)				size+=ulen(f->name)+1+ulen(o->label+1)+1;		break;	case TEXTWIN:		size+=ulen(f->name)+1+plelen(f->textwin)*3+1;		break;	}	buf=emalloc(size);	if(form->method==GET){		strcpy(buf, form->action->reltext);		sep='?';	}	else{		buf[0]='\0';		sep=0;	}	bufp=buf+strlen(buf);	for(f=form->fields;f;f=f->next) switch(f->type){	case TYPE:	case PASSWD:		if(sep) *bufp++=sep;		sep='&';		bufp=ucpy(bufp, f->name);		*bufp++='=';		bufp=ucpy(bufp, plentryval(f->p));		break;	case CHECK:	case RADIO:		if(!f->state) break;	case HIDDEN:		if(sep) *bufp++=sep;		sep='&';		bufp=ucpy(bufp, f->name);		*bufp++='=';		bufp=ucpy(bufp, f->value);		break;	case SELECT:		for(o=f->options;o;o=o->next)			if(o->selected){				if(sep) *bufp++=sep;				sep='&';				bufp=ucpy(bufp, f->name);				*bufp++='=';				bufp=ucpy(bufp, o->label+1);			}		break;	case TEXTWIN:		if(sep) *bufp++=sep;		sep='&';		bufp=ucpy(bufp, f->name);		*bufp++='=';		rp=pleget(f->textwin);		for(nrune=plelen(f->textwin);nrune!=0;--nrune)			bufp=runetou(bufp, *rp++);		*bufp='\0';		break;	}	if(form->method==GET)		geturl(buf, GET, 0, 0);	else		geturl(form->action->fullname, POST, buf, 0);	free(buf);}