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