xref: /inferno-os/appl/charon/chutils.b (revision d0c0f54bdb5840c1f71d49561438fa5d3bd10ff2)
137da2899SCharles.Forsythimplement CharonUtils;
237da2899SCharles.Forsyth
337da2899SCharles.Forsythinclude "common.m";
437da2899SCharles.Forsythinclude "transport.m";
537da2899SCharles.Forsythinclude "date.m";
637da2899SCharles.Forsythinclude "translate.m";
737da2899SCharles.Forsyth
837da2899SCharles.Forsyth	date: Date;
937da2899SCharles.Forsyth	me: CharonUtils;
1037da2899SCharles.Forsyth	sys: Sys;
1137da2899SCharles.Forsyth	D: Draw;
1237da2899SCharles.Forsyth	S: String;
1337da2899SCharles.Forsyth	U: Url;
1437da2899SCharles.Forsyth	T: StringIntTab;
1537da2899SCharles.Forsyth
1637da2899SCharles.ForsythFont : import D;
1737da2899SCharles.ForsythParsedurl: import U;
1837da2899SCharles.Forsythconvcs : Convcs;
1937da2899SCharles.Forsythtrans : Translate;
2037da2899SCharles.Forsyth	Dict : import trans;
2137da2899SCharles.Forsythdict : ref Dict;
2237da2899SCharles.Forsyth
2337da2899SCharles.ForsythNCTimeout : con 100000;		# free NC slot after 100 seconds
2437da2899SCharles.ForsythUBufsize : con 40*1024;		# initial buffer size for unknown lengths
2537da2899SCharles.ForsythUEBufsize : con 1024;		# initial buffer size for unknown lengths, error responses
2637da2899SCharles.Forsyth
2737da2899SCharles.Forsythbotchexception := "EXInternal: ByteSource protocol botch";
2837da2899SCharles.Forsythbytesourceid := 0;
2937da2899SCharles.Forsythcrlf : con "\r\n";
3037da2899SCharles.Forsythctype : array of byte;	# local ref to C->ctype[]
3137da2899SCharles.Forsythdbgproto : int;
3237da2899SCharles.Forsythdbg: int;
3337da2899SCharles.Forsythnetconnid := 0;
3437da2899SCharles.Forsythnetconns := array[10] of ref Netconn;
3537da2899SCharles.Forsythsptab : con " \t";
3637da2899SCharles.Forsyth
3737da2899SCharles.ForsythTHTTP, TFTP, TFILE, TMAX: con iota;
3837da2899SCharles.Forsythtransports := array[TMAX] of Transport;
3937da2899SCharles.Forsythtpaths := array [TMAX] of {
4037da2899SCharles.Forsyth	THTTP =>	Transport->HTTPPATH,
4137da2899SCharles.Forsyth	TFTP =>	Transport->FTPPATH,
4237da2899SCharles.Forsyth	TFILE =>	Transport->FILEPATH,
4337da2899SCharles.Forsyth};
4437da2899SCharles.Forsyth
4537da2899SCharles.Forsythschemes := array [] of {
4637da2899SCharles.Forsyth	("http", 	THTTP),
4737da2899SCharles.Forsyth	("https",	THTTP),
4837da2899SCharles.Forsyth	("ftp",	TFTP),
4937da2899SCharles.Forsyth	("file",	TFILE),
5037da2899SCharles.Forsyth};
5137da2899SCharles.Forsyth
5237da2899SCharles.Forsythngchan : chan of (int, list of ref ByteSource, ref Netconn, chan of ref ByteSource);
5337da2899SCharles.Forsyth
5437da2899SCharles.Forsyth# must track HTTP methods in chutils.m
5537da2899SCharles.Forsyth# (upper-case, since that's required in HTTP requests)
5637da2899SCharles.Forsythhmeth = array[] of { "GET", "POST" };
5737da2899SCharles.Forsyth
5837da2899SCharles.Forsyth# following array must track media type def in chutils.m
5937da2899SCharles.Forsyth# keep in alphabetical order
6037da2899SCharles.Forsythmnames = array[] of {
6137da2899SCharles.Forsyth	"application/msword",
6237da2899SCharles.Forsyth	"application/octet-stream",
6337da2899SCharles.Forsyth	"application/pdf",
6437da2899SCharles.Forsyth	"application/postscript",
6537da2899SCharles.Forsyth	"application/rtf",
6637da2899SCharles.Forsyth	"application/vnd.framemaker",
6737da2899SCharles.Forsyth	"application/vnd.ms-excel",
6837da2899SCharles.Forsyth	"application/vnd.ms-powerpoint",
6937da2899SCharles.Forsyth	"application/x-unknown",
7037da2899SCharles.Forsyth	"audio/32kadpcm",
7137da2899SCharles.Forsyth	"audio/basic",
7237da2899SCharles.Forsyth	"image/cgm",
7337da2899SCharles.Forsyth	"image/g3fax",
7437da2899SCharles.Forsyth	"image/gif",
7537da2899SCharles.Forsyth	"image/ief",
7637da2899SCharles.Forsyth	"image/jpeg",
7737da2899SCharles.Forsyth	"image/png",
7837da2899SCharles.Forsyth	"image/tiff",
7937da2899SCharles.Forsyth	"image/x-bit",
8037da2899SCharles.Forsyth	"image/x-bit2",
8137da2899SCharles.Forsyth	"image/x-bitmulti",
8237da2899SCharles.Forsyth	"image/x-inferno-bit",
8337da2899SCharles.Forsyth	"image/x-xbitmap",
8437da2899SCharles.Forsyth	"model/vrml",
8537da2899SCharles.Forsyth	"multipart/digest",
8637da2899SCharles.Forsyth	"multipart/mixed",
8737da2899SCharles.Forsyth	"text/css",
8837da2899SCharles.Forsyth	"text/enriched",
8937da2899SCharles.Forsyth	"text/html",
9037da2899SCharles.Forsyth	"text/javascript",
9137da2899SCharles.Forsyth	"text/plain",
9237da2899SCharles.Forsyth	"text/richtext",
9337da2899SCharles.Forsyth	"text/sgml",
9437da2899SCharles.Forsyth	"text/tab-separated-values",
9537da2899SCharles.Forsyth	"text/xml",
9637da2899SCharles.Forsyth	"video/mpeg",
9737da2899SCharles.Forsyth	"video/quicktime"
9837da2899SCharles.Forsyth};
9937da2899SCharles.Forsyth
10037da2899SCharles.Forsythncstatenames = array[] of {
10137da2899SCharles.Forsyth	"free", "idle", "connect", "gethdr", "getdata",
10237da2899SCharles.Forsyth	"done", "err"
10337da2899SCharles.Forsyth};
10437da2899SCharles.Forsyth
10537da2899SCharles.Forsythhsnames = array[] of {
10637da2899SCharles.Forsyth	"none", "information", "ok", "redirect", "request error", "server error"
10737da2899SCharles.Forsyth};
10837da2899SCharles.Forsyth
10937da2899SCharles.Forsythhcphrase(code: int) : string
11037da2899SCharles.Forsyth{
11137da2899SCharles.Forsyth	ans : string;
11237da2899SCharles.Forsyth	case code {
11337da2899SCharles.Forsyth	HCContinue =>				ans = X("Continue", "http");
11437da2899SCharles.Forsyth	HCSwitchProto =>			ans = X("Switching Protocols", "http");
11537da2899SCharles.Forsyth	HCOk =>					ans = X("Ok", "http");
11637da2899SCharles.Forsyth	HCCreated =>				ans = X("Created", "http");
11737da2899SCharles.Forsyth	HCAccepted =>				ans = X("Accepted", "http");
11837da2899SCharles.Forsyth	HCOkNonAuthoritative =>		ans = X("Non-Authoratative Information", "http");
11937da2899SCharles.Forsyth	HCNoContent =>			ans = X("No content", "http");
12037da2899SCharles.Forsyth	HCResetContent =>			ans = X("Reset content", "http");
12137da2899SCharles.Forsyth	HCPartialContent =>			ans = X("Partial content", "http");
12237da2899SCharles.Forsyth	HCMultipleChoices =>		ans = X("Multiple choices", "http");
12337da2899SCharles.Forsyth	HCMovedPerm =>			ans = X("Moved permanently", "http");
12437da2899SCharles.Forsyth	HCMovedTemp =>			ans = X("Moved temporarily", "http");
12537da2899SCharles.Forsyth	HCSeeOther =>				ans = X("See other", "http");
12637da2899SCharles.Forsyth	HCNotModified =>			ans = X("Not modified", "http");
12737da2899SCharles.Forsyth	HCUseProxy =>				ans = X("Use proxy", "http");
12837da2899SCharles.Forsyth	HCBadRequest =>			ans = X("Bad request", "http");
12937da2899SCharles.Forsyth	HCUnauthorized =>			ans = X("Unauthorized", "http");
13037da2899SCharles.Forsyth	HCPaymentRequired =>		ans = X("Payment required", "http");
13137da2899SCharles.Forsyth	HCForbidden =>			ans = X("Forbidden", "http");
13237da2899SCharles.Forsyth	HCNotFound =>			ans = X("Not found", "http");
13337da2899SCharles.Forsyth	HCMethodNotAllowed =>		ans = X("Method not allowed", "http");
13437da2899SCharles.Forsyth	HCNotAcceptable =>			ans = X("Not Acceptable", "http");
13537da2899SCharles.Forsyth	HCProxyAuthRequired =>		ans = X("Proxy authentication required", "http");
13637da2899SCharles.Forsyth	HCRequestTimeout =>		ans = X("Request timed-out", "http");
13737da2899SCharles.Forsyth	HCConflict =>				ans = X("Conflict", "http");
13837da2899SCharles.Forsyth	HCGone =>				ans = X("Gone", "http");
13937da2899SCharles.Forsyth	HCLengthRequired =>		ans = X("Length required", "http");
14037da2899SCharles.Forsyth	HCPreconditionFailed =>		ans = X("Precondition failed", "http");
14137da2899SCharles.Forsyth	HCRequestTooLarge =>		ans = X("Request entity too large", "http");
14237da2899SCharles.Forsyth	HCRequestURITooLarge =>	ans = X("Request-URI too large", "http");
14337da2899SCharles.Forsyth	HCUnsupportedMedia =>		ans = X("Unsupported media type", "http");
14437da2899SCharles.Forsyth	HCRangeInvalid =>			ans = X("Requested range not valid", "http");
14537da2899SCharles.Forsyth	HCExpectFailed =>			ans = X("Expectation failed", "http");
14637da2899SCharles.Forsyth	HCServerError =>			ans = X("Internal server error", "http");
14737da2899SCharles.Forsyth	HCNotImplemented =>		ans = X("Not implemented", "http");
14837da2899SCharles.Forsyth	HCBadGateway =>			ans = X("Bad gateway", "http");
14937da2899SCharles.Forsyth	HCServiceUnavailable =>		ans = X("Service unavailable", "http");
15037da2899SCharles.Forsyth	HCGatewayTimeout =>		ans = X("Gateway time-out", "http");
15137da2899SCharles.Forsyth	HCVersionUnsupported =>	ans = X("HTTP version not supported", "http");
15237da2899SCharles.Forsyth	HCRedirectionFailed =>		ans = X("Redirection failed", "http");
15337da2899SCharles.Forsyth	* =>						ans = X("Unknown code", "http");
15437da2899SCharles.Forsyth	}
15537da2899SCharles.Forsyth	return ans;
15637da2899SCharles.Forsyth}
15737da2899SCharles.Forsyth
15837da2899SCharles.Forsyth# This array should be kept sorted
15937da2899SCharles.Forsythfileexttable := array[] of { T->StringInt
16037da2899SCharles.Forsyth	("ai", ApplPostscript),
16137da2899SCharles.Forsyth	("au", AudioBasic),
16237da2899SCharles.Forsyth# ("bit", ImageXBit),
16337da2899SCharles.Forsyth	("bit", ImageXInfernoBit),
16437da2899SCharles.Forsyth	("bit2", ImageXBit2),
16537da2899SCharles.Forsyth	("bitm", ImageXBitmulti),
16637da2899SCharles.Forsyth	("eps", ApplPostscript),
16737da2899SCharles.Forsyth	("gif", ImageGif),
16837da2899SCharles.Forsyth	("gz",	ApplOctets),
16937da2899SCharles.Forsyth	("htm", TextHtml),
17037da2899SCharles.Forsyth	("html", TextHtml),
17137da2899SCharles.Forsyth	("jpe", ImageJpeg),
17237da2899SCharles.Forsyth	("jpeg", ImageJpeg),
17337da2899SCharles.Forsyth	("jpg", ImageJpeg),
17437da2899SCharles.Forsyth	("pdf", ApplPdf),
17537da2899SCharles.Forsyth	("png", ImagePng),
17637da2899SCharles.Forsyth	("ps", ApplPostscript),
17737da2899SCharles.Forsyth	("shtml", TextHtml),
17837da2899SCharles.Forsyth	("text", TextPlain),
17937da2899SCharles.Forsyth	("tif", ImageTiff),
18037da2899SCharles.Forsyth	("tiff", ImageTiff),
18137da2899SCharles.Forsyth	("txt", TextPlain),
18237da2899SCharles.Forsyth	("zip", ApplOctets)
18337da2899SCharles.Forsyth};
18437da2899SCharles.Forsyth
18537da2899SCharles.Forsyth# argl is command line
18637da2899SCharles.Forsyth# Return string that is empty if all ok, else path of module
18737da2899SCharles.Forsyth# that failed to load.
18837da2899SCharles.Forsythinit(ch: Charon, c: CharonUtils, argl: list of string, evc: chan of ref E->Event, cksrv: Cookiesrv, ckc: ref Cookiesrv->Client) : string
18937da2899SCharles.Forsyth{
19037da2899SCharles.Forsyth	me = c;
19137da2899SCharles.Forsyth	sys = load Sys Sys->PATH;
19237da2899SCharles.Forsyth	startres = ResourceState.cur();
19337da2899SCharles.Forsyth	D = load Draw Draw->PATH;
19437da2899SCharles.Forsyth	CH = ch;
19537da2899SCharles.Forsyth	S = load String String->PATH;
19637da2899SCharles.Forsyth	if(S == nil)
19737da2899SCharles.Forsyth		return String->PATH;
19837da2899SCharles.Forsyth
19937da2899SCharles.Forsyth	U = load Url Url->PATH;
20037da2899SCharles.Forsyth	if(U == nil)
20137da2899SCharles.Forsyth		return Url->PATH;
20237da2899SCharles.Forsyth	U->init();
20337da2899SCharles.Forsyth
20462d7827bScharles forsyth	DI = load Dial Dial->PATH;
20562d7827bScharles forsyth	if(DI == nil)
20662d7827bScharles forsyth		return Dial->PATH;
20762d7827bScharles forsyth
20837da2899SCharles.Forsyth	T = load StringIntTab StringIntTab->PATH;
20937da2899SCharles.Forsyth	if(T == nil)
21037da2899SCharles.Forsyth		return StringIntTab->PATH;
21137da2899SCharles.Forsyth
21237da2899SCharles.Forsyth	trans = load Translate Translate->PATH;
21337da2899SCharles.Forsyth	if (trans != nil) {
21437da2899SCharles.Forsyth		trans->init();
21537da2899SCharles.Forsyth		(dict, nil) = trans->opendict(trans->mkdictname(nil, "charon"));
21637da2899SCharles.Forsyth	}
21737da2899SCharles.Forsyth
21837da2899SCharles.Forsyth	# Now have all the modules needed to process command line
21937da2899SCharles.Forsyth	# (hereafter can use our loadpath() function to substitute the
22037da2899SCharles.Forsyth	# build directory version if dbg['u'] is set)
22137da2899SCharles.Forsyth
22237da2899SCharles.Forsyth	setconfig(argl);
22337da2899SCharles.Forsyth	dbg = int config.dbg['d'];
22437da2899SCharles.Forsyth
22537da2899SCharles.Forsyth	G = load Gui loadpath(Gui->PATH);
22637da2899SCharles.Forsyth	if(G == nil)
22737da2899SCharles.Forsyth		return loadpath(Gui->PATH);
22837da2899SCharles.Forsyth
22937da2899SCharles.Forsyth	C = load Ctype loadpath(Ctype->PATH);
23037da2899SCharles.Forsyth	if(C == nil)
23137da2899SCharles.Forsyth		return loadpath(Ctype->PATH);
23237da2899SCharles.Forsyth
23337da2899SCharles.Forsyth	E = load Events Events->PATH;
23437da2899SCharles.Forsyth	if(E == nil)
23537da2899SCharles.Forsyth		return loadpath(Events->PATH);
23637da2899SCharles.Forsyth
23737da2899SCharles.Forsyth	J = load Script loadpath(Script->JSCRIPTPATH);
23837da2899SCharles.Forsyth	# don't report an error loading JavaScript, handled elsewhere
23937da2899SCharles.Forsyth
24037da2899SCharles.Forsyth	LX = load Lex loadpath(Lex->PATH);
24137da2899SCharles.Forsyth	if(LX == nil)
24237da2899SCharles.Forsyth		return loadpath(Lex->PATH);
24337da2899SCharles.Forsyth
24437da2899SCharles.Forsyth	B = load Build loadpath(Build->PATH);
24537da2899SCharles.Forsyth	if(B == nil)
24637da2899SCharles.Forsyth		return loadpath(Build->PATH);
24737da2899SCharles.Forsyth
24837da2899SCharles.Forsyth	I = load Img loadpath(Img->PATH);
24937da2899SCharles.Forsyth	if(I == nil)
25037da2899SCharles.Forsyth		return loadpath(Img->PATH);
25137da2899SCharles.Forsyth
25237da2899SCharles.Forsyth	L = load Layout loadpath(Layout->PATH);
25337da2899SCharles.Forsyth	if(L == nil)
25437da2899SCharles.Forsyth		return loadpath(Layout->PATH);
25537da2899SCharles.Forsyth	date = load Date loadpath(Date->PATH);
25637da2899SCharles.Forsyth	if (date == nil)
25737da2899SCharles.Forsyth		return loadpath(Date->PATH);
25837da2899SCharles.Forsyth
25937da2899SCharles.Forsyth	convcs = load Convcs Convcs->PATH;
26037da2899SCharles.Forsyth	if (convcs == nil)
26137da2899SCharles.Forsyth		return loadpath(Convcs->PATH);
26237da2899SCharles.Forsyth
26337da2899SCharles.Forsyth
26437da2899SCharles.Forsyth	# Intialize all modules after loading all, so that each
26537da2899SCharles.Forsyth	# may cache pointers to the other modules
26637da2899SCharles.Forsyth	# (G will be initialized in main charon routine, and L has to
26737da2899SCharles.Forsyth	# be inited after that, because it needs G's display to allocate fonts)
26837da2899SCharles.Forsyth
26937da2899SCharles.Forsyth	E->init(evc);
27037da2899SCharles.Forsyth	I->init(me);
27137da2899SCharles.Forsyth	err := convcs->init(nil);
27237da2899SCharles.Forsyth	if (err != nil)
27337da2899SCharles.Forsyth		return err;
27437da2899SCharles.Forsyth	if(J != nil) {
27537da2899SCharles.Forsyth		err = J->init(me);
27637da2899SCharles.Forsyth		if (err != nil) {
27737da2899SCharles.Forsyth			# non-fatal: just don't handle javascript
27837da2899SCharles.Forsyth			J = nil;
27937da2899SCharles.Forsyth			if (dbg)
28037da2899SCharles.Forsyth				sys->print("%s\n", err);
28137da2899SCharles.Forsyth		}
28237da2899SCharles.Forsyth	}
28337da2899SCharles.Forsyth	B->init(me);
28437da2899SCharles.Forsyth	LX->init(me);
28537da2899SCharles.Forsyth	date->init(me);
28637da2899SCharles.Forsyth
28737da2899SCharles.Forsyth	if (config.docookies) {
28837da2899SCharles.Forsyth		CK = cksrv;
28937da2899SCharles.Forsyth		ckclient = ckc;
29037da2899SCharles.Forsyth		if (CK == nil) {
29137da2899SCharles.Forsyth			path := loadpath(Cookiesrv->PATH);
29237da2899SCharles.Forsyth			CK = load Cookiesrv path;
29337da2899SCharles.Forsyth			if (CK == nil)
29437da2899SCharles.Forsyth				sys->print("cookies: cannot load server %s: %r\n", path);
29537da2899SCharles.Forsyth			else
29637da2899SCharles.Forsyth				ckclient = CK->start(config.userdir + "/cookies", 0);
29737da2899SCharles.Forsyth		}
29837da2899SCharles.Forsyth	}
29937da2899SCharles.Forsyth
30037da2899SCharles.Forsyth	# preload some transports
30137da2899SCharles.Forsyth	gettransport("http");
30237da2899SCharles.Forsyth	gettransport("file");
30337da2899SCharles.Forsyth
30437da2899SCharles.Forsyth	progresschan = chan of (int, int, int, string);
30537da2899SCharles.Forsyth	imcache = ref ImageCache;
30637da2899SCharles.Forsyth	ctype = C->ctype;
30737da2899SCharles.Forsyth	dbgproto = int config.dbg['p'];
30837da2899SCharles.Forsyth	ngchan = chan of (int, list of ref ByteSource, ref Netconn, chan of ref ByteSource);
30937da2899SCharles.Forsyth	return "";
31037da2899SCharles.Forsyth}
31137da2899SCharles.Forsyth
31237da2899SCharles.Forsyth# like startreq() but special case for a string ByteSource
31337da2899SCharles.Forsyth# which doesn't need an associated netconn
31437da2899SCharles.Forsythstringreq(s : string) : ref ByteSource
31537da2899SCharles.Forsyth{
31637da2899SCharles.Forsyth	bs := ByteSource.stringsource(s);
31737da2899SCharles.Forsyth
31837da2899SCharles.Forsyth	G->progress <-= (bs.id, G->Pstart, 0, "text");
31937da2899SCharles.Forsyth	anschan := chan of ref ByteSource;
32037da2899SCharles.Forsyth	ngchan <-= (NGstartreq, bs :: nil, nil, anschan);
32137da2899SCharles.Forsyth	<-anschan;
32237da2899SCharles.Forsyth	return bs;
32337da2899SCharles.Forsyth}
32437da2899SCharles.Forsyth
32537da2899SCharles.Forsyth# Make a ByteSource for given request, and make sure
32637da2899SCharles.Forsyth# that it is on the queue of some Netconn.
32737da2899SCharles.Forsyth# If don't have a transport for the request's scheme,
32837da2899SCharles.Forsyth# the returned bs will have err set.
32937da2899SCharles.Forsythstartreq(req: ref ReqInfo) : ref ByteSource
33037da2899SCharles.Forsyth{
33137da2899SCharles.Forsyth	bs := ref ByteSource(
33237da2899SCharles.Forsyth			bytesourceid++,
33337da2899SCharles.Forsyth			req,		# req
33437da2899SCharles.Forsyth			nil,		# hdr
33537da2899SCharles.Forsyth			nil,		# data
33637da2899SCharles.Forsyth			0,		# edata
33737da2899SCharles.Forsyth			"",		# err
33837da2899SCharles.Forsyth			nil,		# net
33937da2899SCharles.Forsyth			1,		# refgo
34037da2899SCharles.Forsyth			1,		# refnc
34137da2899SCharles.Forsyth			0,		# eof
34237da2899SCharles.Forsyth			0,		# lim
34337da2899SCharles.Forsyth			0		# seenhdr
34437da2899SCharles.Forsyth		);
34537da2899SCharles.Forsyth
34637da2899SCharles.Forsyth	G->progress <-= (bs.id, G->Pstart, 0, req.url.tostring());
34737da2899SCharles.Forsyth	anschan := chan of ref ByteSource;
34837da2899SCharles.Forsyth	ngchan <-= (NGstartreq, bs::nil, nil, anschan);
34937da2899SCharles.Forsyth	<-anschan;
35037da2899SCharles.Forsyth	return bs;
35137da2899SCharles.Forsyth}
35237da2899SCharles.Forsyth
35337da2899SCharles.Forsyth# Wait for some ByteSource in current go generation to
35437da2899SCharles.Forsyth# have a state change that goproc hasn't seen yet.
35537da2899SCharles.Forsythwaitreq(bsl: list of ref ByteSource) : ref ByteSource
35637da2899SCharles.Forsyth{
35737da2899SCharles.Forsyth	anschan := chan of ref ByteSource;
35837da2899SCharles.Forsyth	ngchan <-= (NGwaitreq, bsl, nil, anschan);
35937da2899SCharles.Forsyth	return <-anschan;
36037da2899SCharles.Forsyth}
36137da2899SCharles.Forsyth
36237da2899SCharles.Forsyth# Notify netget that goproc is finished with bs.
36337da2899SCharles.Forsythfreebs(bs: ref ByteSource)
36437da2899SCharles.Forsyth{
36537da2899SCharles.Forsyth	anschan := chan of ref ByteSource;
36637da2899SCharles.Forsyth	ngchan <-= (NGfreebs, bs::nil, nil, anschan);
36737da2899SCharles.Forsyth	<-anschan;
36837da2899SCharles.Forsyth}
36937da2899SCharles.Forsyth
37037da2899SCharles.Forsythabortgo(gopgrp: int)
37137da2899SCharles.Forsyth{
37237da2899SCharles.Forsyth	if(int config.dbg['d'])
37337da2899SCharles.Forsyth		sys->print("abort go\n");
37437da2899SCharles.Forsyth	kill(gopgrp, 1);
37537da2899SCharles.Forsyth	freegoresources();
37637da2899SCharles.Forsyth	# renew the channels so that receives/sends by killed threads don't
37737da2899SCharles.Forsyth	# muck things up
37837da2899SCharles.Forsyth	ngchan = chan of (int, list of ref ByteSource, ref Netconn, chan of ref ByteSource);
37937da2899SCharles.Forsyth}
38037da2899SCharles.Forsyth
38137da2899SCharles.Forsythfreegoresources()
38237da2899SCharles.Forsyth{
38337da2899SCharles.Forsyth	for(i := 0; i < len netconns; i++) {
38437da2899SCharles.Forsyth		nc := netconns[i];
38537da2899SCharles.Forsyth		nc.makefree();
38637da2899SCharles.Forsyth	}
38737da2899SCharles.Forsyth}
38837da2899SCharles.Forsyth
38937da2899SCharles.Forsyth# This runs as a separate thread.
39037da2899SCharles.Forsyth# It acts as a monitor to synchronize access to the Netconn data
39137da2899SCharles.Forsyth# structures, as a dispatcher to start runnetconn's as needed to
39237da2899SCharles.Forsyth# process work on Netconn queues, and as a notifier to let goproc
39337da2899SCharles.Forsyth# know when any ByteSources have advanced their state.
39437da2899SCharles.Forsythnetget()
39537da2899SCharles.Forsyth{
39637da2899SCharles.Forsyth	msg, n, i: int;
39737da2899SCharles.Forsyth	bsl : list of ref ByteSource;
39837da2899SCharles.Forsyth	nc: ref Netconn;
39937da2899SCharles.Forsyth	waitix := 0;
40037da2899SCharles.Forsyth	c : chan of ref ByteSource;
40137da2899SCharles.Forsyth	waitpending : list of (list of ref ByteSource, chan of ref ByteSource);
40237da2899SCharles.Forsyth	maxconn := config.nthreads;
40337da2899SCharles.Forsyth	gncs := array[maxconn] of int;
40437da2899SCharles.Forsyth
40537da2899SCharles.Forsyth	for(n = 0; n < len netconns; n++)
40637da2899SCharles.Forsyth		netconns[n] = Netconn.new(n);
40737da2899SCharles.Forsyth
40837da2899SCharles.Forsyth	# capture netget chan to prevent abortgo() reset of
40937da2899SCharles.Forsyth	# ngchan from breaking us (channel hungup) before kill() does its job
41037da2899SCharles.Forsyth	ngc := ngchan;
41137da2899SCharles.Forsythmainloop:
41237da2899SCharles.Forsyth	for(;;) {
41337da2899SCharles.Forsyth		(msg,bsl,nc,c) = <- ngc;
41437da2899SCharles.Forsyth		case msg {
41537da2899SCharles.Forsyth		NGstartreq =>
41637da2899SCharles.Forsyth			bs := hd bsl;
41737da2899SCharles.Forsyth			# bs has req filled in, and is otherwise in its initial state.
41837da2899SCharles.Forsyth			# Find a suitable Netconn and add bs to its queue of work,
41937da2899SCharles.Forsyth			# then send nil along c to let goproc continue.
42037da2899SCharles.Forsyth
42137da2899SCharles.Forsyth			# if ReqInfo is nil then this is a string ByteSource
42237da2899SCharles.Forsyth			# in which case we don't need a netconn to service it as we have all
42337da2899SCharles.Forsyth			# data already
42437da2899SCharles.Forsyth			if (bs.req == nil) {
42537da2899SCharles.Forsyth				c <- = nil;
42637da2899SCharles.Forsyth				continue;
42737da2899SCharles.Forsyth			}
42837da2899SCharles.Forsyth
42937da2899SCharles.Forsyth			if(dbgproto)
43037da2899SCharles.Forsyth				sys->print("Startreq BS=%d for %s\n", bs.id, bs.req.url.tostring());
43137da2899SCharles.Forsyth			scheme := bs.req.url.scheme;
43237da2899SCharles.Forsyth			host := bs.req.url.host;
43337da2899SCharles.Forsyth			(transport, err) := gettransport(scheme);
43437da2899SCharles.Forsyth			if(err != "")
43537da2899SCharles.Forsyth				bs.err = err;
43637da2899SCharles.Forsyth			else {
43737da2899SCharles.Forsyth				sport :=bs.req.url.port;
43837da2899SCharles.Forsyth				if(sport == "")
43937da2899SCharles.Forsyth					port := transport->defaultport(scheme);
44037da2899SCharles.Forsyth				else
44137da2899SCharles.Forsyth					port = int sport;
44237da2899SCharles.Forsyth				i = 0;
44337da2899SCharles.Forsyth				freen := -1;
44437da2899SCharles.Forsyth				for(n = 0; n < len netconns && (i < maxconn || freen == -1); n++) {
44537da2899SCharles.Forsyth					nc = netconns[n];
44637da2899SCharles.Forsyth					if(nc.state == NCfree) {
44737da2899SCharles.Forsyth						if(freen == -1)
44837da2899SCharles.Forsyth							freen = n;
44937da2899SCharles.Forsyth					}
45037da2899SCharles.Forsyth					else if(nc.host == host
45137da2899SCharles.Forsyth					   && nc.port == port && nc.scheme == scheme && i < maxconn) {
45237da2899SCharles.Forsyth						gncs[i++] = n;
45337da2899SCharles.Forsyth					}
45437da2899SCharles.Forsyth				}
45537da2899SCharles.Forsyth				if(i < maxconn) {
45637da2899SCharles.Forsyth					# use a new netconn for this bs
45737da2899SCharles.Forsyth					if(freen == -1) {
45837da2899SCharles.Forsyth						freen = len netconns;
45937da2899SCharles.Forsyth						newncs := array[freen+10] of ref Netconn;
46037da2899SCharles.Forsyth						newncs[0:] = netconns;
46137da2899SCharles.Forsyth						for(n = freen; n < freen+10; n++)
46237da2899SCharles.Forsyth							newncs[n] = Netconn.new(n);
46337da2899SCharles.Forsyth						netconns = newncs;
46437da2899SCharles.Forsyth					}
46537da2899SCharles.Forsyth					nc = netconns[freen];
46637da2899SCharles.Forsyth					nc.host = host;
46737da2899SCharles.Forsyth					nc.port = port;
46837da2899SCharles.Forsyth					nc.scheme = scheme;
46937da2899SCharles.Forsyth					nc.qlen = 0;
47037da2899SCharles.Forsyth					nc.ngcur = 0;
47137da2899SCharles.Forsyth					nc.gocur = 0;
47237da2899SCharles.Forsyth					nc.reqsent = 0;
47337da2899SCharles.Forsyth					nc.pipeline = 0;
47437da2899SCharles.Forsyth					nc.connected = 0;
47537da2899SCharles.Forsyth				}
47637da2899SCharles.Forsyth				else {
47737da2899SCharles.Forsyth					# use existing netconn with fewest outstanding requests
47837da2899SCharles.Forsyth					nc = netconns[gncs[0]];
47937da2899SCharles.Forsyth					if(maxconn > 1) {
48037da2899SCharles.Forsyth						minqlen := nc.qlen - nc.gocur;
48137da2899SCharles.Forsyth						for(i = 1; i < maxconn; i++) {
48237da2899SCharles.Forsyth							x := netconns[gncs[i]];
48337da2899SCharles.Forsyth							if(x.qlen-x.gocur < minqlen) {
48437da2899SCharles.Forsyth								nc = x;
48537da2899SCharles.Forsyth								minqlen = x.qlen-x.gocur;
48637da2899SCharles.Forsyth							}
48737da2899SCharles.Forsyth						}
48837da2899SCharles.Forsyth					}
48937da2899SCharles.Forsyth				}
49037da2899SCharles.Forsyth				if(nc.qlen == len nc.queue) {
49137da2899SCharles.Forsyth					nq := array[nc.qlen+10] of ref ByteSource;
49237da2899SCharles.Forsyth					nq[0:] = nc.queue;
49337da2899SCharles.Forsyth					nc.queue = nq;
49437da2899SCharles.Forsyth				}
49537da2899SCharles.Forsyth				nc.queue[nc.qlen++] = bs;
49637da2899SCharles.Forsyth				bs.net = nc;
49737da2899SCharles.Forsyth				if(dbgproto)
49837da2899SCharles.Forsyth					sys->print("Chose NC=%d for BS %d, qlen=%d\n", nc.id, bs.id, nc.qlen);
49937da2899SCharles.Forsyth				if(nc.state == NCfree || nc.state == NCidle) {
50037da2899SCharles.Forsyth					if(nc.connected) {
50137da2899SCharles.Forsyth						nc.state = NCgethdr;
50237da2899SCharles.Forsyth						if(dbgproto)
50337da2899SCharles.Forsyth							sys->print("NC %d: starting runnetconn in gethdr state\n", nc.id);
50437da2899SCharles.Forsyth					}
50537da2899SCharles.Forsyth					else {
50637da2899SCharles.Forsyth						nc.state = NCconnect;
50737da2899SCharles.Forsyth						if(dbgproto)
50837da2899SCharles.Forsyth							sys->print("NC %d: starting runnetconn in connect state\n", nc.id);
50937da2899SCharles.Forsyth					}
51037da2899SCharles.Forsyth					spawn runnetconn(nc, transport);
51137da2899SCharles.Forsyth				}
51237da2899SCharles.Forsyth			}
51337da2899SCharles.Forsyth			c <-= nil;
51437da2899SCharles.Forsyth
51537da2899SCharles.Forsyth		NGwaitreq =>
51637da2899SCharles.Forsyth			# goproc wants to be notified when some ByteSource
51737da2899SCharles.Forsyth			# changes to a state that the goproc hasn't seen yet.
51837da2899SCharles.Forsyth			# Send such a ByteSource along return channel c.
51937da2899SCharles.Forsyth
52037da2899SCharles.Forsyth			if(dbgproto)
52137da2899SCharles.Forsyth				sys->print("Waitreq\n");
52237da2899SCharles.Forsyth
52337da2899SCharles.Forsyth			for (scanlist := bsl; scanlist != nil; scanlist = tl scanlist) {
52437da2899SCharles.Forsyth				bs := hd scanlist;
52537da2899SCharles.Forsyth				if (bs.refnc == 0) {
52637da2899SCharles.Forsyth					# string ByteSource or completed or error
52737da2899SCharles.Forsyth					if (bs.err != nil || bs.edata >= bs.lim) {
52837da2899SCharles.Forsyth						c <-= bs;
52937da2899SCharles.Forsyth						continue mainloop;
53037da2899SCharles.Forsyth					}
53137da2899SCharles.Forsyth					continue;
53237da2899SCharles.Forsyth				}
53337da2899SCharles.Forsyth				# netcon based bytesource
53437da2899SCharles.Forsyth				if ((bs.hdr != nil && !bs.seenhdr && bs.hdr.mtype != UnknownType) || bs.edata > bs.lim) {
53537da2899SCharles.Forsyth					c <-= bs;
53637da2899SCharles.Forsyth					continue mainloop;
53737da2899SCharles.Forsyth				}
53837da2899SCharles.Forsyth			}
53937da2899SCharles.Forsyth
54037da2899SCharles.Forsyth			if(dbgproto)
54137da2899SCharles.Forsyth				sys->print("Waitpending\n");
54237da2899SCharles.Forsyth			waitpending = (bsl, c) :: waitpending;
54337da2899SCharles.Forsyth
54437da2899SCharles.Forsyth		NGfreebs =>
54537da2899SCharles.Forsyth			# goproc is finished with bs.
54637da2899SCharles.Forsyth			bs := hd bsl;
54737da2899SCharles.Forsyth
54837da2899SCharles.Forsyth			if(dbgproto)
54937da2899SCharles.Forsyth				sys->print("Freebs BS=%d\n", bs.id);
55037da2899SCharles.Forsyth			nc = bs.net;
55137da2899SCharles.Forsyth			bs.refgo = 0;
55237da2899SCharles.Forsyth			if(bs.refnc == 0) {
55337da2899SCharles.Forsyth				bs.free();
55437da2899SCharles.Forsyth				if(nc != nil)
55537da2899SCharles.Forsyth					nc.queue[nc.gocur] = nil;
55637da2899SCharles.Forsyth			}
55737da2899SCharles.Forsyth			if(nc != nil) {
55837da2899SCharles.Forsyth				# can be nil if no transport was found
55937da2899SCharles.Forsyth				nc.gocur++;
56037da2899SCharles.Forsyth				if(dbgproto)
56137da2899SCharles.Forsyth					sys->print("NC %d: gocur=%d, ngcur=%d, qlen=%d\n", nc.id, nc.gocur, nc.ngcur, nc.qlen);
56237da2899SCharles.Forsyth				if(nc.gocur == nc.qlen && nc.ngcur == nc.qlen) {
56337da2899SCharles.Forsyth					if(!nc.connected)
56437da2899SCharles.Forsyth						nc.makefree();
56537da2899SCharles.Forsyth				}
56637da2899SCharles.Forsyth			}
56737da2899SCharles.Forsyth			# don't need to check waitpending fro NGwait requests involving bs
56837da2899SCharles.Forsyth			# the only thread doing a freebs() should be the only thread that
56937da2899SCharles.Forsyth			# can do a waitreq() on the same bs.  Same thread cannot be in both states.
57037da2899SCharles.Forsyth
57137da2899SCharles.Forsyth			c <-= nil;
57237da2899SCharles.Forsyth
57337da2899SCharles.Forsyth		NGstatechg =>
57437da2899SCharles.Forsyth			# Some runnetconn is telling us tht it changed the
57537da2899SCharles.Forsyth			# state of nc.  Send a nil along c to let it continue.
57637da2899SCharles.Forsyth			bs : ref ByteSource;
57737da2899SCharles.Forsyth			if(dbgproto)
57837da2899SCharles.Forsyth				sys->print("Statechg NC=%d, state=%s\n",
57937da2899SCharles.Forsyth					nc.id, ncstatenames[nc.state]);
58037da2899SCharles.Forsyth			sendtopending : ref ByteSource = nil;
58137da2899SCharles.Forsyth			pendingchan : chan of ref ByteSource;
58237da2899SCharles.Forsyth			if(waitpending != nil && nc.gocur < nc.qlen) {
58337da2899SCharles.Forsyth				bs = nc.queue[nc.gocur];
58437da2899SCharles.Forsyth				if(dbgproto) {
58537da2899SCharles.Forsyth					totlen := 0;
58637da2899SCharles.Forsyth					if(bs.hdr != nil)
58737da2899SCharles.Forsyth						totlen = bs.hdr.length;
58837da2899SCharles.Forsyth					sys->print("BS %d: havehdr=%d seenhdr=%d edata=%d lim=%d, length=%d\n",
58937da2899SCharles.Forsyth						bs.id, bs.hdr != nil, bs.seenhdr, bs.edata, bs.lim, totlen);
59037da2899SCharles.Forsyth					if(bs.err != "")
59137da2899SCharles.Forsyth						sys->print ("   err=%s\n", bs.err);
59237da2899SCharles.Forsyth				}
59337da2899SCharles.Forsyth				if(bs.refgo &&
59437da2899SCharles.Forsyth				   (bs.err != "" ||
59537da2899SCharles.Forsyth				   (bs.hdr != nil && !bs.seenhdr) ||
59637da2899SCharles.Forsyth				   (nc.gocur == nc.ngcur && nc.state == NCdone) ||
59737da2899SCharles.Forsyth				   (bs.edata > bs.lim))) {
59837da2899SCharles.Forsyth					nwp: list of (list of ref ByteSource, chan of ref ByteSource) = nil;
59937da2899SCharles.Forsyth					for (waitlist := waitpending; waitlist != nil; waitlist = tl waitlist) {
60037da2899SCharles.Forsyth						(bslist, anschan) := hd waitlist;
60137da2899SCharles.Forsyth						if (sendtopending != nil) {
60237da2899SCharles.Forsyth							nwp = (bslist, anschan) :: nwp;
60337da2899SCharles.Forsyth							continue;
60437da2899SCharles.Forsyth						}
60537da2899SCharles.Forsyth						for (look := bslist; look != nil; look = tl look) {
60637da2899SCharles.Forsyth							if (bs == hd look) {
60737da2899SCharles.Forsyth								sendtopending = bs;
60837da2899SCharles.Forsyth								pendingchan = anschan;
60937da2899SCharles.Forsyth								break;
61037da2899SCharles.Forsyth							}
61137da2899SCharles.Forsyth						}
61237da2899SCharles.Forsyth						if (sendtopending == nil)
61337da2899SCharles.Forsyth							nwp = (bslist, anschan) :: nwp;
61437da2899SCharles.Forsyth					}
61537da2899SCharles.Forsyth					waitpending = nwp;
61637da2899SCharles.Forsyth				}
61737da2899SCharles.Forsyth			}
61837da2899SCharles.Forsyth			if(nc.state == NCdone || nc.state == NCerr) {
61937da2899SCharles.Forsyth				if(dbgproto)
62037da2899SCharles.Forsyth					sys->print("NC %d: runnetconn finishing\n", nc.id);
62137da2899SCharles.Forsyth				assert(nc.ngcur < nc.qlen);
62237da2899SCharles.Forsyth				bs = nc.queue[nc.ngcur];
62337da2899SCharles.Forsyth				bs.refnc = 0;
62437da2899SCharles.Forsyth				if(bs.refgo == 0) {
62537da2899SCharles.Forsyth					bs.free();
62637da2899SCharles.Forsyth					nc.queue[nc.ngcur] = nil;
62737da2899SCharles.Forsyth				}
62837da2899SCharles.Forsyth				nc.ngcur++;
62937da2899SCharles.Forsyth				if(dbgproto)
63037da2899SCharles.Forsyth					sys->print("NC %d: ngcur=%d\n", nc.id, nc.ngcur);
63137da2899SCharles.Forsyth				nc.state = NCidle;
63237da2899SCharles.Forsyth				if(dbgproto)
63337da2899SCharles.Forsyth					sys->print("NC %d: idle\n", nc.id);
63437da2899SCharles.Forsyth				if(nc.ngcur < nc.qlen) {
63537da2899SCharles.Forsyth					if(nc.connected) {
63637da2899SCharles.Forsyth						nc.state = NCgethdr;
63737da2899SCharles.Forsyth						if(dbgproto)
63837da2899SCharles.Forsyth							sys->print("NC %d: starting runnetconn in gethdr state\n", nc.id);
63937da2899SCharles.Forsyth					}
64037da2899SCharles.Forsyth					else {
64137da2899SCharles.Forsyth						nc.state = NCconnect;
64237da2899SCharles.Forsyth						if(dbgproto)
64337da2899SCharles.Forsyth							sys->print("NC %d: starting runnetconn in connect state\n", nc.id);
64437da2899SCharles.Forsyth					}
64537da2899SCharles.Forsyth					(t, nil) := gettransport(nc.scheme);
64637da2899SCharles.Forsyth					spawn runnetconn(nc, t);
64737da2899SCharles.Forsyth				}
64837da2899SCharles.Forsyth				else if(nc.gocur == nc.qlen && !nc.connected)
64937da2899SCharles.Forsyth					nc.makefree();
65037da2899SCharles.Forsyth			}
65137da2899SCharles.Forsyth			c <-= nil;
65237da2899SCharles.Forsyth			if(sendtopending != nil) {
65337da2899SCharles.Forsyth				if(dbgproto)
65437da2899SCharles.Forsyth					sys->print("Send BS %d to pending waitreq\n", bs.id);
65537da2899SCharles.Forsyth				pendingchan <-= sendtopending;
65637da2899SCharles.Forsyth				sendtopending = nil;
65737da2899SCharles.Forsyth			}
65837da2899SCharles.Forsyth		}
65937da2899SCharles.Forsyth	}
66037da2899SCharles.Forsyth}
66137da2899SCharles.Forsyth
66237da2899SCharles.Forsyth# A separate thread, to handle ngcur request of transport.
66337da2899SCharles.Forsyth# If nc.gen ever goes < gen, we have aborted this go.
66437da2899SCharles.Forsythrunnetconn(nc: ref Netconn, t: Transport)
66537da2899SCharles.Forsyth{
66637da2899SCharles.Forsyth	ach := chan of ref ByteSource;
66737da2899SCharles.Forsyth	retry := 4;
66837da2899SCharles.Forsyth#	retry := 0;
66937da2899SCharles.Forsyth	err := "";
67037da2899SCharles.Forsyth
67137da2899SCharles.Forsyth	assert(nc.ngcur < nc.qlen);
67237da2899SCharles.Forsyth	bs := nc.queue[nc.ngcur];
67337da2899SCharles.Forsyth
67437da2899SCharles.Forsyth	# dummy loop, just for breaking out of in error cases
67537da2899SCharles.Forsytheloop:
67637da2899SCharles.Forsyth	for(;;) {
67737da2899SCharles.Forsyth		# Make the connection, if necessary
67837da2899SCharles.Forsyth		if(nc.state == NCconnect) {
67937da2899SCharles.Forsyth			t->connect(nc, bs);
68037da2899SCharles.Forsyth			if(bs.err != "") {
68137da2899SCharles.Forsyth				if (retry) {
68237da2899SCharles.Forsyth					retry--;
68337da2899SCharles.Forsyth					bs.err = "";
68437da2899SCharles.Forsyth					sys->sleep(100);
68537da2899SCharles.Forsyth					continue eloop;
68637da2899SCharles.Forsyth				}
68737da2899SCharles.Forsyth				break eloop;
68837da2899SCharles.Forsyth			}
68937da2899SCharles.Forsyth			nc.state = NCgethdr;
69037da2899SCharles.Forsyth		}
69137da2899SCharles.Forsyth		assert(nc.state == NCgethdr && nc.connected);
69237da2899SCharles.Forsyth		if(nc.scheme == "https")
69337da2899SCharles.Forsyth			G->progress <-= (bs.id, G->Psslconnected, 0, "");
69437da2899SCharles.Forsyth		else
69537da2899SCharles.Forsyth			G->progress <-= (bs.id, G->Pconnected, 0, "");
69637da2899SCharles.Forsyth
69737da2899SCharles.Forsyth		t->writereq(nc, bs);
69837da2899SCharles.Forsyth		nc.reqsent++;
69937da2899SCharles.Forsyth		if (bs.err != "") {
70037da2899SCharles.Forsyth			if (retry) {
70137da2899SCharles.Forsyth				retry--;
70237da2899SCharles.Forsyth				bs.err = "";
70337da2899SCharles.Forsyth				nc.state = NCconnect;
70437da2899SCharles.Forsyth				sys->sleep(100);
70537da2899SCharles.Forsyth				continue eloop;
70637da2899SCharles.Forsyth			}
70737da2899SCharles.Forsyth			break eloop;
70837da2899SCharles.Forsyth		}
70937da2899SCharles.Forsyth		# managed to write the request
71037da2899SCharles.Forsyth		# do not retry if we are doing form POSTs
71137da2899SCharles.Forsyth		# See RFC1945 section 12.2 "Safe Methods"
71237da2899SCharles.Forsyth		if (bs.req.method == HPost)
71337da2899SCharles.Forsyth			retry = 0;
71437da2899SCharles.Forsyth
71537da2899SCharles.Forsyth		# Get the header
71637da2899SCharles.Forsyth		t->gethdr(nc, bs);
71737da2899SCharles.Forsyth		if(bs.err != "") {
71837da2899SCharles.Forsyth			if (retry) {
71937da2899SCharles.Forsyth				retry--;
72037da2899SCharles.Forsyth				bs.err = "";
72137da2899SCharles.Forsyth				nc.state = NCconnect;
72237da2899SCharles.Forsyth				sys->sleep(100);
72337da2899SCharles.Forsyth				continue eloop;
72437da2899SCharles.Forsyth			}
72537da2899SCharles.Forsyth			break eloop;
72637da2899SCharles.Forsyth		}
72737da2899SCharles.Forsyth		assert(bs.hdr != nil);
72837da2899SCharles.Forsyth		G->progress <-= (bs.id, G->Phavehdr, 0, "");
72937da2899SCharles.Forsyth
73037da2899SCharles.Forsyth		nc.state = NCgetdata;
73137da2899SCharles.Forsyth
73237da2899SCharles.Forsyth		# read enough data to guess media type
73337da2899SCharles.Forsyth		while (bs.hdr.mtype == UnknownType && ncgetdata(t, nc, bs))
73437da2899SCharles.Forsyth			bs.hdr.setmediatype(bs.hdr.actual.path, bs.data[:bs.edata]);
73537da2899SCharles.Forsyth		if (bs.hdr.mtype == UnknownType) {
73637da2899SCharles.Forsyth			bs.hdr.mtype = TextPlain;
73737da2899SCharles.Forsyth			bs.hdr.chset = "utf8";
73837da2899SCharles.Forsyth		}
73937da2899SCharles.Forsyth		ngchan <-= (NGstatechg,nil,nc,ach);
74037da2899SCharles.Forsyth		<- ach;
74137da2899SCharles.Forsyth		while (ncgetdata(t, nc, bs)) {
74237da2899SCharles.Forsyth			ngchan <-= (NGstatechg,nil,nc,ach);
74337da2899SCharles.Forsyth			<- ach;
74437da2899SCharles.Forsyth		}
74537da2899SCharles.Forsyth		nc.state = NCdone;
74637da2899SCharles.Forsyth		G->progress <-= (bs.id, G->Phavedata, 100, "");
74737da2899SCharles.Forsyth		break;
74837da2899SCharles.Forsyth	}
74937da2899SCharles.Forsyth	if(bs.err != "") {
75037da2899SCharles.Forsyth		nc.state = NCerr;
75137da2899SCharles.Forsyth		nc.connected = 0;
75237da2899SCharles.Forsyth		G->progress <-= (bs.id, G->Perr, 0, bs.err);
75337da2899SCharles.Forsyth	}
75437da2899SCharles.Forsyth	bs.eof = 1;
75537da2899SCharles.Forsyth	ngchan <-= (NGstatechg, nil, nc, ach);
75637da2899SCharles.Forsyth	<- ach;
75737da2899SCharles.Forsyth}
75837da2899SCharles.Forsyth
75937da2899SCharles.Forsythncgetdata(t: Transport, nc: ref Netconn, bs: ref ByteSource): int
76037da2899SCharles.Forsyth{
76137da2899SCharles.Forsyth	hdr := bs.hdr;
76237da2899SCharles.Forsyth	if (bs.data == nil) {
76337da2899SCharles.Forsyth		blen := hdr.length;
76437da2899SCharles.Forsyth		if (blen <= 0) {
76537da2899SCharles.Forsyth			if(hdr.code == HCOk || hdr.code == HCOkNonAuthoritative)
76637da2899SCharles.Forsyth				blen = UBufsize;
76737da2899SCharles.Forsyth			else
76837da2899SCharles.Forsyth				blen = UEBufsize;
76937da2899SCharles.Forsyth		}
77037da2899SCharles.Forsyth		bs.data = array[blen] of byte;
77137da2899SCharles.Forsyth	}
77237da2899SCharles.Forsyth	nr := 0;
77337da2899SCharles.Forsyth	if (hdr.length > 0) {
77437da2899SCharles.Forsyth		if (bs.edata == hdr.length)
77537da2899SCharles.Forsyth			return 0;
77637da2899SCharles.Forsyth		nr = t->getdata(nc, bs);
77737da2899SCharles.Forsyth		if (nr <= 0)
77837da2899SCharles.Forsyth			return 0;
77937da2899SCharles.Forsyth	} else {
78037da2899SCharles.Forsyth		# don't know data length - keep growing input buffer as needed
78137da2899SCharles.Forsyth		if (bs.edata == len bs.data) {
78237da2899SCharles.Forsyth			nd := array [2*len bs.data] of byte;
78337da2899SCharles.Forsyth			nd[:] = bs.data;
78437da2899SCharles.Forsyth			bs.data = nd;
78537da2899SCharles.Forsyth		}
78637da2899SCharles.Forsyth		nr = t->getdata(nc, bs);
78737da2899SCharles.Forsyth		if (nr <= 0) {
78837da2899SCharles.Forsyth			# assume EOF
78937da2899SCharles.Forsyth			bs.data = bs.data[0:bs.edata];
79037da2899SCharles.Forsyth			bs.err = "";
79137da2899SCharles.Forsyth			hdr.length = bs.edata;
79237da2899SCharles.Forsyth			nc.connected = 0;
79337da2899SCharles.Forsyth			return 0;
79437da2899SCharles.Forsyth		}
79537da2899SCharles.Forsyth	}
79637da2899SCharles.Forsyth	bs.edata += nr;
79737da2899SCharles.Forsyth	G->progress <-= (bs.id, G->Phavedata, 100*bs.edata/len bs.data, "");
79837da2899SCharles.Forsyth	return 1;
79937da2899SCharles.Forsyth}
80037da2899SCharles.Forsyth
80137da2899SCharles.ForsythNetconn.new(id: int) : ref Netconn
80237da2899SCharles.Forsyth{
80337da2899SCharles.Forsyth	return ref Netconn(
80437da2899SCharles.Forsyth			id,		# id
80537da2899SCharles.Forsyth			"",		# host
80637da2899SCharles.Forsyth			0,		# port
80737da2899SCharles.Forsyth			"",		# scheme
80862d7827bScharles forsyth			ref Dial->Connection(nil, nil, ""),	# conn
80937da2899SCharles.Forsyth			nil,		# ssl context
81037da2899SCharles.Forsyth			0,		# undetermined ssl version
81137da2899SCharles.Forsyth			NCfree,	# state
81237da2899SCharles.Forsyth			array[10] of ref ByteSource,	# queue
81337da2899SCharles.Forsyth			0,		# qlen
81437da2899SCharles.Forsyth			0,0,0,	# gocur, ngcur, reqsent
81537da2899SCharles.Forsyth			0,		# pipeline
81637da2899SCharles.Forsyth			0,		# connected
81737da2899SCharles.Forsyth			0,		# tstate
81837da2899SCharles.Forsyth			nil,		# tbuf
81937da2899SCharles.Forsyth			0		# idlestart
82037da2899SCharles.Forsyth			);
82137da2899SCharles.Forsyth}
82237da2899SCharles.Forsyth
82337da2899SCharles.ForsythNetconn.makefree(nc: self ref Netconn)
82437da2899SCharles.Forsyth{
82537da2899SCharles.Forsyth	if(dbgproto)
82637da2899SCharles.Forsyth		sys->print("NC %d: free\n", nc.id);
82737da2899SCharles.Forsyth	nc.state = NCfree;
82837da2899SCharles.Forsyth	nc.host = "";
829*d0c0f54bSCharles Forsyth	nc.conn = nil;
83037da2899SCharles.Forsyth	nc.qlen = 0;
83137da2899SCharles.Forsyth	nc.gocur = 0;
83237da2899SCharles.Forsyth	nc.ngcur = 0;
83337da2899SCharles.Forsyth	nc.reqsent = 0;
83437da2899SCharles.Forsyth	nc.pipeline = 0;
83537da2899SCharles.Forsyth	nc.connected = 0;
83637da2899SCharles.Forsyth	nc.tstate = 0;
83737da2899SCharles.Forsyth	nc.tbuf = nil;
83837da2899SCharles.Forsyth	for(i := 0; i < len nc.queue; i++)
83937da2899SCharles.Forsyth		nc.queue[i] = nil;
84037da2899SCharles.Forsyth}
84137da2899SCharles.Forsyth
84237da2899SCharles.ForsythByteSource.free(bs: self ref ByteSource)
84337da2899SCharles.Forsyth{
84437da2899SCharles.Forsyth	if(dbgproto)
84537da2899SCharles.Forsyth		sys->print("BS %d freed\n", bs.id);
84637da2899SCharles.Forsyth	if(bs.err == "")
84737da2899SCharles.Forsyth		G->progress <-= (bs.id, G->Pdone, 100, "");
84837da2899SCharles.Forsyth	else
84937da2899SCharles.Forsyth		G->progress <-= (bs.id, G->Perr, 0, bs.err);
85037da2899SCharles.Forsyth	bs.req = nil;
85137da2899SCharles.Forsyth	bs.hdr = nil;
85237da2899SCharles.Forsyth	bs.data = nil;
85337da2899SCharles.Forsyth	bs.err = "";
85437da2899SCharles.Forsyth	bs.net = nil;
85537da2899SCharles.Forsyth}
85637da2899SCharles.Forsyth
85737da2899SCharles.Forsyth# Return an ByteSource that is completely filled, from string s
85837da2899SCharles.ForsythByteSource.stringsource(s: string) : ref ByteSource
85937da2899SCharles.Forsyth{
86037da2899SCharles.Forsyth	a := array of byte s;
86137da2899SCharles.Forsyth	n := len a;
86237da2899SCharles.Forsyth	hdr := ref Header(
86337da2899SCharles.Forsyth			HCOk,		# code
86437da2899SCharles.Forsyth			nil,			# actual
86537da2899SCharles.Forsyth			nil,			# base
86637da2899SCharles.Forsyth			nil,			# location
86737da2899SCharles.Forsyth			n,			# length
86837da2899SCharles.Forsyth			TextHtml, 	# mtype
86937da2899SCharles.Forsyth			"utf8",		# chset
87037da2899SCharles.Forsyth			"",			# msg
87137da2899SCharles.Forsyth			"",			# refresh
87237da2899SCharles.Forsyth			"",			# chal
87337da2899SCharles.Forsyth			"",			# warn
87437da2899SCharles.Forsyth			""			# last-modified
87537da2899SCharles.Forsyth		);
87637da2899SCharles.Forsyth	bs := ref ByteSource(
87737da2899SCharles.Forsyth			bytesourceid++,
87837da2899SCharles.Forsyth			nil,		# req
87937da2899SCharles.Forsyth			hdr,		# hdr
88037da2899SCharles.Forsyth			a,		# data
88137da2899SCharles.Forsyth			n,		# edata
88237da2899SCharles.Forsyth			"",		# err
88337da2899SCharles.Forsyth			nil,		# net
88437da2899SCharles.Forsyth			1,		# refgo
88537da2899SCharles.Forsyth			0,		# refnc
88637da2899SCharles.Forsyth			1,		# eof	- edata is final
88737da2899SCharles.Forsyth			0,		# lim
88837da2899SCharles.Forsyth			1		# seenhdr
88937da2899SCharles.Forsyth		);
89037da2899SCharles.Forsyth	return bs;
89137da2899SCharles.Forsyth}
89237da2899SCharles.Forsyth
89337da2899SCharles.ForsythMaskedImage.free(mim: self ref MaskedImage)
89437da2899SCharles.Forsyth{
89537da2899SCharles.Forsyth	mim.im = nil;
89637da2899SCharles.Forsyth	mim.mask = nil;
89737da2899SCharles.Forsyth}
89837da2899SCharles.Forsyth
89937da2899SCharles.ForsythCImage.new(src: ref U->Parsedurl, lowsrc: ref U->Parsedurl, width, height: int) : ref CImage
90037da2899SCharles.Forsyth{
90137da2899SCharles.Forsyth	return ref CImage(src, lowsrc, nil, strhash(src.host + "/" + src.path), width, height, nil, nil, 0);
90237da2899SCharles.Forsyth}
90337da2899SCharles.Forsyth
90437da2899SCharles.Forsyth# Return true if Cimages a and b represent the same image.
90537da2899SCharles.Forsyth# As well as matching the src urls, the specified widths and heights must match too.
90637da2899SCharles.Forsyth# (Widths and heights are specified if at least one of those is not zero.)
90737da2899SCharles.Forsyth#
90837da2899SCharles.Forsyth# BUG: the width/height matching code isn't right.  If one has width and height
90937da2899SCharles.Forsyth# specified, and the other doesn't, should say "don't match", because the unspecified
91037da2899SCharles.Forsyth# one should come in at its natural size.  But we overwrite the width and height fields
91137da2899SCharles.Forsyth# when the actual size comes in, so we can't tell whether width and height are nonzero
91237da2899SCharles.Forsyth# because they were specified or because they're their natural size.
91337da2899SCharles.ForsythCImage.match(a: self ref CImage, b: ref CImage) : int
91437da2899SCharles.Forsyth{
91537da2899SCharles.Forsyth	if(a.imhash == b.imhash) {
91637da2899SCharles.Forsyth		if(urlequal(a.src, b.src)) {
91737da2899SCharles.Forsyth			return (a.width == 0 || b.width == 0 || a.width == b.width) &&
91837da2899SCharles.Forsyth				(a.height == 0 || b.height == 0 || a.height == b.height);
91937da2899SCharles.Forsyth			# (above is not quite enough: should also check that don't have
92037da2899SCharles.Forsyth			# situation where one has width set, not height, and the other has reverse,
92137da2899SCharles.Forsyth			# but it is unusual for an image to have a spec in only one dimension anyway)
92237da2899SCharles.Forsyth		}
92337da2899SCharles.Forsyth	}
92437da2899SCharles.Forsyth	return 0;
92537da2899SCharles.Forsyth}
92637da2899SCharles.Forsyth
92737da2899SCharles.Forsyth# Return approximate number of bytes in image memory used
92837da2899SCharles.Forsyth# by ci.
92937da2899SCharles.ForsythCImage.bytes(ci: self ref CImage) : int
93037da2899SCharles.Forsyth{
93137da2899SCharles.Forsyth	tot := 0;
93237da2899SCharles.Forsyth	for(i := 0; i < len ci.mims; i++) {
93337da2899SCharles.Forsyth		mim := ci.mims[i];
93437da2899SCharles.Forsyth		dim := mim.im;
93537da2899SCharles.Forsyth		if(dim != nil)
93637da2899SCharles.Forsyth			tot += ((dim.r.max.x-dim.r.min.x)*dim.depth/8) *
93737da2899SCharles.Forsyth					(dim.r.max.y-dim.r.min.y);
93837da2899SCharles.Forsyth		dim = mim.mask;
93937da2899SCharles.Forsyth		if(dim != nil)
94037da2899SCharles.Forsyth			tot += ((dim.r.max.x-dim.r.min.x)*dim.depth/8) *
94137da2899SCharles.Forsyth					(dim.r.max.y-dim.r.min.y);
94237da2899SCharles.Forsyth	}
94337da2899SCharles.Forsyth	return tot;
94437da2899SCharles.Forsyth}
94537da2899SCharles.Forsyth
94637da2899SCharles.Forsyth# Call this after initial windows have been made,
94737da2899SCharles.Forsyth# so that resetlimits() will exclude the images for those
94837da2899SCharles.Forsyth# windows from the available memory.
94937da2899SCharles.ForsythImageCache.init(ic: self ref ImageCache)
95037da2899SCharles.Forsyth{
95137da2899SCharles.Forsyth	ic.imhd = nil;
95237da2899SCharles.Forsyth	ic.imtl = nil;
95337da2899SCharles.Forsyth	ic.n = 0;
95437da2899SCharles.Forsyth	ic.memused = 0;
95537da2899SCharles.Forsyth	ic.resetlimits();
95637da2899SCharles.Forsyth}
95737da2899SCharles.Forsyth
95837da2899SCharles.Forsyth# Call resetlimits when amount of non-image-cache image
95937da2899SCharles.Forsyth# memory might have changed significantly (e.g., on main window resize).
96037da2899SCharles.ForsythImageCache.resetlimits(ic: self ref ImageCache)
96137da2899SCharles.Forsyth{
96237da2899SCharles.Forsyth	res := ResourceState.cur();
96337da2899SCharles.Forsyth	avail := res.imagelim - (res.image-ic.memused);
96437da2899SCharles.Forsyth		# (res.image-ic.memused) is used memory not in image cache
96537da2899SCharles.Forsyth	avail = 8*avail/10;	# allow 20% slop for other applications, etc.
96637da2899SCharles.Forsyth	ic.memlimit = config.imagecachemem;
96737da2899SCharles.Forsyth	if(ic.memlimit > avail)
96837da2899SCharles.Forsyth		ic.memlimit = avail;
96937da2899SCharles.Forsyth#	ic.nlimit = config.imagecachenum;
97037da2899SCharles.Forsyth	ic.nlimit = 10000;	# let's try this
97137da2899SCharles.Forsyth	ic.need(0);	# if resized, perhaps need to shed some images
97237da2899SCharles.Forsyth}
97337da2899SCharles.Forsyth
97437da2899SCharles.Forsyth# Look for a CImage matching ci, and if found, move it
97537da2899SCharles.Forsyth# to the tail position (i.e., MRU)
97637da2899SCharles.ForsythImageCache.look(ic: self ref ImageCache, ci: ref CImage) : ref CImage
97737da2899SCharles.Forsyth{
97837da2899SCharles.Forsyth	ans : ref CImage = nil;
97937da2899SCharles.Forsyth	prev : ref CImage = nil;
98037da2899SCharles.Forsyth	for(i := ic.imhd; i != nil; i = i.next) {
98137da2899SCharles.Forsyth		if(i.match(ci)) {
98237da2899SCharles.Forsyth			if(ic.imtl != i) {
98337da2899SCharles.Forsyth				# remove from current place in cache chain
98437da2899SCharles.Forsyth				# and put at tail
98537da2899SCharles.Forsyth				if(prev != nil)
98637da2899SCharles.Forsyth					prev.next = i.next;
98737da2899SCharles.Forsyth				else
98837da2899SCharles.Forsyth					ic.imhd = i.next;
98937da2899SCharles.Forsyth				i.next = nil;
99037da2899SCharles.Forsyth				ic.imtl.next = i;
99137da2899SCharles.Forsyth				ic.imtl = i;
99237da2899SCharles.Forsyth			}
99337da2899SCharles.Forsyth			ans = i;
99437da2899SCharles.Forsyth			break;
99537da2899SCharles.Forsyth		}
99637da2899SCharles.Forsyth		prev = i;
99737da2899SCharles.Forsyth	}
99837da2899SCharles.Forsyth	return ans;
99937da2899SCharles.Forsyth}
100037da2899SCharles.Forsyth
100137da2899SCharles.Forsyth# Call this to add ci as MRU of cache chain (should only call if
100237da2899SCharles.Forsyth# it is known that a ci with same image isn't already there).
100337da2899SCharles.Forsyth# Update ic.memused.
100437da2899SCharles.Forsyth# Assume ic.need has been called to ensure that neither
100537da2899SCharles.Forsyth# memlimit nor nlimit will be exceeded.
100637da2899SCharles.ForsythImageCache.add(ic: self ref ImageCache, ci: ref CImage)
100737da2899SCharles.Forsyth{
100837da2899SCharles.Forsyth	ci.next = nil;
100937da2899SCharles.Forsyth	if(ic.imhd == nil)
101037da2899SCharles.Forsyth		ic.imhd = ci;
101137da2899SCharles.Forsyth	else
101237da2899SCharles.Forsyth		ic.imtl.next = ci;
101337da2899SCharles.Forsyth	ic.imtl = ci;
101437da2899SCharles.Forsyth	ic.memused += ci.bytes();
101537da2899SCharles.Forsyth	ic.n++;
101637da2899SCharles.Forsyth}
101737da2899SCharles.Forsyth
101837da2899SCharles.Forsyth# Delete least-recently-used image in image cache
101937da2899SCharles.Forsyth# and update memused and n.
102037da2899SCharles.ForsythImageCache.deletelru(ic: self ref ImageCache)
102137da2899SCharles.Forsyth{
102237da2899SCharles.Forsyth	ci := ic.imhd;
102337da2899SCharles.Forsyth	if(ci != nil) {
102437da2899SCharles.Forsyth		ic.imhd = ci.next;
102537da2899SCharles.Forsyth		if(ic.imhd == nil) {
102637da2899SCharles.Forsyth			ic.imtl = nil;
102737da2899SCharles.Forsyth			ic.memused = 0;
102837da2899SCharles.Forsyth		}
102937da2899SCharles.Forsyth		else
103037da2899SCharles.Forsyth			ic.memused -= ci.bytes();
103137da2899SCharles.Forsyth		for(i := 0; i < len ci.mims; i++)
103237da2899SCharles.Forsyth			ci.mims[i].free();
103337da2899SCharles.Forsyth		ci.mims = nil;
103437da2899SCharles.Forsyth		ic.n--;
103537da2899SCharles.Forsyth	}
103637da2899SCharles.Forsyth}
103737da2899SCharles.Forsyth
103837da2899SCharles.ForsythImageCache.clear(ic: self ref ImageCache)
103937da2899SCharles.Forsyth{
104037da2899SCharles.Forsyth	while(ic.imhd != nil)
104137da2899SCharles.Forsyth		ic.deletelru();
104237da2899SCharles.Forsyth}
104337da2899SCharles.Forsyth
104437da2899SCharles.Forsyth# Call this just before allocating an Image that will used nbytes
104537da2899SCharles.Forsyth# of image memory, to ensure that if the image were to be
104637da2899SCharles.Forsyth# added to the image cache then memlimit and nlimit will be ok.
104737da2899SCharles.Forsyth# LRU images will be shed if necessary.
104837da2899SCharles.Forsyth# Return 0 if it will be impossible to make enough memory.
104937da2899SCharles.ForsythImageCache.need(ic: self ref ImageCache, nbytes: int) : int
105037da2899SCharles.Forsyth{
105137da2899SCharles.Forsyth	while(ic.n >= ic.nlimit || ic.memused+nbytes > ic.memlimit) {
105237da2899SCharles.Forsyth		if(ic.imhd == nil)
105337da2899SCharles.Forsyth			return 0;
105437da2899SCharles.Forsyth		ic.deletelru();
105537da2899SCharles.Forsyth	}
105637da2899SCharles.Forsyth	return 1;
105737da2899SCharles.Forsyth}
105837da2899SCharles.Forsyth
105937da2899SCharles.Forsythstrhash(s: string) : int
106037da2899SCharles.Forsyth{
106137da2899SCharles.Forsyth	prime: con 8388617;
106237da2899SCharles.Forsyth	hash := 0;
106337da2899SCharles.Forsyth	n := len s;
106437da2899SCharles.Forsyth	for(i := 0; i < n; i++) {
106537da2899SCharles.Forsyth		hash = hash % prime;
106637da2899SCharles.Forsyth		hash = (hash << 7) + s[i];
106737da2899SCharles.Forsyth	}
106837da2899SCharles.Forsyth	return hash;
106937da2899SCharles.Forsyth}
107037da2899SCharles.Forsyth
107137da2899SCharles.Forsythschemeid(s: string): int
107237da2899SCharles.Forsyth{
107337da2899SCharles.Forsyth	for (i := 0; i < len schemes; i++) {
107437da2899SCharles.Forsyth		(n, id) := schemes[i];
107537da2899SCharles.Forsyth		if (n == s)
107637da2899SCharles.Forsyth			return id;
107737da2899SCharles.Forsyth	}
107837da2899SCharles.Forsyth	return -1;
107937da2899SCharles.Forsyth}
108037da2899SCharles.Forsyth
108137da2899SCharles.Forsythschemeok(s: string): int
108237da2899SCharles.Forsyth{
108337da2899SCharles.Forsyth	return schemeid(s) != -1;
108437da2899SCharles.Forsyth}
108537da2899SCharles.Forsyth
108637da2899SCharles.Forsythgettransport(scheme: string) : (Transport, string)
108737da2899SCharles.Forsyth{
108837da2899SCharles.Forsyth	err := "";
108937da2899SCharles.Forsyth	transport: Transport = nil;
109037da2899SCharles.Forsyth	tindex := schemeid(scheme);
109137da2899SCharles.Forsyth	if (tindex == -1)
109237da2899SCharles.Forsyth		return (nil, "Unknown scheme");
109337da2899SCharles.Forsyth	transport = transports[tindex];
109437da2899SCharles.Forsyth	if (transport == nil) {
109537da2899SCharles.Forsyth		transport = load Transport loadpath(tpaths[tindex]);
109637da2899SCharles.Forsyth		if(transport == nil)
109737da2899SCharles.Forsyth			return (nil, sys->sprint("Can't load transport %s: %r", tpaths[tindex]));
109837da2899SCharles.Forsyth		transport->init(me);
109937da2899SCharles.Forsyth		transports[tindex] = transport;
110037da2899SCharles.Forsyth	}
110137da2899SCharles.Forsyth	return (transport, err);
110237da2899SCharles.Forsyth}
110337da2899SCharles.Forsyth
110437da2899SCharles.Forsyth# Return new Header with default values for fields
110537da2899SCharles.ForsythHeader.new() : ref Header
110637da2899SCharles.Forsyth{
110737da2899SCharles.Forsyth	return ref Header(
110837da2899SCharles.Forsyth		HCOk,		# code
110937da2899SCharles.Forsyth		nil,		# actual
111037da2899SCharles.Forsyth		nil,		# base
111137da2899SCharles.Forsyth		nil,		# location
111237da2899SCharles.Forsyth		-1,		# length
111337da2899SCharles.Forsyth		UnknownType,	# mtype
111437da2899SCharles.Forsyth		nil,		# chset
111537da2899SCharles.Forsyth		"",		# msg
111637da2899SCharles.Forsyth		"",		# refresh
111737da2899SCharles.Forsyth		"",		# chal
111837da2899SCharles.Forsyth		"",		# warn
111937da2899SCharles.Forsyth		""		# last-modified
112037da2899SCharles.Forsyth	);
112137da2899SCharles.Forsyth}
112237da2899SCharles.Forsyth
112337da2899SCharles.Forsythjpmagic := array[] of {byte 16rFF, byte 16rD8, byte 16rFF, byte 16rE0,
112437da2899SCharles.Forsyth		byte 0, byte 0, byte 'J', byte 'F', byte 'I', byte 'F', byte 0};
112537da2899SCharles.Forsythpngsig := array[] of { byte 137, byte 80, byte 78, byte 71, byte 13, byte 10, byte 26, byte 10 };
112637da2899SCharles.Forsyth
112737da2899SCharles.Forsyth# Set the mtype (and possibly chset) fields of h based on (in order):
112837da2899SCharles.Forsyth#	first bytes of file, if unambigous
112937da2899SCharles.Forsyth#	file name extension
113037da2899SCharles.Forsyth#	first bytes of file, even if unambigous (guess)
113137da2899SCharles.Forsyth#	if all else fails, then leave as UnknownType.
113237da2899SCharles.Forsyth# If it's a text type, also set the chset.
113337da2899SCharles.Forsyth# (HTTP Transport will try to use Content-Type first, and call this if that
113437da2899SCharles.Forsyth# doesn't work; other Transports will have to rely on this "guessing" function.)
113537da2899SCharles.ForsythHeader.setmediatype(h: self ref Header, name: string, first: array of byte)
113637da2899SCharles.Forsyth{
113737da2899SCharles.Forsyth	# Look for key signatures at beginning of file (perhaps after whitespace)
113837da2899SCharles.Forsyth	n := len first;
113937da2899SCharles.Forsyth	mt := UnknownType;
114037da2899SCharles.Forsyth	for(i := 0; i < n; i++)
114137da2899SCharles.Forsyth		if(ctype[int first[i]] != C->W)
114237da2899SCharles.Forsyth			break;
114337da2899SCharles.Forsyth	if(n - i >= 6) {
114437da2899SCharles.Forsyth		s := string first[i:i+6];
114537da2899SCharles.Forsyth		case S->tolower(s) {
114637da2899SCharles.Forsyth		"<html " or "<html\t" or "<html>" or "<head>" or "<title" =>
114737da2899SCharles.Forsyth			mt = TextHtml;
114837da2899SCharles.Forsyth		"<!doct" =>
114937da2899SCharles.Forsyth			if(n - i >= 14 && string first[i+6:i+14] == "ype html")
115037da2899SCharles.Forsyth				mt = TextHtml;
115137da2899SCharles.Forsyth		"gif87a" or "gif89a" =>
115237da2899SCharles.Forsyth			if(i == 0)
115337da2899SCharles.Forsyth				mt = ImageGif;
115437da2899SCharles.Forsyth		"#defin" =>
115537da2899SCharles.Forsyth			# perhaps should check more definitively...
115637da2899SCharles.Forsyth			mt = ImageXXBitmap;
115737da2899SCharles.Forsyth		}
115837da2899SCharles.Forsyth
115937da2899SCharles.Forsyth		if (mt == UnknownType && n > 0) {
116037da2899SCharles.Forsyth			if (first[0] == jpmagic[0] && n >= len jpmagic) {
116137da2899SCharles.Forsyth				for(i++; i<len jpmagic; i++)
116237da2899SCharles.Forsyth					if(jpmagic[i]>byte 0 && first[i]!=jpmagic[i])
116337da2899SCharles.Forsyth						break;
116437da2899SCharles.Forsyth				if (i == len jpmagic)
116537da2899SCharles.Forsyth					mt = ImageJpeg;
116637da2899SCharles.Forsyth			} else if (first[0] == pngsig[0] && n >= len pngsig) {
116737da2899SCharles.Forsyth				for(i++; i<len pngsig; i++)
116837da2899SCharles.Forsyth					if (first[i] != pngsig[i])
116937da2899SCharles.Forsyth						break;
117037da2899SCharles.Forsyth				if (i == len pngsig)
117137da2899SCharles.Forsyth					mt = ImagePng;
117237da2899SCharles.Forsyth			}
117337da2899SCharles.Forsyth		}
117437da2899SCharles.Forsyth	}
117537da2899SCharles.Forsyth
117637da2899SCharles.Forsyth	if(mt == UnknownType) {
117737da2899SCharles.Forsyth		# Try file name extension
117837da2899SCharles.Forsyth		(nil, file) := S->splitr(name, "/");
117937da2899SCharles.Forsyth		if(file != "") {
118037da2899SCharles.Forsyth			(f, ext) := S->splitr(file, ".");
118137da2899SCharles.Forsyth			if(f != "" && ext != "") {
118237da2899SCharles.Forsyth				(fnd, val) := T->lookup(fileexttable, S->tolower(ext));
118337da2899SCharles.Forsyth				if(fnd)
118437da2899SCharles.Forsyth					mt = val;
118537da2899SCharles.Forsyth			}
118637da2899SCharles.Forsyth		}
118737da2899SCharles.Forsyth	}
118837da2899SCharles.Forsyth
118937da2899SCharles.Forsyth#	if(mt == UnknownType) {
119037da2899SCharles.Forsyth#		mt = TextPlain;
119137da2899SCharles.Forsyth#		h.chset = "utf8";
119237da2899SCharles.Forsyth#	}
119337da2899SCharles.Forsyth	h.mtype = mt;
119437da2899SCharles.Forsyth}
119537da2899SCharles.Forsyth
119637da2899SCharles.ForsythHeader.print(h: self ref Header)
119737da2899SCharles.Forsyth{
119837da2899SCharles.Forsyth	mtype := "?";
119937da2899SCharles.Forsyth	if(h.mtype >= 0 && h.mtype < len mnames)
120037da2899SCharles.Forsyth		mtype = mnames[h.mtype];
120137da2899SCharles.Forsyth	chset := "?";
120237da2899SCharles.Forsyth	if(h.chset != nil)
120337da2899SCharles.Forsyth		chset = h.chset;
120437da2899SCharles.Forsyth	# sys->print("code=%d (%s) length=%d mtype=%s chset=%s\n",
120537da2899SCharles.Forsyth	#	h.code, hcphrase(h.code), h.length, mtype, chset);
120637da2899SCharles.Forsyth	if(h.base != nil)
120737da2899SCharles.Forsyth		sys->print("  base=%s\n", h.base.tostring());
120837da2899SCharles.Forsyth	if(h.location != nil)
120937da2899SCharles.Forsyth		sys->print("  location=%s\n", h.location.tostring());
121037da2899SCharles.Forsyth	if(h.refresh != "")
121137da2899SCharles.Forsyth		sys->print("  refresh=%s\n", h.refresh);
121237da2899SCharles.Forsyth	if(h.chal != "")
121337da2899SCharles.Forsyth		sys->print("  chal=%s\n", h.chal);
121437da2899SCharles.Forsyth	if(h.warn != "")
121537da2899SCharles.Forsyth		sys->print("  warn=%s\n", h.warn);
121637da2899SCharles.Forsyth}
121737da2899SCharles.Forsyth
121837da2899SCharles.Forsyth
121937da2899SCharles.Forsythmfd : ref sys->FD = nil;
122037da2899SCharles.ForsythResourceState.cur() : ResourceState
122137da2899SCharles.Forsyth{
122237da2899SCharles.Forsyth	ms := sys->millisec();
122337da2899SCharles.Forsyth	main := 0;
122437da2899SCharles.Forsyth	mainlim := 0;
122537da2899SCharles.Forsyth	heap := 0;
122637da2899SCharles.Forsyth	heaplim := 0;
122737da2899SCharles.Forsyth	image := 0;
122837da2899SCharles.Forsyth	imagelim := 0;
122937da2899SCharles.Forsyth	if(mfd == nil)
123037da2899SCharles.Forsyth		mfd = sys->open("/dev/memory", sys->OREAD);
123137da2899SCharles.Forsyth	if (mfd == nil)
123254041ca4Sforsyth		raise sys->sprint("can't open /dev/memory: %r");
123337da2899SCharles.Forsyth
123437da2899SCharles.Forsyth	sys->seek(mfd, big 0, Sys->SEEKSTART);
123537da2899SCharles.Forsyth
123637da2899SCharles.Forsyth	buf := array[400] of byte;
123737da2899SCharles.Forsyth	n := sys->read(mfd, buf, len buf);
123837da2899SCharles.Forsyth	if (n <= 0)
123954041ca4Sforsyth		raise sys->sprint("can't read /dev/memory: %r");
124037da2899SCharles.Forsyth
124137da2899SCharles.Forsyth	(nil, l) := sys->tokenize(string buf[0:n], "\n");
124237da2899SCharles.Forsyth	# p->cursize, p->maxsize, p->hw, p->nalloc, p->nfree, p->nbrk, poolmax(p), p->name)
124337da2899SCharles.Forsyth	while(l != nil) {
124437da2899SCharles.Forsyth		s := hd l;
124537da2899SCharles.Forsyth		cur_size := int s[0:12];
124637da2899SCharles.Forsyth		max_size := int s[12:24];
124737da2899SCharles.Forsyth		case s[7*12:] {
124837da2899SCharles.Forsyth		"main" =>
124937da2899SCharles.Forsyth			main = cur_size;
125037da2899SCharles.Forsyth			mainlim = max_size;
125137da2899SCharles.Forsyth		"heap" =>
125237da2899SCharles.Forsyth			heap = cur_size;
125337da2899SCharles.Forsyth			heaplim = max_size;
125437da2899SCharles.Forsyth		"image" =>
125537da2899SCharles.Forsyth			image = cur_size;
125637da2899SCharles.Forsyth			imagelim = max_size;
125737da2899SCharles.Forsyth		}
125837da2899SCharles.Forsyth		l = tl l;
125937da2899SCharles.Forsyth	}
126037da2899SCharles.Forsyth
126137da2899SCharles.Forsyth	return ResourceState(ms, main, mainlim, heap, heaplim, image, imagelim);
126237da2899SCharles.Forsyth}
126337da2899SCharles.Forsyth
126437da2899SCharles.ForsythResourceState.since(rnew: self ResourceState, rold: ResourceState) : ResourceState
126537da2899SCharles.Forsyth{
126637da2899SCharles.Forsyth	return (rnew.ms - rold.ms,
126737da2899SCharles.Forsyth		rnew.main - rold.main,
126837da2899SCharles.Forsyth		rnew.heaplim,
126937da2899SCharles.Forsyth		rnew.heap - rold.heap,
127037da2899SCharles.Forsyth		rnew.heaplim,
127137da2899SCharles.Forsyth		rnew.image - rold.image,
127237da2899SCharles.Forsyth		rnew.imagelim);
127337da2899SCharles.Forsyth}
127437da2899SCharles.Forsyth
127537da2899SCharles.ForsythResourceState.print(r: self ResourceState, msg: string)
127637da2899SCharles.Forsyth{
127737da2899SCharles.Forsyth	sys->print("%s:\n\ttime: %d.%#.3ds; memory: main %dk, mainlim %dk, heap %dk, heaplim %dk, image %dk, imagelim %dk\n",
127837da2899SCharles.Forsyth				msg, r.ms/1000, r.ms % 1000, r.main / 1024, r.mainlim / 1024,
127937da2899SCharles.Forsyth				r.heap / 1024, r.heaplim / 1024, r.image / 1024, r.imagelim / 1024);
128037da2899SCharles.Forsyth}
128137da2899SCharles.Forsyth
128237da2899SCharles.Forsyth# Decide what to do based on Header and whether this is
128337da2899SCharles.Forsyth# for the main entity or not, and the number of redirections-so-far.
128437da2899SCharles.Forsyth# Return tuple contains:
128537da2899SCharles.Forsyth#	(use, error, challenge, redir)
128637da2899SCharles.Forsyth# and action to do is:
128737da2899SCharles.Forsyth#	If use==1, use the entity else drain its byte source.
128837da2899SCharles.Forsyth#	If error != nil, mesg was put in progress bar
128937da2899SCharles.Forsyth#	If challenge != nil, get auth info and make new request with auth
129037da2899SCharles.Forsyth#	Else if redir != nil, make a new request with redir for url
129137da2899SCharles.Forsyth#
129237da2899SCharles.Forsyth# (if challenge or redir is non-nil, use will be 0)
129337da2899SCharles.Forsythhdraction(bs: ref ByteSource, ismain: int, nredirs: int) : (int, string, string, ref U->Parsedurl)
129437da2899SCharles.Forsyth{
129537da2899SCharles.Forsyth	use := 1;
129637da2899SCharles.Forsyth	error := "";
129737da2899SCharles.Forsyth	challenge := "";
129837da2899SCharles.Forsyth	redir : ref U->Parsedurl = nil;
129937da2899SCharles.Forsyth
130037da2899SCharles.Forsyth	h := bs.hdr;
130137da2899SCharles.Forsyth	assert(h != nil);
130237da2899SCharles.Forsyth	bs.seenhdr = 1;
130337da2899SCharles.Forsyth	code := h.code;
130437da2899SCharles.Forsyth	case code/100 {
130537da2899SCharles.Forsyth	HSOk =>
130637da2899SCharles.Forsyth		if(code != HCOk)
130737da2899SCharles.Forsyth			error = "unexpected code: " + hcphrase(code);
130837da2899SCharles.Forsyth	HSRedirect =>
130937da2899SCharles.Forsyth		if(h.location != nil) {
131037da2899SCharles.Forsyth			redir = h.location;
131137da2899SCharles.Forsyth			# spec says url should be absolute, but some
131237da2899SCharles.Forsyth			# sites give relative ones
131337da2899SCharles.Forsyth			if(redir.scheme == nil)
131437da2899SCharles.Forsyth				redir = U->mkabs(redir, h.base);
131537da2899SCharles.Forsyth			if(dbg)
131637da2899SCharles.Forsyth				sys->print("redirect %s to %s\n", h.actual.tostring(), redir.tostring());
131737da2899SCharles.Forsyth			if(nredirs >= Maxredir) {
131837da2899SCharles.Forsyth				redir = nil;
131937da2899SCharles.Forsyth				error = "probable redirect loop";
132037da2899SCharles.Forsyth			}
132137da2899SCharles.Forsyth			else
132237da2899SCharles.Forsyth				use = 0;
132337da2899SCharles.Forsyth		}
132437da2899SCharles.Forsyth	HSError =>
132537da2899SCharles.Forsyth		if(code == HCUnauthorized && h.chal != "") {
132637da2899SCharles.Forsyth			challenge = h.chal;
132737da2899SCharles.Forsyth			use = 0;
132837da2899SCharles.Forsyth		}
132937da2899SCharles.Forsyth		else {
133037da2899SCharles.Forsyth			error = hcphrase(code);
133137da2899SCharles.Forsyth			use = ismain;
133237da2899SCharles.Forsyth		}
133337da2899SCharles.Forsyth	HSServererr =>
133437da2899SCharles.Forsyth		error = hcphrase(code);
133537da2899SCharles.Forsyth		use = ismain;
133637da2899SCharles.Forsyth	* =>
133737da2899SCharles.Forsyth		error = "unexpected code: " + string code;
133837da2899SCharles.Forsyth		use = 0;
133937da2899SCharles.Forsyth
134037da2899SCharles.Forsyth	}
134137da2899SCharles.Forsyth	if(error != "")
134237da2899SCharles.Forsyth		G->progress <-= (bs.id, G->Perr, 0, error);
134337da2899SCharles.Forsyth	return (use, error, challenge, redir);
134437da2899SCharles.Forsyth}
134537da2899SCharles.Forsyth
134637da2899SCharles.Forsyth# Use event when only care about time stamps on events
134737da2899SCharles.Forsythevent(s: string, data: int)
134837da2899SCharles.Forsyth{
134937da2899SCharles.Forsyth	sys->print("%s: %d %d\n", s, sys->millisec()-startres.ms, data);
135037da2899SCharles.Forsyth}
135137da2899SCharles.Forsyth
135237da2899SCharles.Forsythkill(pid: int, dogroup: int)
135337da2899SCharles.Forsyth{
135437da2899SCharles.Forsyth	msg : array of byte;
135537da2899SCharles.Forsyth	if(dogroup)
135637da2899SCharles.Forsyth		msg = array of byte "killgrp";
135737da2899SCharles.Forsyth	else
135837da2899SCharles.Forsyth		msg = array of byte "kill";
135937da2899SCharles.Forsyth	ctl := sys->open("#p/" + string pid + "/ctl", sys->OWRITE);
136037da2899SCharles.Forsyth	if(ctl != nil)
136137da2899SCharles.Forsyth		if (sys->write(ctl, msg, len msg) < 0)
136237da2899SCharles.Forsyth			sys->print("charon: kill write failed (pid %d, grp %d): %r\n", pid, dogroup);
136337da2899SCharles.Forsyth}
136437da2899SCharles.Forsyth
136537da2899SCharles.Forsyth# Read a line up to and including cr/lf (be tolerant and allow missing cr).
136637da2899SCharles.Forsyth# Look first in buf[bstart:bend], and if that isn't sufficient to get whole line,
136737da2899SCharles.Forsyth# refill buf from fd as needed.
136837da2899SCharles.Forsyth# Return values:
136937da2899SCharles.Forsyth#	array of byte: the line, not including cr/lf
137037da2899SCharles.Forsyth#	eof, true if there was no line to get or a read error
137137da2899SCharles.Forsyth#	bstart', bend': new valid portion of buf (after cr/lf).
137237da2899SCharles.Forsythgetline(fd: ref sys->FD, buf: array of byte, bstart, bend: int) :
137337da2899SCharles.Forsyth		(array of byte, int, int, int)
137437da2899SCharles.Forsyth{
137537da2899SCharles.Forsyth	ans : array of byte = nil;
137637da2899SCharles.Forsyth	last : array of byte = nil;
137737da2899SCharles.Forsyth	eof := 0;
137837da2899SCharles.Forsythmainloop:
137937da2899SCharles.Forsyth	for(;;) {
138037da2899SCharles.Forsyth		for(i := bstart; i < bend; i++) {
138137da2899SCharles.Forsyth			if(buf[i] == byte '\n') {
138237da2899SCharles.Forsyth				k := i;
138337da2899SCharles.Forsyth				if(k > bstart && buf[k-1] == byte '\r')
138437da2899SCharles.Forsyth					k--;
138537da2899SCharles.Forsyth				last = buf[bstart:k];
138637da2899SCharles.Forsyth				bstart = i+1;
138737da2899SCharles.Forsyth				break mainloop;
138837da2899SCharles.Forsyth			}
138937da2899SCharles.Forsyth		}
139037da2899SCharles.Forsyth		if(bend > bstart)
139137da2899SCharles.Forsyth			ans = append(ans, buf[bstart:bend]);
139237da2899SCharles.Forsyth		last = nil;
139337da2899SCharles.Forsyth		bstart = 0;
139437da2899SCharles.Forsyth		bend = sys->read(fd, buf, len buf);
139537da2899SCharles.Forsyth		if(bend <= 0) {
139637da2899SCharles.Forsyth			eof = 1;
139737da2899SCharles.Forsyth			bend = 0;
139837da2899SCharles.Forsyth			break mainloop;
139937da2899SCharles.Forsyth		}
140037da2899SCharles.Forsyth	}
140137da2899SCharles.Forsyth	return (append(ans, last), eof, bstart, bend);
140237da2899SCharles.Forsyth}
140337da2899SCharles.Forsyth
140437da2899SCharles.Forsyth# Append copy of second array to first, return (possibly new)
140537da2899SCharles.Forsyth# address of the concatenation.
140637da2899SCharles.Forsythappend(a: array of byte, b: array of byte) : array of byte
140737da2899SCharles.Forsyth{
140837da2899SCharles.Forsyth	if(b == nil)
140937da2899SCharles.Forsyth		return a;
141037da2899SCharles.Forsyth	na := len a;
141137da2899SCharles.Forsyth	nb := len b;
141237da2899SCharles.Forsyth	ans := realloc(a, nb);
141337da2899SCharles.Forsyth	ans[na:] = b;
141437da2899SCharles.Forsyth	return ans;
141537da2899SCharles.Forsyth}
141637da2899SCharles.Forsyth
141737da2899SCharles.Forsyth# Return copy of a, but incr bytes bigger
141837da2899SCharles.Forsythrealloc(a: array of byte, incr: int) : array of byte
141937da2899SCharles.Forsyth{
142037da2899SCharles.Forsyth	n := len a;
142137da2899SCharles.Forsyth	newa := array[n + incr] of byte;
142237da2899SCharles.Forsyth	if(a != nil)
142337da2899SCharles.Forsyth		newa[0:] = a;
142437da2899SCharles.Forsyth	return newa;
142537da2899SCharles.Forsyth}
142637da2899SCharles.Forsyth
142737da2899SCharles.Forsyth# Look (linearly) through a for s; return its index if found, else -1.
142837da2899SCharles.Forsythstrlookup(a: array of string, s: string) : int
142937da2899SCharles.Forsyth{
143037da2899SCharles.Forsyth	n := len a;
143137da2899SCharles.Forsyth	for(i := 0; i < n; i++)
143237da2899SCharles.Forsyth		if(s == a[i])
143337da2899SCharles.Forsyth			return i;
143437da2899SCharles.Forsyth	return -1;
143537da2899SCharles.Forsyth}
143637da2899SCharles.Forsyth
143737da2899SCharles.Forsyth# Set up config global to defaults, then try to read user-specifiic
143837da2899SCharles.Forsyth# config data from /usr/<username>/charon/config, then try to
143937da2899SCharles.Forsyth# override from command line arguments.
144037da2899SCharles.Forsythsetconfig(argl: list of string)
144137da2899SCharles.Forsyth{
144237da2899SCharles.Forsyth	# Defaults, in absence of any other information
144337da2899SCharles.Forsyth	config.userdir = "";
144437da2899SCharles.Forsyth	config.srcdir = "/appl/cmd/charon";
144537da2899SCharles.Forsyth	config.starturl = "file:/services/webget/start.html";
144637da2899SCharles.Forsyth	config.homeurl = config.starturl;
144737da2899SCharles.Forsyth	config.change_homeurl = 1;
144837da2899SCharles.Forsyth	config.helpurl = "file:/services/webget/help.html";
144937da2899SCharles.Forsyth	config.usessl = SSLV3;	# was NOSSL
145037da2899SCharles.Forsyth	config.devssl = 0;
145137da2899SCharles.Forsyth	config.custbkurl = "/services/config/bookmarks.html";
145237da2899SCharles.Forsyth	config.dualbkurl = "/services/config/dualdisplay.html";
145337da2899SCharles.Forsyth	config.httpproxy = nil;
145437da2899SCharles.Forsyth	config.noproxydoms = nil;
145537da2899SCharles.Forsyth	config.buttons = "help,resize,hide,exit";
145637da2899SCharles.Forsyth	config.framework = "all";
145737da2899SCharles.Forsyth	config.defaultwidth = 640;
145837da2899SCharles.Forsyth	config.defaultheight = 480;
145937da2899SCharles.Forsyth	config.x = -1;
146037da2899SCharles.Forsyth	config.y = -1;
146137da2899SCharles.Forsyth	config.nocache = 0;
146237da2899SCharles.Forsyth	config.maxstale = 0;
146337da2899SCharles.Forsyth	config.imagelvl = ImgFull;
146437da2899SCharles.Forsyth	config.imagecachenum = 120;
146537da2899SCharles.Forsyth	config.imagecachemem = 100000000;	# 100Meg, will get lowered later
146637da2899SCharles.Forsyth	config.docookies = 1;
146737da2899SCharles.Forsyth	config.doscripts = 1;
146837da2899SCharles.Forsyth	config.httpminor = 0;
146937da2899SCharles.Forsyth	config.agentname = "Mozilla/4.08 (Charon; Inferno)";
147037da2899SCharles.Forsyth	config.nthreads = 4;
147137da2899SCharles.Forsyth	config.offersave = 1;
147237da2899SCharles.Forsyth	config.charset = "windows-1252";
147337da2899SCharles.Forsyth	config.plumbport = "web";
147437da2899SCharles.Forsyth	config.wintitle = "Charon";	# tkclient->titlebar() title, used by GUI
147537da2899SCharles.Forsyth	config.dbgfile = "";
147637da2899SCharles.Forsyth	config.dbg = array[128] of { * => byte 0 };
147737da2899SCharles.Forsyth
147837da2899SCharles.Forsyth	# Reading default config file
147937da2899SCharles.Forsyth	readconf("/services/config/charon.cfg");
148037da2899SCharles.Forsyth
148137da2899SCharles.Forsyth	# Try reading user config file
148237da2899SCharles.Forsyth	user := "";
148337da2899SCharles.Forsyth	fd := sys->open("/dev/user", sys->OREAD);
148437da2899SCharles.Forsyth	if(fd != nil) {
148537da2899SCharles.Forsyth		b := array[40] of byte;
148637da2899SCharles.Forsyth		n := sys->read(fd, b, len b);
148737da2899SCharles.Forsyth		if(n > 0)
148837da2899SCharles.Forsyth			user = string b[0:n];
148937da2899SCharles.Forsyth	}
149037da2899SCharles.Forsyth	if(user != "") {
149137da2899SCharles.Forsyth		config.userdir = "/usr/" + user + "/charon";
149237da2899SCharles.Forsyth		readconf(config.userdir + "/config");
149337da2899SCharles.Forsyth	}
149437da2899SCharles.Forsyth
149537da2899SCharles.Forsyth	if(argl == nil)
149637da2899SCharles.Forsyth		return;
149737da2899SCharles.Forsyth	# Try command line arguments
149837da2899SCharles.Forsyth	# All should be 'key=val' or '-key' or '-key val', except last which can be url to start
149937da2899SCharles.Forsyth	for(l := tl argl; l != nil; l = tl l) {
150037da2899SCharles.Forsyth		s := hd l;
150137da2899SCharles.Forsyth		if(s == "")
150237da2899SCharles.Forsyth			continue;
150337da2899SCharles.Forsyth		if (s[0] != '-')
150437da2899SCharles.Forsyth			break;
150537da2899SCharles.Forsyth		a := s[1:];
150637da2899SCharles.Forsyth		b := "";
150737da2899SCharles.Forsyth		if(tl l != nil) {
150837da2899SCharles.Forsyth			b = hd tl l;
150937da2899SCharles.Forsyth			if(S->prefix("-", b))
151037da2899SCharles.Forsyth				b = "";
151137da2899SCharles.Forsyth			else
151237da2899SCharles.Forsyth				l = tl l;
151337da2899SCharles.Forsyth		}
151437da2899SCharles.Forsyth		if(!setopt(a, b)) {
151537da2899SCharles.Forsyth			if (b != nil)
151637da2899SCharles.Forsyth				s += " "+b;
151737da2899SCharles.Forsyth			sys->print("couldn't set option from arg '%s'\n", s);
151837da2899SCharles.Forsyth		}
151937da2899SCharles.Forsyth	}
152037da2899SCharles.Forsyth	if(l != nil) {
152137da2899SCharles.Forsyth		if (tl l != nil)
152237da2899SCharles.Forsyth			# usage error
152337da2899SCharles.Forsyth			sys->print("too many URL's\n");
152437da2899SCharles.Forsyth		else
152537da2899SCharles.Forsyth			if(!setopt("starturl", hd l))
152637da2899SCharles.Forsyth				sys->print("couldn't set starturl from arg '%s'\n", hd l);
152737da2899SCharles.Forsyth	}
152837da2899SCharles.Forsyth}
152937da2899SCharles.Forsyth
153037da2899SCharles.Forsythreadconf(fname: string)
153137da2899SCharles.Forsyth{
153237da2899SCharles.Forsyth	cfgio := sys->open(fname, sys->OREAD);
153337da2899SCharles.Forsyth	if(cfgio != nil) {
153437da2899SCharles.Forsyth		buf := array[sys->ATOMICIO] of byte;
153537da2899SCharles.Forsyth		i := 0;
153637da2899SCharles.Forsyth		j := 0;
153737da2899SCharles.Forsyth		aline : array of byte;
153837da2899SCharles.Forsyth		eof := 0;
153937da2899SCharles.Forsyth		for(;;) {
154037da2899SCharles.Forsyth			(aline, eof, i, j) = getline(cfgio, buf, i, j);
154137da2899SCharles.Forsyth			if(eof)
154237da2899SCharles.Forsyth				break;
154337da2899SCharles.Forsyth			line := string aline;
154437da2899SCharles.Forsyth			if(len line == 0 || line[0]=='#')
154537da2899SCharles.Forsyth				continue;
154637da2899SCharles.Forsyth			(key, val) := S->splitl(line, " \t=");
154737da2899SCharles.Forsyth			if(key != "") {
154837da2899SCharles.Forsyth				val = S->take(S->drop(val, " \t="), "^#\r\n");
154937da2899SCharles.Forsyth				if(!setopt(key, val))
155037da2899SCharles.Forsyth					sys->print("couldn't set option from line '%s'\n", line);
155137da2899SCharles.Forsyth			}
155237da2899SCharles.Forsyth		}
155337da2899SCharles.Forsyth	}
155437da2899SCharles.Forsyth}
155537da2899SCharles.Forsyth
155637da2899SCharles.Forsyth# Set config option named 'key' to val, returning 1 if OK
155737da2899SCharles.Forsythsetopt(key: string, val: string) : int
155837da2899SCharles.Forsyth{
155937da2899SCharles.Forsyth	ok := 1;
156037da2899SCharles.Forsyth	if(val == "none")
156137da2899SCharles.Forsyth		val = "";
156237da2899SCharles.Forsyth	v := int val;
156337da2899SCharles.Forsyth	case key {
156437da2899SCharles.Forsyth	"userdir" =>
156537da2899SCharles.Forsyth		config.userdir = val;
156637da2899SCharles.Forsyth	"srcdir" =>
156737da2899SCharles.Forsyth		config.srcdir = val;
156837da2899SCharles.Forsyth	"starturl" =>
156937da2899SCharles.Forsyth		if(val != "")
157037da2899SCharles.Forsyth			config.starturl = val;
157137da2899SCharles.Forsyth		else
157237da2899SCharles.Forsyth			ok = 0;
157337da2899SCharles.Forsyth	"change_homeurl" =>
157437da2899SCharles.Forsyth		config.change_homeurl = v;
157537da2899SCharles.Forsyth	"homeurl" =>
157637da2899SCharles.Forsyth		if(val != "")
157737da2899SCharles.Forsyth			if(config.change_homeurl) {
157837da2899SCharles.Forsyth				config.homeurl = val;
157937da2899SCharles.Forsyth				# order dependent
158037da2899SCharles.Forsyth				config.starturl = config.homeurl;
158137da2899SCharles.Forsyth			}
158237da2899SCharles.Forsyth		else
158337da2899SCharles.Forsyth			ok = 0;
158437da2899SCharles.Forsyth	"helpurl" =>
158537da2899SCharles.Forsyth		if(val != "")
158637da2899SCharles.Forsyth			config.helpurl = val;
158737da2899SCharles.Forsyth		else
158837da2899SCharles.Forsyth			ok = 0;
158937da2899SCharles.Forsyth 	"usessl" =>
159037da2899SCharles.Forsyth 		if(val == "v2")
159137da2899SCharles.Forsyth 			config.usessl |= SSLV2;
159237da2899SCharles.Forsyth 		if(val == "v3")
159337da2899SCharles.Forsyth 			config.usessl |= SSLV3;
159437da2899SCharles.Forsyth 	"devssl" =>
159537da2899SCharles.Forsyth 		if(v == 0)
159637da2899SCharles.Forsyth 			config.devssl = 0;
159737da2899SCharles.Forsyth 		else
159837da2899SCharles.Forsyth 			config.devssl = 1;
159937da2899SCharles.Forsyth#	"custbkurl" =>
160037da2899SCharles.Forsyth#	"dualbkurl" =>
160137da2899SCharles.Forsyth	"httpproxy" =>
160237da2899SCharles.Forsyth		if(val != "")
160337da2899SCharles.Forsyth			config.httpproxy = makeabsurl(val);
160437da2899SCharles.Forsyth		else
160537da2899SCharles.Forsyth			config.httpproxy = nil;
160637da2899SCharles.Forsyth	"noproxy" or "noproxydoms" =>
160737da2899SCharles.Forsyth		(nil, config.noproxydoms) = sys->tokenize(val, ";, \t");
160837da2899SCharles.Forsyth	"buttons" =>
160937da2899SCharles.Forsyth		config.buttons = S->tolower(val);
161037da2899SCharles.Forsyth	"framework" =>
161137da2899SCharles.Forsyth		config.framework = S->tolower(val);
161237da2899SCharles.Forsyth	"defaultwidth" or "width" =>
161337da2899SCharles.Forsyth		if(v > 200)
161437da2899SCharles.Forsyth			config.defaultwidth = v;
161537da2899SCharles.Forsyth		else
161637da2899SCharles.Forsyth			ok = 0;
161737da2899SCharles.Forsyth	"defaultheight" or "height" =>
161837da2899SCharles.Forsyth		if(v > 100)
161937da2899SCharles.Forsyth			config.defaultheight = v;
162037da2899SCharles.Forsyth		else
162137da2899SCharles.Forsyth			ok = 0;
162237da2899SCharles.Forsyth	"x" =>
162337da2899SCharles.Forsyth		config.x = v;
162437da2899SCharles.Forsyth	"y" =>
162537da2899SCharles.Forsyth		config.y = v;
162637da2899SCharles.Forsyth	"nocache" =>
162737da2899SCharles.Forsyth		config.nocache = v;
162837da2899SCharles.Forsyth	"maxstale" =>
162937da2899SCharles.Forsyth		config.maxstale = v;
163037da2899SCharles.Forsyth	"imagelvl" =>
163137da2899SCharles.Forsyth		config.imagelvl = v;
163237da2899SCharles.Forsyth	"imagecachenum" =>
163337da2899SCharles.Forsyth		config.imagecachenum = v;
163437da2899SCharles.Forsyth	"imagecachemem" =>
163537da2899SCharles.Forsyth		config.imagecachemem = v;
163637da2899SCharles.Forsyth	"docookies" =>
163737da2899SCharles.Forsyth		config.docookies = v;
163837da2899SCharles.Forsyth	"doscripts" =>
163937da2899SCharles.Forsyth		config.doscripts = v;
164037da2899SCharles.Forsyth	"http" =>
164137da2899SCharles.Forsyth		if(val == "1.1")
164237da2899SCharles.Forsyth			config.httpminor = 1;
164337da2899SCharles.Forsyth		else
164437da2899SCharles.Forsyth			config.httpminor = 0;
164537da2899SCharles.Forsyth	"agentname" =>
164637da2899SCharles.Forsyth		config.agentname = val;
164737da2899SCharles.Forsyth	"nthreads" =>
164837da2899SCharles.Forsyth		if (v < 1)
164937da2899SCharles.Forsyth			ok = 0;
165037da2899SCharles.Forsyth		else
165137da2899SCharles.Forsyth			config.nthreads = v;
165237da2899SCharles.Forsyth	"offersave" =>
165337da2899SCharles.Forsyth		if (v < 1)
165437da2899SCharles.Forsyth			config.offersave = 0;
165537da2899SCharles.Forsyth		else
165637da2899SCharles.Forsyth			config.offersave = 1;
165737da2899SCharles.Forsyth	"charset" =>
165837da2899SCharles.Forsyth		config.charset = val;
165937da2899SCharles.Forsyth	"plumbport" =>
166037da2899SCharles.Forsyth		config.plumbport = val;
166137da2899SCharles.Forsyth	"wintitle" =>
166237da2899SCharles.Forsyth		config.wintitle = val;
166337da2899SCharles.Forsyth	"dbgfile" =>
166437da2899SCharles.Forsyth		config.dbgfile = val;
166537da2899SCharles.Forsyth	"dbg" =>
166637da2899SCharles.Forsyth		for(i := 0; i < len val; i++) {
166737da2899SCharles.Forsyth			c := val[i];
166837da2899SCharles.Forsyth			if(c < len config.dbg)
166937da2899SCharles.Forsyth				config.dbg[c]++;
167037da2899SCharles.Forsyth			else {
167137da2899SCharles.Forsyth				ok = 0;
167237da2899SCharles.Forsyth				break;
167337da2899SCharles.Forsyth			}
167437da2899SCharles.Forsyth		}
167537da2899SCharles.Forsyth	* =>
167637da2899SCharles.Forsyth		ok = 0;
167737da2899SCharles.Forsyth	}
167837da2899SCharles.Forsyth	return ok;
167937da2899SCharles.Forsyth}
168037da2899SCharles.Forsyth
168137da2899SCharles.Forsythsaveconfig(): int
168237da2899SCharles.Forsyth{
168337da2899SCharles.Forsyth	fname := config.userdir + "/config";
168437da2899SCharles.Forsyth	buf := array [Sys->ATOMICIO] of byte;
168537da2899SCharles.Forsyth	fd := sys->create(fname, Sys->OWRITE, 8r600);
168637da2899SCharles.Forsyth	if(fd == nil)
168737da2899SCharles.Forsyth		return -1;
168837da2899SCharles.Forsyth
168937da2899SCharles.Forsyth	nbyte := savealine(fd, buf, "# Charon user configuration\n", 0);
169037da2899SCharles.Forsyth	nbyte = savealine(fd, buf, "userdir=" + config.userdir + "\n", nbyte);
169137da2899SCharles.Forsyth	nbyte = savealine(fd, buf, "srcdir=" + config.srcdir +"\n", nbyte);
169237da2899SCharles.Forsyth	if(config.change_homeurl){
169337da2899SCharles.Forsyth		nbyte = savealine(fd, buf, "starturl=" + config.starturl + "\n", nbyte);
169437da2899SCharles.Forsyth 		nbyte = savealine(fd, buf, "homeurl=" + config.homeurl + "\n", nbyte);
169537da2899SCharles.Forsyth	}
169637da2899SCharles.Forsyth	if(config.httpproxy != nil)
169737da2899SCharles.Forsyth		nbyte = savealine(fd, buf, "httpproxy=" + config.httpproxy.tostring() + "\n", nbyte);
169837da2899SCharles.Forsyth 	if(config.usessl & SSLV23) {
169937da2899SCharles.Forsyth 		nbyte = savealine(fd, buf, "usessl=v2\n", nbyte);
170037da2899SCharles.Forsyth 		nbyte = savealine(fd, buf, "usessl=v3\n", nbyte);
170137da2899SCharles.Forsyth	}
170237da2899SCharles.Forsyth	else {
170337da2899SCharles.Forsyth 		if(config.usessl & SSLV2)
170437da2899SCharles.Forsyth 			nbyte = savealine(fd, buf, "usessl=v2\n", nbyte);
170537da2899SCharles.Forsyth 		if(config.usessl & SSLV3)
170637da2899SCharles.Forsyth 			nbyte = savealine(fd, buf, "usessl=v3\n", nbyte);
170737da2899SCharles.Forsyth 	}
170837da2899SCharles.Forsyth	if(config.devssl == 0)
170937da2899SCharles.Forsyth		nbyte = savealine(fd, buf, "devssl=0\n", nbyte);
171037da2899SCharles.Forsyth	else
171137da2899SCharles.Forsyth		nbyte = savealine(fd, buf, "devssl=1\n", nbyte);
171237da2899SCharles.Forsyth	if(config.noproxydoms != nil) {
171337da2899SCharles.Forsyth		doms := "";
171437da2899SCharles.Forsyth		doml := config.noproxydoms;
171537da2899SCharles.Forsyth		while(doml != nil) {
171637da2899SCharles.Forsyth			doms += hd doml + ",";
171737da2899SCharles.Forsyth			doml = tl doml;
171837da2899SCharles.Forsyth		}
171937da2899SCharles.Forsyth		nbyte = savealine(fd, buf, "noproxy=" + doms + "\n", nbyte);
172037da2899SCharles.Forsyth	}
172137da2899SCharles.Forsyth	nbyte = savealine(fd, buf, "defaultwidth=" + string config.defaultwidth + "\n", nbyte);
172237da2899SCharles.Forsyth	nbyte = savealine(fd, buf, "defaultheight=" + string config.defaultheight + "\n", nbyte);
172337da2899SCharles.Forsyth	if(config.x >= 0)
172437da2899SCharles.Forsyth		nbyte = savealine(fd, buf, "x=" + string config.x + "\n", nbyte);
172537da2899SCharles.Forsyth	if(config.y >= 0)
172637da2899SCharles.Forsyth		nbyte = savealine(fd, buf, "y=" + string config.y + "\n", nbyte);
172737da2899SCharles.Forsyth	nbyte = savealine(fd, buf, "nocache=" + string config.nocache + "\n", nbyte);
172837da2899SCharles.Forsyth	nbyte = savealine(fd, buf, "maxstale=" + string config.maxstale + "\n", nbyte);
172937da2899SCharles.Forsyth	nbyte = savealine(fd, buf, "imagelvl=" + string config.imagelvl + "\n", nbyte);
173037da2899SCharles.Forsyth	nbyte = savealine(fd, buf, "imagecachenum=" + string config.imagecachenum + "\n", nbyte);
173137da2899SCharles.Forsyth	nbyte = savealine(fd, buf, "imagecachemem=" + string config.imagecachemem + "\n", nbyte);
173237da2899SCharles.Forsyth	nbyte = savealine(fd, buf, "docookies=" + string config.docookies + "\n", nbyte);
173337da2899SCharles.Forsyth	nbyte = savealine(fd, buf, "doscripts=" + string config.doscripts + "\n", nbyte);
173437da2899SCharles.Forsyth	nbyte = savealine(fd, buf, "http=" + "1." + string config.httpminor + "\n", nbyte);
173537da2899SCharles.Forsyth	nbyte = savealine(fd, buf, "agentname=" + string config.agentname + "\n", nbyte);
173637da2899SCharles.Forsyth	nbyte = savealine(fd, buf, "nthreads=" + string config.nthreads + "\n", nbyte);
173737da2899SCharles.Forsyth	nbyte = savealine(fd, buf, "charset=" + config.charset + "\n", nbyte);
173837da2899SCharles.Forsyth	#for(i := 0; i < len config.dbg; i++)
173937da2899SCharles.Forsyth		#nbyte = savealine(fd, buf, "dbg=" + string config.dbg[i] + "\n", nbyte);
174037da2899SCharles.Forsyth
174137da2899SCharles.Forsyth	if(nbyte > 0)
174237da2899SCharles.Forsyth		sys->write(fd, buf, nbyte);
174337da2899SCharles.Forsyth
174437da2899SCharles.Forsyth	return 0;
174537da2899SCharles.Forsyth}
174637da2899SCharles.Forsyth
174737da2899SCharles.Forsythsavealine(fd: ref Sys->FD, buf: array of byte, s: string, n: int): int
174837da2899SCharles.Forsyth{
174937da2899SCharles.Forsyth	if(Sys->ATOMICIO < n + len s) {
175037da2899SCharles.Forsyth		sys->write(fd, buf, n);
175137da2899SCharles.Forsyth		buf[0:] = array of byte s;
175237da2899SCharles.Forsyth		return len s;
175337da2899SCharles.Forsyth	}
175437da2899SCharles.Forsyth	buf[n:] = array of byte s;
175537da2899SCharles.Forsyth	return n + len s;
175637da2899SCharles.Forsyth}
175737da2899SCharles.Forsyth
175837da2899SCharles.Forsyth# Make a StringInt table out of a, mapping each string
175937da2899SCharles.Forsyth# to its index.  Check that entries are in alphabetical order.
176037da2899SCharles.Forsythmakestrinttab(a: array of string) : array of T->StringInt
176137da2899SCharles.Forsyth{
176237da2899SCharles.Forsyth	n := len a;
176337da2899SCharles.Forsyth	ans := array[n] of T->StringInt;
176437da2899SCharles.Forsyth	for(i := 0; i < n; i++) {
176537da2899SCharles.Forsyth		ans[i].key = a[i];
176637da2899SCharles.Forsyth		ans[i].val = i;
176737da2899SCharles.Forsyth		if(i > 0 && a[i] < a[i-1])
176854041ca4Sforsyth			raise "EXInternal: table out of alphabetical order";
176937da2899SCharles.Forsyth	}
177037da2899SCharles.Forsyth	return ans;
177137da2899SCharles.Forsyth}
177237da2899SCharles.Forsyth
177337da2899SCharles.Forsyth# Should really move into Url module.
177437da2899SCharles.Forsyth# Don't include fragment in test, since we are testing if the
177537da2899SCharles.Forsyth# pointed to docs are the same, not places within docs.
177637da2899SCharles.Forsythurlequal(a, b: ref U->Parsedurl) : int
177737da2899SCharles.Forsyth{
177837da2899SCharles.Forsyth	return a.scheme == b.scheme
177937da2899SCharles.Forsyth		&& a.host == b.host
178037da2899SCharles.Forsyth		&& a.port == b.port
178137da2899SCharles.Forsyth		&& a.user == b.user
178237da2899SCharles.Forsyth		&& a.passwd == b.passwd
178337da2899SCharles.Forsyth		&& a.path == b.path
178437da2899SCharles.Forsyth		&& a.query == b.query;
178537da2899SCharles.Forsyth}
178637da2899SCharles.Forsyth
178737da2899SCharles.Forsyth# U->makeurl, but add http:// if not an absolute path already
178837da2899SCharles.Forsythmakeabsurl(s: string) : ref Parsedurl
178937da2899SCharles.Forsyth{
179037da2899SCharles.Forsyth	if (s == "")
179137da2899SCharles.Forsyth		return nil;
179237da2899SCharles.Forsyth	u := U->parse(s);
179337da2899SCharles.Forsyth	if (u.scheme != nil)
179437da2899SCharles.Forsyth		return u;
179537da2899SCharles.Forsyth	if (s[0] == '/')
179637da2899SCharles.Forsyth		# try file:
179737da2899SCharles.Forsyth		s = "file://localhost" + s;
179837da2899SCharles.Forsyth	else
179937da2899SCharles.Forsyth		# try http
180037da2899SCharles.Forsyth		s = "http://" + s;
180137da2899SCharles.Forsyth	u = U->parse(s);
180237da2899SCharles.Forsyth	return u;
180337da2899SCharles.Forsyth}
180437da2899SCharles.Forsyth
180537da2899SCharles.Forsyth# Return place to load from, given installed-path name.
180637da2899SCharles.Forsyth# (If config.dbg['u'] is set, change directory to config.srcdir.)
180737da2899SCharles.Forsythloadpath(s: string) : string
180837da2899SCharles.Forsyth{
180937da2899SCharles.Forsyth	if(config.dbg['u'] == byte 0)
181037da2899SCharles.Forsyth		return s;
181137da2899SCharles.Forsyth	(nil, f) := S->splitr(s, "/");
181237da2899SCharles.Forsyth	return config.srcdir + "/" + f;
181337da2899SCharles.Forsyth}
181437da2899SCharles.Forsyth
181537da2899SCharles.Forsythcolor_tab := array[] of { T->StringInt
181637da2899SCharles.Forsyth	("aqua",	16r00FFFF),
181737da2899SCharles.Forsyth	("black",	Black),
181837da2899SCharles.Forsyth	("blue",	Blue),
181937da2899SCharles.Forsyth	("fuchsia",	16rFF00FF),
182037da2899SCharles.Forsyth	("gray",	16r808080),
182137da2899SCharles.Forsyth	("green",	16r008000),
182237da2899SCharles.Forsyth	("lime",	16r00FF00),
182337da2899SCharles.Forsyth	("maroon",	16r800000),
182437da2899SCharles.Forsyth	("navy",	Navy),
182537da2899SCharles.Forsyth	("olive",	16r808000),
182637da2899SCharles.Forsyth	("purple",	16r800080),
182737da2899SCharles.Forsyth	("red",	Red),
182837da2899SCharles.Forsyth	("silver",	16rC0C0C0),
182937da2899SCharles.Forsyth	("teal",	16r008080),
183037da2899SCharles.Forsyth	("white",	White),
183137da2899SCharles.Forsyth	("yellow",	16rFFFF00)
183237da2899SCharles.Forsyth};
183337da2899SCharles.Forsyth# Convert HTML color spec to RGB value, returning dflt if can't.
183437da2899SCharles.Forsyth# Argument is supposed to be a valid HTML color, or "".
183537da2899SCharles.Forsyth# Return the RGB value of the color, using dflt if s
183637da2899SCharles.Forsyth# is "" or an invalid color.
183737da2899SCharles.Forsythcolor(s: string, dflt: int) : int
183837da2899SCharles.Forsyth{
183937da2899SCharles.Forsyth	if(s == "")
184037da2899SCharles.Forsyth		return dflt;
184137da2899SCharles.Forsyth	s = S->tolower(s);
184237da2899SCharles.Forsyth	c := s[0];
184337da2899SCharles.Forsyth	if(c < C->NCTYPE && ctype[c] == C->L) {
184437da2899SCharles.Forsyth		(fnd, v) := T->lookup(color_tab, s);
184537da2899SCharles.Forsyth		if(fnd)
184637da2899SCharles.Forsyth			return v;
184737da2899SCharles.Forsyth	}
184837da2899SCharles.Forsyth	if(s[0] == '#')
184937da2899SCharles.Forsyth		s = s[1:];
185037da2899SCharles.Forsyth	(v, rest) := S->toint(s, 16);
185137da2899SCharles.Forsyth	if(rest == "")
185237da2899SCharles.Forsyth		return v;
185337da2899SCharles.Forsyth	# s was invalid, so choose a valid one
185437da2899SCharles.Forsyth	return dflt;
185537da2899SCharles.Forsyth}
185637da2899SCharles.Forsyth
185737da2899SCharles.Forsythmax(a,b: int) : int
185837da2899SCharles.Forsyth{
185937da2899SCharles.Forsyth	if(a > b)
186037da2899SCharles.Forsyth		return a;
186137da2899SCharles.Forsyth	return b;
186237da2899SCharles.Forsyth}
186337da2899SCharles.Forsyth
186437da2899SCharles.Forsythmin(a,b: int) : int
186537da2899SCharles.Forsyth{
186637da2899SCharles.Forsyth	if(a < b)
186737da2899SCharles.Forsyth		return a;
186837da2899SCharles.Forsyth	return b;
186937da2899SCharles.Forsyth}
187037da2899SCharles.Forsyth
187137da2899SCharles.Forsythassert(i: int)
187237da2899SCharles.Forsyth{
187337da2899SCharles.Forsyth	if(!i) {
187454041ca4Sforsyth		raise "EXInternal: assertion failed";
187537da2899SCharles.Forsyth#		sys->print("assertion failed\n");
187637da2899SCharles.Forsyth#		s := hmeth[-1];
187737da2899SCharles.Forsyth	}
187837da2899SCharles.Forsyth}
187937da2899SCharles.Forsyth
188037da2899SCharles.Forsythgetcookies(host, path: string, secure: int): string
188137da2899SCharles.Forsyth{
188237da2899SCharles.Forsyth	if (CK == nil || ckclient == nil)
188337da2899SCharles.Forsyth		return nil;
188437da2899SCharles.Forsyth	Client: import CK;
188537da2899SCharles.Forsyth	return ckclient.getcookies(host, path, secure);
188637da2899SCharles.Forsyth}
188737da2899SCharles.Forsyth
188837da2899SCharles.Forsythsetcookie(host, path, cookie: string)
188937da2899SCharles.Forsyth{
189037da2899SCharles.Forsyth	if (CK == nil || ckclient == nil)
189137da2899SCharles.Forsyth		return;
189237da2899SCharles.Forsyth	Client: import CK;
189337da2899SCharles.Forsyth	ckclient.set(host, path, cookie);
189437da2899SCharles.Forsyth}
189537da2899SCharles.Forsyth
189637da2899SCharles.Forsythex_mkdir(dirname: string): int
189737da2899SCharles.Forsyth{
189837da2899SCharles.Forsyth	(ok, nil) := sys->stat(dirname);
189937da2899SCharles.Forsyth	if(ok < 0) {
190037da2899SCharles.Forsyth		f := sys->create(dirname, sys->OREAD, sys->DMDIR + 8r777);
190137da2899SCharles.Forsyth		if(f == nil) {
190237da2899SCharles.Forsyth			sys->print("mkdir: can't create %s: %r\n", dirname);
190337da2899SCharles.Forsyth			return 0;
190437da2899SCharles.Forsyth		}
190537da2899SCharles.Forsyth		f = nil;
190637da2899SCharles.Forsyth	}
190737da2899SCharles.Forsyth	return 1;
190837da2899SCharles.Forsyth}
190937da2899SCharles.Forsyth
191037da2899SCharles.Forsythstripscript(s: string): string
191137da2899SCharles.Forsyth{
191237da2899SCharles.Forsyth	# strip leading whitespace and SGML comment start symbol '<!--'
191337da2899SCharles.Forsyth	if (s == nil)
191437da2899SCharles.Forsyth		return nil;
191537da2899SCharles.Forsyth	cs := "<!--";
191637da2899SCharles.Forsyth	ci := 0;
191737da2899SCharles.Forsyth	for (si := 0; si < len s; si++) {
191837da2899SCharles.Forsyth		c := s[si];
191937da2899SCharles.Forsyth		if (c == cs[ci]) {
192037da2899SCharles.Forsyth			if (++ci >= len cs)
192137da2899SCharles.Forsyth				ci = 0;
192237da2899SCharles.Forsyth		} else {
192337da2899SCharles.Forsyth			ci = 0;
192437da2899SCharles.Forsyth			if (c == ' ' || c == '\t' || c == '\r' || c == '\n')
192537da2899SCharles.Forsyth				continue;
192637da2899SCharles.Forsyth			break;
192737da2899SCharles.Forsyth		}
192837da2899SCharles.Forsyth	}
192937da2899SCharles.Forsyth	# strip trailing whitespace and SGML comment terminator '-->'
193037da2899SCharles.Forsyth	cs = "-->";
193137da2899SCharles.Forsyth	ci = len cs -1;
193237da2899SCharles.Forsyth	for (se := len s - 1; se > si; se--) {
193337da2899SCharles.Forsyth		c := s[se];
193437da2899SCharles.Forsyth		if (c == cs[ci]) {
193537da2899SCharles.Forsyth			if (ci-- == 0)
193637da2899SCharles.Forsyth				ci = len cs -1;
193737da2899SCharles.Forsyth		} else {
193837da2899SCharles.Forsyth			ci = len cs - 1;
193937da2899SCharles.Forsyth			if (c == ' ' || c == '\t' || c == '\r' || c == '\n')
194037da2899SCharles.Forsyth				continue;
194137da2899SCharles.Forsyth			break;
194237da2899SCharles.Forsyth		}
194337da2899SCharles.Forsyth	}
194437da2899SCharles.Forsyth	if (se < si)
194537da2899SCharles.Forsyth		return nil;
194637da2899SCharles.Forsyth	return s[si:se+1];
194737da2899SCharles.Forsyth}
194837da2899SCharles.Forsyth
194937da2899SCharles.Forsyth# Split a value (guaranteed trimmed) into sep-separated list of one of
195037da2899SCharles.Forsyth# 	token
195137da2899SCharles.Forsyth#	token = token
195237da2899SCharles.Forsyth#	token = "quoted string"
195337da2899SCharles.Forsyth# and put into list of Namevals (lowercase the first token)
195437da2899SCharles.ForsythNameval.namevals(s: string, sep: int) : list of Nameval
195537da2899SCharles.Forsyth{
195637da2899SCharles.Forsyth	ans : list of Nameval = nil;
195737da2899SCharles.Forsyth	n := len s;
195837da2899SCharles.Forsyth	i := 0;
195937da2899SCharles.Forsyth	while(i < n) {
196037da2899SCharles.Forsyth		tok : string;
196137da2899SCharles.Forsyth		(tok, i) = gettok(s, i, n);
196237da2899SCharles.Forsyth		if(tok == "")
196337da2899SCharles.Forsyth			break;
196437da2899SCharles.Forsyth		tok = S->tolower(tok);
196537da2899SCharles.Forsyth		val := "";
196637da2899SCharles.Forsyth		while(i < n && ctype[s[i]] == C->W)
196737da2899SCharles.Forsyth			i++;
196837da2899SCharles.Forsyth		if(i == n || s[i] == sep)
196937da2899SCharles.Forsyth			i++;
197037da2899SCharles.Forsyth		else if(s[i] == '=') {
197137da2899SCharles.Forsyth			i++;
197237da2899SCharles.Forsyth			while(i < n && ctype[s[i]] == C->W)
197337da2899SCharles.Forsyth				i++;
197437da2899SCharles.Forsyth			if (i == n)
197537da2899SCharles.Forsyth				break;
197637da2899SCharles.Forsyth			if(s[i] == '"')
197737da2899SCharles.Forsyth				(val, i) = getqstring(s, i, n);
197837da2899SCharles.Forsyth			else
197937da2899SCharles.Forsyth				(val, i) = gettok(s, i, n);
198037da2899SCharles.Forsyth		}
198137da2899SCharles.Forsyth		else
198237da2899SCharles.Forsyth			break;
198337da2899SCharles.Forsyth		ans = Nameval(tok, val) :: ans;
198437da2899SCharles.Forsyth	}
198537da2899SCharles.Forsyth	return ans;
198637da2899SCharles.Forsyth}
198737da2899SCharles.Forsyth
198837da2899SCharles.Forsythgettok(s: string, i,n: int) : (string, int)
198937da2899SCharles.Forsyth{
199037da2899SCharles.Forsyth	while(i < n && ctype[s[i]] == C->W)
199137da2899SCharles.Forsyth		i++;
199237da2899SCharles.Forsyth	if(i == n)
199337da2899SCharles.Forsyth		return ("", i);
199437da2899SCharles.Forsyth	is := i;
199537da2899SCharles.Forsyth	for(; i < n; i++) {
199637da2899SCharles.Forsyth		c := s[i];
199737da2899SCharles.Forsyth		ct := ctype[c];
199837da2899SCharles.Forsyth		if(!(int (ct&(C->D|C->L|C->U|C->N|C->S))))
199937da2899SCharles.Forsyth			if(int (ct&(C->W|C->C)) || S->in(c, "()<>@,;:\\\"/[]?={}"))
200037da2899SCharles.Forsyth				break;
200137da2899SCharles.Forsyth	}
200237da2899SCharles.Forsyth	return (s[is:i], i);
200337da2899SCharles.Forsyth}
200437da2899SCharles.Forsyth
200537da2899SCharles.Forsyth# get quoted string; return it without quotes, and index after it
200637da2899SCharles.Forsythgetqstring(s: string, i,n: int) : (string, int)
200737da2899SCharles.Forsyth{
200837da2899SCharles.Forsyth	while(i < n && ctype[s[i]] == C->W)
200937da2899SCharles.Forsyth		i++;
201037da2899SCharles.Forsyth	if(i == n || s[i] != '"')
201137da2899SCharles.Forsyth		return ("", i);
201237da2899SCharles.Forsyth	is := ++i;
201337da2899SCharles.Forsyth	for(; i < n; i++) {
201437da2899SCharles.Forsyth		c := s[i];
201537da2899SCharles.Forsyth		if(c == '\\')
201637da2899SCharles.Forsyth			i++;
201737da2899SCharles.Forsyth		else if(c == '"')
201837da2899SCharles.Forsyth			return (s[is:i], i+1);
201937da2899SCharles.Forsyth	}
202037da2899SCharles.Forsyth	return (s[is:i], i);
202137da2899SCharles.Forsyth}
202237da2899SCharles.Forsyth
202337da2899SCharles.Forsyth# Find value corresponding to key (should be lowercase)
202437da2899SCharles.Forsyth# and return (1, value) if found or (0, "")
202537da2899SCharles.ForsythNameval.find(l: list of Nameval, key: string) : (int, string)
202637da2899SCharles.Forsyth{
202737da2899SCharles.Forsyth	for(; l != nil; l = tl l)
202837da2899SCharles.Forsyth		if((hd l).key == key)
202937da2899SCharles.Forsyth			return (1, (hd l).val);
203037da2899SCharles.Forsyth	return (0, "");
203137da2899SCharles.Forsyth}
203237da2899SCharles.Forsyth
203337da2899SCharles.Forsyth# this should be a converter cache
203437da2899SCharles.Forsythgetconv(chset : string) : Btos
203537da2899SCharles.Forsyth{
203637da2899SCharles.Forsyth	(btos, err) := convcs->getbtos(chset);
203737da2899SCharles.Forsyth	if (err != nil)
203837da2899SCharles.Forsyth		sys->print("Converter error: %s\n", err);
203937da2899SCharles.Forsyth	return btos;
204037da2899SCharles.Forsyth}
204137da2899SCharles.Forsyth
204237da2899SCharles.ForsythX(s, note : string) : string
204337da2899SCharles.Forsyth{
204437da2899SCharles.Forsyth	if (dict == nil)
204537da2899SCharles.Forsyth		return s;
204637da2899SCharles.Forsyth	return dict.xlaten(s, note);
204737da2899SCharles.Forsyth}
2048