summaryrefslogtreecommitdiffstats
path: root/WWW/Library/Implementation
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--WWW/Library/Implementation/HTAABrow.c1354
-rw-r--r--WWW/Library/Implementation/HTAABrow.h142
-rw-r--r--WWW/Library/Implementation/HTAAProt.c738
-rw-r--r--WWW/Library/Implementation/HTAAProt.h226
-rw-r--r--WWW/Library/Implementation/HTAAUtil.c605
-rw-r--r--WWW/Library/Implementation/HTAAUtil.h318
-rw-r--r--WWW/Library/Implementation/HTAccess.c1460
-rw-r--r--WWW/Library/Implementation/HTAccess.h268
-rw-r--r--WWW/Library/Implementation/HTAnchor.c1460
-rw-r--r--WWW/Library/Implementation/HTAnchor.h412
-rw-r--r--WWW/Library/Implementation/HTAssoc.c82
-rw-r--r--WWW/Library/Implementation/HTAssoc.h35
-rw-r--r--WWW/Library/Implementation/HTAtom.c107
-rw-r--r--WWW/Library/Implementation/HTAtom.h53
-rw-r--r--WWW/Library/Implementation/HTBTree.c680
-rw-r--r--WWW/Library/Implementation/HTBTree.h104
-rw-r--r--WWW/Library/Implementation/HTCJK.h121
-rw-r--r--WWW/Library/Implementation/HTChunk.c332
-rw-r--r--WWW/Library/Implementation/HTChunk.h228
-rw-r--r--WWW/Library/Implementation/HTDOS.c241
-rw-r--r--WWW/Library/Implementation/HTDOS.h56
-rw-r--r--WWW/Library/Implementation/HTFTP.c4177
-rw-r--r--WWW/Library/Implementation/HTFTP.h70
-rw-r--r--WWW/Library/Implementation/HTFWriter.h30
-rw-r--r--WWW/Library/Implementation/HTFile.c3395
-rw-r--r--WWW/Library/Implementation/HTFile.h368
-rw-r--r--WWW/Library/Implementation/HTFinger.c418
-rw-r--r--WWW/Library/Implementation/HTFinger.h30
-rw-r--r--WWW/Library/Implementation/HTFormat.c2181
-rw-r--r--WWW/Library/Implementation/HTFormat.h588
-rw-r--r--WWW/Library/Implementation/HTGopher.c2071
-rw-r--r--WWW/Library/Implementation/HTGopher.h29
-rw-r--r--WWW/Library/Implementation/HTGroup.c758
-rw-r--r--WWW/Library/Implementation/HTGroup.h182
-rw-r--r--WWW/Library/Implementation/HTInit.h34
-rw-r--r--WWW/Library/Implementation/HTLex.c142
-rw-r--r--WWW/Library/Implementation/HTLex.h64
-rw-r--r--WWW/Library/Implementation/HTList.c402
-rw-r--r--WWW/Library/Implementation/HTList.h142
-rw-r--r--WWW/Library/Implementation/HTMIME.c2594
-rw-r--r--WWW/Library/Implementation/HTMIME.h84
-rw-r--r--WWW/Library/Implementation/HTMLDTD.c334
-rw-r--r--WWW/Library/Implementation/HTMLDTD.h97
-rw-r--r--WWW/Library/Implementation/HTMLGen.c738
-rw-r--r--WWW/Library/Implementation/HTMLGen.h26
-rw-r--r--WWW/Library/Implementation/HTNews.c3147
-rw-r--r--WWW/Library/Implementation/HTNews.h60
-rw-r--r--WWW/Library/Implementation/HTParse.c1383
-rw-r--r--WWW/Library/Implementation/HTParse.h212
-rw-r--r--WWW/Library/Implementation/HTPlain.c601
-rw-r--r--WWW/Library/Implementation/HTPlain.h21
-rw-r--r--WWW/Library/Implementation/HTRules.c704
-rw-r--r--WWW/Library/Implementation/HTRules.h169
-rw-r--r--WWW/Library/Implementation/HTStream.h69
-rw-r--r--WWW/Library/Implementation/HTString.c1464
-rw-r--r--WWW/Library/Implementation/HTString.h167
-rw-r--r--WWW/Library/Implementation/HTStyle.c378
-rw-r--r--WWW/Library/Implementation/HTStyle.h241
-rw-r--r--WWW/Library/Implementation/HTTCP.c2623
-rw-r--r--WWW/Library/Implementation/HTTCP.h111
-rw-r--r--WWW/Library/Implementation/HTTP.c2838
-rw-r--r--WWW/Library/Implementation/HTTP.h47
-rw-r--r--WWW/Library/Implementation/HTTelnet.c553
-rw-r--r--WWW/Library/Implementation/HTTelnet.h28
-rw-r--r--WWW/Library/Implementation/HTUU.c210
-rw-r--r--WWW/Library/Implementation/HTUU.h36
-rw-r--r--WWW/Library/Implementation/HTUtils.h847
-rw-r--r--WWW/Library/Implementation/HTVMSUtils.c1131
-rw-r--r--WWW/Library/Implementation/HTVMSUtils.h101
-rw-r--r--WWW/Library/Implementation/HTVMS_WaisProt.c2469
-rw-r--r--WWW/Library/Implementation/HTVMS_WaisProt.h425
-rw-r--r--WWW/Library/Implementation/HTVMS_WaisUI.c2279
-rw-r--r--WWW/Library/Implementation/HTVMS_WaisUI.h664
-rw-r--r--WWW/Library/Implementation/HTWAIS.c1078
-rw-r--r--WWW/Library/Implementation/HTWAIS.h43
-rw-r--r--WWW/Library/Implementation/HTWSRC.c486
-rw-r--r--WWW/Library/Implementation/HTWSRC.h43
-rw-r--r--WWW/Library/Implementation/HText.h219
-rw-r--r--WWW/Library/Implementation/HTioctl.h11
-rw-r--r--WWW/Library/Implementation/LYLeaks.h309
-rw-r--r--WWW/Library/Implementation/LYexit.h67
-rw-r--r--WWW/Library/Implementation/SGML.c5159
-rw-r--r--WWW/Library/Implementation/SGML.h287
-rw-r--r--WWW/Library/Implementation/UCAux.h92
-rw-r--r--WWW/Library/Implementation/UCDefs.h106
-rw-r--r--WWW/Library/Implementation/UCMap.h114
-rw-r--r--WWW/Library/Implementation/Version.make1
-rw-r--r--WWW/Library/Implementation/dtd_util.c1722
-rw-r--r--WWW/Library/Implementation/hdr_HTMLDTD.h1000
-rw-r--r--WWW/Library/Implementation/makefile.in384
-rw-r--r--WWW/Library/Implementation/src0_HTMLDTD.h2478
-rw-r--r--WWW/Library/Implementation/src0_HTMLDTD.txt3901
-rw-r--r--WWW/Library/Implementation/src1_HTMLDTD.h2478
-rw-r--r--WWW/Library/Implementation/src1_HTMLDTD.txt3901
-rw-r--r--WWW/Library/Implementation/tidy_tls.h147
-rw-r--r--WWW/Library/Implementation/www_tcp.h1017
-rw-r--r--WWW/Library/Implementation/www_wait.h34
97 files changed, 76031 insertions, 0 deletions
diff --git a/WWW/Library/Implementation/HTAABrow.c b/WWW/Library/Implementation/HTAABrow.c
new file mode 100644
index 0000000..c963acd
--- /dev/null
+++ b/WWW/Library/Implementation/HTAABrow.c
@@ -0,0 +1,1354 @@
+/*
+ * $LynxId: HTAABrow.c,v 1.43 2018/05/11 22:54:19 tom Exp $
+ *
+ * MODULE HTAABrow.c
+ * BROWSER SIDE ACCESS AUTHORIZATION MODULE
+ *
+ * Contains the code for keeping track on server hostnames,
+ * port numbers, scheme names, usernames, passwords
+ * (and servers' public keys).
+ *
+ * IMPORTANT:
+ * Routines in this module use dynamic allocation, but free
+ * automatically all the memory reserved by them.
+ *
+ * Therefore the caller never has to (and never should)
+ * free() any object returned by these functions.
+ *
+ * Therefore also all the strings returned by this package
+ * are only valid until the next call to the same function
+ * is made. This approach is selected, because of the nature
+ * of access authorization: no string returned by the package
+ * needs to be valid longer than until the next call.
+ *
+ * This also makes it easy to plug the AA package in:
+ * you don't have to ponder whether to free() something
+ * here or is it done somewhere else (because it is always
+ * done somewhere else).
+ *
+ * The strings that the package needs to store are copied
+ * so the original strings given as parameters to AA
+ * functions may be freed or modified with no side effects.
+ *
+ * The AA package does not free() anything else than what
+ * it has itself allocated.
+ *
+ * AUTHORS:
+ * AL Ari Luotonen luotonen@dxcern.cern.ch
+ *
+ * HISTORY:
+ * Oct 17 AL Made corrections suggested by marca:
+ * Added if (!realm->username) return NULL;
+ * Changed some ""s to NULLs.
+ * Now doing calloc() to init uuencode source;
+ * otherwise HTUU_encode() reads uninitialized memory
+ * every now and then (not a real bug but not pretty).
+ * Corrected the formula for uuencode destination size.
+ *
+ * 28 Apr 1997 AJL Do Proxy Authorisation.
+ *
+ * BUGS:
+ *
+ *
+ */
+
+#include <HTUtils.h>
+#include <HTString.h>
+#include <HTParse.h> /* URL parsing function */
+#include <HTList.h> /* HTList object */
+#include <HTAlert.h> /* HTConfirm(), HTPrompt() */
+#include <HTAAUtil.h> /* AA common to both sides */
+#include <HTAssoc.h> /* Assoc list */
+#include <HTAccess.h> /* Are we using an HTTP gateway? */
+#include <HTAABrow.h> /* Implemented here */
+#include <HTUU.h> /* Uuencoding and uudecoding */
+
+#include <LYLeaks.h>
+
+/*
+ * Local datatype definitions
+ *
+ * HTAAServer contains all the information about one server.
+ */
+typedef struct {
+
+ char *hostname; /* Host's name */
+ int portnumber; /* Port number */
+ BOOL IsProxy; /* Is it a proxy? */
+ HTList *setups; /* List of protection setups
+ on this server; i.e., valid
+ authentication schemes and
+ templates when to use them.
+ This is actually a list of
+ HTAASetup objects. */
+ HTList *realms; /* Information about passwords */
+} HTAAServer;
+
+/*
+ * HTAASetup contains information about one server's one
+ * protected tree of documents.
+ */
+typedef struct {
+ HTAAServer *server; /* Which server serves this tree */
+ char *ctemplate; /* Template for this tree */
+ HTList *valid_schemes; /* Valid authentic.schemes */
+ HTAssocList **scheme_specifics; /* Scheme specific params */
+ BOOL retry; /* Failed last time -- reprompt (or whatever) */
+} HTAASetup;
+
+/*
+ * Information about usernames and passwords in
+ * Basic and Pubkey authentication schemes;
+ */
+typedef struct {
+ char *realmname; /* Password domain name */
+ char *username; /* Username in that domain */
+ char *password; /* Corresponding password */
+} HTAARealm;
+
+/*
+ * To free off all globals. - FM
+ */
+static void free_HTAAGlobals(void);
+static BOOL free_HTAAGlobalsSet = FALSE;
+static char *HTAA_composeAuthResult = NULL;
+static char *compose_auth_stringResult = NULL; /* Uuencoded presentation */
+
+/*
+ * Module-wide global variables
+ */
+static HTList *server_table = NULL; /* Browser's info about servers */
+static char *secret_key = NULL; /* Browser's latest secret key */
+static HTAASetup *current_setup = NULL; /* The server setup we are currently */
+
+ /* talking to */
+static char *current_hostname = NULL; /* The server's name and portnumber */
+static int current_portnumber = 80; /* where we are currently trying to */
+
+ /* connect. */
+static char *current_docname = NULL; /* The document's name we are */
+
+ /* trying to access. */
+static char *HTAAForwardAuth = NULL; /* Authorization: line to forward */
+
+ /* (used by gateway httpds) */
+static HTAASetup *proxy_setup = NULL; /* Same as above, but for Proxy -AJL */
+static char *proxy_hostname = NULL;
+static char *proxy_docname = NULL;
+static int proxy_portnumber = 80;
+
+/*** HTAAForwardAuth for enabling gateway-httpds to forward Authorization ***/
+
+void HTAAForwardAuth_set(const char *scheme_name,
+ const char *scheme_specifics)
+{
+ size_t len = (20
+ + (scheme_name ? strlen(scheme_name) : 0)
+ + (scheme_specifics ? strlen(scheme_specifics) : 0));
+
+ FREE(HTAAForwardAuth);
+ if ((HTAAForwardAuth = typecallocn(char, len)) == 0)
+ outofmem(__FILE__, "HTAAForwardAuth_set");
+
+ strcpy(HTAAForwardAuth, "Authorization: ");
+ if (scheme_name) {
+ strcat(HTAAForwardAuth, scheme_name);
+ strcat(HTAAForwardAuth, " ");
+ if (scheme_specifics) {
+ strcat(HTAAForwardAuth, scheme_specifics);
+ }
+ }
+}
+
+void HTAAForwardAuth_reset(void)
+{
+ FREE(HTAAForwardAuth);
+}
+
+/**************************** HTAAServer ***********************************/
+
+static void HTAASetup_delete(HTAASetup * killme); /* Forward */
+
+/* static HTAAServer_new()
+ * ALLOCATE A NEW NODE TO HOLD SERVER INFO
+ * AND ADD IT TO THE LIST OF SERVERS
+ * ON ENTRY:
+ * hostname is the name of the host that the server
+ * is running in.
+ * portnumber is the portnumber which the server listens.
+ * IsProxy should be TRUE if this is a proxy.
+ *
+ * ON EXIT:
+ * returns the newly-allocated node with all the strings
+ * duplicated.
+ * Strings will be automatically freed by
+ * the function HTAAServer_delete(), which also
+ * frees the node itself.
+ */
+static HTAAServer *HTAAServer_new(const char *hostname,
+ int portnumber,
+ int IsProxy)
+{
+ HTAAServer *server;
+
+ if ((server = typecalloc(HTAAServer)) == 0)
+ outofmem(__FILE__, "HTAAServer_new");
+
+ server->hostname = NULL;
+ server->portnumber = (portnumber > 0 ? portnumber : 80);
+ server->IsProxy = (BOOLEAN) IsProxy;
+ server->setups = HTList_new();
+ server->realms = HTList_new();
+
+ if (hostname)
+ StrAllocCopy(server->hostname, hostname);
+
+ if (!server_table)
+ server_table = HTList_new();
+
+ HTList_addObject(server_table, (void *) server);
+
+ return server;
+}
+
+/* static HTAAServer_delete()
+ *
+ * DELETE THE ENTRY FOR THE SERVER FROM THE HOST TABLE,
+ * AND FREE THE MEMORY USED BY IT.
+ *
+ * ON ENTRY:
+ * killme points to the HTAAServer to be freed.
+ *
+ * ON EXIT:
+ * returns nothing.
+ */
+static void HTAAServer_delete(HTAAServer *killme)
+{
+ int n, i;
+ HTAASetup *setup;
+ HTAARealm *realm;
+ HTList *cur;
+
+ if (killme) {
+ if (killme->setups != NULL) {
+ n = HTList_count(killme->setups);
+ for (i = (n - 1); i >= 0; i--) {
+ if ((setup = (HTAASetup *) HTList_objectAt(killme->setups,
+ i)) != NULL) {
+ HTAASetup_delete(setup);
+ setup = NULL;
+ }
+ }
+ HTList_delete(killme->setups);
+ killme->setups = NULL;
+ }
+
+ cur = killme->realms;
+ while (NULL != (realm = (HTAARealm *) HTList_nextObject(cur))) {
+ FREE(realm->realmname);
+ FREE(realm->username);
+ FREE(realm->password);
+ FREE(realm);
+ }
+ HTList_delete(killme->realms);
+ killme->realms = NULL;
+
+ FREE(killme->hostname);
+
+ HTList_removeObject(server_table, (void *) killme);
+ FREE(killme);
+ }
+}
+
+/* static HTAAServer_lookup()
+ * LOOK UP SERVER BY HOSTNAME AND PORTNUMBER
+ * ON ENTRY:
+ * hostname obvious.
+ * portnumber if non-positive defaults to 80.
+ * IsProxy should be TRUE if this is a proxy.
+ *
+ * Looks up the server in the module-global server_table.
+ *
+ * ON EXIT:
+ * returns pointer to a HTAAServer structure
+ * representing the looked-up server.
+ * NULL, if not found.
+ */
+static HTAAServer *HTAAServer_lookup(const char *hostname,
+ int portnumber,
+ int IsProxy)
+{
+ if (hostname) {
+ HTList *cur = server_table;
+ HTAAServer *server;
+
+ if (portnumber <= 0)
+ portnumber = 80;
+
+ while (NULL != (server = (HTAAServer *) HTList_nextObject(cur))) {
+ if (server->portnumber == portnumber &&
+ 0 == strcmp(server->hostname, hostname) &&
+ server->IsProxy == IsProxy)
+ return server;
+ }
+ }
+ return NULL; /* NULL parameter, or not found */
+}
+
+/*************************** HTAASetup *******************************/
+
+/* static HTAASetup_lookup()
+ * FIGURE OUT WHICH AUTHENTICATION SETUP THE SERVER
+ * IS USING FOR A GIVEN FILE ON A GIVEN HOST AND PORT
+ *
+ * ON ENTRY:
+ * hostname is the name of the server host machine.
+ * portnumber is the port that the server is running in.
+ * docname is the (URL-)pathname of the document we
+ * are trying to access.
+ * IsProxy should be TRUE if this is a proxy.
+ *
+ * This function goes through the information known about
+ * all the setups of the server, and finds out if the given
+ * filename resides in one of the protected directories.
+ *
+ * ON EXIT:
+ * returns NULL if no match.
+ * Otherwise, a HTAASetup structure representing
+ * the protected server setup on the corresponding
+ * document tree.
+ *
+ */
+static HTAASetup *HTAASetup_lookup(const char *hostname,
+ int portnumber,
+ const char *docname,
+ int IsProxy)
+{
+ HTAAServer *server;
+ HTAASetup *setup;
+
+ if (portnumber <= 0)
+ portnumber = 80;
+
+ if (hostname && docname && *hostname && *docname &&
+ NULL != (server = HTAAServer_lookup(hostname,
+ portnumber,
+ IsProxy))) {
+
+ HTList *cur = server->setups;
+
+ CTRACE((tfp, "%s %s (%s:%d:%s)\n",
+ "HTAASetup_lookup: resolving setup for",
+ (IsProxy ? "proxy" : "server"),
+ hostname, portnumber, docname));
+
+ while (NULL != (setup = (HTAASetup *) HTList_nextObject(cur))) {
+ if (HTAA_templateMatch(setup->ctemplate, docname)) {
+ CTRACE((tfp, "%s `%s' %s `%s'\n",
+ "HTAASetup_lookup:", docname,
+ "matched template", setup->ctemplate));
+ return setup;
+ } else {
+ CTRACE((tfp, "%s `%s' %s `%s'\n",
+ "HTAASetup_lookup:", docname,
+ "did NOT match template", setup->ctemplate));
+ }
+ } /* while setups remain */
+ }
+ /* if valid parameters and server found */
+ CTRACE((tfp, "%s `%s' %s\n",
+ "HTAASetup_lookup: No template matched",
+ NONNULL(docname),
+ "(so probably not protected)"));
+
+ return NULL; /* NULL in parameters, or not found */
+}
+
+/* static HTAASetup_new()
+ * CREATE A NEW SETUP NODE
+ * ON ENTRY:
+ * server is a pointer to a HTAAServer structure
+ * to which this setup belongs.
+ * ctemplate documents matching this template
+ * are protected according to this setup.
+ * valid_schemes a list containing all valid authentication
+ * schemes for this setup.
+ * If NULL, all schemes are disallowed.
+ * scheme_specifics is an array of assoc lists, which
+ * contain scheme specific parameters given
+ * by server in Authenticate: fields.
+ * If NULL, all scheme specifics are
+ * set to NULL.
+ * ON EXIT:
+ * returns a new HTAASetup node, and also adds it as
+ * part of the HTAAServer given as parameter.
+ */
+static HTAASetup *HTAASetup_new(HTAAServer *server, char *ctemplate,
+ HTList *valid_schemes,
+ HTAssocList **scheme_specifics)
+{
+ HTAASetup *setup;
+
+ if (!server || isEmpty(ctemplate))
+ return NULL;
+
+ if ((setup = typecalloc(HTAASetup)) == 0)
+ outofmem(__FILE__, "HTAASetup_new");
+
+ setup->retry = NO;
+ setup->server = server;
+ setup->ctemplate = NULL;
+ if (ctemplate)
+ StrAllocCopy(setup->ctemplate, ctemplate);
+ setup->valid_schemes = valid_schemes;
+ setup->scheme_specifics = scheme_specifics;
+
+ HTList_addObject(server->setups, (void *) setup);
+
+ return setup;
+}
+
+/* static HTAASetup_delete()
+ * FREE A HTAASetup STRUCTURE
+ * ON ENTRY:
+ * killme is a pointer to the structure to free().
+ *
+ * ON EXIT:
+ * returns nothing.
+ */
+static void HTAASetup_delete(HTAASetup * killme)
+{
+ int scheme;
+
+ if (killme) {
+ FREE(killme->ctemplate);
+ if (killme->valid_schemes) {
+ HTList_delete(killme->valid_schemes);
+ killme->valid_schemes = NULL;
+ }
+ for (scheme = 0; scheme < HTAA_MAX_SCHEMES; scheme++)
+ if (killme->scheme_specifics[scheme])
+ HTAssocList_delete(killme->scheme_specifics[scheme]);
+ FREE(killme->scheme_specifics);
+ FREE(killme);
+ }
+}
+
+/* static HTAASetup_updateSpecifics()
+ * COPY SCHEME SPECIFIC PARAMETERS
+ * TO HTAASetup STRUCTURE
+ * ON ENTRY:
+ * setup destination setup structure.
+ * specifics string array containing scheme
+ * specific parameters for each scheme.
+ * If NULL, all the scheme specific
+ * parameters are set to NULL.
+ *
+ * ON EXIT:
+ * returns nothing.
+ */
+static void HTAASetup_updateSpecifics(HTAASetup * setup, HTAssocList **specifics)
+{
+ int scheme;
+
+ if (setup) {
+ if (setup->scheme_specifics) {
+ for (scheme = 0; scheme < HTAA_MAX_SCHEMES; scheme++) {
+ if (setup->scheme_specifics[scheme])
+ HTAssocList_delete(setup->scheme_specifics[scheme]);
+ }
+ FREE(setup->scheme_specifics);
+ }
+ setup->scheme_specifics = specifics;
+ }
+}
+
+/*************************** HTAARealm **********************************/
+
+/* static HTAARealm_lookup()
+ * LOOKUP HTAARealm STRUCTURE BY REALM NAME
+ * ON ENTRY:
+ * realm_table a list of realm objects.
+ * realmname is the name of realm to look for.
+ *
+ * ON EXIT:
+ * returns the realm. NULL, if not found.
+ */
+static HTAARealm *HTAARealm_lookup(HTList *realm_table,
+ const char *realmname)
+{
+ if (realm_table && realmname) {
+ HTList *cur = realm_table;
+ HTAARealm *realm;
+
+ while (NULL != (realm = (HTAARealm *) HTList_nextObject(cur))) {
+ if (0 == strcmp(realm->realmname, realmname))
+ return realm;
+ }
+ }
+ return NULL; /* No table, NULL param, or not found */
+}
+
+/* static HTAARealm_new()
+ * CREATE A NODE CONTAINING USERNAME AND
+ * PASSWORD USED FOR THE GIVEN REALM.
+ * IF REALM ALREADY EXISTS, CHANGE
+ * USERNAME/PASSWORD.
+ * ON ENTRY:
+ * realm_table a list of realms to where to add
+ * the new one, too.
+ * realmname is the name of the password domain.
+ * username and
+ * password are what you can expect them to be.
+ *
+ * ON EXIT:
+ * returns the created realm.
+ */
+static HTAARealm *HTAARealm_new(HTList *realm_table,
+ const char *realmname,
+ const char *username,
+ const char *password)
+{
+ HTAARealm *realm;
+
+ realm = HTAARealm_lookup(realm_table, realmname);
+
+ if (!realm) {
+ if ((realm = typecalloc(HTAARealm)) == 0)
+ outofmem(__FILE__, "HTAARealm_new");
+
+ realm->realmname = NULL;
+ realm->username = NULL;
+ realm->password = NULL;
+ StrAllocCopy(realm->realmname, realmname);
+ if (realm_table)
+ HTList_addObject(realm_table, (void *) realm);
+ }
+ if (username)
+ StrAllocCopy(realm->username, username);
+ if (password)
+ StrAllocCopy(realm->password, password);
+
+ return realm;
+}
+
+BOOL HTAA_HaveUserinfo(const char *hostname)
+{
+ int gen_delims = 0;
+ BOOL result = FALSE;
+ char *my_info = NULL;
+
+ if (StrAllocCopy(my_info, hostname) != NULL) {
+ char *at_sign = HTSkipToAt(my_info, &gen_delims);
+
+ free(my_info);
+ if (at_sign != NULL && gen_delims == 0)
+ result = TRUE;
+ }
+ return result;
+}
+
+/*
+ * If there is userinfo in the hostname string, update the realm to use that
+ * information. The command-line "-auth" option will override this.
+ */
+static void fill_in_userinfo(HTAARealm *realm, const char *hostname)
+{
+ int gen_delims = 0;
+ char *my_info = NULL;
+ char *at_sign = HTSkipToAt(StrAllocCopy(my_info, hostname), &gen_delims);
+
+ if (at_sign != NULL && gen_delims == 0) {
+ char *colon;
+
+ *at_sign = '\0';
+ if ((colon = StrChr(my_info, ':')) != 0) {
+ *colon++ = '\0';
+ }
+ if (non_empty(my_info)) {
+ char *msg;
+ BOOL prior = non_empty(realm->username);
+
+ if (prior && strcmp(realm->username, my_info)) {
+ msg = 0;
+ HTSprintf0(&msg,
+ gettext("username for realm %s changed from %s to %s"),
+ realm->realmname,
+ realm->username,
+ my_info);
+ HTAlert(msg);
+ free(msg);
+ FREE(realm->username);
+ StrAllocCopy(realm->username, my_info);
+ } else if (!prior) {
+ StrAllocCopy(realm->username, my_info);
+ }
+ if (non_empty(colon)) {
+ prior = non_empty(realm->password);
+ if (prior && strcmp(realm->password, colon)) {
+ msg = 0;
+ HTSprintf0(&msg,
+ gettext("password for realm %s user %s changed"),
+ realm->realmname,
+ realm->username);
+ HTAlert(msg);
+ free(msg);
+ FREE(realm->password);
+ StrAllocCopy(realm->password, colon);
+ } else if (!prior) {
+ StrAllocCopy(realm->password, colon);
+ }
+ }
+ }
+ }
+ free(my_info);
+}
+
+/***************** Basic and Pubkey Authentication ************************/
+
+/* static compose_auth_string()
+ *
+ * COMPOSE Basic OR Pubkey AUTHENTICATION STRING;
+ * PROMPTS FOR USERNAME AND PASSWORD IF NEEDED
+ *
+ * ON ENTRY:
+ * hostname may include user- and password information
+ * scheme is either HTAA_BASIC or HTAA_PUBKEY.
+ * setup is the current server setup.
+ * IsProxy should be TRUE if this is a proxy.
+ *
+ * ON EXIT:
+ * returns a newly composed authorization string,
+ * (with, of course, a newly generated secret
+ * key and fresh timestamp, if Pubkey-scheme
+ * is being used).
+ * NULL, if something fails.
+ * NOTE:
+ * Like throughout the entire AA package, no string or structure
+ * returned by AA package needs to (or should) be freed.
+ *
+ */
+static char *compose_auth_string(const char *hostname,
+ HTAAScheme scheme,
+ HTAASetup * setup,
+ int IsProxy)
+{
+ char *cleartext = NULL; /* Cleartext presentation */
+ char *ciphertext = NULL; /* Encrypted presentation */
+ size_t len;
+ char *msg = NULL;
+ char *username = NULL;
+ char *password = NULL;
+ char *realmname = NULL;
+ char *theHost = NULL;
+ char *proxiedHost = NULL;
+ char *thePort = NULL;
+ HTAARealm *realm;
+ const char *i_net_addr = "0.0.0.0"; /* Change... @@@@ */
+ const char *timestamp = "42"; /* ... these @@@@ */
+
+ FREE(compose_auth_stringResult); /* From previous call */
+
+ if ((scheme != HTAA_BASIC && scheme != HTAA_PUBKEY) ||
+ !(setup &&
+ setup->scheme_specifics &&
+ setup->scheme_specifics[scheme] &&
+ setup->server &&
+ setup->server->realms))
+ return NULL;
+
+ realmname = HTAssocList_lookup(setup->scheme_specifics[scheme], "realm");
+ if (!realmname)
+ return NULL;
+
+ realm = HTAARealm_lookup(setup->server->realms, realmname);
+ setup->retry |= HTAA_HaveUserinfo(hostname);
+
+ if (!(realm &&
+ non_empty(realm->username) &&
+ non_empty(realm->password)) || setup->retry) {
+ if (!realm) {
+ CTRACE((tfp, "%s `%s' %s\n",
+ "compose_auth_string: realm:", realmname,
+ "not found -- creating"));
+ realm = HTAARealm_new(setup->server->realms,
+ realmname, NULL, NULL);
+ }
+ fill_in_userinfo(realm, hostname);
+ /*
+ * The template should be either the '*' global for everything on the
+ * server (always true for proxy authorization setups), or a path for
+ * the start of a protected limb, with no host field, but we'll check
+ * for a host anyway in case a WWW-Protection-Template header set an
+ * absolute URL instead of a path. If we do get a host from this, it
+ * will include the port. - FM
+ */
+ if ((!IsProxy) && using_proxy && setup->ctemplate) {
+ proxiedHost = HTParse(setup->ctemplate, "", PARSE_HOST);
+ if (proxiedHost && *proxiedHost != '\0') {
+ theHost = proxiedHost;
+ }
+ }
+ /*
+ * If we didn't get a host field from the template, set up the host
+ * name and port from the setup->server elements. - FM
+ */
+ if (!theHost)
+ theHost = setup->server->hostname;
+ if (setup->server->portnumber > 0 &&
+ setup->server->portnumber != 80) {
+ HTSprintf0(&thePort, ":%d", setup->server->portnumber);
+ }
+
+ HTSprintf0(&msg, gettext("Username for '%s' at %s '%s%s':"),
+ realm->realmname,
+ (IsProxy ? "proxy" : "server"),
+ (theHost ? theHost : "??"),
+ NonNull(thePort));
+ FREE(proxiedHost);
+ FREE(thePort);
+ if (non_empty(realm->username)) {
+ StrAllocCopy(username, realm->username);
+ }
+ if (non_empty(realm->password)) {
+ StrAllocCopy(password, realm->password);
+ }
+ HTPromptUsernameAndPassword(msg, &username, &password, IsProxy);
+
+ FREE(msg);
+ FREE(realm->username);
+ FREE(realm->password);
+
+ realm->username = username;
+ realm->password = password;
+
+ if (!realm->username || !realm->password) {
+ /*
+ * Signals to retry. - FM
+ */
+ return NULL;
+ } else if (*realm->username == '\0') {
+ /*
+ * Signals to abort. - FM
+ */
+ StrAllocCopy(compose_auth_stringResult, "");
+ return compose_auth_stringResult;
+ }
+ }
+
+ len = (strlen(NonNull(realm->username)) +
+ strlen(NonNull(realm->password)) + 3);
+
+ if (scheme == HTAA_PUBKEY) {
+#ifdef PUBKEY
+ /* Generate new secret key */
+ StrAllocCopy(secret_key, HTAA_generateRandomKey());
+#endif /* PUBKEY */
+ /* Room for secret key, timestamp and inet address */
+ len += strlen(NonNull(secret_key)) + 30;
+ } else {
+ FREE(secret_key);
+ }
+
+ if ((cleartext = typecallocn(char, len)) == 0)
+ outofmem(__FILE__, "compose_auth_string");
+
+ if (realm->username)
+ strcpy(cleartext, realm->username);
+ else
+ *cleartext = '\0';
+
+ strcat(cleartext, ":");
+
+ if (realm->password)
+ strcat(cleartext, realm->password);
+
+ if (scheme == HTAA_PUBKEY) {
+ strcat(cleartext, ":");
+ strcat(cleartext, i_net_addr);
+ strcat(cleartext, ":");
+ strcat(cleartext, timestamp);
+ strcat(cleartext, ":");
+ if (secret_key)
+ strcat(cleartext, secret_key);
+
+ if (!((ciphertext = typecallocn(char, 2 * len)) &&
+ (compose_auth_stringResult = typecallocn(char, 3 * len))))
+ outofmem(__FILE__, "compose_auth_string");
+
+#ifdef PUBKEY
+ HTPK_encrypt(cleartext, ciphertext, server->public_key);
+ HTUU_encode((unsigned char *) ciphertext, strlen(ciphertext),
+ compose_auth_stringResult);
+#endif /* PUBKEY */
+ FREE(cleartext);
+ FREE(ciphertext);
+ } else { /* scheme == HTAA_BASIC */
+ if (!(compose_auth_stringResult =
+ typecallocn(char, (4 * ((len + 2) / 3)) + 1)))
+ outofmem(__FILE__, "compose_auth_string");
+
+ HTUU_encode((unsigned char *) cleartext, strlen(cleartext),
+ compose_auth_stringResult);
+ FREE(cleartext);
+ }
+ return compose_auth_stringResult;
+}
+
+/* BROWSER static HTAA_selectScheme()
+ * SELECT THE AUTHENTICATION SCHEME TO USE
+ * ON ENTRY:
+ * setup is the server setup structure which can
+ * be used to make the decision about the
+ * used scheme.
+ *
+ * When new authentication methods are added to library
+ * this function makes the decision about which one to
+ * use at a given time. This can be done by inspecting
+ * environment variables etc.
+ *
+ * Currently only searches for the first valid scheme,
+ * and if nothing found suggests Basic scheme;
+ *
+ * ON EXIT:
+ * returns the authentication scheme to use.
+ */
+static HTAAScheme HTAA_selectScheme(HTAASetup * setup)
+{
+ int scheme;
+
+ if (setup && setup->valid_schemes) {
+ for (scheme = HTAA_BASIC; scheme < HTAA_MAX_SCHEMES; scheme++) {
+ void *object = (void *) (intptr_t) scheme;
+
+ if (-1 < HTList_indexOf(setup->valid_schemes, object))
+ return (HTAAScheme) scheme;
+ }
+ }
+ return HTAA_BASIC;
+}
+
+/*
+ * Purpose: Free off all module globals.
+ * Arguments: void
+ * Return Value: void
+ * Remarks/Portability/Dependencies/Restrictions:
+ * To be used at program exit.
+ * Revision History:
+ * 06-19-96 created - FM
+ */
+static void free_HTAAGlobals(void)
+{
+ HTAAServer *server;
+ int n, i;
+
+ if (server_table != NULL) {
+ n = HTList_count(server_table);
+ for (i = (n - 1); i >= 0; i--) {
+ if ((server = (HTAAServer *) HTList_objectAt(server_table,
+ i)) != NULL) {
+ HTAAServer_delete(server);
+ server = NULL;
+ }
+ }
+ HTList_delete(server_table);
+ server_table = NULL;
+ }
+
+ HTAAForwardAuth_reset();
+ FREE(HTAA_composeAuthResult);
+ FREE(current_hostname);
+ FREE(current_docname);
+ FREE(proxy_hostname);
+ FREE(proxy_docname);
+ FREE(compose_auth_stringResult);
+ FREE(secret_key);
+}
+
+/* BROWSER PUBLIC HTAA_composeAuth()
+ *
+ * SELECT THE AUTHENTICATION SCHEME AND
+ * COMPOSE THE ENTIRE AUTHORIZATION HEADER LINE
+ * IF WE ALREADY KNOW THAT THE HOST REQUIRES AUTHENTICATION
+ *
+ * ON ENTRY:
+ * hostname is the hostname of the server.
+ * portnumber is the portnumber in which the server runs.
+ * docname is the pathname of the document (as in URL)
+ * IsProxy should be TRUE if this is a proxy.
+ *
+ * ON EXIT:
+ * returns NULL, if no authorization seems to be needed, or
+ * if it is the entire Authorization: line, e.g.
+ *
+ * "Authorization: Basic username:password"
+ *
+ * As usual, this string is automatically freed.
+ */
+char *HTAA_composeAuth(const char *hostname,
+ const int portnumber,
+ const char *docname,
+ int IsProxy)
+{
+ char *auth_string;
+ BOOL retry;
+ HTAAScheme scheme;
+ size_t len;
+
+ /*
+ * Setup atexit() freeing if not done already. - FM
+ */
+ if (!free_HTAAGlobalsSet) {
+#ifdef LY_FIND_LEAKS
+ atexit(free_HTAAGlobals);
+#endif
+ free_HTAAGlobalsSet = TRUE;
+ }
+
+ /*
+ * Make gateway httpds pass authorization field as it was received. (This
+ * still doesn't really work because Authenticate: headers from remote
+ * server are not forwarded to client yet so it cannot really know that it
+ * should send authorization; I will not implement it yet because I feel we
+ * will soon change radically the way requests are represented to allow
+ * multithreading on server-side. Life is hard.)
+ */
+ if (HTAAForwardAuth) {
+ CTRACE((tfp, "HTAA_composeAuth: %s\n",
+ "Forwarding received authorization"));
+ StrAllocCopy(HTAA_composeAuthResult, HTAAForwardAuth);
+ HTAAForwardAuth_reset(); /* Just a precaution */
+ return HTAA_composeAuthResult;
+ }
+
+ FREE(HTAA_composeAuthResult); /* From previous call */
+
+ if (IsProxy) {
+ /*
+ * Proxy Authorization required. - AJL
+ */
+
+ CTRACE((tfp, "Composing Proxy Authorization for %s:%d/%s\n",
+ hostname, portnumber, docname));
+
+ if (proxy_portnumber != portnumber ||
+ !proxy_hostname || !proxy_docname ||
+ !hostname || !docname ||
+ 0 != strcmp(proxy_hostname, hostname) ||
+ 0 != strcmp(proxy_docname, docname)) {
+
+ retry = NO;
+
+ proxy_portnumber = portnumber;
+
+ if (hostname)
+ StrAllocCopy(proxy_hostname, hostname);
+ else
+ FREE(proxy_hostname);
+
+ if (docname)
+ StrAllocCopy(proxy_docname, docname);
+ else
+ FREE(proxy_docname);
+ } else {
+ retry = YES;
+ }
+
+ if (!proxy_setup || !retry)
+ proxy_setup = HTAASetup_lookup(hostname, portnumber,
+ docname, IsProxy);
+
+ if (!proxy_setup)
+ return NULL;
+
+ switch (scheme = HTAA_selectScheme(proxy_setup)) {
+ case HTAA_BASIC:
+ case HTAA_PUBKEY:
+ auth_string = compose_auth_string(hostname, scheme, proxy_setup, IsProxy);
+ break;
+ case HTAA_KERBEROS_V4:
+ /* OTHER AUTHENTICATION ROUTINES ARE CALLED HERE */
+ default:
+ {
+ char *msg = NULL;
+
+ HTSprintf0(&msg, "%s `%s'",
+ gettext("This client doesn't know how to compose proxy authorization information for scheme"),
+ HTAAScheme_name(scheme));
+ HTAlert(msg);
+ FREE(msg);
+ auth_string = NULL;
+ }
+ } /* switch scheme */
+
+ proxy_setup->retry = NO;
+
+ if (!auth_string)
+ /*
+ * Signal a failure. - FM
+ */
+ return NULL; /* Added by marca. */
+ if (*auth_string == '\0') {
+ /*
+ * Signal an abort. - FM
+ */
+ StrAllocCopy(HTAA_composeAuthResult, "");
+ return (HTAA_composeAuthResult);
+ }
+ len = strlen(auth_string) + strlen(HTAAScheme_name(scheme)) + 26;
+ if ((HTAA_composeAuthResult = typecallocn(char, len)) == 0)
+ outofmem(__FILE__, "HTAA_composeAuth");
+
+ strcpy(HTAA_composeAuthResult, "Proxy-Authorization: ");
+
+ } else {
+ /*
+ * Normal WWW authorization.
+ */
+ CTRACE((tfp, "Composing Authorization for %s:%d/%s\n",
+ hostname, portnumber, docname));
+
+ if (current_portnumber != portnumber ||
+ !current_hostname || !current_docname ||
+ !hostname || !docname ||
+ 0 != strcmp(current_hostname, hostname) ||
+ 0 != strcmp(current_docname, docname)) {
+
+ retry = NO;
+
+ current_portnumber = portnumber;
+
+ if (hostname)
+ StrAllocCopy(current_hostname, hostname);
+ else
+ FREE(current_hostname);
+
+ if (docname)
+ StrAllocCopy(current_docname, docname);
+ else
+ FREE(current_docname);
+ } else {
+ retry = YES;
+ }
+
+ if (!current_setup || !retry)
+ current_setup = HTAASetup_lookup(hostname, portnumber,
+ docname, IsProxy);
+
+ if (!current_setup)
+ return NULL;
+
+ switch (scheme = HTAA_selectScheme(current_setup)) {
+ case HTAA_BASIC:
+ case HTAA_PUBKEY:
+ auth_string = compose_auth_string(hostname, scheme, current_setup, IsProxy);
+ break;
+ case HTAA_KERBEROS_V4:
+ /* OTHER AUTHENTICATION ROUTINES ARE CALLED HERE */
+ default:
+ {
+ char *msg = 0;
+
+ HTSprintf0(&msg, "%s `%s'",
+ gettext("This client doesn't know how to compose authorization information for scheme"),
+ HTAAScheme_name(scheme));
+ HTAlert(msg);
+ FREE(msg);
+ auth_string = NULL;
+ }
+ } /* switch scheme */
+
+ current_setup->retry = NO;
+
+ if (!auth_string)
+ /*
+ * Signal a failure. - FM
+ */
+ return NULL; /* Added by marca. */
+ if (*auth_string == '\0') {
+ /*
+ * Signal an abort. - FM
+ */
+ StrAllocCopy(HTAA_composeAuthResult, "");
+ return (HTAA_composeAuthResult);
+ }
+
+ len = strlen(auth_string) + strlen(HTAAScheme_name(scheme)) + 20;
+ if ((HTAA_composeAuthResult = typecallocn(char, len)) == 0)
+ outofmem(__FILE__, "HTAA_composeAuth");
+
+ strcpy(HTAA_composeAuthResult, "Authorization: ");
+ }
+
+ strcat(HTAA_composeAuthResult, HTAAScheme_name(scheme));
+ strcat(HTAA_composeAuthResult, " ");
+ strcat(HTAA_composeAuthResult, auth_string);
+ return HTAA_composeAuthResult;
+}
+
+/* BROWSER PUBLIC HTAA_shouldRetryWithAuth()
+ *
+ * DETERMINES IF WE SHOULD RETRY THE SERVER
+ * WITH AUTHORIZATION
+ * (OR IF ALREADY RETRIED, WITH A DIFFERENT
+ * USERNAME AND/OR PASSWORD (IF MISSPELLED))
+ * ON ENTRY:
+ * start_of_headers is the first block already read from socket,
+ * but status line skipped; i.e., points to the
+ * start of the header section.
+ * length is the remaining length of the first block.
+ * soc is the socket to read the rest of server reply.
+ * IsProxy should be TRUE if this is a proxy.
+ *
+ * This function should only be called when
+ * server has replied with a 401 (Unauthorized)
+ * status code.
+ * ON EXIT:
+ * returns YES, if connection should be retried.
+ * The node containing all the necessary
+ * information is
+ * * either constructed if it does not exist
+ * * or password is reset to NULL to indicate
+ * that username and password should be
+ * reprompted when composing Authorization:
+ * field (in function HTAA_composeAuth()).
+ * NO, otherwise.
+ */
+BOOL HTAA_shouldRetryWithAuth(char *start_of_headers,
+ size_t length,
+ int soc,
+ int IsProxy)
+{
+ HTAAScheme scheme;
+ char *line = NULL;
+ int num_schemes = 0;
+ HTList *valid_schemes = HTList_new();
+ HTAssocList **scheme_specifics = NULL;
+ char *ctemplate = NULL;
+ char *temp = NULL;
+ BOOL result = NO;
+
+ /*
+ * Setup atexit() freeing if not done already. - FM
+ */
+ if (!free_HTAAGlobalsSet) {
+#ifdef LY_FIND_LEAKS
+ atexit(free_HTAAGlobals);
+#endif
+ free_HTAAGlobalsSet = TRUE;
+ }
+
+ /*
+ * Read server reply header lines
+ */
+ CTRACE((tfp, "Server reply header lines:\n"));
+
+ HTAA_setupReader(start_of_headers, length, soc);
+ while (NULL != (line = HTAA_getUnfoldedLine()) && *line != '\0') {
+ CTRACE((tfp, "%s\n", line));
+
+ if (StrChr(line, ':')) { /* Valid header line */
+
+ char *p = line;
+ char *fieldname = HTNextField(&p);
+ char *arg1 = HTNextField(&p);
+ char *args = p;
+
+ if ((IsProxy &&
+ 0 == strcasecomp(fieldname, "Proxy-Authenticate:")) ||
+ (!IsProxy &&
+ 0 == strcasecomp(fieldname, "WWW-Authenticate:"))) {
+ if (isEmpty(arg1) || isEmpty(args)) {
+ HTSprintf0(&temp, gettext("Invalid header '%s%s%s%s%s'"), line,
+ (non_empty(arg1) ? " " : ""),
+ NonNull(arg1),
+ (non_empty(args) ? " " : ""),
+ NonNull(args));
+ HTAlert(temp);
+ FREE(temp);
+ } else if (HTAA_UNKNOWN != (scheme = HTAAScheme_enum(arg1))) {
+ HTList_addObject(valid_schemes, (void *) scheme);
+ if (!scheme_specifics) {
+ int i;
+
+ scheme_specifics =
+ typecallocn(HTAssocList *, HTAA_MAX_SCHEMES);
+
+ if (!scheme_specifics)
+ outofmem(__FILE__, "HTAA_shouldRetryWithAuth");
+
+ for (i = 0; i < HTAA_MAX_SCHEMES; i++)
+ scheme_specifics[i] = NULL;
+ }
+ scheme_specifics[scheme] = HTAA_parseArgList(args);
+ num_schemes++;
+ } else {
+ CTRACE((tfp, "Unknown scheme `%s' %s\n",
+ NONNULL(arg1),
+ (IsProxy ?
+ "in Proxy-Authenticate: field" :
+ "in WWW-Authenticate: field")));
+ }
+ }
+
+ else if (!IsProxy &&
+ 0 == strcasecomp(fieldname, "WWW-Protection-Template:")) {
+ CTRACE((tfp, "Protection template set to `%s'\n", arg1));
+ StrAllocCopy(ctemplate, arg1);
+ }
+
+ } else {
+ CTRACE((tfp, "Invalid header line `%s' ignored\n", line));
+ }
+
+ FREE(line);
+ } /* while header lines remain */
+ FREE(line);
+
+ /*
+ * So should we retry with authorization?
+ */
+ if (IsProxy) {
+ if (num_schemes == 0) {
+ /*
+ * No proxy authorization valid
+ */
+ proxy_setup = NULL;
+ result = NO;
+ }
+ /*
+ * Doing it for proxy. -AJL
+ */
+ else if (proxy_setup && proxy_setup->server) {
+ /*
+ * We have already tried with proxy authorization. Either we don't
+ * have access or username or password was misspelled.
+ *
+ * Update scheme-specific parameters (in case they have expired by
+ * chance).
+ */
+ HTAASetup_updateSpecifics(proxy_setup, scheme_specifics);
+
+ if (NO == HTConfirm(AUTH_FAILED_PROMPT)) {
+ proxy_setup = NULL;
+ result = NO;
+ } else {
+ /*
+ * Re-ask username+password (if misspelled).
+ */
+ HTList_delete(valid_schemes);
+ proxy_setup->retry = YES;
+ result = YES;
+ }
+ } else {
+ /*
+ * proxy_setup == NULL, i.e., we have a first connection to a
+ * protected server or the server serves a wider set of documents
+ * than we expected so far.
+ */
+ HTAAServer *server = HTAAServer_lookup(proxy_hostname,
+ proxy_portnumber,
+ IsProxy);
+
+ if (!server) {
+ server = HTAAServer_new(proxy_hostname,
+ proxy_portnumber,
+ IsProxy);
+ }
+ if (!ctemplate) /* Proxy matches everything -AJL */
+ StrAllocCopy(ctemplate, "*");
+ proxy_setup = HTAASetup_new(server,
+ ctemplate,
+ valid_schemes,
+ scheme_specifics);
+ FREE(ctemplate);
+
+ HTAlert(gettext("Proxy authorization required -- retrying"));
+ result = YES;
+ }
+ }
+ /*
+ * Normal WWW authorization.
+ */
+ else if (num_schemes == 0) {
+ /*
+ * No authorization valid.
+ */
+ current_setup = NULL;
+ result = NO;
+ } else if (current_setup && current_setup->server) {
+ /*
+ * So we have already tried with WWW authorization. Either we don't
+ * have access or username or password was misspelled.
+ *
+ * Update scheme-specific parameters (in case they have expired by
+ * chance).
+ */
+ HTAASetup_updateSpecifics(current_setup, scheme_specifics);
+
+ if (NO == HTConfirm(AUTH_FAILED_PROMPT)) {
+ current_setup = NULL;
+ result = NO;
+ } else {
+ /*
+ * Re-ask username+password (if misspelled).
+ */
+ current_setup->retry = YES;
+ result = YES;
+ }
+ } else {
+ /*
+ * current_setup == NULL, i.e., we have a first connection to a
+ * protected server or the server serves a wider set of documents than
+ * we expected so far.
+ */
+ HTAAServer *server = HTAAServer_lookup(current_hostname,
+ current_portnumber,
+ IsProxy);
+
+ if (!server) {
+ server = HTAAServer_new(current_hostname,
+ current_portnumber,
+ IsProxy);
+ }
+ if (!ctemplate)
+ ctemplate = HTAA_makeProtectionTemplate(current_docname);
+ current_setup = HTAASetup_new(server,
+ ctemplate,
+ valid_schemes,
+ scheme_specifics);
+ FREE(ctemplate);
+
+ HTAlert(gettext("Access without authorization denied -- retrying"));
+ result = YES;
+ }
+
+ if (result == NO) {
+ HTList_delete(valid_schemes);
+ }
+ return result;
+}
+
+/*
+ * This function clears all authorization information by
+ * invoking the free_HTAAGlobals() function, which normally
+ * is invoked at exit. It allows a browser command to do
+ * this at any time, for example, if the user is leaving
+ * the terminal for a period of time, but does not want
+ * to end the current session. - FM
+ */
+void HTClearHTTPAuthInfo(void)
+{
+ /*
+ * Need code to check cached documents against the protection templates,
+ * and do something to ensure that any protected documents no longer can be
+ * accessed without a new retrieval. - FM
+ */
+
+ /*
+ * Now free all of the authorization info, and reset the
+ * free_HTAAGlobalsSet flag. - FM
+ */
+ free_HTAAGlobals();
+ free_HTAAGlobalsSet = FALSE;
+}
diff --git a/WWW/Library/Implementation/HTAABrow.h b/WWW/Library/Implementation/HTAABrow.h
new file mode 100644
index 0000000..f151deb
--- /dev/null
+++ b/WWW/Library/Implementation/HTAABrow.h
@@ -0,0 +1,142 @@
+/*
+ * $LynxId: HTAABrow.h,v 1.17 2016/11/24 23:32:22 tom Exp $
+ *
+ * BROWSER SIDE ACCESS AUTHORIZATION MODULE
+
+ This module is the browser side interface to Access Authorization (AA) package. It
+ contains code only for browser.
+
+ Important to know about memory allocation:
+
+ Routines in this module use dynamic allocation, but free automatically all the memory
+ reserved by them.
+
+ Therefore the caller never has to (and never should) free() any object returned by
+ these functions.
+
+ Therefore also all the strings returned by this package are only valid until the next
+ call to the same function is made. This approach is selected, because of the nature of
+ access authorization: no string returned by the package needs to be valid longer than
+ until the next call.
+
+ This also makes it easy to plug the AA package in: you don't have to ponder whether to
+ free()something here or is it done somewhere else (because it is always done somewhere
+ else).
+
+ The strings that the package needs to store are copied so the original strings given as
+ parameters to AA functions may be freed or modified with no side effects.
+
+ Also note:The AA package does not free() anything else than what it has itself
+ allocated.
+
+ */
+
+#ifndef HTAABROW_H
+#define HTAABROW_H
+
+#include <HTAAUtil.h> /* Common parts of AA */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+ Routines for Browser Side Recording of AA Info
+
+ Most of the browser-side AA is done by the following two functions (which are called
+ from file HTTP.c so the browsers using libwww only need to be linked with the new
+ library and not be changed at all):
+
+ HTAA_composeAuth() composes the Authorization: line contents, if the AA package
+ thinks that the given document is protected. Otherwise this function returns NULL.
+ This function also calls the functions HTPrompt(),HTPromptPassword() and HTConfirm()
+ to get the username, password and some confirmation from the user.
+
+ HTAA_shouldRetryWithAuth() determines whether to retry the request with AA or with a
+ new AA (in case username or password was misspelled).
+
+ */
+/* PUBLIC HTAA_composeAuth()
+ *
+ * COMPOSE THE ENTIRE AUTHORIZATION HEADER LINE IF WE
+ * ALREADY KNOW, THAT THE HOST MIGHT REQUIRE AUTHORIZATION
+ *
+ * ON ENTRY:
+ * hostname is the hostname of the server.
+ * portnumber is the portnumber in which the server runs.
+ * docname is the pathname of the document (as in URL)
+ *
+ * ON EXIT:
+ * returns NULL, if no authorization seems to be needed, or
+ * if it is the entire Authorization: line, e.g.
+ *
+ * "Authorization: basic username:password"
+ *
+ * As usual, this string is automatically freed.
+ */
+ extern char *HTAA_composeAuth(const char *hostname,
+ const int portnumber,
+ const char *docname,
+ int IsProxy);
+
+/* BROWSER PUBLIC HTAA_shouldRetryWithAuth()
+ *
+ * DETERMINES IF WE SHOULD RETRY THE SERVER
+ * WITH AUTHORIZATION
+ * (OR IF ALREADY RETRIED, WITH A DIFFERENT
+ * USERNAME AND/OR PASSWORD (IF MISSPELLED))
+ * ON ENTRY:
+ * start_of_headers is the first block already read from socket,
+ * but status line skipped; i.e., points to the
+ * start of the header section.
+ * length is the remaining length of the first block.
+ * soc is the socket to read the rest of server reply.
+ *
+ * This function should only be called when
+ * server has replied with a 401 (Unauthorized)
+ * status code.
+ * ON EXIT:
+ * returns YES, if connection should be retried.
+ * The node containing all the necessary
+ * information is
+ * * either constructed if it does not exist
+ * * or password is reset to NULL to indicate
+ * that username and password should be
+ * reprompted when composing Authorization:
+ * field (in function HTAA_composeAuth()).
+ * NO, otherwise.
+ */
+ extern BOOL HTAA_shouldRetryWithAuth(char *start_of_headers,
+ size_t length,
+ int soc,
+ int IsProxy);
+
+/*
+ * Function to allow clearing of all Authorization info
+ * via a browser command. - FM
+ */
+ extern void HTClearHTTPAuthInfo(void);
+
+/*
+ * Check if a hostname-string contains user information.
+ */
+ extern BOOL HTAA_HaveUserinfo(const char *hostname);
+
+/*
+
+Enabling Gateway httpds to Forward Authorization
+
+ These functions should only be called from daemon code, and HTAAForwardAuth_reset()
+ must be called before the next request is handled to make sure that authorization
+ string isn't cached in daemon so that other people can access private files using
+ somebody else's previous authorization information.
+
+ */
+
+ extern void HTAAForwardAuth_set(const char *scheme_name,
+ const char *scheme_specifics);
+ extern void HTAAForwardAuth_reset(void);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* NOT HTAABROW_H */
diff --git a/WWW/Library/Implementation/HTAAProt.c b/WWW/Library/Implementation/HTAAProt.c
new file mode 100644
index 0000000..d5117f1
--- /dev/null
+++ b/WWW/Library/Implementation/HTAAProt.c
@@ -0,0 +1,738 @@
+/*
+ * $LynxId: HTAAProt.c,v 1.34 2016/11/24 15:29:50 tom Exp $
+ *
+ * MODULE HTAAProt.c
+ * PROTECTION FILE PARSING MODULE
+ *
+ * AUTHORS:
+ * AL Ari Luotonen luotonen@dxcern.cern.ch
+ * MD Mark Donszelmann duns@vxdeop.cern.ch
+ *
+ * HISTORY:
+ * 20 Oct 93 AL Now finds uid/gid for nobody/nogroup by name
+ * (doesn't use default 65534 right away).
+ * Also understands negative uids/gids.
+ * 14 Nov 93 MD Added VMS compatibility
+ *
+ * BUGS:
+ *
+ *
+ */
+
+#include <HTUtils.h>
+
+#ifndef VMS
+#ifndef NOUSERS
+#include <pwd.h> /* Unix password file routine: getpwnam() */
+#include <grp.h> /* Unix group file routine: getgrnam() */
+#endif /* NOUSERS */
+#endif /* not VMS */
+
+#include <HTAAUtil.h>
+#include <HTLex.h> /* Lexical analysor */
+#include <HTAAProt.h> /* Implemented here */
+
+#include <LYUtils.h>
+#include <LYLeaks.h>
+
+#define NOBODY 65534 /* -2 in 16-bit environment */
+#define NONESUCH 65533 /* -3 in 16-bit environment */
+
+/*
+ * Protection setup caching
+ */
+typedef struct {
+ char *prot_filename;
+ HTAAProt *prot;
+} HTAAProtCache;
+
+static HTList *prot_cache = NULL; /* Protection setup cache. */
+static HTAAProt *default_prot = NULL; /* Default protection. */
+static HTAAProt *current_prot = NULL; /* Current protection mode */
+
+ /* which is set up by callbacks */
+ /* from the rule system when */
+ /* a "protect" rule is matched. */
+
+#ifndef NOUSERS
+/* static isNumber()
+ * DOES A CHARACTER STRING REPRESENT A NUMBER
+ */
+static BOOL isNumber(const char *s)
+{
+ const char *cur = s;
+
+ if (isEmpty(s))
+ return NO;
+
+ if (*cur == '-')
+ cur++; /* Allow initial minus sign in a number */
+
+ while (*cur) {
+ if (*cur < '0' || *cur > '9')
+ return NO;
+ cur++;
+ }
+ return YES;
+}
+
+/* PUBLIC HTAA_getUid()
+ * GET THE USER ID TO CHANGE THE PROCESS UID TO
+ * ON ENTRY:
+ * No arguments.
+ *
+ * ON EXIT:
+ * returns the uid number to give to setuid() system call.
+ * Default is 65534 (nobody).
+ */
+int HTAA_getUid(void)
+{
+ int uid;
+
+ if (current_prot && current_prot->uid_name) {
+ if (isNumber(current_prot->uid_name)) {
+ uid = atoi(current_prot->uid_name);
+ if ((*HTAA_UidToName(uid)) != '\0') {
+ return uid;
+ }
+ } else { /* User name (not a number) */
+ if ((uid = HTAA_NameToUid(current_prot->uid_name)) != NONESUCH) {
+ return uid;
+ }
+ }
+ }
+ /*
+ * Ok, then let's get uid for nobody.
+ */
+ if ((uid = HTAA_NameToUid("nobody")) != NONESUCH) {
+ return uid;
+ }
+ /*
+ * Ok, then use default.
+ */
+ return NOBODY; /* nobody */
+}
+
+/* PUBLIC HTAA_getGid()
+ * GET THE GROUP ID TO CHANGE THE PROCESS GID TO
+ * ON ENTRY:
+ * No arguments.
+ *
+ * ON EXIT:
+ * returns the uid number to give to setgid() system call.
+ * Default is 65534 (nogroup).
+ */
+int HTAA_getGid(void)
+{
+ int gid;
+
+ if (current_prot && current_prot->gid_name) {
+ if (isNumber(current_prot->gid_name)) {
+ gid = atoi(current_prot->gid_name);
+ if (*HTAA_GidToName(gid) != '\0') {
+ return gid;
+ }
+ } else { /* Group name (not number) */
+ if ((gid = HTAA_NameToGid(current_prot->gid_name)) != NONESUCH) {
+ return gid;
+ }
+ }
+ }
+ /*
+ * Ok, then let's get gid for nogroup.
+ */
+ if ((gid = HTAA_NameToGid("nogroup")) != NONESUCH) {
+ return gid;
+ }
+ /*
+ * Ok, then use default.
+ */
+ return NOBODY; /* nogroup */
+}
+#endif /* !NOUSERS */
+
+/* static HTAA_setIds()
+ * SET UID AND GID (AS NAMES OR NUMBERS)
+ * TO HTAAProt STRUCTURE
+ * ON ENTRY:
+ * prot destination.
+ * ids is a string like "james.www" or "1422.69" etc.
+ * giving uid and gid.
+ *
+ * ON EXIT:
+ * returns nothing.
+ */
+static void HTAA_setIds(HTAAProt *prot, const char *ids)
+{
+ if (ids) {
+ char *local_copy = NULL;
+ char *point;
+
+ StrAllocCopy(local_copy, ids);
+ point = StrChr(local_copy, '.');
+ if (point) {
+ *(point++) = (char) 0;
+ StrAllocCopy(prot->gid_name, point);
+ } else {
+ StrAllocCopy(prot->gid_name, "nogroup");
+ }
+ StrAllocCopy(prot->uid_name, local_copy);
+ FREE(local_copy);
+ } else {
+ StrAllocCopy(prot->uid_name, "nobody");
+ StrAllocCopy(prot->gid_name, "nogroup");
+ }
+}
+
+/* static HTAA_parseProtFile()
+ * PARSE A PROTECTION SETUP FILE AND
+ * PUT THE RESULT IN A HTAAProt STRUCTURE
+ * ON ENTRY:
+ * prot destination structure.
+ * fp open protection file.
+ *
+ * ON EXIT:
+ * returns nothing.
+ */
+static void HTAA_parseProtFile(HTAAProt *prot, FILE *fp)
+{
+ if (prot && fp) {
+ LexItem lex_item;
+ char *fieldname = NULL;
+
+ while (LEX_EOF != (lex_item = lex(fp))) {
+
+ while (lex_item == LEX_REC_SEP) /* Ignore empty lines */
+ lex_item = lex(fp);
+
+ if (lex_item == LEX_EOF) /* End of file */
+ break;
+
+ if (lex_item == LEX_ALPH_STR) { /* Valid setup record */
+
+ StrAllocCopy(fieldname, HTlex_buffer);
+
+ if (LEX_FIELD_SEP != (lex_item = lex(fp)))
+ unlex(lex_item); /* If someone wants to use colon */
+ /* after field name it's ok, but */
+ /* not required. Here we read it. */
+
+ if (0 == strncasecomp(fieldname, "Auth", 4)) {
+ lex_item = lex(fp);
+ while (lex_item == LEX_ALPH_STR) {
+ HTAAScheme scheme = HTAAScheme_enum(HTlex_buffer);
+
+ if (scheme != HTAA_UNKNOWN) {
+ if (!prot->valid_schemes)
+ prot->valid_schemes = HTList_new();
+ HTList_addObject(prot->valid_schemes, (void *) scheme);
+ CTRACE((tfp, "%s %s `%s'\n",
+ "HTAA_parseProtFile: valid",
+ "authentication scheme:",
+ HTAAScheme_name(scheme)));
+ } else {
+ CTRACE((tfp, "%s %s `%s'\n",
+ "HTAA_parseProtFile: unknown",
+ "authentication scheme:",
+ HTlex_buffer));
+ }
+
+ if (LEX_ITEM_SEP != (lex_item = lex(fp)))
+ break;
+ /*
+ * Here lex_item == LEX_ITEM_SEP; after item separator
+ * it is ok to have one or more newlines (LEX_REC_SEP)
+ * and they are ignored (continuation line).
+ */
+ do {
+ lex_item = lex(fp);
+ } while (lex_item == LEX_REC_SEP);
+ } /* while items in list */
+ }
+ /* if "Authenticate" */
+ else if (0 == strncasecomp(fieldname, "mask", 4)) {
+ prot->mask_group = HTAA_parseGroupDef(fp);
+ lex_item = LEX_REC_SEP; /*groupdef parser read this already */
+ if (TRACE) {
+ if (prot->mask_group) {
+ fprintf(tfp,
+ "HTAA_parseProtFile: Mask group:\n");
+ HTAA_printGroupDef(prot->mask_group);
+ } else
+ fprintf(tfp,
+ "HTAA_parseProtFile: Mask group syntax error\n");
+ }
+ }
+ /* if "Mask" */
+ else { /* Just a name-value pair, put it to assoclist */
+
+ if (LEX_ALPH_STR == (lex_item = lex(fp))) {
+ if (!prot->values)
+ prot->values = HTAssocList_new();
+ HTAssocList_add(prot->values, fieldname, HTlex_buffer);
+ lex_item = lex(fp); /* Read record separator */
+ CTRACE((tfp, "%s `%s' bound to value `%s'\n",
+ "HTAA_parseProtFile: Name",
+ fieldname, HTlex_buffer));
+ }
+ } /* else name-value pair */
+
+ }
+ /* if valid field */
+ if (lex_item != LEX_EOF && lex_item != LEX_REC_SEP) {
+ CTRACE((tfp, "%s %s %d (that line ignored)\n",
+ "HTAA_parseProtFile: Syntax error",
+ "in protection setup file at line",
+ HTlex_line));
+ do {
+ lex_item = lex(fp);
+ } while (lex_item != LEX_EOF && lex_item != LEX_REC_SEP);
+ } /* if syntax error */
+ } /* while not end-of-file */
+ FREE(fieldname);
+ } /* if valid parameters */
+}
+
+/* static HTAAProt_new()
+ * ALLOCATE A NEW HTAAProt STRUCTURE AND
+ * INITIALIZE IT FROM PROTECTION SETUP FILE
+ * ON ENTRY:
+ * cur_docname current filename after rule translations.
+ * prot_filename protection setup file name.
+ * If NULL, not an error.
+ * ids Uid and gid names or numbers,
+ * examples:
+ * james ( <=> james.nogroup)
+ * .www ( <=> nobody.www)
+ * james.www
+ * james.69
+ * 1422.69
+ * 1422.www
+ *
+ * May be NULL, defaults to nobody.nogroup.
+ * Should be NULL, if prot_file is NULL.
+ *
+ * ON EXIT:
+ * returns returns a new and initialized protection
+ * setup structure.
+ * If setup file is already read in (found
+ * in cache), only sets uid_name and gid
+ * fields, and returns that.
+ */
+static HTAAProt *HTAAProt_new(const char *cur_docname,
+ const char *prot_filename,
+ const char *ids)
+{
+ HTList *cur = prot_cache;
+ HTAAProtCache *cache_item = NULL;
+ HTAAProt *prot;
+ FILE *fp;
+
+ if (!prot_cache)
+ prot_cache = HTList_new();
+
+ while (NULL != (cache_item = (HTAAProtCache *) HTList_nextObject(cur))) {
+ if (!strcmp(cache_item->prot_filename, prot_filename))
+ break;
+ }
+ if (cache_item) {
+ prot = cache_item->prot;
+ CTRACE((tfp, "%s `%s' already in cache\n",
+ "HTAAProt_new: Protection file", prot_filename));
+ } else {
+ CTRACE((tfp, "HTAAProt_new: Loading protection file `%s'\n",
+ prot_filename));
+
+ if ((prot = typecalloc(HTAAProt)) == 0)
+ outofmem(__FILE__, "HTAAProt_new");
+
+ prot->ctemplate = NULL;
+ prot->filename = NULL;
+ prot->uid_name = NULL;
+ prot->gid_name = NULL;
+ prot->valid_schemes = HTList_new();
+ prot->mask_group = NULL; /* Masking disabled by defaults */
+ prot->values = HTAssocList_new();
+
+ if (prot_filename && NULL != (fp = fopen(prot_filename, TXT_R))) {
+ HTAA_parseProtFile(prot, fp);
+ fclose(fp);
+ if ((cache_item = typecalloc(HTAAProtCache)) == 0)
+ outofmem(__FILE__, "HTAAProt_new");
+
+ cache_item->prot = prot;
+ cache_item->prot_filename = NULL;
+ StrAllocCopy(cache_item->prot_filename, prot_filename);
+ HTList_addObject(prot_cache, (void *) cache_item);
+ } else {
+ CTRACE((tfp, "HTAAProt_new: %s `%s'\n",
+ "Unable to open protection setup file",
+ NONNULL(prot_filename)));
+ }
+ }
+
+ if (cur_docname)
+ StrAllocCopy(prot->filename, cur_docname);
+ HTAA_setIds(prot, ids);
+
+ return prot;
+}
+
+/* PUBLIC HTAA_setDefaultProtection()
+ * SET THE DEFAULT PROTECTION MODE
+ * (called by rule system when a
+ * "defprot" rule is matched)
+ * ON ENTRY:
+ * cur_docname is the current result of rule translations.
+ * prot_filename is the protection setup file (second argument
+ * for "defprot" rule, optional)
+ * ids contains user and group names separated by
+ * a dot, corresponding to the uid
+ * gid under which the server should run,
+ * default is "nobody.nogroup" (third argument
+ * for "defprot" rule, optional; can be given
+ * only if protection setup file is also given).
+ *
+ * ON EXIT:
+ * returns nothing.
+ * Sets the module-wide variable default_prot.
+ */
+void HTAA_setDefaultProtection(const char *cur_docname,
+ const char *prot_filename,
+ const char *ids)
+{
+ default_prot = NULL; /* Not free()'d because this is in cache */
+
+ if (prot_filename) {
+ default_prot = HTAAProt_new(cur_docname, prot_filename, ids);
+ } else {
+ CTRACE((tfp, "%s %s\n",
+ "HTAA_setDefaultProtection: ERROR: Protection file",
+ "not specified (obligatory for DefProt rule)!!\n"));
+ }
+}
+
+/* PUBLIC HTAA_setCurrentProtection()
+ * SET THE CURRENT PROTECTION MODE
+ * (called by rule system when a
+ * "protect" rule is matched)
+ * ON ENTRY:
+ * cur_docname is the current result of rule translations.
+ * prot_filename is the protection setup file (second argument
+ * for "protect" rule, optional)
+ * ids contains user and group names separated by
+ * a dot, corresponding to the uid
+ * gid under which the server should run,
+ * default is "nobody.nogroup" (third argument
+ * for "protect" rule, optional; can be given
+ * only if protection setup file is also given).
+ *
+ * ON EXIT:
+ * returns nothing.
+ * Sets the module-wide variable current_prot.
+ */
+void HTAA_setCurrentProtection(const char *cur_docname,
+ const char *prot_filename,
+ const char *ids)
+{
+ current_prot = NULL; /* Not free()'d because this is in cache */
+
+ if (prot_filename) {
+ current_prot = HTAAProt_new(cur_docname, prot_filename, ids);
+ } else {
+ if (default_prot) {
+ current_prot = default_prot;
+ HTAA_setIds(current_prot, ids);
+ CTRACE((tfp, "%s %s %s\n",
+ "HTAA_setCurrentProtection: Protection file",
+ "not specified for Protect rule",
+ "-- using default protection"));
+ } else {
+ CTRACE((tfp, "%s %s %s\n",
+ "HTAA_setCurrentProtection: ERROR: Protection",
+ "file not specified for Protect rule, and",
+ "default protection is not set!!"));
+ }
+ }
+}
+
+/* PUBLIC HTAA_getCurrentProtection()
+ * GET CURRENT PROTECTION SETUP STRUCTURE
+ * (this is set up by callbacks made from
+ * the rule system when matching "protect"
+ * (and "defprot") rules)
+ * ON ENTRY:
+ * HTTranslate() must have been called before calling
+ * this function.
+ *
+ * ON EXIT:
+ * returns a HTAAProt structure representing the
+ * protection setup of the HTTranslate()'d file.
+ * This must not be free()'d.
+ */
+HTAAProt *HTAA_getCurrentProtection(void)
+{
+ return current_prot;
+}
+
+/* PUBLIC HTAA_getDefaultProtection()
+ * GET DEFAULT PROTECTION SETUP STRUCTURE
+ * AND SET IT TO CURRENT PROTECTION
+ * (this is set up by callbacks made from
+ * the rule system when matching "defprot"
+ * rules)
+ * ON ENTRY:
+ * HTTranslate() must have been called before calling
+ * this function.
+ *
+ * ON EXIT:
+ * returns a HTAAProt structure representing the
+ * default protection setup of the HTTranslate()'d
+ * file (if HTAA_getCurrentProtection() returned
+ * NULL, i.e., if there is no "protect" rule
+ * but ACL exists, and we need to know default
+ * protection settings).
+ * This must not be free()'d.
+ * IMPORTANT:
+ * As a side-effect this tells the protection system that
+ * the file is in fact protected and sets the current
+ * protection mode to default.
+ */
+HTAAProt *HTAA_getDefaultProtection(void)
+{
+ if (!current_prot) {
+ current_prot = default_prot;
+ default_prot = NULL;
+ }
+ return current_prot;
+}
+
+/* SERVER INTERNAL HTAA_clearProtections()
+ * CLEAR DOCUMENT PROTECTION MODE
+ * (ALSO DEFAULT PROTECTION)
+ * (called by the rule system)
+ * ON ENTRY:
+ * No arguments.
+ *
+ * ON EXIT:
+ * returns nothing.
+ * Frees the memory used by protection information.
+ */
+void HTAA_clearProtections(void)
+{
+ current_prot = NULL; /* These are not freed because */
+ default_prot = NULL; /* they are actually in cache. */
+}
+
+typedef struct {
+ char *name;
+ int user;
+} USER_DATA;
+
+#ifndef NOUSERS
+static HTList *known_grp = NULL;
+static HTList *known_pwd = NULL;
+static BOOL uidgid_cache_inited = NO;
+#endif
+
+#ifdef LY_FIND_LEAKS
+static void clear_uidgid_cache(void)
+{
+#ifndef NOUSERS
+ USER_DATA *data;
+
+ if (known_grp) {
+ while ((data = HTList_removeLastObject(known_grp)) != NULL) {
+ FREE(data->name);
+ FREE(data);
+ }
+ FREE(known_grp);
+ }
+ if (known_pwd) {
+ while ((data = HTList_removeLastObject(known_pwd)) != NULL) {
+ FREE(data->name);
+ FREE(data);
+ }
+ FREE(known_pwd);
+ }
+#endif
+}
+#endif /* LY_FIND_LEAKS */
+
+#ifndef NOUSERS
+static void save_gid_info(const char *name, int user)
+{
+ USER_DATA *data = typecalloc(USER_DATA);
+
+ if (!data)
+ return;
+ if (!known_grp) {
+ known_grp = HTList_new();
+ if (!uidgid_cache_inited) {
+#ifdef LY_FIND_LEAKS
+ atexit(clear_uidgid_cache);
+#endif
+ uidgid_cache_inited = YES;
+ }
+ }
+ StrAllocCopy(data->name, name);
+ data->user = user;
+ HTList_addObject(known_grp, data);
+}
+#endif /* NOUSERS */
+
+#ifndef NOUSERS
+static void save_uid_info(const char *name, int user)
+{
+ USER_DATA *data = typecalloc(USER_DATA);
+
+ if (!data)
+ return;
+ if (!known_pwd) {
+ known_pwd = HTList_new();
+ if (!uidgid_cache_inited) {
+#ifdef LY_FIND_LEAKS
+ atexit(clear_uidgid_cache);
+#endif
+ uidgid_cache_inited = YES;
+ }
+ }
+ StrAllocCopy(data->name, name);
+ data->user = user;
+ HTList_addObject(known_pwd, data);
+}
+#endif /* !NOUSERS */
+
+/* PUBLIC HTAA_UidToName
+ * GET THE USER NAME
+ * ON ENTRY:
+ * The user-id
+ *
+ * ON EXIT:
+ * returns the user name, or an empty string if not found.
+ */
+const char *HTAA_UidToName(int uid GCC_UNUSED)
+{
+#ifndef NOUSERS
+ struct passwd *pw;
+ HTList *me = known_pwd;
+
+ while (HTList_nextObject(me)) {
+ USER_DATA *data = (USER_DATA *) (me->object);
+
+ if (uid == data->user)
+ return data->name;
+ }
+
+ if ((pw = getpwuid((uid_t) uid)) != 0
+ && pw->pw_name != 0) {
+ CTRACE((tfp, "%s(%d) returned (%s:%d:...)\n",
+ "HTAA_UidToName: getpwuid",
+ uid,
+ pw->pw_name, (int) pw->pw_uid));
+ save_uid_info(pw->pw_name, (int) pw->pw_uid);
+ return pw->pw_name;
+ }
+#endif
+ return "";
+}
+
+/* PUBLIC HTAA_NameToUid
+ * GET THE USER ID
+ * ON ENTRY:
+ * The user-name
+ *
+ * ON EXIT:
+ * returns the user id, or NONESUCH if not found.
+ */
+int HTAA_NameToUid(const char *name GCC_UNUSED)
+{
+#ifndef NOUSERS
+ struct passwd *pw;
+ HTList *me = known_pwd;
+
+ while (HTList_nextObject(me)) {
+ USER_DATA *data = (USER_DATA *) (me->object);
+
+ if (!strcmp(name, data->name))
+ return data->user;
+ }
+
+ if ((pw = getpwnam(name)) != 0) {
+ CTRACE((tfp, "%s(%s) returned (%s:%d:...)\n",
+ "HTAA_NameToUid: getpwnam",
+ name,
+ pw->pw_name, (int) pw->pw_uid));
+ save_uid_info(pw->pw_name, (int) pw->pw_uid);
+ return (int) pw->pw_uid;
+ }
+#endif
+ return NONESUCH;
+}
+
+/* PUBLIC HTAA_GidToName
+ * GET THE GROUP NAME
+ * ON ENTRY:
+ * The group-id
+ *
+ * ON EXIT:
+ * returns the group name, or an empty string if not found.
+ */
+const char *HTAA_GidToName(int gid GCC_UNUSED)
+{
+#ifndef NOUSERS
+ struct group *gr;
+ HTList *me = known_grp;
+
+ while (HTList_nextObject(me)) {
+ USER_DATA *data = (USER_DATA *) (me->object);
+
+ if (gid == data->user)
+ return data->name;
+ }
+
+ if ((gr = getgrgid((gid_t) gid)) != 0
+ && gr->gr_name != 0) {
+ CTRACE((tfp, "%s(%d) returned (%s:%d:...)\n",
+ "HTAA_GidToName: getgrgid",
+ gid,
+ gr->gr_name, (int) gr->gr_gid));
+ save_gid_info(gr->gr_name, (int) gr->gr_gid);
+ return gr->gr_name;
+ }
+#endif
+ return "";
+}
+
+/* PUBLIC HTAA_NameToGid
+ * GET THE GROUP ID
+ * ON ENTRY:
+ * The group-name
+ *
+ * ON EXIT:
+ * returns the group id, or NONESUCH if not found.
+ */
+int HTAA_NameToGid(const char *name GCC_UNUSED)
+{
+#ifndef NOUSERS
+ struct group *gr;
+ HTList *me = known_grp;
+
+ while (HTList_nextObject(me)) {
+ USER_DATA *data = (USER_DATA *) (me->object);
+
+ if (!strcmp(name, data->name))
+ return data->user;
+ }
+
+ if ((gr = getgrnam(name)) != 0) {
+ CTRACE((tfp, "%s(%s) returned (%s:%d:...)\n",
+ "HTAA_NameToGid: getgrnam",
+ name,
+ gr->gr_name, (int) gr->gr_gid));
+ save_gid_info(gr->gr_name, (int) gr->gr_gid);
+ return (int) gr->gr_gid;
+ }
+#endif
+ return NONESUCH;
+}
diff --git a/WWW/Library/Implementation/HTAAProt.h b/WWW/Library/Implementation/HTAAProt.h
new file mode 100644
index 0000000..22e3d92
--- /dev/null
+++ b/WWW/Library/Implementation/HTAAProt.h
@@ -0,0 +1,226 @@
+/* PROTECTION SETUP FILE
+
+ */
+
+#ifndef HTAAPROT_H
+#define HTAAPROT_H
+
+#include <HTGroup.h>
+#include <HTAssoc.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+
+Server's Representation of Document (Tree) Protections
+
+ */ typedef struct {
+ char *ctemplate; /* Template for this protection */
+ char *filename; /* Current document file */
+ char *uid_name; /* Effective uid (name of it) */
+ char *gid_name; /* Effective gid (name of it) */
+ GroupDef *mask_group; /* Allowed users and IP addresses */
+ HTList *valid_schemes; /* Valid authentication schemes */
+ HTAssocList *values; /* Association list for scheme specific */
+ /* parameters. */
+ } HTAAProt;
+
+/*
+
+Callbacks for rule system
+
+ The following three functioncs are called by the rule system:
+
+ HTAA_clearProtections() when starting to translate a filename
+
+ HTAA_setDefaultProtection() when "defprot" rule is matched
+
+ HTAA_setCurrentProtection() when "protect" rule is matched
+
+ Protection setup files are cached by these functions.
+
+ */
+
+/* PUBLIC HTAA_setDefaultProtection()
+ * SET THE DEFAULT PROTECTION MODE
+ * (called by rule system when a
+ * "defprot" rule is matched)
+ * ON ENTRY:
+ * cur_docname is the current result of rule translations.
+ * prot_filename is the protection setup file (second argument
+ * for "defprot" rule, optional)
+ * eff_ids contains user and group names separated by
+ * a dot, corresponding to the effective uid
+ * gid under which the server should run,
+ * default is "nobody.nogroup" (third argument
+ * for "defprot" rule, optional; can be given
+ * only if protection setup file is also given).
+ *
+ * ON EXIT:
+ * returns nothing.
+ * Sets the module-wide variable default_prot.
+ */
+ extern void HTAA_setDefaultProtection(const char *cur_docname,
+ const char *prot_filename,
+ const char *eff_ids);
+
+/* PUBLIC HTAA_setCurrentProtection()
+ * SET THE CURRENT PROTECTION MODE
+ * (called by rule system when a
+ * "protect" rule is matched)
+ * ON ENTRY:
+ * cur_docname is the current result of rule translations.
+ * prot_filename is the protection setup file (second argument
+ * for "protect" rule, optional)
+ * eff_ids contains user and group names separated by
+ * a dot, corresponding to the effective uid
+ * gid under which the server should run,
+ * default is "nobody.nogroup" (third argument
+ * for "protect" rule, optional; can be given
+ * only if protection setup file is also given).
+ *
+ * ON EXIT:
+ * returns nothing.
+ * Sets the module-wide variable current_prot.
+ */
+ extern void HTAA_setCurrentProtection(const char *cur_docname,
+ const char *prot_filename,
+ const char *eff_ids);
+
+/* SERVER INTERNAL HTAA_clearProtections()
+ * CLEAR DOCUMENT PROTECTION MODE
+ * (ALSO DEFAULT PROTECTION)
+ * (called by the rule system)
+ * ON ENTRY:
+ * No arguments.
+ *
+ * ON EXIT:
+ * returns nothing.
+ * Frees the memory used by protection information.
+ */
+ extern void HTAA_clearProtections(void);
+
+/*
+
+Getting Protection Settings
+
+ HTAA_getCurrentProtection() returns the current protection mode (if there was a
+ "protect" rule). NULL, if no "protect" rule has been matched.
+
+ HTAA_getDefaultProtection() sets the current protection mode to what it was set to
+ by "defprot" rule and also returns it (therefore after this call also
+ HTAA_getCurrentProtection() returns the same structure.
+
+ */
+
+/* PUBLIC HTAA_getCurrentProtection()
+ * GET CURRENT PROTECTION SETUP STRUCTURE
+ * (this is set up by callbacks made from
+ * the rule system when matching "protect"
+ * (and "defprot") rules)
+ * ON ENTRY:
+ * HTTranslate() must have been called before calling
+ * this function.
+ *
+ * ON EXIT:
+ * returns a HTAAProt structure representing the
+ * protection setup of the HTTranslate()'d file.
+ * This must not be free()'d.
+ */
+ extern HTAAProt *HTAA_getCurrentProtection(void);
+
+/* PUBLIC HTAA_getDefaultProtection()
+ * GET DEFAULT PROTECTION SETUP STRUCTURE
+ * (this is set up by callbacks made from
+ * the rule system when matching "defprot"
+ * rules)
+ * ON ENTRY:
+ * HTTranslate() must have been called before calling
+ * this function.
+ *
+ * ON EXIT:
+ * returns a HTAAProt structure representing the
+ * default protection setup of the HTTranslate()'d
+ * file (if HTAA_getCurrentProtection() returned
+ * NULL, i.e., if there is no "protect" rule
+ * but ACL exists, and we need to know default
+ * protection settings).
+ * This must not be free()'d.
+ */
+ extern HTAAProt *HTAA_getDefaultProtection(void);
+
+/*
+
+Get User and Group IDs to Which Set to
+
+ */
+
+#ifndef NOUSERS
+/* PUBLIC HTAA_getUid()
+ * GET THE USER ID TO CHANGE THE PROCESS UID TO
+ * ON ENTRY:
+ * No arguments.
+ *
+ * ON EXIT:
+ * returns the uid number to give to setuid() system call.
+ * Default is 65534 (nobody).
+ */
+ extern int HTAA_getUid(void);
+
+/* PUBLIC HTAA_getGid()
+ * GET THE GROUP ID TO CHANGE THE PROCESS GID TO
+ * ON ENTRY:
+ * No arguments.
+ *
+ * ON EXIT:
+ * returns the uid number to give to setgid() system call.
+ * Default is 65534 (nogroup).
+ */
+ extern int HTAA_getGid(void);
+#endif /* !NOUSERS */
+
+/* PUBLIC HTAA_UidToName
+ * GET THE USER NAME
+ * ON ENTRY:
+ * The user-id
+ *
+ * ON EXIT:
+ * returns the user name
+ */
+ extern const char *HTAA_UidToName(int uid);
+
+/* PUBLIC HTAA_NameToUid
+ * GET THE USER ID
+ * ON ENTRY:
+ * The user-name
+ *
+ * ON EXIT:
+ * returns the user id
+ */
+ extern int HTAA_NameToUid(const char *name);
+
+/* PUBLIC HTAA_GidToName
+ * GET THE GROUP NAME
+ * ON ENTRY:
+ * The group-id
+ *
+ * ON EXIT:
+ * returns the group name
+ */
+ extern const char *HTAA_GidToName(int gid);
+
+/* PUBLIC HTAA_NameToGid
+ * GET THE GROUP ID
+ * ON ENTRY:
+ * The group-name
+ *
+ * ON EXIT:
+ * returns the group id
+ */
+ extern int HTAA_NameToGid(const char *name);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* not HTAAPROT_H */
diff --git a/WWW/Library/Implementation/HTAAUtil.c b/WWW/Library/Implementation/HTAAUtil.c
new file mode 100644
index 0000000..1be26f9
--- /dev/null
+++ b/WWW/Library/Implementation/HTAAUtil.c
@@ -0,0 +1,605 @@
+/*
+ * $LynxId: HTAAUtil.c,v 1.36 2016/11/24 15:29:50 tom Exp $
+ *
+ * MODULE HTAAUtil.c
+ * COMMON PARTS OF ACCESS AUTHORIZATION MODULE
+ * FOR BOTH SERVER AND BROWSER
+ *
+ * IMPORTANT:
+ * Routines in this module use dynamic allocation, but free
+ * automatically all the memory reserved by them.
+ *
+ * Therefore the caller never has to (and never should)
+ * free() any object returned by these functions.
+ *
+ * Therefore also all the strings returned by this package
+ * are only valid until the next call to the same function
+ * is made. This approach is selected, because of the nature
+ * of access authorization: no string returned by the package
+ * needs to be valid longer than until the next call.
+ *
+ * This also makes it easy to plug the AA package in:
+ * you don't have to ponder whether to free() something
+ * here or is it done somewhere else (because it is always
+ * done somewhere else).
+ *
+ * The strings that the package needs to store are copied
+ * so the original strings given as parameters to AA
+ * functions may be freed or modified with no side effects.
+ *
+ * The AA package does not free() anything else than what
+ * it has itself allocated.
+ *
+ * AA (Access Authorization) package means modules which
+ * names start with HTAA.
+ *
+ * AUTHORS:
+ * AL Ari Luotonen luotonen@dxcern.cern.ch
+ * MD Mark Donszelmann duns@vxdeop.cern.ch
+ *
+ * HISTORY:
+ * 8 Nov 93 MD (VMS only) Added case insensitive comparison in HTAA_templateCaseMatch
+ *
+ *
+ * BUGS:
+ *
+ *
+ */
+
+#include <HTUtils.h>
+
+#include <HTAAUtil.h> /* Implemented here */
+#include <HTAssoc.h> /* Assoc list */
+#include <HTTCP.h>
+#include <HTTP.h>
+
+#include <LYStrings.h>
+#include <LYUtils.h>
+#include <LYLeaks.h>
+
+/* PUBLIC HTAAScheme_enum()
+ * TRANSLATE SCHEME NAME INTO
+ * A SCHEME ENUMERATION
+ *
+ * ON ENTRY:
+ * name is a string representing the scheme name.
+ *
+ * ON EXIT:
+ * returns the enumerated constant for that scheme.
+ */
+HTAAScheme HTAAScheme_enum(const char *name)
+{
+ char *upcased = NULL;
+
+ if (!name)
+ return HTAA_UNKNOWN;
+
+ StrAllocCopy(upcased, name);
+ LYUpperCase(upcased);
+
+ if (!StrNCmp(upcased, "NONE", 4)) {
+ FREE(upcased);
+ return HTAA_NONE;
+ } else if (!StrNCmp(upcased, "BASIC", 5)) {
+ FREE(upcased);
+ return HTAA_BASIC;
+ } else if (!StrNCmp(upcased, "PUBKEY", 6)) {
+ FREE(upcased);
+ return HTAA_PUBKEY;
+ } else if (!StrNCmp(upcased, "KERBEROSV4", 10)) {
+ FREE(upcased);
+ return HTAA_KERBEROS_V4;
+ } else if (!StrNCmp(upcased, "KERBEROSV5", 10)) {
+ FREE(upcased);
+ return HTAA_KERBEROS_V5;
+ } else {
+ FREE(upcased);
+ return HTAA_UNKNOWN;
+ }
+}
+
+/* PUBLIC HTAAScheme_name()
+ * GET THE NAME OF A GIVEN SCHEME
+ * ON ENTRY:
+ * scheme is one of the scheme enum values:
+ * HTAA_NONE, HTAA_BASIC, HTAA_PUBKEY, ...
+ *
+ * ON EXIT:
+ * returns the name of the scheme, i.e.
+ * "None", "Basic", "Pubkey", ...
+ */
+const char *HTAAScheme_name(HTAAScheme scheme)
+{
+ switch (scheme) {
+ case HTAA_NONE:
+ return "None";
+ case HTAA_BASIC:
+ return "Basic";
+ case HTAA_PUBKEY:
+ return "Pubkey";
+ case HTAA_KERBEROS_V4:
+ return "KerberosV4";
+ case HTAA_KERBEROS_V5:
+ return "KerberosV5";
+ case HTAA_UNKNOWN:
+ return "UNKNOWN";
+ default:
+ return "THIS-IS-A-BUG";
+ }
+}
+
+/* PUBLIC HTAAMethod_enum()
+ * TRANSLATE METHOD NAME INTO AN ENUMERATED VALUE
+ * ON ENTRY:
+ * name is the method name to translate.
+ *
+ * ON EXIT:
+ * returns HTAAMethod enumerated value corresponding
+ * to the given name.
+ */
+HTAAMethod HTAAMethod_enum(const char *name)
+{
+ if (!name)
+ return METHOD_UNKNOWN;
+
+ if (0 == strcasecomp(name, "GET"))
+ return METHOD_GET;
+ else if (0 == strcasecomp(name, "PUT"))
+ return METHOD_PUT;
+ else
+ return METHOD_UNKNOWN;
+}
+
+/* PUBLIC HTAAMethod_name()
+ * GET THE NAME OF A GIVEN METHOD
+ * ON ENTRY:
+ * method is one of the method enum values:
+ * METHOD_GET, METHOD_PUT, ...
+ *
+ * ON EXIT:
+ * returns the name of the scheme, i.e.
+ * "GET", "PUT", ...
+ */
+const char *HTAAMethod_name(HTAAMethod method)
+{
+ switch (method) {
+ case METHOD_GET:
+ return "GET";
+ case METHOD_PUT:
+ return "PUT";
+ case METHOD_UNKNOWN:
+ return "UNKNOWN";
+ default:
+ return "THIS-IS-A-BUG";
+ }
+}
+
+/* PUBLIC HTAAMethod_inList()
+ * IS A METHOD IN A LIST OF METHOD NAMES
+ * ON ENTRY:
+ * method is the method to look for.
+ * list is a list of method names.
+ *
+ * ON EXIT:
+ * returns YES, if method was found.
+ * NO, if not found.
+ */
+BOOL HTAAMethod_inList(HTAAMethod method, HTList *list)
+{
+ HTList *cur = list;
+ char *item;
+
+ while (NULL != (item = (char *) HTList_nextObject(cur))) {
+ CTRACE((tfp, " %s", item));
+ if (method == HTAAMethod_enum(item))
+ return YES;
+ }
+
+ return NO; /* Not found */
+}
+
+/* PUBLIC HTAA_templateMatch()
+ * STRING COMPARISON FUNCTION FOR FILE NAMES
+ * WITH ONE WILDCARD * IN THE TEMPLATE
+ * NOTE:
+ * This is essentially the same code as in HTRules.c, but it
+ * cannot be used because it is embedded in between other code.
+ * (In fact, HTRules.c should use this routine, but then this
+ * routine would have to be more sophisticated... why is life
+ * sometimes so hard...)
+ *
+ * ON ENTRY:
+ * ctemplate is a template string to match the file name
+ * against, may contain a single wildcard
+ * character * which matches zero or more
+ * arbitrary characters.
+ * filename is the filename (or pathname) to be matched
+ * against the template.
+ *
+ * ON EXIT:
+ * returns YES, if filename matches the template.
+ * NO, otherwise.
+ */
+BOOL HTAA_templateMatch(const char *ctemplate,
+ const char *filename)
+{
+ const char *p = ctemplate;
+ const char *q = filename;
+ int m;
+
+ for (; *p && *q && *p == *q; p++, q++) /* Find first mismatch */
+ ; /* do nothing else */
+
+ if (!*p && !*q)
+ return YES; /* Equally long equal strings */
+ else if ('*' == *p) { /* Wildcard */
+ p++; /* Skip wildcard character */
+ m = (int) (strlen(q) - strlen(p)); /* Amount to match to wildcard */
+ if (m < 0)
+ return NO; /* No match, filename too short */
+ else { /* Skip the matched characters and compare */
+ if (strcmp(p, q + m))
+ return NO; /* Tail mismatch */
+ else
+ return YES; /* Tail match */
+ }
+ /* if wildcard */
+ } else
+ return NO; /* Length or character mismatch */
+}
+
+/* PUBLIC HTAA_templateCaseMatch()
+ * STRING COMPARISON FUNCTION FOR FILE NAMES
+ * WITH ONE WILDCARD * IN THE TEMPLATE (Case Insensitive)
+ * NOTE:
+ * This is essentially the same code as in HTAA_templateMatch, but
+ * it compares case insensitive (for VMS). Reason for this routine
+ * is that HTAA_templateMatch gets called from several places, also
+ * there where a case sensitive match is needed, so one cannot just
+ * change the HTAA_templateMatch routine for VMS.
+ *
+ * ON ENTRY:
+ * template is a template string to match the file name
+ * against, may contain a single wildcard
+ * character * which matches zero or more
+ * arbitrary characters.
+ * filename is the filename (or pathname) to be matched
+ * against the template.
+ *
+ * ON EXIT:
+ * returns YES, if filename matches the template.
+ * NO, otherwise.
+ */
+BOOL HTAA_templateCaseMatch(const char *ctemplate,
+ const char *filename)
+{
+ const char *p = ctemplate;
+ const char *q = filename;
+ int m;
+
+ /* Find first mismatch */
+ for (; *p && *q && TOUPPER(*p) == TOUPPER(*q); p++, q++) ; /* do nothing else */
+
+ if (!*p && !*q)
+ return YES; /* Equally long equal strings */
+ else if ('*' == *p) { /* Wildcard */
+ p++; /* Skip wildcard character */
+ m = (int) (strlen(q) - strlen(p)); /* Amount to match to wildcard */
+ if (m < 0)
+ return NO; /* No match, filename too short */
+ else { /* Skip the matched characters and compare */
+ if (strcasecomp(p, q + m))
+ return NO; /* Tail mismatch */
+ else
+ return YES; /* Tail match */
+ }
+ /* if wildcard */
+ } else
+ return NO; /* Length or character mismatch */
+}
+
+/* PUBLIC HTAA_makeProtectionTemplate()
+ * CREATE A PROTECTION TEMPLATE FOR THE FILES
+ * IN THE SAME DIRECTORY AS THE GIVEN FILE
+ * (Used by server if there is no fancier way for
+ * it to tell the client, and by browser if server
+ * didn't send WWW-ProtectionTemplate: field)
+ * ON ENTRY:
+ * docname is the document pathname (from URL).
+ *
+ * ON EXIT:
+ * returns a template matching docname, and other files
+ * files in that directory.
+ *
+ * E.g. /foo/bar/x.html => /foo/bar/ *
+ * ^
+ * Space only to prevent it from
+ * being a comment marker here,
+ * there really isn't any space.
+ */
+char *HTAA_makeProtectionTemplate(const char *docname)
+{
+ char *ctemplate = NULL;
+ char *slash = NULL;
+
+ if (docname) {
+ StrAllocCopy(ctemplate, docname);
+ slash = strrchr(ctemplate, '/');
+ if (slash)
+ slash++;
+ else
+ slash = ctemplate;
+ *slash = '\0';
+ StrAllocCat(ctemplate, "*");
+ } else
+ StrAllocCopy(ctemplate, "*");
+
+ CTRACE((tfp, "make_template: made template `%s' for file `%s'\n",
+ ctemplate, docname));
+
+ return ctemplate;
+}
+
+/*
+ * Skip leading whitespace from *s forward
+ */
+#define SKIPWS(s) while (*s==' ' || *s=='\t') s++;
+
+/*
+ * Kill trailing whitespace starting from *(s-1) backwards
+ */
+#define KILLWS(s) {char *c=s-1; while (*c==' ' || *c=='\t') *(c--)='\0';}
+
+/* PUBLIC HTAA_parseArgList()
+ * PARSE AN ARGUMENT LIST GIVEN IN A HEADER FIELD
+ * ON ENTRY:
+ * str is a comma-separated list:
+ *
+ * item, item, item
+ * where
+ * item ::= value
+ * | name=value
+ * | name="value"
+ *
+ * Leading and trailing whitespace is ignored
+ * everywhere except inside quotes, so the following
+ * examples are equal:
+ *
+ * name=value,foo=bar
+ * name="value",foo="bar"
+ * name = value , foo = bar
+ * name = "value" , foo = "bar"
+ *
+ * ON EXIT:
+ * returns a list of name-value pairs (actually HTAssocList*).
+ * For items with no name, just value, the name is
+ * the number of order number of that item. E.g.
+ * "1" for the first, etc.
+ */
+HTAssocList *HTAA_parseArgList(char *str)
+{
+ HTAssocList *assoc_list = HTAssocList_new();
+ char *cur = NULL;
+ char *name = NULL;
+ int n = 0;
+
+ if (!str)
+ return assoc_list;
+
+ while (*str) {
+ SKIPWS(str); /* Skip leading whitespace */
+ cur = str;
+ n++;
+
+ while (*cur && *cur != '=' && *cur != ',')
+ cur++; /* Find end of name (or lonely value without a name) */
+ KILLWS(cur); /* Kill trailing whitespace */
+
+ if (*cur == '=') { /* Name followed by a value */
+ *(cur++) = '\0'; /* Terminate name */
+ StrAllocCopy(name, str);
+ SKIPWS(cur); /* Skip WS leading the value */
+ str = cur;
+ if (*str == '"') { /* Quoted value */
+ str++;
+ cur = str;
+ while (*cur && *cur != '"')
+ cur++;
+ if (*cur == '"')
+ *(cur++) = '\0'; /* Terminate value */
+ /* else it is lacking terminating quote */
+ SKIPWS(cur); /* Skip WS leading comma */
+ if (*cur == ',')
+ cur++; /* Skip separating colon */
+ } else { /* Unquoted value */
+ while (*cur && *cur != ',')
+ cur++;
+ KILLWS(cur); /* Kill trailing whitespace */
+ if (*cur == ',')
+ *(cur++) = '\0';
+ /* else *cur already NULL */
+ }
+ } else { /* No name, just a value */
+ if (*cur == ',')
+ *(cur++) = '\0'; /* Terminate value */
+ /* else last value on line (already terminated by NULL) */
+ HTSprintf0(&name, "%d", n); /* Item order number for name */
+ }
+ HTAssocList_add(assoc_list, name, str);
+ str = cur;
+ } /* while *str */
+
+ FREE(name);
+ return assoc_list;
+}
+
+/************** HEADER LINE READER -- DOES UNFOLDING *************************/
+
+#define BUFFER_SIZE 1024
+
+static size_t buffer_length;
+static char *buffer = 0;
+static char *start_pointer;
+static char *end_pointer;
+static int in_soc = -1;
+
+#ifdef LY_FIND_LEAKS
+static void FreeHTAAUtil(void)
+{
+ FREE(buffer);
+}
+#endif /* LY_FIND_LEAKS */
+
+/* PUBLIC HTAA_setupReader()
+ * SET UP HEADER LINE READER, i.e., give
+ * the already-read-but-not-yet-processed
+ * buffer of text to be read before more
+ * is read from the socket.
+ * ON ENTRY:
+ * start_of_headers is a pointer to a buffer containing
+ * the beginning of the header lines
+ * (rest will be read from a socket).
+ * length is the number of valid characters in
+ * 'start_of_headers' buffer.
+ * soc is the socket to use when start_of_headers
+ * buffer is used up.
+ * ON EXIT:
+ * returns nothing.
+ * Subsequent calls to HTAA_getUnfoldedLine()
+ * will use this buffer first and then
+ * proceed to read from socket.
+ */
+void HTAA_setupReader(char *start_of_headers,
+ size_t length,
+ int soc)
+{
+ if (!start_of_headers)
+ length = 0; /* initialize length (is this reached at all?) */
+ if (buffer == NULL) { /* first call? */
+ buffer_length = length;
+ if (buffer_length < BUFFER_SIZE) /* would fall below BUFFER_SIZE? */
+ buffer_length = BUFFER_SIZE;
+ buffer = (char *) malloc((size_t) (sizeof(char) * (buffer_length + 1)));
+ } else if (length > buffer_length) { /* need more space? */
+ buffer_length = length;
+ buffer = (char *) realloc((char *) buffer,
+ (size_t) (sizeof(char) * (buffer_length + 1)));
+ }
+ if (buffer == NULL)
+ outofmem(__FILE__, "HTAA_setupReader");
+
+#ifdef LY_FIND_LEAKS
+ atexit(FreeHTAAUtil);
+#endif
+ start_pointer = buffer;
+ if (start_of_headers) {
+ LYStrNCpy(buffer, start_of_headers, length);
+ end_pointer = buffer + length;
+ } else {
+ *start_pointer = '\0';
+ end_pointer = start_pointer;
+ }
+ in_soc = soc;
+}
+
+/* PUBLIC HTAA_getUnfoldedLine()
+ * READ AN UNFOLDED HEADER LINE FROM SOCKET
+ * ON ENTRY:
+ * HTAA_setupReader must absolutely be called before
+ * this function to set up internal buffer.
+ *
+ * ON EXIT:
+ * returns a newly-allocated character string representing
+ * the read line. The line is unfolded, i.e.
+ * lines that begin with whitespace are appended
+ * to current line. E.g.
+ *
+ * Field-Name: Blaa-Blaa
+ * This-Is-A-Continuation-Line
+ * Here-Is_Another
+ *
+ * is seen by the caller as:
+ *
+ * Field-Name: Blaa-Blaa This-Is-A-Continuation-Line Here-Is_Another
+ *
+ */
+char *HTAA_getUnfoldedLine(void)
+{
+ char *line = NULL;
+ char *cur;
+ int count;
+ BOOL peek_for_folding = NO;
+
+ if (in_soc < 0) {
+ CTRACE((tfp, "%s %s\n",
+ "HTAA_getUnfoldedLine: buffer not initialized",
+ "with function HTAA_setupReader()"));
+ return NULL;
+ }
+
+ for (;;) {
+
+ /* Reading from socket */
+
+ if (start_pointer >= end_pointer) { /*Read the next block and continue */
+#ifdef USE_SSL
+ if (SSL_handle)
+ count = SSL_read(SSL_handle, buffer, BUFFER_SIZE);
+ else
+ count = NETREAD(in_soc, buffer, BUFFER_SIZE);
+#else
+ count = NETREAD(in_soc, buffer, BUFFER_SIZE);
+#endif /* USE_SSL */
+ if (count <= 0) {
+ in_soc = -1;
+ return line;
+ }
+ if (count > (int) buffer_length)
+ count = (int) buffer_length;
+ start_pointer = buffer;
+ end_pointer = buffer + count;
+ *end_pointer = '\0';
+#ifdef NOT_ASCII
+ cur = start_pointer;
+ while (cur < end_pointer) {
+ *cur = TOASCII(*cur);
+ cur++;
+ }
+#endif /*NOT_ASCII */
+ }
+ cur = start_pointer;
+
+ /* Unfolding */
+
+ if (peek_for_folding) {
+ if (*cur != ' ' && *cur != '\t')
+ return line; /* Ok, no continuation line */
+ else /* So this is a continuation line, continue */
+ peek_for_folding = NO;
+ }
+
+ /* Finding end-of-line */
+
+ while (cur < end_pointer && *cur != '\n') /* Find the end-of-line */
+ cur++; /* (or end-of-buffer). */
+
+ /* Terminating line */
+
+ if (cur < end_pointer) { /* So *cur==LF, terminate line */
+ *cur = '\0'; /* Overwrite LF */
+ if (*(cur - 1) == '\r')
+ *(cur - 1) = '\0'; /* Overwrite CR */
+ peek_for_folding = YES; /* Check for a continuation line */
+ }
+
+ /* Copying the result */
+
+ if (line)
+ StrAllocCat(line, start_pointer); /* Append */
+ else
+ StrAllocCopy(line, start_pointer); /* A new line */
+
+ start_pointer = cur + 1; /* Skip the read line */
+
+ } /* forever */
+}
diff --git a/WWW/Library/Implementation/HTAAUtil.h b/WWW/Library/Implementation/HTAAUtil.h
new file mode 100644
index 0000000..33a8ee3
--- /dev/null
+++ b/WWW/Library/Implementation/HTAAUtil.h
@@ -0,0 +1,318 @@
+/*
+ * $LynxId: HTAAUtil.h,v 1.13 2010/10/27 00:09:52 tom Exp $
+ *
+ * Utilities for the Authorization parts of libwww
+ * COMMON PARTS OF AUTHORIZATION MODULE TO BOTH SERVER AND BROWSER
+ *
+ * This module is the interface to the common parts of Access Authorization (AA) package
+ * for both server and browser. Important to know about memory allocation:
+ *
+ * Routines in this module use dynamic allocation, but free automatically all the memory
+ * reserved by them.
+ *
+ * Therefore the caller never has to (and never should) free() any object returned by
+ * these functions.
+ *
+ * Therefore also all the strings returned by this package are only valid until the next
+ * call to the same function is made. This approach is selected, because of the nature of
+ * access authorization: no string returned by the package needs to be valid longer than
+ * until the next call.
+ *
+ * This also makes it easy to plug the AA package in: you don't have to ponder whether to
+ * free() something here or is it done somewhere else (because it is always done somewhere
+ * else).
+ *
+ * The strings that the package needs to store are copied so the original strings given as
+ * parameters to AA functions may be freed or modified with no side effects.
+ *
+ * Also note: The AA package does not free() anything else than what it has itself
+ * allocated.
+ *
+ */
+
+#ifndef HTAAUTIL_H
+#define HTAAUTIL_H
+
+#include <HTList.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+ * Numeric constants
+ */
+#define MAX_USERNAME_LEN 16 /* @@ Longest allowed username */
+#define MAX_PASSWORD_LEN 3*13 /* @@ Longest allowed password */
+ /* (encrypted, so really only 3*8) */
+#define MAX_METHODNAME_LEN 12 /* @@ Longest allowed method name */
+#define MAX_FIELDNAME_LEN 16 /* @@ Longest field name in */
+ /* protection setup file */
+#define MAX_PATHNAME_LEN 80 /* @@ Longest passwd/group file */
+/* pathname to allow */
+/*
+
+ Datatype definitions
+
+ HTAASCHEME
+
+ The enumeration HTAAScheme represents the possible authentication schemes used by the
+ WWW Access Authorization.
+
+ */ typedef enum {
+ HTAA_UNKNOWN,
+ HTAA_NONE,
+ HTAA_BASIC,
+ HTAA_PUBKEY,
+ HTAA_KERBEROS_V4,
+ HTAA_KERBEROS_V5,
+ HTAA_MAX_SCHEMES /* THIS MUST ALWAYS BE LAST! Number of schemes */
+ } HTAAScheme;
+
+/*
+
+ ENUMERATION TO REPRESENT HTTP METHODS
+
+ */
+
+ typedef enum {
+ METHOD_UNKNOWN,
+ METHOD_GET,
+ METHOD_PUT
+ } HTAAMethod;
+
+/*
+
+Authentication Schemes
+
+ */
+
+/* PUBLIC HTAAScheme_enum()
+ * TRANSLATE SCHEME NAME TO A SCHEME ENUMERATION
+ * ON ENTRY:
+ * name is a string representing the scheme name.
+ *
+ * ON EXIT:
+ * returns the enumerated constant for that scheme.
+ */
+ extern HTAAScheme HTAAScheme_enum(const char *name);
+
+/* PUBLIC HTAAScheme_name()
+ * GET THE NAME OF A GIVEN SCHEME
+ * ON ENTRY:
+ * scheme is one of the scheme enum values:
+ * HTAA_NONE, HTAA_BASIC, HTAA_PUBKEY, ...
+ *
+ * ON EXIT:
+ * returns the name of the scheme, i.e.
+ * "none", "basic", "pubkey", ...
+ */
+ extern const char *HTAAScheme_name(HTAAScheme scheme);
+
+/*
+
+Methods
+
+ */
+
+/* PUBLIC HTAAMethod_enum()
+ * TRANSLATE METHOD NAME INTO AN ENUMERATED VALUE
+ * ON ENTRY:
+ * name is the method name to translate.
+ *
+ * ON EXIT:
+ * returns HTAAMethod enumerated value corresponding
+ * to the given name.
+ */
+ extern HTAAMethod HTAAMethod_enum(const char *name);
+
+/* PUBLIC HTAAMethod_name()
+ * GET THE NAME OF A GIVEN METHOD
+ * ON ENTRY:
+ * method is one of the method enum values:
+ * METHOD_GET, METHOD_PUT, ...
+ *
+ * ON EXIT:
+ * returns the name of the scheme, i.e.
+ * "GET", "PUT", ...
+ */
+ extern const char *HTAAMethod_name(HTAAMethod method);
+
+/* PUBLIC HTAAMethod_inList()
+ * IS A METHOD IN A LIST OF METHOD NAMES
+ * ON ENTRY:
+ * method is the method to look for.
+ * list is a list of method names.
+ *
+ * ON EXIT:
+ * returns YES, if method was found.
+ * NO, if not found.
+ */
+ extern BOOL HTAAMethod_inList(HTAAMethod method, HTList *list);
+
+/*
+
+Match Template Against Filename
+
+ */
+
+/* PUBLIC HTAA_templateMatch()
+ * STRING COMPARISON FUNCTION FOR FILE NAMES
+ * WITH ONE WILDCARD * IN THE TEMPLATE
+ * NOTE:
+ * This is essentially the same code as in HTRules.c, but it
+ * cannot be used because it is embedded in between other code.
+ * (In fact, HTRules.c should use this routine, but then this
+ * routine would have to be more sophisticated... why is life
+ * sometimes so hard...)
+ *
+ * ON ENTRY:
+ * ctemplate is a template string to match the file name
+ * against, may contain a single wildcard
+ * character * which matches zero or more
+ * arbitrary characters.
+ * filename is the filename (or pathname) to be matched
+ * against the template.
+ *
+ * ON EXIT:
+ * returns YES, if filename matches the template.
+ * NO, otherwise.
+ */
+ extern BOOL HTAA_templateMatch(const char *ctemplate,
+ const char *filename);
+
+/* PUBLIC HTAA_templateCaseMatch()
+ * STRING COMPARISON FUNCTION FOR FILE NAMES
+ * WITH ONE WILDCARD * IN THE TEMPLATE (Case Insensitive)
+ * NOTE:
+ * This is essentially the same code as in HTAA_templateMatch, but
+ * it compares case insensitive (for VMS). Reason for this routine
+ * is that HTAA_templateMatch gets called from several places, also
+ * there where a case sensitive match is needed, so one cannot just
+ * change the HTAA_templateMatch routine for VMS.
+ *
+ * ON ENTRY:
+ * ctemplate is a template string to match the file name
+ * against, may contain a single wildcard
+ * character * which matches zero or more
+ * arbitrary characters.
+ * filename is the filename (or pathname) to be matched
+ * against the template.
+ *
+ * ON EXIT:
+ * returns YES, if filename matches the template.
+ * NO, otherwise.
+ */
+ extern BOOL HTAA_templateCaseMatch(const char *ctemplate,
+ const char *filename);
+
+/* PUBLIC HTAA_makeProtectionTemplate()
+ * CREATE A PROTECTION TEMPLATE FOR THE FILES
+ * IN THE SAME DIRECTORY AS THE GIVEN FILE
+ * (Used by server if there is no fancier way for
+ * it to tell the client, and by browser if server
+ * didn't send WWW-ProtectionTemplate: field)
+ * ON ENTRY:
+ * docname is the document pathname (from URL).
+ *
+ * ON EXIT:
+ * returns a template matching docname, and other files
+ * files in that directory.
+ *
+ * E.g. /foo/bar/x.html => /foo/bar/ *
+ * ^
+ * Space only to prevent it from
+ * being a comment marker here,
+ * there really isn't any space.
+ */
+ extern char *HTAA_makeProtectionTemplate(const char *docname);
+
+/*
+
+MIME Argument List Parser
+
+ */
+
+/* PUBLIC HTAA_parseArgList()
+ * PARSE AN ARGUMENT LIST GIVEN IN A HEADER FIELD
+ * ON ENTRY:
+ * str is a comma-separated list:
+ *
+ * item, item, item
+ * where
+ * item ::= value
+ * | name=value
+ * | name="value"
+ *
+ * Leading and trailing whitespace is ignored
+ * everywhere except inside quotes, so the following
+ * examples are equal:
+ *
+ * name=value,foo=bar
+ * name="value",foo="bar"
+ * name = value , foo = bar
+ * name = "value" , foo = "bar"
+ *
+ * ON EXIT:
+ * returns a list of name-value pairs (actually HTAssocList*).
+ * For items with no name, just value, the name is
+ * the number of order number of that item. E.g.
+ * "1" for the first, etc.
+ */
+ extern HTList *HTAA_parseArgList(char *str);
+
+/*
+
+Header Line Reader
+
+ */
+
+/* PUBLIC HTAA_setupReader()
+ * SET UP HEADER LINE READER, i.e., give
+ * the already-read-but-not-yet-processed
+ * buffer of text to be read before more
+ * is read from the socket.
+ * ON ENTRY:
+ * start_of_headers is a pointer to a buffer containing
+ * the beginning of the header lines
+ * (rest will be read from a socket).
+ * length is the number of valid characters in
+ * 'start_of_headers' buffer.
+ * soc is the socket to use when start_of_headers
+ * buffer is used up.
+ * ON EXIT:
+ * returns nothing.
+ * Subsequent calls to HTAA_getUnfoldedLine()
+ * will use this buffer first and then
+ * proceed to read from socket.
+ */
+ extern void HTAA_setupReader(char *start_of_headers,
+ size_t length,
+ int soc);
+
+/* PUBLIC HTAA_getUnfoldedLine()
+ * READ AN UNFOLDED HEADER LINE FROM SOCKET
+ * ON ENTRY:
+ * HTAA_setupReader must absolutely be called before
+ * this function to set up internal buffer.
+ *
+ * ON EXIT:
+ * returns a newly-allocated character string representing
+ * the read line. The line is unfolded, i.e.
+ * lines that begin with whitespace are appended
+ * to current line. E.g.
+ *
+ * Field-Name: Blaa-Blaa
+ * This-Is-A-Continuation-Line
+ * Here-Is_Another
+ *
+ * is seen by the caller as:
+ *
+ * Field-Name: Blaa-Blaa This-Is-A-Continuation-Line Here-Is_Another
+ *
+ */
+ extern char *HTAA_getUnfoldedLine(void);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* NOT HTAAUTIL_H */
diff --git a/WWW/Library/Implementation/HTAccess.c b/WWW/Library/Implementation/HTAccess.c
new file mode 100644
index 0000000..a79a8a2
--- /dev/null
+++ b/WWW/Library/Implementation/HTAccess.c
@@ -0,0 +1,1460 @@
+/*
+ * $LynxId: HTAccess.c,v 1.85 2019/08/24 00:27:06 tom Exp $
+ *
+ * Access Manager HTAccess.c
+ * ==============
+ *
+ * Authors
+ * TBL Tim Berners-Lee timbl@info.cern.ch
+ * JFG Jean-Francois Groff jfg@dxcern.cern.ch
+ * DD Denis DeLaRoca (310) 825-4580 <CSP1DWD@mvs.oac.ucla.edu>
+ * FM Foteos Macrides macrides@sci.wfeb.edu
+ * PDM Danny Mayer mayer@ljo.dec.com
+ *
+ * History
+ * 8 Jun 92 Telnet hopping prohibited as telnet is not secure TBL
+ * 26 Jun 92 When over DECnet, suppressed FTP, Gopher and News. JFG
+ * 6 Oct 92 Moved HTClientHost and logfile into here. TBL
+ * 17 Dec 92 Tn3270 added, bug fix. DD
+ * 4 Feb 93 Access registration, Search escapes bad chars TBL
+ * PARAMETERS TO HTSEARCH AND HTLOADRELATIVE CHANGED
+ * 28 May 93 WAIS gateway explicit if no WAIS library linked in.
+ * 31 May 94 Added DIRECT_WAIS support for VMS. FM
+ * 27 Jan 95 Fixed proxy support to use NNTPSERVER for checking
+ * whether or not to use the proxy server. PDM
+ * 27 Jan 95 Ensured that proxy service will be overridden for files
+ * on the local host (because HTLoadFile() doesn't try ftp
+ * for those) and will substitute ftp for remote files. FM
+ * 28 Jan 95 Tweaked PDM's proxy override mods to handle port info
+ * for news and wais URL's. FM
+ *
+ * Bugs
+ * This module assumes that that the graphic object is hypertext, as it
+ * needs to select it when it has been loaded. A superclass needs to be
+ * defined which accepts select and select_anchor.
+ */
+
+#ifdef VMS
+#define DIRECT_WAIS
+#endif /* VMS */
+
+#include <HTUtils.h>
+#include <HTTP.h>
+#include <HTAlert.h>
+/*
+ * Implements:
+ */
+#include <HTAccess.h>
+
+/*
+ * Uses:
+ */
+#include <HTParse.h>
+#include <HTML.h> /* SCW */
+
+#ifndef NO_RULES
+#include <HTRules.h>
+#endif
+
+#include <HTList.h>
+#include <HText.h> /* See bugs above */
+#include <HTCJK.h>
+#include <UCMap.h>
+#include <GridText.h>
+
+#include <LYGlobalDefs.h>
+#include <LYexit.h>
+#include <LYStrings.h>
+#include <LYUtils.h>
+#include <LYLeaks.h>
+
+/*
+ * These flags may be set to modify the operation of this module
+ */
+char *HTClientHost = NULL; /* Name of remote login host if any */
+FILE *HTlogfile = NULL; /* File to which to output one-liners */
+BOOL HTSecure = NO; /* Disable access for telnet users? */
+BOOL HTPermitRedir = NO; /* Always allow redirection in getfile()? */
+
+BOOL using_proxy = NO; /* are we using a proxy gateway? */
+
+/*
+ * To generate other things, play with these:
+ */
+HTFormat HTOutputFormat = NULL;
+HTStream *HTOutputStream = NULL; /* For non-interactive, set this */
+
+static HTList *protocols = NULL; /* List of registered protocol descriptors */
+
+char *use_this_url_instead = NULL;
+
+static int pushed_assume_LYhndl = -1; /* see LYUC* functions below - kw */
+static char *pushed_assume_MIMEname = NULL;
+
+#ifdef LY_FIND_LEAKS
+static void free_protocols(void)
+{
+ HTList_delete(protocols);
+ protocols = NULL;
+ FREE(pushed_assume_MIMEname); /* shouldn't happen, just in case - kw */
+}
+#endif /* LY_FIND_LEAKS */
+
+/* Register a Protocol. HTRegisterProtocol()
+ * --------------------
+ */
+BOOL HTRegisterProtocol(HTProtocol * protocol)
+{
+ if (!protocols) {
+ protocols = HTList_new();
+#ifdef LY_FIND_LEAKS
+ atexit(free_protocols);
+#endif
+ }
+ HTList_addObject(protocols, protocol);
+ return YES;
+}
+
+/* Register all known protocols. HTAccessInit()
+ * -----------------------------
+ *
+ * Add to or subtract from this list if you add or remove protocol
+ * modules. This routine is called the first time the protocol list
+ * is needed, unless any protocols are already registered, in which
+ * case it is not called. Therefore the application can override
+ * this list.
+ *
+ * Compiling with NO_INIT prevents all known protocols from being
+ * forced in at link time.
+ */
+#ifndef NO_INIT
+#ifdef GLOBALREF_IS_MACRO
+extern GLOBALREF (HTProtocol, HTTP);
+extern GLOBALREF (HTProtocol, HTTPS);
+extern GLOBALREF (HTProtocol, HTFile);
+extern GLOBALREF (HTProtocol, HTTelnet);
+extern GLOBALREF (HTProtocol, HTTn3270);
+extern GLOBALREF (HTProtocol, HTRlogin);
+
+#ifndef DECNET
+#ifndef DISABLE_FTP
+extern GLOBALREF (HTProtocol, HTFTP);
+#endif /* DISABLE_FTP */
+#ifndef DISABLE_NEWS
+extern GLOBALREF (HTProtocol, HTNews);
+extern GLOBALREF (HTProtocol, HTNNTP);
+extern GLOBALREF (HTProtocol, HTNewsPost);
+extern GLOBALREF (HTProtocol, HTNewsReply);
+extern GLOBALREF (HTProtocol, HTSNews);
+extern GLOBALREF (HTProtocol, HTSNewsPost);
+extern GLOBALREF (HTProtocol, HTSNewsReply);
+#endif /* not DISABLE_NEWS */
+#ifndef DISABLE_GOPHER
+extern GLOBALREF (HTProtocol, HTGopher);
+extern GLOBALREF (HTProtocol, HTCSO);
+#endif /* not DISABLE_GOPHER */
+#ifndef DISABLE_FINGER
+extern GLOBALREF (HTProtocol, HTFinger);
+#endif /* not DISABLE_FINGER */
+#ifdef DIRECT_WAIS
+extern GLOBALREF (HTProtocol, HTWAIS);
+#endif /* DIRECT_WAIS */
+#endif /* !DECNET */
+#else
+GLOBALREF HTProtocol HTTP, HTTPS, HTFile, HTTelnet, HTTn3270, HTRlogin;
+
+#ifndef DECNET
+#ifndef DISABLE_FTP
+GLOBALREF HTProtocol HTFTP;
+#endif /* DISABLE_FTP */
+#ifndef DISABLE_NEWS
+GLOBALREF HTProtocol HTNews, HTNNTP, HTNewsPost, HTNewsReply;
+GLOBALREF HTProtocol HTSNews, HTSNewsPost, HTSNewsReply;
+#endif /* not DISABLE_NEWS */
+#ifndef DISABLE_GOPHER
+GLOBALREF HTProtocol HTGopher, HTCSO;
+#endif /* not DISABLE_GOPHER */
+#ifndef DISABLE_FINGER
+GLOBALREF HTProtocol HTFinger;
+#endif /* not DISABLE_FINGER */
+#ifdef DIRECT_WAIS
+GLOBALREF HTProtocol HTWAIS;
+#endif /* DIRECT_WAIS */
+#endif /* !DECNET */
+#endif /* GLOBALREF_IS_MACRO */
+
+static void HTAccessInit(void) /* Call me once */
+{
+ HTRegisterProtocol(&HTTP);
+ HTRegisterProtocol(&HTTPS);
+ HTRegisterProtocol(&HTFile);
+ HTRegisterProtocol(&HTTelnet);
+ HTRegisterProtocol(&HTTn3270);
+ HTRegisterProtocol(&HTRlogin);
+#ifndef DECNET
+#ifndef DISABLE_FTP
+ HTRegisterProtocol(&HTFTP);
+#endif /* DISABLE_FTP */
+#ifndef DISABLE_NEWS
+ HTRegisterProtocol(&HTNews);
+ HTRegisterProtocol(&HTNNTP);
+ HTRegisterProtocol(&HTNewsPost);
+ HTRegisterProtocol(&HTNewsReply);
+ HTRegisterProtocol(&HTSNews);
+ HTRegisterProtocol(&HTSNewsPost);
+ HTRegisterProtocol(&HTSNewsReply);
+#endif /* not DISABLE_NEWS */
+#ifndef DISABLE_GOPHER
+ HTRegisterProtocol(&HTGopher);
+ HTRegisterProtocol(&HTCSO);
+#endif /* not DISABLE_GOPHER */
+#ifndef DISABLE_FINGER
+ HTRegisterProtocol(&HTFinger);
+#endif /* not DISABLE_FINGER */
+#ifdef DIRECT_WAIS
+ HTRegisterProtocol(&HTWAIS);
+#endif /* DIRECT_WAIS */
+#endif /* !DECNET */
+ LYRegisterLynxProtocols();
+}
+#endif /* !NO_INIT */
+
+/* Check for proxy override. override_proxy()
+ * -------------------------
+ *
+ * Check the no_proxy environment variable to get the list
+ * of hosts for which proxy server is not consulted.
+ *
+ * no_proxy is a comma- or space-separated list of machine
+ * or domain names, with optional :port part. If no :port
+ * part is present, it applies to all ports on that domain.
+ *
+ * Example:
+ * no_proxy="cern.ch,some.domain:8001"
+ *
+ * Use "*" to override all proxy service:
+ * no_proxy="*"
+ */
+BOOL override_proxy(const char *addr)
+{
+ const char *no_proxy = getenv("no_proxy");
+ char *p = NULL;
+ char *at = NULL;
+ char *host = NULL;
+ char *Host = NULL;
+ char *acc_method = NULL;
+ int port = 0;
+ int h_len = 0;
+
+ /*
+ * Check for global override.
+ */
+ if (no_proxy) {
+ if (!strcmp(no_proxy, "*"))
+ return YES;
+ }
+
+ /*
+ * Never proxy file:// URLs if they are on the local host. HTLoadFile()
+ * will not attempt ftp for those if direct access fails. We'll check that
+ * first, in case no_proxy hasn't been defined. - FM
+ */
+ if (!addr)
+ return NO;
+ if (!(host = HTParse(addr, "", PARSE_HOST)))
+ return NO;
+ if (!*host) {
+ FREE(host);
+ return NO;
+ }
+ Host = (((at = StrChr(host, '@')) != NULL) ? (at + 1) : host);
+
+ if ((acc_method = HTParse(addr, "", PARSE_ACCESS))) {
+ if (!strcmp("file", acc_method) &&
+ (LYSameHostname(Host, "localhost") ||
+ LYSameHostname(Host, HTHostName()))) {
+ FREE(host);
+ FREE(acc_method);
+ return YES;
+ }
+ FREE(acc_method);
+ }
+
+ if (!no_proxy) {
+ FREE(host);
+ return NO;
+ }
+
+ if (NULL != (p = HTParsePort(Host, &port))) { /* Port specified */
+ *p = 0; /* Chop off port */
+ } else { /* Use default port */
+ acc_method = HTParse(addr, "", PARSE_ACCESS);
+ if (acc_method != NULL) {
+ /* *INDENT-OFF* */
+ if (!strcmp(acc_method, "http")) port = 80;
+ else if (!strcmp(acc_method, "https")) port = 443;
+ else if (!strcmp(acc_method, "ftp")) port = 21;
+#ifndef DISABLE_GOPHER
+ else if (!strcmp(acc_method, "gopher")) port = 70;
+#endif
+ else if (!strcmp(acc_method, "cso")) port = 105;
+#ifndef DISABLE_NEWS
+ else if (!strcmp(acc_method, "news")) port = 119;
+ else if (!strcmp(acc_method, "nntp")) port = 119;
+ else if (!strcmp(acc_method, "newspost")) port = 119;
+ else if (!strcmp(acc_method, "newsreply")) port = 119;
+ else if (!strcmp(acc_method, "snews")) port = 563;
+ else if (!strcmp(acc_method, "snewspost")) port = 563;
+ else if (!strcmp(acc_method, "snewsreply")) port = 563;
+#endif
+ else if (!strcmp(acc_method, "wais")) port = 210;
+#ifndef DISABLE_FINGER
+ else if (!strcmp(acc_method, "finger")) port = 79;
+#endif
+ else if (!strcmp(acc_method, "telnet")) port = 23;
+ else if (!strcmp(acc_method, "tn3270")) port = 23;
+ else if (!strcmp(acc_method, "rlogin")) port = 513;
+ /* *INDENT-ON* */
+
+ FREE(acc_method);
+ }
+ }
+ if (!port)
+ port = 80; /* Default */
+ h_len = (int) strlen(Host);
+
+ while (*no_proxy) {
+ const char *end;
+ const char *colon = NULL;
+ int templ_port = 0;
+ int t_len;
+ int brackets = 0;
+
+ while (*no_proxy && (WHITE(*no_proxy) || *no_proxy == ','))
+ no_proxy++; /* Skip whitespace and separators */
+
+ end = no_proxy;
+ while (*end && !WHITE(*end) && *end != ',') { /* Find separator */
+ if (!brackets && (*end == ':'))
+ colon = end; /* Port number given */
+ else if (*end == '[')
+ ++brackets;
+ else if (*end == ']')
+ --brackets;
+ end++;
+ }
+
+ if (colon) {
+ /* unlike HTParsePort(), this may be followed by non-digits */
+ templ_port = atoi(colon + 1);
+ t_len = (int) (colon - no_proxy);
+ } else {
+ t_len = (int) (end - no_proxy);
+ }
+
+ if ((!templ_port || templ_port == port) &&
+ (t_len > 0 && t_len <= h_len &&
+ !strncasecomp(Host + h_len - t_len, no_proxy, t_len))) {
+ FREE(host);
+ return YES;
+ }
+#ifdef CJK_EX /* ASATAKU PROXY HACK */
+ if ((!templ_port || templ_port == port) &&
+ (t_len > 0 && t_len <= h_len &&
+ isdigit(UCH(*no_proxy)) &&
+ !StrNCmp(host, no_proxy, t_len))) {
+ FREE(host);
+ return YES;
+ }
+#endif /* ASATAKU PROXY HACK */
+
+ if (*end)
+ no_proxy = (end + 1);
+ else
+ break;
+ }
+
+ FREE(host);
+ return NO;
+}
+
+/* Find physical name and access protocol get_physical()
+ * --------------------------------------
+ *
+ * On entry,
+ * addr must point to the fully qualified hypertext reference.
+ * anchor a parent anchor with whose address is addr
+ *
+ * On exit,
+ * returns HT_NO_ACCESS Error has occurred.
+ * HT_OK Success
+ */
+static int get_physical(const char *addr,
+ HTParentAnchor *anchor)
+{
+ int result;
+ char *acc_method = NULL; /* Name of access method */
+ char *physical = NULL;
+ char *Server_addr = NULL;
+ BOOL override_flag = NO;
+
+ CTRACE((tfp, "get_physical %s\n", addr));
+
+ /*
+ * Make sure the using_proxy variable is FALSE.
+ */
+ using_proxy = NO;
+
+#ifndef NO_RULES
+ if ((physical = HTTranslate(addr)) == 0) {
+ if (redirecting_url) {
+ return HT_REDIRECTING;
+ }
+ return HT_FORBIDDEN;
+ }
+ if (anchor->isISMAPScript == TRUE) {
+ StrAllocCat(physical, "?0,0");
+ CTRACE((tfp, "HTAccess: Appending '?0,0' coordinate pair.\n"));
+ }
+ if (!StrNCmp(physical, "Proxied=", 8)) {
+ HTAnchor_setPhysical(anchor, physical + 8);
+ using_proxy = YES;
+ } else if (!StrNCmp(physical, "NoProxy=", 8)) {
+ HTAnchor_setPhysical(anchor, physical + 8);
+ override_flag = YES;
+ } else {
+ HTAnchor_setPhysical(anchor, physical);
+ }
+ FREE(physical); /* free our copy */
+#else
+ if (anchor->isISMAPScript == TRUE) {
+ StrAllocCopy(physical, addr);
+ StrAllocCat(physical, "?0,0");
+ CTRACE((tfp, "HTAccess: Appending '?0,0' coordinate pair.\n"));
+ HTAnchor_setPhysical(anchor, physical);
+ FREE(physical); /* free our copy */
+ } else {
+ HTAnchor_setPhysical(anchor, addr);
+ }
+#endif /* NO_RULES */
+
+ acc_method = HTParse(HTAnchor_physical(anchor), STR_FILE_URL, PARSE_ACCESS);
+
+ /*
+ * Check whether gateway access has been set up for this.
+ *
+ * This function can be replaced by the rule system above.
+ *
+ * If the rule system has already determined that we should use a proxy, or
+ * that we shouldn't, ignore proxy-related settings, don't use no_proxy
+ * either.
+ */
+#define USE_GATEWAYS
+#ifdef USE_GATEWAYS
+
+ if (!override_flag && !using_proxy) { /* else ignore no_proxy env var */
+ char *host = NULL;
+ int port;
+
+ if (!strcasecomp(acc_method, "news")) {
+ /*
+ * News is different, so we need to check the name of the server,
+ * as well as the default port for selective exclusions.
+ */
+ if ((host = HTParse(addr, "", PARSE_HOST))) {
+ if (HTParsePort(host, &port) == NULL) {
+ StrAllocCopy(Server_addr, "news://");
+ StrAllocCat(Server_addr, host);
+ StrAllocCat(Server_addr, ":119/");
+ }
+ FREE(host);
+ } else if (LYGetEnv("NNTPSERVER") != NULL) {
+ StrAllocCopy(Server_addr, "news://");
+ StrAllocCat(Server_addr, LYGetEnv("NNTPSERVER"));
+ StrAllocCat(Server_addr, ":119/");
+ }
+ } else if (!strcasecomp(acc_method, "wais")) {
+ /*
+ * Wais also needs checking of the default port for selective
+ * exclusions.
+ */
+ if ((host = HTParse(addr, "", PARSE_HOST))) {
+ if (!(HTParsePort(host, &port))) {
+ StrAllocCopy(Server_addr, "wais://");
+ StrAllocCat(Server_addr, host);
+ StrAllocCat(Server_addr, ":210/");
+ }
+ FREE(host);
+ } else
+ StrAllocCopy(Server_addr, addr);
+ } else {
+ StrAllocCopy(Server_addr, addr);
+ }
+ override_flag = override_proxy(Server_addr);
+ }
+
+ if (!override_flag && !using_proxy) {
+ char *gateway_parameter = NULL, *gateway, *proxy;
+
+ /*
+ * Search for gateways.
+ */
+ HTSprintf0(&gateway_parameter, "WWW_%s_GATEWAY", acc_method);
+ gateway = LYGetEnv(gateway_parameter); /* coerce for decstation */
+
+ /*
+ * Search for proxy servers.
+ */
+ if (!strcmp(acc_method, "file"))
+ /*
+ * If we got to here, a file URL is for ftp on a remote host. - FM
+ */
+ strcpy(gateway_parameter, "ftp_proxy");
+ else
+ sprintf(gateway_parameter, "%s_proxy", acc_method);
+ proxy = LYGetEnv(gateway_parameter);
+ FREE(gateway_parameter);
+
+ if (gateway)
+ CTRACE((tfp, "Gateway found: %s\n", gateway));
+ if (proxy)
+ CTRACE((tfp, "proxy server found: %s\n", proxy));
+
+ /*
+ * Proxy servers have precedence over gateway servers.
+ */
+ if (proxy) {
+ char *gatewayed = NULL;
+
+ StrAllocCopy(gatewayed, proxy);
+ if (!StrNCmp(gatewayed, "http", 4)) {
+ char *cp = strrchr(gatewayed, '/');
+
+ /* Append a slash to the proxy specification if it doesn't
+ * end in one but otherwise looks normal (starts with "http",
+ * has no '/' other than ones before the hostname). - kw */
+ if (cp && (cp - gatewayed) <= 7)
+ LYAddHtmlSep(&gatewayed);
+ }
+ /*
+ * Ensure that the proxy server uses ftp for file URLs. - FM
+ */
+ if (!StrNCmp(addr, "file", 4)) {
+ StrAllocCat(gatewayed, "ftp");
+ StrAllocCat(gatewayed, (addr + 4));
+ } else
+ StrAllocCat(gatewayed, addr);
+ using_proxy = YES;
+ if (anchor->isISMAPScript == TRUE)
+ StrAllocCat(gatewayed, "?0,0");
+ HTAnchor_setPhysical(anchor, gatewayed);
+ FREE(gatewayed);
+ FREE(acc_method);
+
+ acc_method = HTParse(HTAnchor_physical(anchor),
+ STR_HTTP_URL, PARSE_ACCESS);
+
+ } else if (gateway) {
+ char *path = HTParse(addr, "",
+ PARSE_HOST + PARSE_PATH + PARSE_PUNCTUATION);
+
+ /* Chop leading / off to make host into part of path */
+ char *gatewayed = HTParse(path + 1, gateway, PARSE_ALL);
+
+ FREE(path);
+ HTAnchor_setPhysical(anchor, gatewayed);
+ FREE(gatewayed);
+ FREE(acc_method);
+
+ acc_method = HTParse(HTAnchor_physical(anchor),
+ STR_HTTP_URL, PARSE_ACCESS);
+ }
+ }
+ FREE(Server_addr);
+#endif /* use gateways */
+
+ /*
+ * Search registered protocols to find suitable one.
+ */
+ result = HT_NO_ACCESS;
+ {
+ int i, n;
+
+#ifndef NO_INIT
+ if (!protocols)
+ HTAccessInit();
+#endif
+ n = HTList_count(protocols);
+ for (i = 0; i < n; i++) {
+ HTProtocol *p = (HTProtocol *) HTList_objectAt(protocols, i);
+
+ if (!strcmp(p->name, acc_method)) {
+ HTAnchor_setProtocol(anchor, p);
+ FREE(acc_method);
+ result = HT_OK;
+ break;
+ }
+ }
+ }
+
+ FREE(acc_method);
+ return result;
+}
+
+/*
+ * Temporarily set the int UCLYhndl_for_unspec and string UCLYhndl_for_unspec
+ * used for charset "assuming" to the values implied by a HTParentAnchor's
+ * UCStages, after saving the current values for later restoration. - kw @@@
+ * These functions may not really belong here, but where else? I want the
+ * "pop" to occur as soon as possible after loading has finished. - kw @@@
+ */
+void LYUCPushAssumed(HTParentAnchor *anchor)
+{
+ int anchor_LYhndl = -1;
+ LYUCcharset *anchor_UCI = NULL;
+
+ if (anchor) {
+ anchor_LYhndl = HTAnchor_getUCLYhndl(anchor, UCT_STAGE_PARSER);
+ if (anchor_LYhndl >= 0)
+ anchor_UCI = HTAnchor_getUCInfoStage(anchor,
+ UCT_STAGE_PARSER);
+ if (anchor_UCI && anchor_UCI->MIMEname) {
+ pushed_assume_MIMEname = UCAssume_MIMEcharset;
+ UCAssume_MIMEcharset = NULL;
+ if (HTCJK == JAPANESE)
+ StrAllocCopy(UCAssume_MIMEcharset, pushed_assume_MIMEname);
+ else
+ StrAllocCopy(UCAssume_MIMEcharset, anchor_UCI->MIMEname);
+ pushed_assume_LYhndl = anchor_LYhndl;
+ /* some diagnostics */
+ if (UCLYhndl_for_unspec != anchor_LYhndl)
+ CTRACE((tfp,
+ "LYUCPushAssumed: UCLYhndl_for_unspec changed %d -> %d\n",
+ UCLYhndl_for_unspec,
+ anchor_LYhndl));
+ UCLYhndl_for_unspec = anchor_LYhndl;
+ return;
+ }
+ }
+ pushed_assume_LYhndl = -1;
+ FREE(pushed_assume_MIMEname);
+}
+
+/*
+ * Restore the int UCLYhndl_for_unspec and string UCLYhndl_for_unspec used for
+ * charset "assuming" from the values saved by LYUCPushAssumed, if any. - kw
+ */
+int LYUCPopAssumed(void)
+{
+ if (pushed_assume_LYhndl >= 0) {
+ /* some diagnostics */
+ if (UCLYhndl_for_unspec != pushed_assume_LYhndl)
+ CTRACE((tfp,
+ "LYUCPopAssumed: UCLYhndl_for_unspec changed %d -> %d\n",
+ UCLYhndl_for_unspec,
+ pushed_assume_LYhndl));
+ UCLYhndl_for_unspec = pushed_assume_LYhndl;
+ pushed_assume_LYhndl = -1;
+ FREE(UCAssume_MIMEcharset);
+ UCAssume_MIMEcharset = pushed_assume_MIMEname;
+ pushed_assume_MIMEname = NULL;
+ return UCLYhndl_for_unspec;
+ }
+ return -1;
+}
+
+/* Load a document HTLoad()
+ * ---------------
+ *
+ * This is an internal routine, which has an address AND a matching
+ * anchor. (The public routines are called with one OR the other.)
+ *
+ * On entry,
+ * addr must point to the fully qualified hypertext reference.
+ * anchor a parent anchor with whose address is addr
+ *
+ * On exit,
+ * returns <0 Error has occurred.
+ * HT_LOADED Success
+ * HT_NO_DATA Success, but no document loaded.
+ * (telnet session started etc)
+ */
+static int HTLoad(const char *addr,
+ HTParentAnchor *anchor,
+ HTFormat format_out,
+ HTStream *sink)
+{
+ HTProtocol *p;
+ int status = get_physical(addr, anchor);
+
+ if (reloading) {
+ FREE(anchor->charset);
+ FREE(anchor->UCStages);
+ }
+
+ if (status == HT_FORBIDDEN) {
+ /* prevent crash if telnet or similar was forbidden by rule. - kw */
+ LYFixCursesOn("show alert:");
+ status = HTLoadError(sink, 500, gettext("Access forbidden by rule"));
+ } else if (status == HT_REDIRECTING) {
+ ; /* fake redirection by rule, to redirecting_url */
+ } else if (status >= 0) {
+ /* prevent crash if telnet or similar mapped or proxied by rule. - kw */
+ LYFixCursesOnForAccess(addr, HTAnchor_physical(anchor));
+ p = (HTProtocol *) HTAnchor_protocol(anchor);
+ anchor->parent->underway = TRUE; /* Hack to deal with caching */
+ status = p->load(HTAnchor_physical(anchor),
+ anchor, format_out, sink);
+ anchor->parent->underway = FALSE;
+ LYUCPopAssumed();
+ }
+ return status;
+}
+
+/* Get a save stream for a document HTSaveStream()
+ * --------------------------------
+ */
+HTStream *HTSaveStream(HTParentAnchor *anchor)
+{
+ HTProtocol *p = (HTProtocol *) HTAnchor_protocol(anchor);
+
+ if (!p)
+ return NULL;
+
+ return p->saveStream(anchor);
+}
+
+int redirection_limit = 10;
+int redirection_attempts = 0; /* counter in HTLoadDocument */
+
+static BOOL too_many_redirections(void)
+{
+ if (redirection_attempts > redirection_limit) {
+ char *msg = NULL;
+
+ HTSprintf0(&msg, TOO_MANY_REDIRECTIONS, redirection_limit);
+ free(msg);
+ redirection_attempts = 0;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/* Load a document - with logging etc HTLoadDocument()
+ * ----------------------------------
+ *
+ * - Checks or documents already loaded
+ * - Logs the access
+ * - Allows stdin filter option
+ * - Trace output and error messages
+ *
+ * On Entry,
+ * anchor is the node_anchor for the document
+ * full_address The address of the document to be accessed.
+ * filter if YES, treat stdin as HTML
+ *
+ * On Exit,
+ * returns YES Success in opening document
+ * NO Failure
+ */
+static BOOL HTLoadDocument(const char *full_address, /* may include #fragment */
+ HTParentAnchor *anchor,
+ HTFormat format_out,
+ HTStream *sink)
+{
+ int status;
+ HText *text;
+ const char *address_to_load = full_address;
+ char *cp;
+ BOOL ForcingNoCache = LYforce_no_cache;
+
+ CTRACE((tfp, "HTAccess: loading document %s\n", NonNull(address_to_load)));
+ if (isEmpty(address_to_load))
+ return NO;
+
+ /*
+ * Free use_this_url_instead and reset permanent_redirection if not done
+ * elsewhere. - FM
+ */
+ FREE(use_this_url_instead);
+ permanent_redirection = FALSE;
+
+ if (too_many_redirections()) {
+ return NO;
+ }
+
+ /*
+ * If this is marked as an internal link but we don't have the document
+ * loaded any more, and we haven't explicitly flagged that we want to
+ * reload with LYforce_no_cache, then something has disappeared from the
+ * cache when we expected it to be still there. The user probably doesn't
+ * expect a new network access. So if we have POST data and safe is not
+ * set in the anchor, ask for confirmation, and fail if not granted. The
+ * exception are LYNXIMGMAP documents, for which we defer to LYLoadIMGmap
+ * for prompting if necessary. - kw
+ */
+ text = (HText *) HTAnchor_document(anchor);
+ if (LYinternal_flag && !text && !LYforce_no_cache &&
+ anchor->post_data && !anchor->safe &&
+ !isLYNXIMGMAP(full_address) &&
+ HTConfirm(gettext("Document with POST content not found in cache. Resubmit?"))
+ != TRUE) {
+ return NO;
+ }
+
+ /*
+ * If we don't have POST content, check whether this is a previous
+ * redirecting URL, and keep re-checking until we get to the final
+ * destination or redirection limit. If we do have POST content, we didn't
+ * allow permanent redirection, and an interactive user will be deciding
+ * whether to keep redirecting. - FM
+ */
+ if (!anchor->post_data) {
+ while ((cp = HTAnchor_physical(anchor)) != NULL &&
+ !StrNCmp(cp, "Location=", 9)) {
+ DocAddress NewDoc;
+
+ CTRACE((tfp, "HTAccess: '%s' is a redirection URL.\n",
+ anchor->address));
+ CTRACE((tfp, "HTAccess: Redirecting to '%s'\n", cp + 9));
+
+ /*
+ * Don't exceed the redirection_attempts limit. - FM
+ */
+ ++redirection_attempts;
+ if (too_many_redirections()) {
+ FREE(use_this_url_instead);
+ return NO;
+ }
+
+ /*
+ * Set up the redirection. - FM
+ */
+ StrAllocCopy(use_this_url_instead, cp + 9);
+ NewDoc.address = use_this_url_instead;
+ NewDoc.post_data = NULL;
+ NewDoc.post_content_type = NULL;
+ NewDoc.bookmark = anchor->bookmark;
+ NewDoc.isHEAD = anchor->isHEAD;
+ NewDoc.safe = anchor->safe;
+ anchor = HTAnchor_findAddress(&NewDoc);
+ }
+ }
+ /*
+ * If we had previous redirection, go back and check out that the URL under
+ * the current restrictions. - FM
+ */
+ if (use_this_url_instead) {
+ FREE(redirecting_url);
+ return (NO);
+ }
+
+ /*
+ * See if we can use an already loaded document.
+ */
+ text = (HText *) HTAnchor_document(anchor);
+ if (text && !LYforce_no_cache) {
+ /*
+ * We have a cached rendition of the target document. Check if it's OK
+ * to re-use it. We consider it OK if:
+ * (1) the anchor does not have the no_cache element set, or
+ * (2) we've overridden it, e.g., because we are acting on a PREV_DOC
+ * command or a link in the History Page and it's not a reply from a
+ * POST with the LYresubmit_posts flag set, or
+ * (3) we are repositioning within the currently loaded document based
+ * on the target anchor's address (URL_Reference).
+ *
+ * If track_internal_links is false, HText_AreDifferent() is
+ * used to determine whether (3) applies. If the target address
+ * differs from that of the current document only by a fragment and the
+ * target address has an appended fragment, repositioning without
+ * reloading is always assumed. Note that HText_AreDifferent()
+ * currently always returns TRUE if the target has a LYNXIMGMAP URL, so
+ * that an internally generated pseudo-document will normally not be
+ * re-used unless condition (2) applies. (Condition (1) cannot apply
+ * since in LYMap.c, no_cache is always set in the anchor object).
+ * This doesn't guarantee that the resource from which the MAP element
+ * is taken will be read again (reloaded) when the list of links for a
+ * client-side image map is regenerated, when in some cases it should
+ * (e.g., user requested RELOAD, or HTTP response with no-cache header
+ * and we are not overriding).
+ *
+ * If track_internal_links is true, a target address that
+ * points to the same URL as the current document may still result in
+ * reloading, depending on whether the original URL-Reference was given
+ * as an internal link in the context of the previously loaded
+ * document. HText_AreDifferent() is not used here for testing whether
+ * we are just repositioning. For an internal link, the potential
+ * callers of this function from mainloop() down will either avoid
+ * making the call (and do the repositioning differently) or set
+ * LYinternal_flag (or LYoverride_no_cache). Note that (a) LYNXIMGMAP
+ * pseudo-documents and (b) The "List Page" document are treated
+ * logically as being part of the document on which they are based, for
+ * the purpose of whether to treat a link as internal, but the logic
+ * for this (by setting LYinternal_flag as necessary) is implemented
+ * elsewhere. There is a specific test for LYNXIMGMAP here so that the
+ * generated pseudo-document will not be re-used unless
+ * LYoverride_no_cache is set. The same caveat as above applies w.r.t.
+ * reloading of the underlying resource.
+ *
+ * We also should be checking other aspects of cache regulation (e.g.,
+ * based on an If-Modified-Since check, etc.) but the code for doing
+ * those other things isn't available yet.
+ */
+ if ((reloading != REAL_RELOAD) &&
+ (LYoverride_no_cache ||
+ ((!track_internal_links &&
+ (!HText_hasNoCacheSet(text) ||
+ !HText_AreDifferent(anchor, full_address))) ||
+ (track_internal_links &&
+ (((LYinternal_flag || !HText_hasNoCacheSet(text)) &&
+ !isLYNXIMGMAP(full_address))))))) {
+ CTRACE((tfp, "HTAccess: Document already in memory.\n"));
+ HText_select(text);
+
+#ifdef DIRED_SUPPORT
+ if (HTAnchor_format(anchor) == WWW_DIRED)
+ lynx_edit_mode = TRUE;
+#endif
+ redirection_attempts = 0;
+ return YES;
+ } else {
+ ForcingNoCache = YES;
+ BStrFree(anchor->post_data);
+ CTRACE((tfp, "HTAccess: Auto-reloading document.\n"));
+ }
+ }
+
+ if (HText_HaveUserChangedForms(text)) {
+ /*
+ * Issue a warning. User forms content will be lost.
+ * Will not restore changed forms, currently.
+ */
+ HTAlert(RELOADING_FORM);
+ }
+
+ /*
+ * Get the document from the net. If we are auto-reloading, the mutable
+ * anchor elements from the previous rendition should be freed in
+ * conjunction with loading of the new rendition. - FM
+ */
+ LYforce_no_cache = NO; /* reset after each time through */
+ if (ForcingNoCache) {
+ FREE(anchor->title); /* ??? */
+ }
+ status = HTLoad(address_to_load, anchor, format_out, sink);
+ CTRACE((tfp, "HTAccess: status=%d\n", status));
+
+ /*
+ * RECOVERY: if the loading failed, and we had a cached HText copy, and no
+ * new HText created - use a previous copy, issue a warning.
+ */
+ if (text && status < 0 && (HText *) HTAnchor_document(anchor) == text) {
+ HTAlert(gettext("Loading failed, use a previous copy."));
+ CTRACE((tfp, "HTAccess: Loading failed, use a previous copy.\n"));
+ HText_select(text);
+
+#ifdef DIRED_SUPPORT
+ if (HTAnchor_format(anchor) == WWW_DIRED)
+ lynx_edit_mode = TRUE;
+#endif
+ redirection_attempts = 0;
+ return YES;
+ }
+
+ /*
+ * Log the access if necessary.
+ */
+ if (HTlogfile) {
+ time_t theTime;
+
+ time(&theTime);
+ fprintf(HTlogfile, "%24.24s %s %s %s\n",
+ ctime(&theTime),
+ HTClientHost ? HTClientHost : "local",
+ status < 0 ? "FAIL" : "GET",
+ full_address);
+ fflush(HTlogfile); /* Actually update it on disk */
+ CTRACE((tfp, "Log: %24.24s %s %s %s\n",
+ ctime(&theTime),
+ HTClientHost ? HTClientHost : "local",
+ status < 0 ? "FAIL" : "GET",
+ full_address));
+ }
+
+ /*
+ * Check out what we received from the net.
+ */
+ if (status == HT_REDIRECTING) {
+ /* Exported from HTMIME.c, of all places. */
+ /* NO!! - FM */
+ /*
+ * Doing this via HTMIME.c meant that the redirection cover page was
+ * already loaded before we learned that we want a different URL.
+ * Also, changing anchor->address, as Lynx was doing, meant we could
+ * never again access its hash table entry, creating an insolvable
+ * memory leak. Instead, if we had a 301 status and set
+ * permanent_redirection, we'll load the new URL in anchor->physical,
+ * preceded by a token, which we can check to make replacements on
+ * subsequent access attempts. We'll check recursively, and retrieve
+ * the final URL if we had multiple redirections to it. If we just
+ * went to HTLoad now, as Lou originally had this, we couldn't do
+ * Lynx's security checks and alternate handling of some URL types.
+ * So, instead, we'll go all the way back to the top of getfile in
+ * LYGetFile.c when the status is HT_REDIRECTING. This may seem
+ * bizarre, but it works like a charm! - FM
+ *
+ * Actually, the location header for redirections is now again picked
+ * up in HTMIME.c. But that's an internal matter between HTTP.c and
+ * HTMIME.c, is still under control of HTLoadHTTP for http URLs, is
+ * done in a way that doesn't load the redirection response's body
+ * (except when wanted as an error fallback), and thus need not concern
+ * us here. - kw 1999-12-02
+ */
+ CTRACE((tfp, "HTAccess: '%s' is a redirection URL.\n",
+ address_to_load));
+ CTRACE((tfp, "HTAccess: Redirecting to '%s'\n",
+ redirecting_url));
+ /*
+ * Prevent circular references.
+ */
+ if (strcmp(address_to_load, redirecting_url)) { /* if different */
+ /*
+ * Load token and redirecting url into anchor->physical if we had
+ * 301 Permanent redirection. HTTP.c does not allow this if we
+ * have POST content. - FM
+ */
+ if (permanent_redirection) {
+ StrAllocCopy(anchor->physical, "Location=");
+ StrAllocCat(anchor->physical, redirecting_url);
+ }
+
+ /*
+ * Set up flags before return to getfile. - FM
+ */
+ StrAllocCopy(use_this_url_instead, redirecting_url);
+ if (ForcingNoCache)
+ LYforce_no_cache = YES;
+ ++redirection_attempts;
+ FREE(redirecting_url);
+ permanent_redirection = FALSE;
+ return (NO);
+ }
+ ++redirection_attempts;
+ FREE(redirecting_url);
+ permanent_redirection = FALSE;
+ return (YES);
+ }
+
+ /*
+ * We did not receive a redirecting URL. - FM
+ */
+ redirection_attempts = 0;
+ FREE(redirecting_url);
+ permanent_redirection = FALSE;
+
+ if (status == HT_LOADED) {
+ CTRACE((tfp, "HTAccess: `%s' has been accessed.\n",
+ full_address));
+ return YES;
+ }
+ if (status == HT_PARTIAL_CONTENT) {
+ HTAlert(gettext("Loading incomplete."));
+ CTRACE((tfp, "HTAccess: `%s' has been accessed, partial content.\n",
+ full_address));
+ return YES;
+ }
+
+ if (status == HT_NO_DATA) {
+ CTRACE((tfp, "HTAccess: `%s' has been accessed, No data left.\n",
+ full_address));
+ return NO;
+ }
+
+ if (status == HT_NOT_LOADED) {
+ CTRACE((tfp, "HTAccess: `%s' has been accessed, No data loaded.\n",
+ full_address));
+ return NO;
+ }
+
+ if (status == HT_INTERRUPTED) {
+ CTRACE((tfp,
+ "HTAccess: `%s' has been accessed, transfer interrupted.\n",
+ full_address));
+ return NO;
+ }
+
+ if (status > 0) {
+ /*
+ * If you get this, then please find which routine is returning a
+ * positive unrecognized error code!
+ */
+ fprintf(stderr,
+ gettext("**** HTAccess: socket or file number returned by obsolete load routine!\n"));
+ fprintf(stderr,
+ gettext("**** HTAccess: Internal software error. Please mail lynx-dev@nongnu.org!\n"));
+ fprintf(stderr, gettext("**** HTAccess: Status returned was: %d\n"), status);
+ exit_immediately(EXIT_FAILURE);
+ }
+
+ /* Failure in accessing a document */
+ cp = NULL;
+ StrAllocCopy(cp, gettext("Can't Access"));
+ StrAllocCat(cp, " `");
+ StrAllocCat(cp, full_address);
+ StrAllocCat(cp, "'");
+ _HTProgress(cp);
+ FREE(cp);
+
+ CTRACE((tfp, "HTAccess: Can't access `%s'\n", full_address));
+ HTLoadError(sink, 500, gettext("Unable to access document."));
+ return NO;
+} /* HTLoadDocument */
+
+/* Load a document from absolute name. HTLoadAbsolute()
+ * -----------------------------------
+ *
+ * On Entry,
+ * addr The absolute address of the document to be accessed.
+ * filter if YES, treat document as HTML
+ *
+ * On Exit,
+ * returns YES Success in opening document
+ * NO Failure
+ */
+BOOL HTLoadAbsolute(const DocAddress *docaddr)
+{
+ BOOL result;
+ HTParentAnchor *anchor = HTAnchor_findAddress(docaddr);
+
+ result = HTLoadDocument(docaddr->address,
+ anchor,
+ (HTOutputFormat ? HTOutputFormat : WWW_PRESENT),
+ HTOutputStream);
+ if (!result) {
+ HTAnchor_delete(anchor->parent);
+ }
+ return result;
+}
+
+#ifdef NOT_USED_CODE
+/* Load a document from absolute name to stream. HTLoadToStream()
+ * ---------------------------------------------
+ *
+ * On Entry,
+ * addr The absolute address of the document to be accessed.
+ * sink if non-NULL, send data down this stream
+ *
+ * On Exit,
+ * returns YES Success in opening document
+ * NO Failure
+ */
+BOOL HTLoadToStream(const char *addr,
+ BOOL filter,
+ HTStream *sink)
+{
+ return HTLoadDocument(addr,
+ HTAnchor_findSimpleAddress(addr),
+ (HTOutputFormat ? HTOutputFormat : WWW_PRESENT),
+ sink);
+}
+#endif /* NOT_USED_CODE */
+
+/* Load a document from relative name. HTLoadRelative()
+ * -----------------------------------
+ *
+ * On Entry,
+ * relative_name The relative address of the document
+ * to be accessed.
+ *
+ * On Exit,
+ * returns YES Success in opening document
+ * NO Failure
+ */
+BOOL HTLoadRelative(const char *relative_name,
+ HTParentAnchor *here)
+{
+ DocAddress full_address;
+ BOOL result;
+ char *mycopy = NULL;
+ char *stripped = NULL;
+
+ full_address.address = NULL;
+ full_address.post_data = NULL;
+ full_address.post_content_type = NULL;
+ full_address.bookmark = NULL;
+ full_address.isHEAD = FALSE;
+ full_address.safe = FALSE;
+
+ StrAllocCopy(mycopy, relative_name);
+
+ stripped = HTStrip(mycopy);
+ full_address.address =
+ HTParse(stripped,
+ here->address,
+ PARSE_ALL_WITHOUT_ANCHOR);
+ result = HTLoadAbsolute(&full_address);
+ /*
+ * If we got redirection, result will be NO, but use_this_url_instead will
+ * be set. The calling routine should check both and do whatever is
+ * appropriate. - FM
+ */
+ FREE(full_address.address);
+ FREE(mycopy); /* Memory leak fixed 10/7/92 -- JFG */
+ return result;
+}
+
+/* Load if necessary, and select an anchor. HTLoadAnchor()
+ * ----------------------------------------
+ *
+ * On Entry,
+ * destination The child or parent anchor to be loaded.
+ *
+ * On Exit,
+ * returns YES Success
+ * NO Failure
+ */
+BOOL HTLoadAnchor(HTAnchor * destination)
+{
+ HTParentAnchor *parent;
+ BOOL loaded = NO;
+
+ if (!destination)
+ return NO; /* No link */
+
+ parent = HTAnchor_parent(destination);
+
+ if (HTAnchor_document(parent) == NULL) { /* If not already loaded */
+ /* TBL 921202 */
+ BOOL result;
+
+ result = HTLoadDocument(parent->address,
+ parent,
+ HTOutputFormat ?
+ HTOutputFormat : WWW_PRESENT,
+ HTOutputStream);
+ if (!result)
+ return NO;
+ loaded = YES;
+ } {
+ HText *text = (HText *) HTAnchor_document(parent);
+
+ if ((destination != (HTAnchor *) parent) &&
+ (destination != (HTAnchor *) (parent->parent))) {
+ /* If child anchor */
+ HText_selectAnchor(text, /* Double display? @@ */
+ (HTChildAnchor *) destination);
+ } else {
+ if (!loaded)
+ HText_select(text);
+ }
+ }
+ return YES;
+
+} /* HTLoadAnchor */
+
+/* Search. HTSearch()
+ * -------
+ *
+ * Performs a keyword search on word given by the user. Adds the
+ * keyword to the end of the current address and attempts to open
+ * the new address.
+ *
+ * On Entry,
+ * *keywords space-separated keyword list or similar search list
+ * here is anchor search is to be done on.
+ */
+static char hex(int i)
+{
+ const char *hexchars = "0123456789ABCDEF";
+
+ return hexchars[i];
+}
+
+BOOL HTSearch(const char *keywords,
+ HTParentAnchor *here)
+{
+#define acceptable \
+"1234567890abcdefghijlkmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.-_"
+
+ char *q, *u;
+ const char *p, *s, *e; /* Pointers into keywords */
+ char *address = NULL;
+ BOOL result;
+ char *escaped = typecallocn(char, (strlen(keywords) * 3) + 1);
+ static const BOOL isAcceptable[96] =
+ /* *INDENT-OFF* */
+ /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */
+ { 0,0,0,0,0,0,0,0,0,0,1,0,0,1,1,0, /* 2x !"#$%&'()*+,-./ */
+ 1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0, /* 3x 0123456789:;<=>? */
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 4x @ABCDEFGHIJKLMNO */
+ 1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,1, /* 5X PQRSTUVWXYZ[\]^_ */
+ 0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 6x `abcdefghijklmno */
+ 1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0 }; /* 7X pqrstuvwxyz{\}~ DEL */
+ /* *INDENT-ON* */
+
+ if (escaped == NULL)
+ outofmem(__FILE__, "HTSearch");
+
+ if (here->isIndexAction == NULL) {
+ free(escaped);
+ return FALSE;
+ }
+ StrAllocCopy(address, here->isIndexAction);
+
+ /*
+ * Convert spaces to + and hex escape unacceptable characters.
+ */
+ for (s = keywords; *s && WHITE(*s); s++) /* Scan */
+ ; /* Skip white space */
+ for (e = s + strlen(s); e > s && WHITE(*(e - 1)); e--) /* Scan */
+ ; /* Skip trailers */
+ for (q = escaped, p = s; p < e; p++) { /* Scan stripped field */
+ unsigned char c = UCH(TOASCII(*p));
+
+ if (WHITE(*p)) {
+ *q++ = '+';
+ } else if (IS_CJK_TTY) {
+ *q++ = *p;
+ } else if (c >= 32 && c <= UCH(127) && isAcceptable[c - 32]) {
+ *q++ = *p; /* 930706 TBL for MVS bug */
+ } else {
+ *q++ = '%';
+ *q++ = hex((int) (c >> 4));
+ *q++ = hex((int) (c & 15));
+ }
+ } /* Loop over string */
+ *q = '\0'; /* Terminate escaped string */
+ u = StrChr(address, '?'); /* Find old search string */
+ if (u != NULL)
+ *u = '\0'; /* Chop old search off */
+
+ StrAllocCat(address, "?");
+ StrAllocCat(address, escaped);
+ FREE(escaped);
+ result = HTLoadRelative(address, here);
+ FREE(address);
+
+ /*
+ * If we got redirection, result will be NO, but use_this_url_instead will
+ * be set. The calling routine should check both and do whatever is
+ * appropriate. Only an http server (not a gopher or wais server) could
+ * return redirection. Lynx will go all the way back to its mainloop() and
+ * subject a redirecting URL to all of its security and restrictions
+ * checks. - FM
+ */
+ return result;
+}
+
+/* Search Given Indexname. HTSearchAbsolute()
+ * -----------------------
+ *
+ * Performs a keyword search on word given by the user. Adds the
+ * keyword to the end of the current address and attempts to open
+ * the new address.
+ *
+ * On Entry,
+ * *keywords space-separated keyword list or similar search list
+ * *indexname is name of object search is to be done on.
+ */
+BOOL HTSearchAbsolute(const char *keywords,
+ char *indexname)
+{
+ DocAddress abs_doc;
+ HTParentAnchor *anchor;
+
+ abs_doc.address = indexname;
+ abs_doc.post_data = NULL;
+ abs_doc.post_content_type = NULL;
+ abs_doc.bookmark = NULL;
+ abs_doc.isHEAD = FALSE;
+ abs_doc.safe = FALSE;
+
+ anchor = HTAnchor_findAddress(&abs_doc);
+ return HTSearch(keywords, anchor);
+}
+
+#ifdef NOT_USED_CODE
+/* Generate the anchor for the home page. HTHomeAnchor()
+ * --------------------------------------
+ *
+ * As it involves file access, this should only be done once
+ * when the program first runs.
+ * This is a default algorithm -- browser don't HAVE to use this.
+ * But consistency between browsers is STRONGLY recommended!
+ *
+ * Priority order is:
+ * 1 WWW_HOME environment variable (logical name, etc)
+ * 2 ~/WWW/default.html
+ * 3 /usr/local/bin/default.html
+ * 4 http://www.w3.org/default.html
+ */
+HTParentAnchor *HTHomeAnchor(void)
+{
+ char *my_home_document = NULL;
+ char *home = LYGetEnv(LOGICAL_DEFAULT);
+ char *ref;
+ HTParentAnchor *anchor;
+
+ if (home) {
+ StrAllocCopy(my_home_document, home);
+#define MAX_FILE_NAME 1024 /* @@@ */
+ } else if (HTClientHost) { /* Telnet server */
+ /*
+ * Someone telnets in, they get a special home.
+ */
+ FILE *fp = fopen(REMOTE_POINTER, "r");
+ char *status;
+
+ if (fp) {
+ my_home_document = typecallocn(char, MAX_FILE_NAME);
+
+ if (my_home_document == NULL)
+ outofmem(__FILE__, "HTHomeAnchor");
+ status = fgets(my_home_document, MAX_FILE_NAME, fp);
+ if (!status) {
+ FREE(my_home_document);
+ }
+ fclose(fp);
+ }
+ if (my_home_document == NULL)
+ StrAllocCopy(my_home_document, REMOTE_ADDRESS);
+ }
+#ifdef UNIX
+ if (my_home_document == NULL) {
+ FILE *fp = NULL;
+ char *home = LYGetEnv("HOME");
+
+ if (home != 0) {
+ HTSprintf0(&my_home_document, "%s/%s", home, PERSONAL_DEFAULT);
+ fp = fopen(my_home_document, "r");
+ }
+
+ if (!fp) {
+ StrAllocCopy(my_home_document, LOCAL_DEFAULT_FILE);
+ fp = fopen(my_home_document, "r");
+ }
+ if (fp) {
+ fclose(fp);
+ } else {
+ CTRACE((tfp, "HTBrowse: No local home document ~/%s or %s\n",
+ PERSONAL_DEFAULT, LOCAL_DEFAULT_FILE));
+ FREE(my_home_document);
+ }
+ }
+#endif /* UNIX */
+ ref = HTParse((my_home_document ?
+ my_home_document : (HTClientHost ?
+ REMOTE_ADDRESS : LAST_RESORT)),
+ STR_FILE_URL,
+ PARSE_ALL_WITHOUT_ANCHOR);
+ if (my_home_document) {
+ CTRACE((tfp, "HTAccess: Using custom home page %s i.e., address %s\n",
+ my_home_document, ref));
+ FREE(my_home_document);
+ }
+ anchor = HTAnchor_findSimpleAddress(ref);
+ FREE(ref);
+ return anchor;
+}
+#endif /* NOT_USED_CODE */
diff --git a/WWW/Library/Implementation/HTAccess.h b/WWW/Library/Implementation/HTAccess.h
new file mode 100644
index 0000000..b898535
--- /dev/null
+++ b/WWW/Library/Implementation/HTAccess.h
@@ -0,0 +1,268 @@
+/*
+ * $LynxId: HTAccess.h,v 1.21 2019/08/23 23:51:45 tom Exp $
+ * HTAccess: Access manager for libwww
+ * ACCESS MANAGER
+ *
+ * This module keeps a list of valid protocol (naming scheme) specifiers with
+ * associated access code. It allows documents to be loaded given various
+ * combinations of parameters. New access protocols may be registered at any
+ * time.
+ *
+ * Part of the libwww library .
+ *
+ */
+#ifndef HTACCESS_H
+#define HTACCESS_H
+
+/* Definition uses:
+*/
+#include <HTAnchor.h>
+#include <HTFormat.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+ extern char *use_this_url_instead;
+
+ extern int redirection_limit;
+ extern int redirection_attempts;
+
+/* Return codes from load routines:
+ *
+ * These codes may be returned by the protocol modules,
+ * and by the HTLoad routines.
+ * In general, positive codes are OK and negative ones are bad.
+ */
+
+/*
+
+Default Addresses
+
+ These control the home page selection. To mess with these for normal browses is asking
+ for user confusion.
+
+ */
+#define LOGICAL_DEFAULT "WWW_HOME" /* Defined to be the home page */
+
+#ifndef PERSONAL_DEFAULT
+#define PERSONAL_DEFAULT "WWW/default.html" /* in home directory */
+#endif
+#ifndef LOCAL_DEFAULT_FILE
+#define LOCAL_DEFAULT_FILE "/usr/local/lib/WWW/default.html"
+#endif
+/* If one telnets to a www access point,
+ it will look in this file for home page */
+#ifndef REMOTE_POINTER
+#define REMOTE_POINTER "/etc/www-remote.url" /* can't be file */
+#endif
+/* and if that fails it will use this. */
+#ifndef REMOTE_ADDRESS
+#define REMOTE_ADDRESS "http://www.w3.org/remote.html" /* can't be file */
+#endif
+
+/* If run from telnet daemon and no -l specified, use this file:
+*/
+#ifndef DEFAULT_LOGFILE
+#define DEFAULT_LOGFILE "/usr/adm/www-log/www-log"
+#endif
+
+/* If the home page isn't found, use this file:
+*/
+#ifndef LAST_RESORT
+#define LAST_RESORT "http://www.w3.org/default.html"
+#endif
+
+/*
+
+Flags which may be set to control this module
+
+ */
+#ifdef NOT
+ extern int HTDiag; /* Flag: load source as plain text */
+#endif /* NOT */
+ extern char *HTClientHost; /* Name or number of telnetting host */
+ extern FILE *HTlogfile; /* File to output one-liners to */
+ extern BOOL HTSecure; /* Disable security holes? */
+ extern BOOL HTPermitRedir; /* Special flag for getfile() */
+ extern HTStream *HTOutputStream; /* For non-interactive, set this */
+ extern HTFormat HTOutputFormat; /* To convert on load, set this */
+
+/* Check for proxy override. override_proxy()
+ *
+ * Check the no_proxy environment variable to get the list
+ * of hosts for which proxy server is not consulted.
+ *
+ * no_proxy is a comma- or space-separated list of machine
+ * or domain names, with optional :port part. If no :port
+ * part is present, it applies to all ports on that domain.
+ *
+ * Example:
+ * no_proxy="cern.ch,some.domain:8001"
+ *
+ * Use "*" to override all proxy service:
+ * no_proxy="*"
+ */
+ extern BOOL override_proxy(const char *addr);
+
+/*
+
+Load a document from relative name
+
+ ON ENTRY,
+ relative_name The relative address of the file to be accessed.
+ here The anchor of the object being searched
+
+ ON EXIT,
+ returns YES Success in opening file
+ NO Failure
+
+ */
+ extern BOOL HTLoadRelative(const char *relative_name,
+ HTParentAnchor *here);
+
+/*
+
+Load a document from absolute name
+
+ ON ENTRY,
+ addr The absolute address of the document to be accessed.
+ filter_it if YES, treat document as HTML
+
+ ON EXIT,
+ returns YES Success in opening document
+ NO Failure
+
+ */
+ extern BOOL HTLoadAbsolute(const DocAddress *addr);
+
+/*
+
+Load a document from absolute name to a stream
+
+ ON ENTRY,
+ addr The absolute address of the document to be accessed.
+ filter_it if YES, treat document as HTML
+
+ ON EXIT,
+ returns YES Success in opening document
+ NO Failure
+
+ Note: This is equivalent to HTLoadDocument
+
+ */
+ extern BOOL HTLoadToStream(const char *addr, BOOL filter_it,
+ HTStream *sink);
+
+/*
+
+Load if necessary, and select an anchor
+
+ ON ENTRY,
+ destination The child or parent anchor to be loaded.
+
+ ON EXIT,
+ returns YES Success
+ returns NO Failure
+
+ */
+ extern BOOL HTLoadAnchor(HTAnchor * destination);
+
+/*
+
+Make a stream for Saving object back
+
+ ON ENTRY,
+ anchor is valid anchor which has previously been loaded
+
+ ON EXIT,
+ returns 0 if error else a stream to save the object to.
+
+ */
+ extern HTStream *HTSaveStream(HTParentAnchor *anchor);
+
+/*
+
+Search
+
+ Performs a search on word given by the user. Adds the search words to the end of the
+ current address and attempts to open the new address.
+
+ ON ENTRY,
+ *keywords space-separated keyword list or similar search list
+ here The anchor of the object being searched
+
+ */
+ extern BOOL HTSearch(const char *keywords, HTParentAnchor *here);
+
+/*
+
+Search Given Indexname
+
+ Performs a keyword search on word given by the user. Adds the keyword to the end of
+ the current address and attempts to open the new address.
+
+ ON ENTRY,
+ *keywords space-separated keyword list or similar search list
+ *indexname is name of object search is to be done on.
+
+ */
+ extern BOOL HTSearchAbsolute(const char *keywords,
+ char *indexname);
+
+/*
+
+Register an access method
+
+ */
+
+ typedef struct _HTProtocol {
+ const char *name;
+
+ int (*load) (const char *full_address,
+ HTParentAnchor *anchor,
+ HTFormat format_out,
+ HTStream *sink);
+
+ HTStream *(*saveStream) (HTParentAnchor *anchor);
+
+ } HTProtocol;
+
+ extern BOOL HTRegisterProtocol(HTProtocol * protocol);
+
+/*
+
+Generate the anchor for the home page
+
+ */
+
+/*
+
+ As it involves file access, this should only be done once when the program first runs.
+ This is a default algorithm -- browser don't HAVE to use this.
+
+ */
+ extern HTParentAnchor *HTHomeAnchor(void);
+
+/*
+
+Return Host Name
+
+ */
+ extern const char *HTHostName(void);
+
+/*
+
+For registering protocols supported by Lynx
+
+*/
+ extern void LYRegisterLynxProtocols(void);
+
+ extern void LYUCPushAssumed(HTParentAnchor *anchor);
+ extern int LYUCPopAssumed(void);
+
+ extern BOOL using_proxy; /* Are we using an NNTP proxy? */
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* HTACCESS_H */
diff --git a/WWW/Library/Implementation/HTAnchor.c b/WWW/Library/Implementation/HTAnchor.c
new file mode 100644
index 0000000..b5ca251
--- /dev/null
+++ b/WWW/Library/Implementation/HTAnchor.c
@@ -0,0 +1,1460 @@
+/*
+ * $LynxId: HTAnchor.c,v 1.82 2020/01/21 21:58:52 tom Exp $
+ *
+ * Hypertext "Anchor" Object HTAnchor.c
+ * ==========================
+ *
+ * An anchor represents a region of a hypertext document which is linked to
+ * another anchor in the same or a different document.
+ *
+ * History
+ *
+ * Nov 1990 Written in Objective-C for the NeXT browser (TBL)
+ * 24-Oct-1991 (JFG), written in C, browser-independent
+ * 21-Nov-1991 (JFG), first complete version
+ *
+ * (c) Copyright CERN 1991 - See Copyright.html
+ */
+
+#define HASH_SIZE 997 /* Arbitrary prime. Memory/speed tradeoff */
+
+#include <HTUtils.h>
+#include <HTAnchor.h>
+#include <HTParse.h>
+#include <HTString.h>
+#include <UCAux.h>
+#include <UCMap.h>
+
+#include <GridText.h>
+#include <LYUtils.h>
+#include <LYCharSets.h>
+#include <LYUtils.h>
+#include <LYLeaks.h>
+
+#define HASH_OF(h, v) ((HASH_TYPE)((h) * 3 + UCH(v)) % HASH_SIZE)
+
+static HASH_TYPE anchor_hash(const char *cp_address)
+{
+ HASH_TYPE hash;
+ const char *p;
+
+ for (p = cp_address, hash = 0; *p; p++)
+ hash = HASH_OF(hash, *p);
+
+ return (hash);
+}
+
+typedef struct _HyperDoc Hyperdoc;
+
+#ifdef VMS
+struct _HyperDoc {
+ int junk; /* VMS cannot handle pointers to undefined structs */
+};
+#endif /* VMS */
+
+/* Table of lists of all parents */
+static HTList adult_table[HASH_SIZE] =
+{
+ {NULL, NULL}};
+
+/* Creation Methods
+ * ================
+ *
+ * Do not use "new" by itself outside this module. In order to enforce
+ * consistency, we insist that you furnish more information about the
+ * anchor you are creating : use newWithParent or newWithAddress.
+ */
+static HTParentAnchor0 *HTParentAnchor0_new(const char *address,
+ unsigned hash)
+{
+ HTParentAnchor0 *newAnchor = typecalloc(HTParentAnchor0);
+
+ if (newAnchor == NULL)
+ outofmem(__FILE__, "HTParentAnchor0_new");
+
+ newAnchor->parent = newAnchor; /* self */
+ StrAllocCopy(newAnchor->address, address);
+ newAnchor->adult_hash = (HASH_TYPE) hash;
+
+ return (newAnchor);
+}
+
+static HTParentAnchor *HTParentAnchor_new(HTParentAnchor0 *parent)
+{
+ HTParentAnchor *newAnchor = typecalloc(HTParentAnchor);
+
+ if (newAnchor == NULL)
+ outofmem(__FILE__, "HTParentAnchor_new");
+
+ newAnchor->parent = parent; /* cross reference */
+ parent->info = newAnchor; /* cross reference */
+ newAnchor->address = parent->address; /* copy pointer */
+
+ newAnchor->isISMAPScript = FALSE; /* Lynx appends ?0,0 if TRUE. - FM */
+ newAnchor->isHEAD = FALSE; /* HEAD request if TRUE. - FM */
+ newAnchor->safe = FALSE; /* Safe. - FM */
+ newAnchor->no_cache = FALSE; /* no-cache? - FM */
+ newAnchor->inBASE = FALSE; /* duplicated from HTML.c/h */
+ newAnchor->content_length = 0; /* Content-Length. - FM */
+ return (newAnchor);
+}
+
+static HTChildAnchor *HTChildAnchor_new(HTParentAnchor0 *parent)
+{
+ HTChildAnchor *p = typecalloc(HTChildAnchor);
+
+ if (p == NULL)
+ outofmem(__FILE__, "HTChildAnchor_new");
+
+ p->parent = parent; /* parent reference */
+ return p;
+}
+
+static HTChildAnchor *HText_pool_ChildAnchor_new(HTParentAnchor *parent)
+{
+ HTChildAnchor *p = (HTChildAnchor *) HText_pool_calloc((HText *) (parent->document),
+ (unsigned) sizeof(HTChildAnchor));
+
+ if (p == NULL)
+ outofmem(__FILE__, "HText_pool_ChildAnchor_new");
+
+ p->parent = parent->parent; /* parent reference */
+ return p;
+}
+
+#ifdef CASE_INSENSITIVE_ANCHORS
+/* Case insensitive string comparison */
+#define HT_EQUIV(a,b) (TOUPPER(a) == TOUPPER(b))
+#else
+/* Case sensitive string comparison */
+#define HT_EQUIV(a,b) ((a) == (b))
+#endif
+
+/* Null-terminated string comparison
+ * ---------------------------------
+ * On entry,
+ * s Points to one string, null terminated
+ * t points to the other.
+ * On exit,
+ * returns YES if the strings are equivalent
+ * NO if they differ.
+ */
+static BOOL HTSEquivalent(const char *s,
+ const char *t)
+{
+ if (s && t) { /* Make sure they point to something */
+ for (; *s && *t; s++, t++) {
+ if (!HT_EQUIV(*s, *t)) {
+ return (NO);
+ }
+ }
+ return (BOOL) (HT_EQUIV(*s, *t));
+ } else {
+ return (BOOL) (s == t); /* Two NULLs are equivalent, aren't they ? */
+ }
+}
+
+/* Binary string comparison
+ * ------------------------
+ * On entry,
+ * s Points to one bstring
+ * t points to the other.
+ * On exit,
+ * returns YES if the strings are equivalent
+ * NO if they differ.
+ */
+static BOOL HTBEquivalent(const bstring *s,
+ const bstring *t)
+{
+ if (s && t && BStrLen(s) == BStrLen(t)) {
+ int j;
+ int len = BStrLen(s);
+
+ for (j = 0; j < len; ++j) {
+ if (!HT_EQUIV(BStrData(s)[j], BStrData(t)[j])) {
+ return (NO);
+ }
+ }
+ return (YES);
+ } else {
+ return (BOOL) (s == t); /* Two NULLs are equivalent, aren't they ? */
+ }
+}
+
+/*
+ * Three-way compare function
+ */
+static int compare_anchors(void *l,
+ void *r)
+{
+ const char *a = ((HTChildAnchor *) l)->tag;
+ const char *b = ((HTChildAnchor *) r)->tag;
+
+ /* both tags are not NULL */
+
+#ifdef CASE_INSENSITIVE_ANCHORS
+ return strcasecomp(a, b); /* Case insensitive */
+#else
+ return strcmp(a, b); /* Case sensitive - FM */
+#endif /* CASE_INSENSITIVE_ANCHORS */
+}
+
+/* Create new or find old sub-anchor
+ * ---------------------------------
+ *
+ * This one is for a named child.
+ * The parent anchor must already exist.
+ */
+static HTChildAnchor *HTAnchor_findNamedChild(HTParentAnchor0 *parent,
+ const char *tag)
+{
+ HTChildAnchor *child;
+
+ if (parent && tag && *tag) { /* TBL */
+ if (parent->children) {
+ /*
+ * Parent has children. Search them.
+ */
+ HTChildAnchor sample;
+
+ sample.tag = DeConst(tag); /* for compare_anchors() only */
+
+ child = (HTChildAnchor *) HTBTree_search(parent->children, &sample);
+ if (child != NULL) {
+ CTRACE((tfp,
+ "Child anchor %p of parent %p with name `%s' already exists.\n",
+ (void *) child, (void *) parent, tag));
+ return (child);
+ }
+ } else { /* parent doesn't have any children yet : create family */
+ parent->children = HTBTree_new(compare_anchors);
+ }
+
+ child = HTChildAnchor_new(parent);
+ CTRACE((tfp, "HTAnchor: New Anchor %p named `%s' is child of %p\n",
+ (void *) child,
+ NonNull(tag),
+ (void *) child->parent));
+
+ StrAllocCopy(child->tag, tag); /* should be set before HTBTree_add */
+ HTBTree_add(parent->children, child);
+ return (child);
+
+ } else {
+ CTRACE((tfp, "HTAnchor_findNamedChild called with NULL parent.\n"));
+ return (NULL);
+ }
+
+}
+
+/*
+ * This one is for a new unnamed child being edited into an existing
+ * document. The parent anchor and the document must already exist.
+ * (Just add new unnamed child).
+ */
+static HTChildAnchor *HTAnchor_addChild(HTParentAnchor *parent)
+{
+ HTChildAnchor *child;
+
+ if (!parent) {
+ CTRACE((tfp, "HTAnchor_addChild called with NULL parent.\n"));
+ return (NULL);
+ }
+
+ child = HText_pool_ChildAnchor_new(parent);
+ CTRACE((tfp, "HTAnchor: New unnamed Anchor %p is child of %p\n",
+ (void *) child,
+ (void *) child->parent));
+
+ child->tag = 0;
+ HTList_linkObject(&parent->children_notag, child, &child->_add_children_notag);
+
+ return (child);
+}
+
+static HTParentAnchor0 *HTAnchor_findAddress_in_adult_table(const DocAddress *newdoc);
+
+static BOOL HTAnchor_link(HTChildAnchor *child,
+ HTAnchor * destination,
+ HTLinkType *type);
+
+/* Create or find a child anchor with a possible link
+ * --------------------------------------------------
+ *
+ * Create new anchor with a given parent and possibly
+ * a name, and possibly a link to a _relatively_ named anchor.
+ * (Code originally in ParseHTML.h)
+ */
+HTChildAnchor *HTAnchor_findChildAndLink(HTParentAnchor *parent, /* May not be 0 */
+ const char *tag, /* May be "" or 0 */
+ const char *href, /* May be "" or 0 */
+ HTLinkType *ltype) /* May be 0 */
+{
+ HTChildAnchor *child;
+
+ CTRACE((tfp, "Entered HTAnchor_findChildAndLink: tag=`%s',%s href=`%s'\n",
+ NonNull(tag),
+ (ltype == HTInternalLink) ? " (internal link)" : "",
+ NonNull(href)));
+
+ if (parent == 0) {
+ child = 0;
+ } else {
+ if (non_empty(tag)) {
+ child = HTAnchor_findNamedChild(parent->parent, tag);
+ } else {
+ child = HTAnchor_addChild(parent);
+ }
+
+ if (non_empty(href)) {
+ const char *fragment = NULL;
+ HTParentAnchor0 *dest;
+
+ if (ltype == HTInternalLink && *href == '#') {
+ dest = parent->parent;
+ } else {
+ const char *relative_to = ((parent->inBASE && *href != '#')
+ ? parent->content_base
+ : parent->address);
+ DocAddress parsed_doc;
+
+ parsed_doc.address = HTParse(href, relative_to,
+ PARSE_ALL_WITHOUT_ANCHOR);
+
+ parsed_doc.post_data = NULL;
+ parsed_doc.post_content_type = NULL;
+ if (ltype && parent->post_data && ltype == HTInternalLink) {
+ /* for internal links, find a destination with the same
+ post data if the source of the link has post data. - kw
+ Example: LYNXIMGMAP: */
+ parsed_doc.post_data = parent->post_data;
+ parsed_doc.post_content_type = parent->post_content_type;
+ }
+ parsed_doc.bookmark = NULL;
+ parsed_doc.isHEAD = FALSE;
+ parsed_doc.safe = FALSE;
+
+ dest = HTAnchor_findAddress_in_adult_table(&parsed_doc);
+ FREE(parsed_doc.address);
+ }
+
+ /*
+ * [from HTAnchor_findAddress()]
+ * If the address represents a sub-anchor, we load its parent (above),
+ * then we create a named child anchor within that parent.
+ */
+ fragment = (*href == '#') ? href + 1 : HTParseAnchor(href);
+
+ if (*fragment)
+ dest = (HTParentAnchor0 *) HTAnchor_findNamedChild(dest, fragment);
+
+ if (tag && *tag) {
+ if (child->dest) { /* DUPLICATE_ANCHOR_NAME_WORKAROUND - kw */
+ CTRACE((tfp,
+ "*** Duplicate ChildAnchor %p named `%s'",
+ (void *) child, tag));
+ if ((HTAnchor *) dest != child->dest || ltype != child->type) {
+ CTRACE((tfp,
+ ", different dest %p or type, creating unnamed child\n",
+ (void *) child->dest));
+ child = HTAnchor_addChild(parent);
+ }
+ }
+ }
+ HTAnchor_link(child, (HTAnchor *) dest, ltype);
+ }
+ }
+ return child;
+}
+
+/* Create new or find old parent anchor
+ * ------------------------------------
+ *
+ * Me one is for a reference which is found in a document, and might
+ * not be already loaded.
+ * Note: You are not guaranteed a new anchor -- you might get an old one,
+ * like with fonts.
+ */
+HTParentAnchor *HTAnchor_findAddress(const DocAddress *newdoc)
+{
+ /* Anchor tag specified ? */
+ const char *tag = HTParseAnchor(newdoc->address);
+
+ CTRACE((tfp, "Entered HTAnchor_findAddress\n"));
+
+ /*
+ * If the address represents a sub-anchor, we load its parent, then we
+ * create a named child anchor within that parent.
+ */
+ if (*tag) {
+ DocAddress parsed_doc;
+ HTParentAnchor0 *foundParent;
+
+ parsed_doc.address = HTParse(newdoc->address, "",
+ PARSE_ALL_WITHOUT_ANCHOR);
+ parsed_doc.post_data = newdoc->post_data;
+ parsed_doc.post_content_type = newdoc->post_content_type;
+ parsed_doc.bookmark = newdoc->bookmark;
+ parsed_doc.isHEAD = newdoc->isHEAD;
+ parsed_doc.safe = newdoc->safe;
+
+ foundParent = HTAnchor_findAddress_in_adult_table(&parsed_doc);
+ (void) HTAnchor_findNamedChild(foundParent, tag);
+ FREE(parsed_doc.address);
+ return HTAnchor_parent((HTAnchor *) foundParent);
+ }
+ return HTAnchor_parent((HTAnchor *) HTAnchor_findAddress_in_adult_table(newdoc));
+}
+
+/* The address has no anchor tag, for sure.
+ */
+static HTParentAnchor0 *HTAnchor_findAddress_in_adult_table(const DocAddress *newdoc)
+{
+ /*
+ * Check whether we have this node.
+ */
+ HASH_TYPE hash;
+ HTList *adults;
+ HTList *grownups;
+ HTParentAnchor0 *foundAnchor;
+ BOOL need_extra_info = (BOOL) (newdoc->post_data ||
+ newdoc->post_content_type ||
+ newdoc->bookmark ||
+ newdoc->isHEAD ||
+ newdoc->safe);
+
+ /*
+ * We need not free adult_table[] atexit - it should be perfectly empty
+ * after free'ing all HText's. (There is an error if it is not empty at
+ * exit). -LP
+ */
+
+ /*
+ * Select list from hash table,
+ */
+ hash = anchor_hash(newdoc->address);
+ adults = &(adult_table[hash]);
+
+ /*
+ * Search list for anchor.
+ */
+ grownups = adults;
+ while (NULL != (foundAnchor =
+ (HTParentAnchor0 *) HTList_nextObject(grownups))) {
+ if (HTSEquivalent(foundAnchor->address, newdoc->address) &&
+
+ ((!foundAnchor->info && !need_extra_info) ||
+ (foundAnchor->info &&
+ HTBEquivalent(foundAnchor->info->post_data, newdoc->post_data) &&
+ foundAnchor->info->isHEAD == newdoc->isHEAD))) {
+ CTRACE((tfp, "Anchor %p with address `%s' already exists.\n",
+ (void *) foundAnchor, newdoc->address));
+ return foundAnchor;
+ }
+ }
+
+ /*
+ * Node not found: create new anchor.
+ */
+ foundAnchor = HTParentAnchor0_new(newdoc->address, hash);
+ CTRACE((tfp, "New anchor %p has hash %d and address `%s'\n",
+ (void *) foundAnchor, hash, newdoc->address));
+
+ if (need_extra_info) {
+ /* rare case, create a big structure */
+ HTParentAnchor *p = HTParentAnchor_new(foundAnchor);
+
+ if (newdoc->post_data)
+ BStrCopy(p->post_data, newdoc->post_data);
+ if (newdoc->post_content_type)
+ StrAllocCopy(p->post_content_type,
+ newdoc->post_content_type);
+ if (newdoc->bookmark)
+ StrAllocCopy(p->bookmark, newdoc->bookmark);
+ p->isHEAD = newdoc->isHEAD;
+ p->safe = newdoc->safe;
+ }
+ HTList_linkObject(adults, foundAnchor, &foundAnchor->_add_adult);
+
+ return foundAnchor;
+}
+
+/* Create new or find old named anchor - simple form
+ * -------------------------------------------------
+ *
+ * Like HTAnchor_findAddress, but simpler to use for simple cases.
+ * No post data etc. can be supplied. - kw
+ */
+HTParentAnchor *HTAnchor_findSimpleAddress(const char *url)
+{
+ DocAddress urldoc;
+
+ urldoc.address = DeConst(url); /* ignore warning, it IS treated like const - kw */
+ urldoc.post_data = NULL;
+ urldoc.post_content_type = NULL;
+ urldoc.bookmark = NULL;
+ urldoc.isHEAD = FALSE;
+ urldoc.safe = FALSE;
+ return HTAnchor_findAddress(&urldoc);
+}
+
+/* Link me Anchor to another given one
+ * -------------------------------------
+ */
+static BOOL HTAnchor_link(HTChildAnchor *child,
+ HTAnchor * destination,
+ HTLinkType *type)
+{
+ if (!(child && destination))
+ return (NO); /* Can't link to/from non-existing anchor */
+
+ CTRACE((tfp, "Linking child %p to anchor %p\n", (void *) child, (void *) destination));
+ if (child->dest) {
+ CTRACE((tfp, "*** child anchor already has destination, exiting!\n"));
+ return (NO);
+ }
+
+ child->dest = destination;
+ child->type = type;
+
+ if (child->parent != destination->parent)
+ /* link only foreign children */
+ HTList_linkObject(&destination->parent->sources, child, &child->_add_sources);
+
+ return (YES); /* Success */
+}
+
+/* Delete an anchor and possibly related things (auto garbage collection)
+ * --------------------------------------------
+ *
+ * The anchor is only deleted if the corresponding document is not loaded.
+ * All outgoing links from children are deleted, and children are
+ * removed from the sources lists of theirs targets.
+ * We also try to delete the targets whose documents are not loaded.
+ * If this anchor's sources list is empty, we delete it and its children.
+ */
+
+/*
+ * Recursively try to delete destination anchor of this child.
+ * In any event, this will tell destination anchor that we
+ * no longer consider it a destination.
+ */
+static void deleteLinks(HTChildAnchor *me)
+{
+ /*
+ * Unregister me with our destination anchor's parent.
+ */
+ if (me->dest) {
+ HTParentAnchor0 *parent = me->dest->parent;
+
+ /*
+ * Start. Set the dest pointer to zero.
+ */
+ me->dest = NULL;
+
+ /*
+ * Remove me from the parent's sources so that the parent knows one
+ * less anchor is its dest.
+ */
+ if ((me->parent != parent) && !HTList_isEmpty(&parent->sources)) {
+ /*
+ * Really should only need to deregister once.
+ */
+ HTList_unlinkObject(&parent->sources, (void *) me);
+ }
+
+ /*
+ * Recursive call. Test here to avoid calling overhead. Don't delete
+ * if document is loaded or being loaded.
+ */
+ if ((me->parent != parent) &&
+ parent != NULL &&
+ !parent->underway &&
+ (!parent->info || !parent->info->document)) {
+ HTAnchor_delete(parent);
+ }
+
+ /*
+ * At this point, we haven't a destination. Set it to be so. Leave
+ * the HTAtom pointed to by type up to other code to handle (reusable,
+ * near static).
+ */
+ me->type = NULL;
+ }
+}
+
+static void HTParentAnchor_free(HTParentAnchor *me);
+
+BOOL HTAnchor_delete(HTParentAnchor0 *me)
+{
+ /*
+ * Memory leaks fixed.
+ * 05-27-94 Lynx 2-3-1 Garrett Arch Blythe
+ */
+ HTBTElement *ele;
+ HTChildAnchor *child;
+
+ /*
+ * Do nothing if nothing to do.
+ */
+ if (!me) {
+ return (NO);
+ }
+
+ /*
+ * Don't delete if document is loaded or being loaded.
+ */
+ if (me->underway || (me->info && me->info->document)) {
+ return (NO);
+ }
+
+ /*
+ * Mark ourselves busy, so that recursive calls of this function on this
+ * HTParentAnchor0 will not free it from under our feet. - kw
+ */
+ me->underway = TRUE;
+
+ {
+ /*
+ * Delete all outgoing links from named children. Do not delete named
+ * children itself (may have incoming links).
+ */
+ if (me->children) {
+ ele = HTBTree_next(me->children, NULL);
+ while (ele != NULL) {
+ child = (HTChildAnchor *) HTBTree_object(ele);
+ if (child->dest)
+ deleteLinks(child);
+ ele = HTBTree_next(me->children, ele);
+ }
+ }
+ }
+ me->underway = FALSE;
+
+ /*
+ * There are still incoming links to this one (we are the
+ * destination of another anchor).
+ */
+ if (!HTList_isEmpty(&me->sources)) {
+ /*
+ * Can't delete parent, still have sources.
+ */
+ return (NO);
+ }
+
+ /*
+ * No more incoming and outgoing links : kill everything First, delete
+ * named children.
+ */
+ if (me->children) {
+ ele = HTBTree_next(me->children, NULL);
+ while (ele != NULL) {
+ child = (HTChildAnchor *) HTBTree_object(ele);
+ FREE(child->tag);
+ FREE(child);
+ ele = HTBTree_next(me->children, ele);
+ }
+ HTBTree_free(me->children);
+ }
+
+ /*
+ * Delete the ParentAnchor, if any. (Document was already deleted).
+ */
+ if (me->info) {
+ HTParentAnchor_free(me->info);
+ FREE(me->info);
+ }
+
+ /*
+ * Remove ourselves from the hash table's list.
+ */
+ HTList_unlinkObject(&(adult_table[me->adult_hash]), (void *) me);
+
+ /*
+ * Free the address.
+ */
+ FREE(me->address);
+
+ /*
+ * Finally, kill the parent anchor passed in.
+ */
+ FREE(me);
+
+ return (YES);
+}
+
+/*
+ * Unnamed children (children_notag) have no sense without HText - delete them
+ * and their links if we are about to free HText. Document currently exists.
+ * Called within HText_free().
+ */
+void HTAnchor_delete_links(HTParentAnchor *me)
+{
+ HTList *cur;
+ HTChildAnchor *child;
+
+ /*
+ * Do nothing if nothing to do.
+ */
+ if (!me || !me->document) {
+ return;
+ }
+
+ /*
+ * Mark ourselves busy, so that recursive calls on this HTParentAnchor0
+ * will not free it from under our feet. - kw
+ */
+ me->parent->underway = TRUE;
+
+ /*
+ * Delete all outgoing links from unnamed children.
+ */
+ if (!HTList_isEmpty(&me->children_notag)) {
+ cur = &me->children_notag;
+ while ((child =
+ (HTChildAnchor *) HTList_unlinkLastObject(cur)) != 0) {
+ deleteLinks(child);
+ /* child allocated in HText pool, HText_free() will free it later */
+ }
+ }
+ me->parent->underway = FALSE;
+}
+
+static void HTParentAnchor_free(HTParentAnchor *me)
+{
+ /*
+ * Delete the methods list.
+ */
+ if (me->methods) {
+ /*
+ * Leave what the methods point to up in memory for other code (near
+ * static stuff).
+ */
+ HTList_delete(me->methods);
+ me->methods = NULL;
+ }
+
+ /*
+ * Free up all allocated members.
+ */
+ FREE(me->charset);
+ FREE(me->isIndexAction);
+ FREE(me->isIndexPrompt);
+ FREE(me->title);
+ FREE(me->physical);
+ BStrFree(me->post_data);
+ FREE(me->post_content_type);
+ FREE(me->bookmark);
+ FREE(me->owner);
+ FREE(me->RevTitle);
+ FREE(me->citehost);
+#ifdef USE_SOURCE_CACHE
+ HTAnchor_clearSourceCache(me);
+#endif
+ if (me->FileCache) {
+ FILE *fd;
+
+ if ((fd = fopen(me->FileCache, "r")) != NULL) {
+ fclose(fd);
+ (void) remove(me->FileCache);
+ }
+ FREE(me->FileCache);
+ }
+ FREE(me->SugFname);
+ FREE(me->cache_control);
+ HTChunkClear(&(me->http_headers));
+ FREE(me->content_type_params);
+ FREE(me->content_type);
+ FREE(me->content_language);
+ FREE(me->content_encoding);
+ FREE(me->content_base);
+ FREE(me->content_disposition);
+ FREE(me->content_location);
+ FREE(me->content_md5);
+ FREE(me->message_id);
+ FREE(me->subject);
+ FREE(me->date);
+ FREE(me->expires);
+
+ FREE(me->last_modified);
+ FREE(me->ETag);
+ FREE(me->server);
+#ifdef USE_COLOR_STYLE
+ FREE(me->style);
+#endif
+
+ /*
+ * Original code wanted a way to clean out the HTFormat if no longer needed
+ * (ref count?). I'll leave it alone since those HTAtom objects are a
+ * little harder to know where they are being referenced all at one time.
+ * (near static)
+ */
+
+ FREE(me->UCStages);
+ ImageMapList_free(me->imaps);
+}
+
+#ifdef USE_SOURCE_CACHE
+void HTAnchor_clearSourceCache(HTParentAnchor *me)
+{
+ /*
+ * Clean up the source cache, if any.
+ */
+ if (me->source_cache_file) {
+ CTRACE((tfp, "SourceCache: Removing file %s\n",
+ me->source_cache_file));
+ (void) LYRemoveTemp(me->source_cache_file);
+ FREE(me->source_cache_file);
+ }
+ if (me->source_cache_chunk) {
+ CTRACE((tfp, "SourceCache: Removing memory chunk %p\n",
+ (void *) me->source_cache_chunk));
+ HTChunkFree(me->source_cache_chunk);
+ me->source_cache_chunk = NULL;
+ }
+}
+#endif /* USE_SOURCE_CACHE */
+
+/* Data access functions
+ * ---------------------
+ */
+HTParentAnchor *HTAnchor_parent(HTAnchor * me)
+{
+ if (!me)
+ return NULL;
+
+ if (me->parent->info)
+ return me->parent->info;
+
+ /* else: create a new structure */
+ return HTParentAnchor_new(me->parent);
+}
+
+void HTAnchor_setDocument(HTParentAnchor *me,
+ HyperDoc *doc)
+{
+ if (me)
+ me->document = doc;
+}
+
+HyperDoc *HTAnchor_document(HTParentAnchor *me)
+{
+ return (me ? me->document : NULL);
+}
+
+char *HTAnchor_address(HTAnchor * me)
+{
+ char *addr = NULL;
+
+ if (me) {
+ if (((HTParentAnchor0 *) me == me->parent) ||
+ ((HTParentAnchor *) me == me->parent->info) ||
+ !((HTChildAnchor *) me)->tag) { /* it's an adult or no tag */
+ StrAllocCopy(addr, me->parent->address);
+ } else { /* it's a named child */
+ HTSprintf0(&addr, "%s#%s",
+ me->parent->address, ((HTChildAnchor *) me)->tag);
+ }
+ }
+ return (addr);
+}
+
+char *HTAnchor_short_address(HTAnchor * me)
+{
+ const char chop[] = "file://localhost/";
+ char *addr = HTAnchor_address(me);
+
+ if (!strncmp(addr, chop, sizeof(chop) - 1)) {
+ char *a = addr + 7;
+ char *b = addr + sizeof(chop) - 2;
+
+ while ((*a++ = *b++) != '\0') {
+ ;
+ }
+ }
+ return addr;
+}
+
+void HTAnchor_setFormat(HTParentAnchor *me,
+ HTFormat form)
+{
+ if (me)
+ me->format = form;
+}
+
+HTFormat HTAnchor_format(HTParentAnchor *me)
+{
+ return (me ? me->format : NULL);
+}
+
+void HTAnchor_setIndex(HTParentAnchor *me,
+ const char *address)
+{
+ if (me) {
+ me->isIndex = YES;
+ StrAllocCopy(me->isIndexAction, address);
+ }
+}
+
+void HTAnchor_setPrompt(HTParentAnchor *me,
+ const char *prompt)
+{
+ if (me) {
+ StrAllocCopy(me->isIndexPrompt, prompt);
+ }
+}
+
+BOOL HTAnchor_isIndex(HTParentAnchor *me)
+{
+ return (BOOL) (me
+ ? me->isIndex
+ : NO);
+}
+
+/* Whether Anchor has been designated as an ISMAP link
+ * (normally by presence of an ISMAP attribute on A or IMG) - KW
+ */
+BOOL HTAnchor_isISMAPScript(HTAnchor * me)
+{
+ return (BOOL) ((me && me->parent->info)
+ ? me->parent->info->isISMAPScript
+ : NO);
+}
+
+#if defined(USE_COLOR_STYLE)
+/* Style handling.
+*/
+const char *HTAnchor_style(HTParentAnchor *me)
+{
+ return (me ? me->style : NULL);
+}
+
+void HTAnchor_setStyle(HTParentAnchor *me,
+ const char *style)
+{
+ if (me) {
+ StrAllocCopy(me->style, style);
+ }
+}
+#endif
+
+/* Title handling.
+*/
+const char *HTAnchor_title(HTParentAnchor *me)
+{
+ return (me ? me->title : NULL);
+}
+
+void HTAnchor_setTitle(HTParentAnchor *me,
+ const char *title)
+{
+ int i;
+
+ if (me) {
+ if (title) {
+ StrAllocCopy(me->title, title);
+ for (i = 0; me->title[i]; i++) {
+ if (UCH(me->title[i]) == 1 ||
+ UCH(me->title[i]) == 2) {
+ me->title[i] = ' ';
+ }
+ }
+ } else {
+ CTRACE((tfp, "HTAnchor_setTitle: New title is NULL! "));
+ if (me->title) {
+ CTRACE((tfp, "Old title was \"%s\".\n", me->title));
+ FREE(me->title);
+ } else {
+ CTRACE((tfp, "Old title was NULL.\n"));
+ }
+ }
+ }
+}
+
+void HTAnchor_appendTitle(HTParentAnchor *me,
+ const char *title)
+{
+ int i;
+
+ if (me) {
+ StrAllocCat(me->title, title);
+ for (i = 0; me->title[i]; i++) {
+ if (UCH(me->title[i]) == 1 ||
+ UCH(me->title[i]) == 2) {
+ me->title[i] = ' ';
+ }
+ }
+ }
+}
+
+/* Bookmark handling.
+*/
+const char *HTAnchor_bookmark(HTParentAnchor *me)
+{
+ return (me ? me->bookmark : NULL);
+}
+
+void HTAnchor_setBookmark(HTParentAnchor *me,
+ const char *bookmark)
+{
+ if (me)
+ StrAllocCopy(me->bookmark, bookmark);
+}
+
+/* Owner handling.
+*/
+const char *HTAnchor_owner(HTParentAnchor *me)
+{
+ return (me ? me->owner : NULL);
+}
+
+void HTAnchor_setOwner(HTParentAnchor *me,
+ const char *owner)
+{
+ if (me) {
+ StrAllocCopy(me->owner, owner);
+ }
+}
+
+/* TITLE handling in LINKs with REV="made" or REV="owner". - FM
+*/
+const char *HTAnchor_RevTitle(HTParentAnchor *me)
+{
+ return (me ? me->RevTitle : NULL);
+}
+
+void HTAnchor_setRevTitle(HTParentAnchor *me,
+ const char *title)
+{
+ int i;
+
+ if (me) {
+ StrAllocCopy(me->RevTitle, title);
+ for (i = 0; me->RevTitle[i]; i++) {
+ if (UCH(me->RevTitle[i]) == 1 ||
+ UCH(me->RevTitle[i]) == 2) {
+ me->RevTitle[i] = ' ';
+ }
+ }
+ }
+}
+
+#ifndef DISABLE_BIBP
+/* Citehost for bibp links from LINKs with REL="citehost". - RDC
+*/
+const char *HTAnchor_citehost(HTParentAnchor *me)
+{
+ return (me ? me->citehost : NULL);
+}
+
+void HTAnchor_setCitehost(HTParentAnchor *me,
+ const char *citehost)
+{
+ if (me) {
+ StrAllocCopy(me->citehost, citehost);
+ }
+}
+#endif /* !DISABLE_BIBP */
+
+/* Suggested filename handling. - FM
+ * (will be loaded if we had a Content-Disposition
+ * header or META element with filename=name.suffix)
+ */
+const char *HTAnchor_SugFname(HTParentAnchor *me)
+{
+ return (me ? me->SugFname : NULL);
+}
+
+/* HTTP Headers.
+*/
+const char *HTAnchor_http_headers(HTParentAnchor *me)
+{
+ return (me ? me->http_headers.data : NULL);
+}
+
+/* Content-Type handling (parameter list).
+*/
+const char *HTAnchor_content_type_params(HTParentAnchor *me)
+{
+ return (me ? me->content_type_params : NULL);
+}
+
+/* Content-Encoding handling. - FM
+ * (will be loaded if we had a Content-Encoding
+ * header.)
+ */
+const char *HTAnchor_content_encoding(HTParentAnchor *me)
+{
+ return (me ? me->content_encoding : NULL);
+}
+
+/* Content-Type handling. - FM
+*/
+const char *HTAnchor_content_type(HTParentAnchor *me)
+{
+ return (me ? me->content_type : NULL);
+}
+
+/* Last-Modified header handling. - FM
+*/
+const char *HTAnchor_last_modified(HTParentAnchor *me)
+{
+ return (me ? me->last_modified : NULL);
+}
+
+/* Date header handling. - FM
+*/
+const char *HTAnchor_date(HTParentAnchor *me)
+{
+ return (me ? me->date : NULL);
+}
+
+/* Server header handling. - FM
+*/
+const char *HTAnchor_server(HTParentAnchor *me)
+{
+ return (me ? me->server : NULL);
+}
+
+/* Safe header handling. - FM
+*/
+BOOL HTAnchor_safe(HTParentAnchor *me)
+{
+ return (BOOL) (me ? me->safe : FALSE);
+}
+
+/* Content-Base header handling. - FM
+*/
+const char *HTAnchor_content_base(HTParentAnchor *me)
+{
+ return (me ? me->content_base : NULL);
+}
+
+/* Content-Location header handling. - FM
+*/
+const char *HTAnchor_content_location(HTParentAnchor *me)
+{
+ return (me ? me->content_location : NULL);
+}
+
+/* Message-ID, used for mail replies - kw
+*/
+const char *HTAnchor_messageID(HTParentAnchor *me)
+{
+ return (me ? me->message_id : NULL);
+}
+
+BOOL HTAnchor_setMessageID(HTParentAnchor *me,
+ const char *messageid)
+{
+ if (!(me && messageid && *messageid)) {
+ return FALSE;
+ }
+ StrAllocCopy(me->message_id, messageid);
+ return TRUE;
+}
+
+/* Subject, used for mail replies - kw
+*/
+const char *HTAnchor_subject(HTParentAnchor *me)
+{
+ return (me ? me->subject : NULL);
+}
+
+BOOL HTAnchor_setSubject(HTParentAnchor *me,
+ const char *subject)
+{
+ if (!(me && subject && *subject)) {
+ return FALSE;
+ }
+ StrAllocCopy(me->subject, subject);
+ return TRUE;
+}
+
+/* Manipulation of links
+ * ---------------------
+ */
+HTAnchor *HTAnchor_followLink(HTChildAnchor *me)
+{
+ return (me->dest);
+}
+
+HTAnchor *HTAnchor_followTypedLink(HTChildAnchor *me,
+ HTLinkType *type)
+{
+ if (me->type == type)
+ return (me->dest);
+ return (NULL); /* No link of me type */
+}
+
+/* Methods List
+ * ------------
+ */
+HTList *HTAnchor_methods(HTParentAnchor *me)
+{
+ if (!me->methods) {
+ me->methods = HTList_new();
+ }
+ return (me->methods);
+}
+
+/* Protocol
+ * --------
+ */
+void *HTAnchor_protocol(HTParentAnchor *me)
+{
+ return (me->protocol);
+}
+
+void HTAnchor_setProtocol(HTParentAnchor *me,
+ void *protocol)
+{
+ me->protocol = protocol;
+}
+
+/* Physical Address
+ * ----------------
+ */
+char *HTAnchor_physical(HTParentAnchor *me)
+{
+ return (me->physical);
+}
+
+void HTAnchor_setPhysical(HTParentAnchor *me,
+ char *physical)
+{
+ if (me) {
+ StrAllocCopy(me->physical, physical);
+ }
+}
+
+#ifdef DEBUG
+static void show_stages(HTParentAnchor *me, const char *tag, int which_stage)
+{
+ int j;
+
+ CTRACE((tfp, "Stages %s*%s", NonNull(me->charset), tag));
+ for (j = 0; j < UCT_STAGEMAX; j++) {
+ CTRACE((tfp, " "));
+ if (j == which_stage)
+ CTRACE((tfp, "("));
+ CTRACE((tfp, "%d:%d:%s",
+ j,
+ me->UCStages->s[j].LYhndl,
+ NonNull(me->UCStages->s[j].C.MIMEname)));
+ if (j == which_stage)
+ CTRACE((tfp, ")"));
+ }
+ CTRACE((tfp, "\n"));
+}
+#else
+#define show_stages(me,tag,which_stage) /* nothing */
+#endif
+
+/*
+ * We store charset info in the HTParentAnchor object, for several
+ * "stages". (See UCDefs.h)
+ * A stream method is supposed to know what stage in the model it is.
+ *
+ * General model MIME -> parser -> structured -> HText
+ * e.g., text/html
+ * from HTTP: HTMIME.c -> SGML.c -> HTML.c -> GridText.c
+ * text/plain
+ * from file: HTFile.c -> HTPlain.c -> GridText.c
+ *
+ * The lock/set_by is used to lock e.g. a charset set by an explicit
+ * HTTP MIME header against overriding by a HTML META tag - the MIME
+ * header has higher priority. Defaults (from -assume_.. options etc.)
+ * will not override charset explicitly given by server.
+ *
+ * Some advantages of keeping this in the HTAnchor:
+ * - Global variables are bad.
+ * - Can remember a charset given by META tag when toggling to SOURCE view.
+ * - Can remember a charset given by <A CHARSET=...> href in another doc.
+ *
+ * We don't modify the HTParentAnchor's charset element
+ * here, that one will only be set when explicitly given.
+ */
+LYUCcharset *HTAnchor_getUCInfoStage(HTParentAnchor *me,
+ int which_stage)
+{
+ LYUCcharset *result = NULL;
+
+ if (me) {
+ if (!me->UCStages) {
+ int i;
+ int chndl = UCLYhndl_for_unspec; /* always >= 0 */
+ UCAnchorInfo *stages = typecalloc(UCAnchorInfo);
+
+ if (stages == NULL)
+ outofmem(__FILE__, "HTAnchor_getUCInfoStage");
+
+ for (i = 0; i < UCT_STAGEMAX; i++) {
+ stages->s[i].C.MIMEname = "";
+ stages->s[i].LYhndl = -1;
+ }
+ if (me->charset) {
+ chndl = UCGetLYhndl_byMIME(me->charset);
+ if (chndl < 0)
+ chndl = UCLYhndl_for_unrec;
+ if (chndl < 0)
+ /*
+ * UCLYhndl_for_unrec not defined :-(
+ * fallback to UCLYhndl_for_unspec which always valid.
+ */
+ chndl = UCLYhndl_for_unspec; /* always >= 0 */
+ }
+ MemCpy(&stages->s[UCT_STAGE_MIME].C, &LYCharSet_UC[chndl],
+ sizeof(LYUCcharset));
+
+ stages->s[UCT_STAGE_MIME].lock = UCT_SETBY_DEFAULT;
+ stages->s[UCT_STAGE_MIME].LYhndl = chndl;
+ me->UCStages = stages;
+ }
+ result = (&me->UCStages->s[which_stage].C);
+ show_stages(me, "_getUCInfoStage", which_stage);
+ }
+ return (result);
+}
+
+int HTAnchor_getUCLYhndl(HTParentAnchor *me,
+ int which_stage)
+{
+ if (me) {
+ if (!me->UCStages) {
+ /*
+ * This will allocate and initialize, if not yet done.
+ */
+ (void) HTAnchor_getUCInfoStage(me, which_stage);
+ }
+ if (me->UCStages->s[which_stage].lock > UCT_SETBY_NONE) {
+ return (me->UCStages->s[which_stage].LYhndl);
+ }
+ }
+ return (-1);
+}
+
+#ifdef CAN_SWITCH_DISPLAY_CHARSET
+static void setup_switch_display_charset(HTParentAnchor *me, int h)
+{
+ if (!Switch_Display_Charset(h, SWITCH_DISPLAY_CHARSET_MAYBE))
+ return;
+ HTAnchor_setUCInfoStage(me, current_char_set,
+ UCT_STAGE_HTEXT, UCT_SETBY_MIME); /* highest priority! */
+ HTAnchor_setUCInfoStage(me, current_char_set,
+ UCT_STAGE_STRUCTURED, UCT_SETBY_MIME); /* highest priority! */
+ CTRACE((tfp,
+ "changing UCInfoStage: HTEXT/STRUCTURED stages charset='%s'.\n",
+ LYCharSet_UC[current_char_set].MIMEname));
+}
+#endif
+
+LYUCcharset *HTAnchor_setUCInfoStage(HTParentAnchor *me,
+ int LYhndl,
+ int which_stage,
+ int set_by)
+{
+ if (me) {
+ /*
+ * This will allocate and initialize, if not yet done.
+ */
+ LYUCcharset *p = HTAnchor_getUCInfoStage(me, which_stage);
+
+ /*
+ * Can we override?
+ */
+ if (set_by >= me->UCStages->s[which_stage].lock) {
+#ifdef CAN_SWITCH_DISPLAY_CHARSET
+ int ohandle = me->UCStages->s[which_stage].LYhndl;
+#endif
+ me->UCStages->s[which_stage].lock = set_by;
+ me->UCStages->s[which_stage].LYhndl = LYhndl;
+ if (LYhndl >= 0) {
+ MemCpy(p, &LYCharSet_UC[LYhndl], sizeof(LYUCcharset));
+
+#ifdef CAN_SWITCH_DISPLAY_CHARSET
+ /* Allow a switch to a more suitable display charset */
+ if (LYhndl != ohandle && which_stage == UCT_STAGE_PARSER)
+ setup_switch_display_charset(me, LYhndl);
+#endif
+ } else {
+ p->UChndl = -1;
+ }
+ show_stages(me, "_setUCInfoStage", which_stage);
+ return (p);
+ }
+ }
+ return (NULL);
+}
+
+LYUCcharset *HTAnchor_resetUCInfoStage(HTParentAnchor *me,
+ int LYhndl,
+ int which_stage,
+ int set_by)
+{
+ LYUCcharset *result = NULL;
+ int ohandle;
+
+ if (me && me->UCStages) {
+ me->UCStages->s[which_stage].lock = set_by;
+ ohandle = me->UCStages->s[which_stage].LYhndl;
+ me->UCStages->s[which_stage].LYhndl = LYhndl;
+#ifdef CAN_SWITCH_DISPLAY_CHARSET
+ /* Allow a switch to a more suitable display charset */
+ if (LYhndl >= 0 && LYhndl != ohandle
+ && which_stage == UCT_STAGE_PARSER)
+ setup_switch_display_charset(me, LYhndl);
+#else
+ (void) ohandle;
+#endif
+ show_stages(me, "_resetUCInfoStage", which_stage);
+ result = (&me->UCStages->s[which_stage].C);
+ }
+ return result;
+}
+
+/*
+ * A set_by of (-1) means use the lock value from the from_stage.
+ */
+LYUCcharset *HTAnchor_copyUCInfoStage(HTParentAnchor *me,
+ int to_stage,
+ int from_stage,
+ int set_by)
+{
+ if (me) {
+ /*
+ * This will allocate and initialize, if not yet done.
+ */
+ LYUCcharset *p_from = HTAnchor_getUCInfoStage(me, from_stage);
+ LYUCcharset *p_to = HTAnchor_getUCInfoStage(me, to_stage);
+
+ /*
+ * Can we override?
+ */
+ if (set_by == -1)
+ set_by = me->UCStages->s[from_stage].lock;
+ if (set_by == UCT_SETBY_NONE)
+ set_by = UCT_SETBY_DEFAULT;
+ if (set_by >= me->UCStages->s[to_stage].lock) {
+#ifdef CAN_SWITCH_DISPLAY_CHARSET
+ int ohandle = me->UCStages->s[to_stage].LYhndl;
+#endif
+ me->UCStages->s[to_stage].lock = set_by;
+ me->UCStages->s[to_stage].LYhndl =
+ me->UCStages->s[from_stage].LYhndl;
+#ifdef CAN_SWITCH_DISPLAY_CHARSET
+ /* Allow a switch to a more suitable display charset */
+ if (me->UCStages->s[to_stage].LYhndl >= 0
+ && me->UCStages->s[to_stage].LYhndl != ohandle
+ && to_stage == UCT_STAGE_PARSER)
+ setup_switch_display_charset(me,
+ me->UCStages->s[to_stage].LYhndl);
+#endif
+ if (p_to != p_from)
+ MemCpy(p_to, p_from, sizeof(LYUCcharset));
+
+ return (p_to);
+ }
+ }
+ return (NULL);
+}
diff --git a/WWW/Library/Implementation/HTAnchor.h b/WWW/Library/Implementation/HTAnchor.h
new file mode 100644
index 0000000..3b1e6e6
--- /dev/null
+++ b/WWW/Library/Implementation/HTAnchor.h
@@ -0,0 +1,412 @@
+/*
+ * $LynxId: HTAnchor.h,v 1.40 2018/03/11 18:43:50 tom Exp $
+ *
+ * Hypertext "Anchor" Object HTAnchor.h
+ * ==========================
+ *
+ * An anchor represents a region of a hypertext document which is linked
+ * to another anchor in the same or a different document.
+ */
+
+#ifndef HTANCHOR_H
+#define HTANCHOR_H
+
+/* Version 0 (TBL) written in Objective-C for the NeXT browser */
+/* Version 1 of 24-Oct-1991 (JFG), written in C, browser-independent */
+
+#include <HTList.h>
+#include <HTBTree.h>
+#include <HTChunk.h>
+#include <HTAtom.h>
+#include <UCDefs.h>
+
+typedef struct _HyperDoc HyperDoc; /* Ready for forward references */
+typedef struct _HTAnchor HTAnchor;
+typedef struct _HTParentAnchor HTParentAnchor;
+typedef struct _HTParentAnchor0 HTParentAnchor0;
+
+#include <HTFormat.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+ struct _HTAnchor {
+ /* Generic anchor */
+ HTParentAnchor0 *parent; /* Parent of this anchor (self for adults) */
+ };
+
+#define HASH_TYPE unsigned short
+
+ struct _HTParentAnchor0 { /* One for adult_table,
+ * generally not used outside HTAnchor.c */
+ /* Common part from the generic anchor structure */
+ HTParentAnchor0 *parent; /* (self) */
+
+ /* ParentAnchor0-specific information */
+ char *address; /* Absolute address of this node */
+ HTParentAnchor *info; /* additional info, allocated on demand */
+
+ HTBTree *children; /* Subanchors <a name="tag">, sorted by tag */
+ HTList sources; /* List of anchors pointing to this, if any */
+
+ HTList _add_adult; /* - just a memory for list entry:) */
+ HASH_TYPE adult_hash; /* adult list number */
+ BOOL underway; /* Document about to be attached to it */
+ };
+
+ /*
+ * Separated from the above to save memory: allocated on demand,
+ * it is nearly 1:1 to HText (well, sometimes without HText...),
+ * available for SGML, HTML, and HText stages.
+ * [being precise, we currently allocate it before HTLoadDocument(),
+ * in HTAnchor_findAddress() and HTAnchor_parent()].
+ */
+ struct _HTParentAnchor {
+ /* Common part from the generic anchor structure */
+ HTParentAnchor0 *parent; /* Parent of this anchor */
+
+ /* ParentAnchor-specific information */
+ HTList children_notag; /* Subanchors <a href=...>, tag is NULL */
+ HyperDoc *document; /* The document within which this is an anchor */
+
+ char *address; /* parent->address, a pointer */
+ bstring *post_data; /* Posting data */
+ char *post_content_type; /* Type of post data */
+ char *bookmark; /* Bookmark filename */
+ HTFormat format; /* Pointer to node format descriptor */
+ char *charset; /* Pointer to character set (kludge, for now */
+ BOOL isIndex; /* Acceptance of a keyword search */
+ char *isIndexAction; /* URL of isIndex server */
+ char *isIndexPrompt; /* Prompt for isIndex query */
+ char *title; /* Title of document */
+ char *owner; /* Owner of document */
+ char *RevTitle; /* TITLE in REV="made" or REV="owner" LINK */
+ char *citehost; /* Citehost from REL="citehost" LINK */
+#ifdef USE_COLOR_STYLE
+ char *style;
+#endif
+
+ HTList *methods; /* Methods available as HTAtoms */
+ void *protocol; /* Protocol object */
+ char *physical; /* Physical address */
+ BOOL isISMAPScript; /* Script for clickable image map */
+ BOOL isHEAD; /* Document is headers from a HEAD request */
+ BOOL safe; /* Safe */
+#ifdef USE_SOURCE_CACHE
+ char *source_cache_file;
+ HTChunk *source_cache_chunk;
+#endif
+ char *FileCache; /* Path to a disk-cached copy (see src/HTFWriter.c) */
+ char *SugFname; /* Suggested filename */
+ char *cache_control; /* Cache-Control */
+ BOOL no_cache; /* Cache-Control, Pragma or META "no-cache"? */
+ BOOL inHEAD; /* HTMIMEConvert is decoding server-headers */
+ BOOL inBASE; /* duplicated from HTStructured (HTML.c/h) */
+ HTChunk http_headers;
+ BOOL no_content_encoding; /* server did not use C-T? */
+ char *content_type_params; /* Content-Type (with parameters if any) */
+ char *content_type; /* Content-Type */
+ char *content_language; /* Content-Language */
+ char *content_encoding; /* Compression algorithm */
+ char *content_base; /* Content-Base */
+ char *content_disposition; /* Content-Disposition */
+ char *content_location; /* Content-Location */
+ char *content_md5; /* Content-MD5 */
+ char *message_id; /* Message-ID */
+ char *subject; /* Subject */
+ off_t header_length; /* length of headers */
+ off_t content_length; /* Content-Length */
+ off_t actual_length; /* actual length may differ */
+ char *date; /* Date */
+ char *expires; /* Expires */
+ char *last_modified; /* Last-Modified */
+ char *ETag; /* ETag (HTTP1.1 cache validator) */
+ char *server; /* Server */
+ UCAnchorInfo *UCStages; /* chartrans stages */
+ HTList *imaps; /* client side image maps */
+ };
+
+ typedef HTAtom HTLinkType;
+
+ typedef struct {
+ /* Common part from the generic anchor structure */
+ HTParentAnchor0 *parent; /* Parent of this anchor */
+
+ /* ChildAnchor-specific information */
+ char *tag; /* #fragment, relative to the parent */
+
+ HTAnchor *dest; /* The anchor to which this leads */
+ HTLinkType *type; /* Semantics of this link */
+
+ HTList _add_children_notag; /* - just a memory for list entry:) */
+ HTList _add_sources; /* - just a memory for list entry:) */
+ } HTChildAnchor;
+
+ /*
+ * DocAddress structure is used for loading an absolute anchor with all
+ * needed information including posting data and post content type.
+ */
+ typedef struct _DocAddress {
+ char *address;
+ bstring *post_data;
+ char *post_content_type;
+ char *bookmark;
+ BOOL isHEAD;
+ BOOL safe;
+ } DocAddress;
+
+ /* "internal" means "within the same document, with certainty". */
+ extern HTLinkType *HTInternalLink;
+
+ /* Create or find a child anchor with a possible link
+ * --------------------------------------------------
+ *
+ * Create new anchor with a given parent and possibly
+ * a name, and possibly a link to a _relatively_ named anchor.
+ * (Code originally in ParseHTML.h)
+ */
+ extern HTChildAnchor *HTAnchor_findChildAndLink(HTParentAnchor *parent, /* May not be 0 */
+ const char *tag, /* May be "" or 0 */
+ const char *href, /* May be "" or 0 */
+ HTLinkType *ltype); /* May be 0 */
+
+ /* Create new or find old parent anchor
+ * ------------------------------------
+ *
+ * This one is for a reference which is found in a document, and might
+ * not be already loaded.
+ * Note: You are not guaranteed a new anchor -- you might get an old one,
+ * like with fonts.
+ */
+ extern HTParentAnchor *HTAnchor_findAddress(const DocAddress *address);
+
+ /* Create new or find old named anchor - simple form
+ * -------------------------------------------------
+ *
+ * Like the previous one, but simpler to use for simple cases.
+ * No post data etc. can be supplied. - kw
+ */
+ extern HTParentAnchor *HTAnchor_findSimpleAddress(const char *url);
+
+ /* Delete an anchor and possibly related things (auto garbage collection)
+ * --------------------------------------------
+ *
+ * The anchor is only deleted if the corresponding document is not loaded.
+ * All outgoing links from children are deleted, and children are
+ * removed from the sources lists of their targets.
+ * We also try to delete the targets whose documents are not loaded.
+ * If this anchor's sources list is empty, we delete it and its children.
+ */
+ extern BOOL HTAnchor_delete(HTParentAnchor0 *me);
+
+ /*
+ * Unnamed children (children_notag) have no sense without HText -
+ * delete them and their links if we are about to free HText.
+ * Document currently exists. Called within HText_free().
+ */
+ extern void HTAnchor_delete_links(HTParentAnchor *me);
+
+#ifdef USE_SOURCE_CACHE
+ extern void HTAnchor_clearSourceCache(HTParentAnchor *me);
+#endif
+
+ /* Data access functions
+ * ---------------------
+ */
+ extern HTParentAnchor *HTAnchor_parent(HTAnchor * me);
+
+ extern void HTAnchor_setDocument(HTParentAnchor *me,
+ HyperDoc *doc);
+
+ extern HyperDoc *HTAnchor_document(HTParentAnchor *me);
+
+ /* Returns the full URI of the anchor, child or parent
+ * as a malloc'd string to be freed by the caller.
+ */
+ extern char *HTAnchor_address(HTAnchor * me);
+
+ extern char *HTAnchor_short_address(HTAnchor * me);
+
+ extern void HTAnchor_setFormat(HTParentAnchor *me,
+ HTFormat form);
+
+ extern HTFormat HTAnchor_format(HTParentAnchor *me);
+
+ extern void HTAnchor_setIndex(HTParentAnchor *me,
+ const char *address);
+
+ extern void HTAnchor_setPrompt(HTParentAnchor *me,
+ const char *prompt);
+
+ extern BOOL HTAnchor_isIndex(HTParentAnchor *me);
+
+ extern BOOL HTAnchor_isISMAPScript(HTAnchor * me);
+
+#if defined(USE_COLOR_STYLE)
+ extern const char *HTAnchor_style(HTParentAnchor *me);
+
+ extern void HTAnchor_setStyle(HTParentAnchor *me,
+ const char *style);
+#endif
+
+ /* Title handling.
+ */
+ extern const char *HTAnchor_title(HTParentAnchor *me);
+
+ extern void HTAnchor_setTitle(HTParentAnchor *me,
+ const char *title);
+
+ extern void HTAnchor_appendTitle(HTParentAnchor *me,
+ const char *title);
+
+ /* Bookmark handling.
+ */
+ extern const char *HTAnchor_bookmark(HTParentAnchor *me);
+
+ extern void HTAnchor_setBookmark(HTParentAnchor *me,
+ const char *bookmark);
+
+ /* Owner handling.
+ */
+ extern const char *HTAnchor_owner(HTParentAnchor *me);
+
+ extern void HTAnchor_setOwner(HTParentAnchor *me,
+ const char *owner);
+
+ /* TITLE handling in LINKs with REV="made" or REV="owner". - FM
+ */
+ extern const char *HTAnchor_RevTitle(HTParentAnchor *me);
+
+ extern void HTAnchor_setRevTitle(HTParentAnchor *me,
+ const char *title);
+
+ /* Citehost for bibp links from LINKs with REL="citehost". - RDC
+ */
+ extern const char *HTAnchor_citehost(HTParentAnchor *me);
+
+ extern void HTAnchor_setCitehost(HTParentAnchor *me,
+ const char *citehost);
+
+ /* Suggested filename handling. - FM
+ * (will be loaded if we had a Content-Disposition
+ * header or META element with filename=name.suffix)
+ */
+ extern const char *HTAnchor_SugFname(HTParentAnchor *me);
+
+ /* HTTP Headers.
+ */
+ extern const char *HTAnchor_http_headers(HTParentAnchor *me);
+
+ /* Content-Type handling (parameter list).
+ */
+ extern const char *HTAnchor_content_type_params(HTParentAnchor *me);
+
+ /* Content-Type handling. - FM
+ */
+ extern const char *HTAnchor_content_type(HTParentAnchor *me);
+
+ /* Content-Encoding handling. - FM
+ * (will be loaded if we had a Content-Encoding
+ * header.)
+ */
+ extern const char *HTAnchor_content_encoding(HTParentAnchor *me);
+
+ /* Last-Modified header handling. - FM
+ */
+ extern const char *HTAnchor_last_modified(HTParentAnchor *me);
+
+ /* Date header handling. - FM
+ */
+ extern const char *HTAnchor_date(HTParentAnchor *me);
+
+ /* Server header handling. - FM
+ */
+ extern const char *HTAnchor_server(HTParentAnchor *me);
+
+ /* Safe header handling. - FM
+ */
+ extern BOOL HTAnchor_safe(HTParentAnchor *me);
+
+ /* Content-Base header handling. - FM
+ */
+ extern const char *HTAnchor_content_base(HTParentAnchor *me);
+
+ /* Content-Location header handling. - FM
+ */
+ extern const char *HTAnchor_content_location(HTParentAnchor *me);
+
+ /* Message-ID, used for mail replies - kw
+ */
+ extern const char *HTAnchor_messageID(HTParentAnchor *me);
+
+ extern BOOL HTAnchor_setMessageID(HTParentAnchor *me,
+ const char *messageid);
+
+ /* Subject, used for mail replies - kw
+ */
+ extern const char *HTAnchor_subject(HTParentAnchor *me);
+
+ extern BOOL HTAnchor_setSubject(HTParentAnchor *me,
+ const char *subject);
+
+ /* Manipulation of links
+ * ---------------------
+ */
+ extern HTAnchor *HTAnchor_followLink(HTChildAnchor *me);
+
+ extern HTAnchor *HTAnchor_followTypedLink(HTChildAnchor *me,
+ HTLinkType *type);
+
+ /* Read and write methods
+ * ----------------------
+ */
+ extern HTList *HTAnchor_methods(HTParentAnchor *me);
+
+ /* Protocol
+ * --------
+ */
+ extern void *HTAnchor_protocol(HTParentAnchor *me);
+
+ extern void HTAnchor_setProtocol(HTParentAnchor *me,
+ void *protocol);
+
+ /* Physical address
+ * ----------------
+ */
+ extern char *HTAnchor_physical(HTParentAnchor *me);
+
+ extern void HTAnchor_setPhysical(HTParentAnchor *me,
+ char *protocol);
+
+ extern LYUCcharset *HTAnchor_getUCInfoStage(HTParentAnchor *me,
+ int which_stage);
+
+ extern int HTAnchor_getUCLYhndl(HTParentAnchor *me,
+ int which_stage);
+
+ extern LYUCcharset *HTAnchor_setUCInfoStage(HTParentAnchor *me,
+ int LYhndl,
+ int which_stage,
+ int set_by);
+
+ extern LYUCcharset *HTAnchor_setUCInfoStage(HTParentAnchor *me,
+ int LYhndl,
+ int which_stage,
+ int set_by);
+
+ extern LYUCcharset *HTAnchor_resetUCInfoStage(HTParentAnchor *me,
+ int LYhndl,
+ int which_stage,
+ int set_by);
+
+ extern LYUCcharset *HTAnchor_copyUCInfoStage(HTParentAnchor *me,
+ int to_stage,
+ int from_stage,
+ int set_by);
+
+ extern void ImageMapList_free(HTList *list);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* HTANCHOR_H */
diff --git a/WWW/Library/Implementation/HTAssoc.c b/WWW/Library/Implementation/HTAssoc.c
new file mode 100644
index 0000000..831b196
--- /dev/null
+++ b/WWW/Library/Implementation/HTAssoc.c
@@ -0,0 +1,82 @@
+/*
+ * $LynxId: HTAssoc.c,v 1.11 2016/11/24 15:29:50 tom Exp $
+ *
+ * MODULE HTAssoc.c
+ * ASSOCIATION LIST FOR STORING NAME-VALUE PAIRS.
+ * NAMES NOT CASE SENSITIVE, AND ONLY COMMON LENGTH
+ * IS CHECKED (allows abbreviations; well, length is
+ * taken from lookup-up name, so if table contains
+ * a shorter abbrev it is not found).
+ * AUTHORS:
+ * AL Ari Luotonen luotonen@dxcern.cern.ch
+ *
+ * HISTORY:
+ *
+ *
+ * BUGS:
+ *
+ *
+ */
+
+#include <HTUtils.h>
+
+#include <HTAssoc.h>
+
+#include <LYLeaks.h>
+
+HTAssocList *HTAssocList_new(void)
+{
+ return HTList_new();
+}
+
+void HTAssocList_delete(HTAssocList *alist)
+{
+ if (alist) {
+ HTAssocList *cur = alist;
+ HTAssoc *assoc;
+
+ while (NULL != (assoc = (HTAssoc *) HTList_nextObject(cur))) {
+ FREE(assoc->name);
+ FREE(assoc->value);
+ FREE(assoc);
+ }
+ HTList_delete(alist);
+ alist = NULL;
+ }
+}
+
+void HTAssocList_add(HTAssocList *alist,
+ const char *name,
+ const char *value)
+{
+ HTAssoc *assoc;
+
+ if (alist) {
+ if (!(assoc = (HTAssoc *) malloc(sizeof(HTAssoc))))
+ outofmem(__FILE__, "HTAssoc_add");
+
+ assoc->name = NULL;
+ assoc->value = NULL;
+
+ if (name)
+ StrAllocCopy(assoc->name, name);
+ if (value)
+ StrAllocCopy(assoc->value, value);
+ HTList_addObject(alist, (void *) assoc);
+ } else {
+ CTRACE((tfp, "HTAssoc_add: ERROR: assoc list NULL!!\n"));
+ }
+}
+
+char *HTAssocList_lookup(HTAssocList *alist,
+ const char *name)
+{
+ HTAssocList *cur = alist;
+ HTAssoc *assoc;
+
+ while (NULL != (assoc = (HTAssoc *) HTList_nextObject(cur))) {
+ if (!strncasecomp(assoc->name, name, (int) strlen(name)))
+ return assoc->value;
+ }
+ return NULL;
+}
diff --git a/WWW/Library/Implementation/HTAssoc.h b/WWW/Library/Implementation/HTAssoc.h
new file mode 100644
index 0000000..327809c
--- /dev/null
+++ b/WWW/Library/Implementation/HTAssoc.h
@@ -0,0 +1,35 @@
+/* ASSOCIATION LIST FOR STORING NAME-VALUE PAIRS
+
+ Lookups from association list are not case-sensitive.
+
+ */
+
+#ifndef HTASSOC_H
+#define HTASSOC_H
+
+#include <HTList.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+ typedef HTList HTAssocList;
+
+ typedef struct {
+ char *name;
+ char *value;
+ } HTAssoc;
+
+ extern HTAssocList *HTAssocList_new(void);
+ extern void HTAssocList_delete(HTAssocList *alist);
+
+ extern void HTAssocList_add(HTAssocList *alist,
+ const char *name,
+ const char *value);
+
+ extern char *HTAssocList_lookup(HTAssocList *alist,
+ const char *name);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* not HTASSOC_H */
diff --git a/WWW/Library/Implementation/HTAtom.c b/WWW/Library/Implementation/HTAtom.c
new file mode 100644
index 0000000..07eca77
--- /dev/null
+++ b/WWW/Library/Implementation/HTAtom.c
@@ -0,0 +1,107 @@
+/*
+ * $LynxId: HTAtom.c,v 1.22 2018/03/06 09:46:58 tom Exp $
+ *
+ * Atoms: Names to numbers HTAtom.c
+ * =======================
+ *
+ * Atoms are names which are given representative pointer values
+ * so that they can be stored more efficiently, and comparisons
+ * for equality done more efficiently.
+ *
+ * Atoms are kept in a hash table consisting of an array of linked lists.
+ *
+ * Authors:
+ * TBL Tim Berners-Lee, WorldWideWeb project, CERN
+ * (c) Copyright CERN 1991 - See Copyright.html
+ *
+ */
+
+#include <HTUtils.h>
+#include <HTAtom.h>
+#include <HTList.h>
+#include <LYexit.h>
+#include <LYLeaks.h>
+
+#define HASH_SIZE 101 /* Arbitrary prime. Memory/speed tradeoff */
+
+static HTAtom *hash_table[HASH_SIZE];
+static BOOL initialised = NO;
+
+/*
+ * To free off all atoms.
+ */
+#ifdef LY_FIND_LEAKS
+static void free_atoms(void);
+#endif
+
+#define HASH_FUNCTION(cp_hash) ((strlen(cp_hash) * UCH(*cp_hash)) % HASH_SIZE)
+
+HTAtom *HTAtom_for(const char *string)
+{
+ size_t hash;
+ HTAtom *a;
+
+ if (!initialised) {
+ memset(hash_table, 0, sizeof(hash_table));
+ initialised = YES;
+#ifdef LY_FIND_LEAKS
+ atexit(free_atoms);
+#endif
+ }
+
+ hash = HASH_FUNCTION(string);
+
+ for (a = hash_table[hash]; a; a = a->next) {
+ if (0 == strcasecomp(a->name, string)) {
+ return a;
+ }
+ }
+
+ a = (HTAtom *) malloc(sizeof(*a));
+ if (a == NULL)
+ outofmem(__FILE__, "HTAtom_for");
+
+ a->name = (char *) malloc(strlen(string) + 1);
+ if (a->name == NULL)
+ outofmem(__FILE__, "HTAtom_for");
+
+ strcpy(a->name, string);
+ a->next = hash_table[hash];
+ hash_table[hash] = a;
+ return a;
+}
+
+#ifdef LY_FIND_LEAKS
+/*
+ * Purpose: Free off all atoms.
+ * Arguments: void
+ * Return Value: void
+ * Remarks/Portability/Dependencies/Restrictions:
+ * To be used at program exit.
+ * Revision History:
+ * 05-29-94 created Lynx 2-3-1 Garrett Arch Blythe
+ */
+static void free_atoms(void)
+{
+ auto int i_counter;
+ HTAtom *HTAp_freeme;
+
+ /*
+ * Loop through all lists of atoms.
+ */
+ for (i_counter = 0; i_counter < HASH_SIZE; i_counter++) {
+ /*
+ * Loop through the list.
+ */
+ while (hash_table[i_counter] != NULL) {
+ /*
+ * Free off atoms and any members.
+ */
+ HTAp_freeme = hash_table[i_counter];
+ hash_table[i_counter] = HTAp_freeme->next;
+ FREE(HTAp_freeme->name);
+ FREE(HTAp_freeme);
+ }
+ }
+}
+#endif /* LY_FIND_LEAKS */
diff --git a/WWW/Library/Implementation/HTAtom.h b/WWW/Library/Implementation/HTAtom.h
new file mode 100644
index 0000000..0d787bc
--- /dev/null
+++ b/WWW/Library/Implementation/HTAtom.h
@@ -0,0 +1,53 @@
+/* */
+
+/* Atoms: Names to numbers HTAtom.h
+ * =======================
+ *
+ * Atoms are names which are given representative pointer values
+ * so that they can be stored more efficiently, and compaisons
+ * for equality done more efficiently.
+ *
+ * HTAtom_for(string) returns a representative value such that it
+ * will always (within one run of the program) return the same
+ * value for the same given string.
+ *
+ * Authors:
+ * TBL Tim Berners-Lee, WorldWideWeb project, CERN
+ *
+ * (c) Copyright CERN 1991 - See Copyright.html
+ *
+ */
+
+#ifndef HTATOM_H
+#define HTATOM_H
+
+#include <HTList.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+ typedef struct _HTAtom HTAtom;
+
+ struct _HTAtom {
+ HTAtom *next;
+ char *name;
+ }; /* struct _HTAtom */
+
+ extern HTAtom *HTAtom_for(const char *string);
+
+#define HTAtom_name(a) ((a)->name)
+
+/*
+
+The HTFormat type
+
+ We use the HTAtom object for holding representations. This allows faster manipulation
+ (comparison and copying) that if we stayed with strings.
+
+ */
+ typedef HTAtom *HTFormat;
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* HTATOM_H */
diff --git a/WWW/Library/Implementation/HTBTree.c b/WWW/Library/Implementation/HTBTree.c
new file mode 100644
index 0000000..f595bae
--- /dev/null
+++ b/WWW/Library/Implementation/HTBTree.c
@@ -0,0 +1,680 @@
+/* Binary Tree for sorting things
+ * ==============================
+ * Author: Arthur Secret
+ *
+ * 4 March 94: Bug fixed in the balancing procedure
+ *
+ */
+
+#include <HTUtils.h>
+#include <HTBTree.h>
+
+#define MAXIMUM(a,b) ((a)>(b)?(a):(b))
+
+#include <LYLeaks.h>
+
+/*********************************************************
+ * This function returns an HTBTree with memory allocated
+ * for it when given a mean to compare things
+ */
+HTBTree *HTBTree_new(HTComparer comp)
+{
+ HTBTree *tree = typeMalloc(HTBTree);
+
+ if (tree == NULL)
+ outofmem(__FILE__, "HTBTree_new");
+
+ tree->compare = comp;
+ tree->top = NULL;
+
+ return tree;
+}
+
+/*********************************************************
+ * This void will free the memory allocated for one element
+ */
+static void HTBTElement_free(HTBTElement *element)
+{
+ if (element) {
+ if (element->left != NULL)
+ HTBTElement_free(element->left);
+ if (element->right != NULL)
+ HTBTElement_free(element->right);
+ FREE(element);
+ }
+}
+
+/*************************************************************
+ * This void will free the memory allocated for the whole tree
+ */
+void HTBTree_free(HTBTree *tree)
+{
+ HTBTElement_free(tree->top);
+ FREE(tree);
+}
+
+/*********************************************************
+ * This void will free the memory allocated for one element
+ */
+static void HTBTElementAndObject_free(HTBTElement *element)
+{
+ if (element) { /* Just in case nothing was in the tree anyway */
+ if (element->left != NULL)
+ HTBTElementAndObject_free(element->left);
+ if (element->right != NULL)
+ HTBTElementAndObject_free(element->right);
+ FREE(element->object);
+ FREE(element);
+ }
+}
+
+/*************************************************************
+ * This void will free the memory allocated for the whole tree
+ */
+void HTBTreeAndObject_free(HTBTree *tree)
+{
+ HTBTElementAndObject_free(tree->top);
+ FREE(tree);
+}
+
+/*********************************************************************
+ * Returns a pointer to equivalent object in a tree or NULL if none.
+ */
+void *HTBTree_search(HTBTree *tree,
+ void *object)
+{
+ HTBTElement *cur = tree->top;
+ int res;
+
+ while (cur != NULL) {
+ res = tree->compare(object, cur->object);
+
+ if (res == 0)
+ return cur->object;
+ else if (res < 0)
+ cur = cur->left;
+ else if (res > 0)
+ cur = cur->right;
+ }
+ return NULL;
+}
+
+/*********************************************************************
+ * This void is the core of HTBTree.c . It will
+ * 1/ add a new element to the tree at the right place
+ * so that the tree remains sorted
+ * 2/ balance the tree to be as fast as possible when reading it
+ */
+void HTBTree_add(HTBTree *tree,
+ void *object)
+{
+ HTBTElement *father_of_element;
+ HTBTElement *added_element;
+ HTBTElement *forefather_of_element;
+ HTBTElement *father_of_forefather;
+ BOOL father_found, top_found;
+ int depth, depth2, corrections;
+
+ /* father_of_element is a pointer to the structure that is the father of
+ * the new object "object". added_element is a pointer to the structure
+ * that contains or will contain the new object "object".
+ * father_of_forefather and forefather_of_element are pointers that are
+ * used to modify the depths of upper elements, when needed.
+ *
+ * father_found indicates by a value NO when the future father of "object"
+ * is found. top_found indicates by a value NO when, in case of a
+ * difference of depths < 2, the top of the tree is encountered and forbids
+ * any further try to balance the tree. corrections is an integer used to
+ * avoid infinite loops in cases such as:
+ *
+ * 3 3
+ * 4 4
+ * 5 5
+ *
+ * 3 is used here to show that it need not be the top of the tree.
+ */
+
+ /*
+ * 1/ Adding of the element to the binary tree
+ */
+
+ if (tree->top == NULL) {
+ tree->top = typeMalloc(HTBTElement);
+
+ if (tree->top == NULL)
+ outofmem(__FILE__, "HTBTree_add");
+
+ tree->top->up = NULL;
+ tree->top->object = object;
+ tree->top->left = NULL;
+ tree->top->left_depth = 0;
+ tree->top->right = NULL;
+ tree->top->right_depth = 0;
+ } else {
+ father_found = YES;
+ father_of_element = tree->top;
+ added_element = NULL;
+ father_of_forefather = NULL;
+ forefather_of_element = NULL;
+ while (father_found) {
+ int res = tree->compare(object, father_of_element->object);
+
+ if (res < 0) {
+ if (father_of_element->left != NULL)
+ father_of_element = father_of_element->left;
+ else {
+ father_found = NO;
+ father_of_element->left = typeMalloc(HTBTElement);
+
+ if (father_of_element->left == NULL)
+ outofmem(__FILE__, "HTBTree_add");
+
+ added_element = father_of_element->left;
+ added_element->up = father_of_element;
+ added_element->object = object;
+ added_element->left = NULL;
+ added_element->left_depth = 0;
+ added_element->right = NULL;
+ added_element->right_depth = 0;
+ }
+ } else { /* res >= 0 */
+ if (father_of_element->right != NULL) {
+ father_of_element = father_of_element->right;
+ } else {
+ father_found = NO;
+ father_of_element->right = typeMalloc(HTBTElement);
+
+ if (father_of_element->right == NULL)
+ outofmem(__FILE__, "HTBTree_add");
+
+ added_element = father_of_element->right;
+ added_element->up = father_of_element;
+ added_element->object = object;
+ added_element->left = NULL;
+ added_element->left_depth = 0;
+ added_element->right = NULL;
+ added_element->right_depth = 0;
+ }
+ }
+ }
+
+ /*
+ * Changing of all depths that need to be changed
+ */
+ father_of_forefather = father_of_element;
+ forefather_of_element = added_element;
+ do {
+ if (father_of_forefather->left == forefather_of_element) {
+ depth = father_of_forefather->left_depth;
+ father_of_forefather->left_depth = 1
+ + MAXIMUM(forefather_of_element->right_depth,
+ forefather_of_element->left_depth);
+ depth2 = father_of_forefather->left_depth;
+ } else {
+ depth = father_of_forefather->right_depth;
+ father_of_forefather->right_depth = 1
+ + MAXIMUM(forefather_of_element->right_depth,
+ forefather_of_element->left_depth);
+ depth2 = father_of_forefather->right_depth;
+ }
+ forefather_of_element = father_of_forefather;
+ father_of_forefather = father_of_forefather->up;
+ } while ((depth != depth2) && (father_of_forefather != NULL));
+
+ /*
+ * 2/ Balancing the binary tree, if necessary
+ */
+ top_found = YES;
+ corrections = 0;
+ while ((top_found) && (corrections < 7)) {
+ if ((abs(father_of_element->left_depth
+ - father_of_element->right_depth)) < 2) {
+ if (father_of_element->up != NULL)
+ father_of_element = father_of_element->up;
+ else
+ top_found = NO;
+ } else { /* We start the process of balancing */
+
+ corrections = corrections + 1;
+ /*
+ * corrections is an integer used to avoid infinite
+ * loops in cases such as:
+ *
+ * 3 3
+ * 4 4
+ * 5 5
+ *
+ * 3 is used to show that it need not be the top of the tree
+ * But let's avoid these two exceptions anyhow
+ * with the two following conditions (4 March 94 - AS)
+ */
+
+ if (father_of_element->left == NULL) {
+ if ((father_of_element->right != NULL)
+ && (father_of_element->right->right == NULL)
+ && (father_of_element->right->left != NULL)
+ && (father_of_element->right->left->left == NULL)
+ && (father_of_element->right->left->right == NULL)) {
+ corrections = 7;
+ }
+ } else {
+ if ((father_of_element->right == NULL)
+ && (father_of_element->left->left == NULL)
+ && (father_of_element->left->right != NULL)
+ && (father_of_element->left->right->right == NULL)
+ && (father_of_element->left->right->left == NULL)) {
+ corrections = 7;
+ }
+ }
+
+ if ((father_of_element->left != NULL)
+ && (father_of_element->left_depth > father_of_element->right_depth)) {
+ added_element = father_of_element->left;
+ father_of_element->left_depth = added_element->right_depth;
+ added_element->right_depth = 1
+ + MAXIMUM(father_of_element->right_depth,
+ father_of_element->left_depth);
+ if (father_of_element->up != NULL) {
+ /* Bug fixed in March 94 - AS */
+ BOOL first_time;
+
+ father_of_forefather = father_of_element->up;
+ forefather_of_element = added_element;
+ first_time = YES;
+ do {
+ if (father_of_forefather->left
+ == forefather_of_element->up) {
+ depth = father_of_forefather->left_depth;
+ if (first_time) {
+ father_of_forefather->left_depth = 1
+ + MAXIMUM(forefather_of_element->left_depth,
+ forefather_of_element->right_depth);
+ first_time = NO;
+ } else
+ father_of_forefather->left_depth = 1
+ + MAXIMUM(forefather_of_element->up->left_depth,
+ forefather_of_element->up->right_depth);
+
+ depth2 = father_of_forefather->left_depth;
+ } else {
+ depth = father_of_forefather->right_depth;
+ if (first_time) {
+ father_of_forefather->right_depth = 1
+ + MAXIMUM(forefather_of_element->left_depth,
+ forefather_of_element->right_depth);
+ first_time = NO;
+ } else
+ father_of_forefather->right_depth = 1
+ + MAXIMUM(forefather_of_element->up->left_depth,
+ forefather_of_element->up->right_depth);
+ depth2 = father_of_forefather->right_depth;
+ }
+ forefather_of_element = forefather_of_element->up;
+ father_of_forefather = father_of_forefather->up;
+ } while ((depth != depth2) &&
+ (father_of_forefather != NULL));
+ father_of_forefather = father_of_element->up;
+ if (father_of_forefather->left == father_of_element) {
+ /*
+ * 3 3
+ * 4 5
+ * When tree 5 6 becomes 7 4
+ * 7 8 8 6
+ *
+ * 3 is used to show that it may not be the top of the
+ * tree.
+ */
+ father_of_forefather->left = added_element;
+ father_of_element->left = added_element->right;
+ added_element->right = father_of_element;
+ }
+ if (father_of_forefather->right == father_of_element) {
+ /*
+ * 3 3
+ * 4 5
+ * When tree 5 6 becomes 7 4
+ * 7 8 8 6
+ *
+ * 3 is used to show that it may not be the top of the
+ * tree
+ */
+ father_of_forefather->right = added_element;
+ father_of_element->left = added_element->right;
+ added_element->right = father_of_element;
+ }
+ added_element->up = father_of_forefather;
+ } else {
+ /*
+
+ * 1 2
+ * When tree 2 3 becomes 4 1
+ * 4 5 5 3
+ *
+ * 1 is used to show that it is the top of the tree
+ */
+ added_element->up = NULL;
+ father_of_element->left = added_element->right;
+ added_element->right = father_of_element;
+ }
+ father_of_element->up = added_element;
+ if (father_of_element->left != NULL)
+ father_of_element->left->up = father_of_element;
+ } else if (father_of_element->right != NULL) {
+ added_element = father_of_element->right;
+ father_of_element->right_depth = added_element->left_depth;
+ added_element->left_depth = 1 +
+ MAXIMUM(father_of_element->right_depth,
+ father_of_element->left_depth);
+ if (father_of_element->up != NULL)
+ /* Bug fixed in March 94 - AS */
+ {
+ BOOL first_time;
+
+ father_of_forefather = father_of_element->up;
+ forefather_of_element = added_element;
+ first_time = YES;
+ do {
+ if (father_of_forefather->left
+ == forefather_of_element->up) {
+ depth = father_of_forefather->left_depth;
+ if (first_time) {
+ father_of_forefather->left_depth = 1
+ + MAXIMUM(forefather_of_element->left_depth,
+ forefather_of_element->right_depth);
+ first_time = NO;
+ } else
+ father_of_forefather->left_depth = 1
+ + MAXIMUM(forefather_of_element->up->left_depth,
+ forefather_of_element->up->right_depth);
+ depth2 = father_of_forefather->left_depth;
+ } else {
+ depth = father_of_forefather->right_depth;
+ if (first_time) {
+ father_of_forefather->right_depth = 1
+ + MAXIMUM(forefather_of_element->left_depth,
+ forefather_of_element->right_depth);
+ first_time = NO;
+ } else
+ father_of_forefather->right_depth = 1
+ + MAXIMUM(forefather_of_element->up->left_depth,
+ forefather_of_element->up->right_depth);
+ depth2 = father_of_forefather->right_depth;
+ }
+ father_of_forefather = father_of_forefather->up;
+ forefather_of_element = forefather_of_element->up;
+ } while ((depth != depth2) &&
+ (father_of_forefather != NULL));
+ father_of_forefather = father_of_element->up;
+ if (father_of_forefather->left == father_of_element) {
+ /*
+ * 3 3
+ * 4 6
+ * When tree 5 6 becomes 4 8
+ * 7 8 5 7
+ *
+ * 3 is used to show that it may not be the top of the
+ * tree.
+ */
+ father_of_forefather->left = added_element;
+ father_of_element->right = added_element->left;
+ added_element->left = father_of_element;
+ }
+ if (father_of_forefather->right == father_of_element) {
+ /*
+ * 3 3
+ * 4 6
+ * When tree 5 6 becomes 4 8
+ * 7 8 5 7
+ *
+ * 3 is used to show that it may not be the top of the
+ * tree
+ */
+ father_of_forefather->right = added_element;
+ father_of_element->right = added_element->left;
+ added_element->left = father_of_element;
+ }
+ added_element->up = father_of_forefather;
+ } else {
+ /*
+
+ * 1 3
+ * When tree 2 3 becomes 1 5
+ * 4 5 2 4
+ *
+ * 1 is used to show that it is the top of the tree.
+ */
+ added_element->up = NULL;
+ father_of_element->right = added_element->left;
+ added_element->left = father_of_element;
+ }
+ father_of_element->up = added_element;
+ if (father_of_element->right != NULL)
+ father_of_element->right->up = father_of_element;
+ }
+ }
+ }
+ while (father_of_element->up != NULL) {
+ father_of_element = father_of_element->up;
+ }
+ tree->top = father_of_element;
+ }
+}
+
+/*************************************************************************
+ * this function returns a pointer to the leftmost element if ele is NULL,
+ * and to the next object to the right otherwise.
+ * If no elements left, returns a pointer to NULL.
+ */
+HTBTElement *HTBTree_next(HTBTree *tree,
+ HTBTElement *ele)
+{
+ HTBTElement *father_of_element;
+ HTBTElement *father_of_forefather;
+
+ if (ele == NULL) {
+ father_of_element = tree->top;
+ if (father_of_element != NULL)
+ while (father_of_element->left != NULL)
+ father_of_element = father_of_element->left;
+ } else {
+ father_of_element = ele;
+ if (father_of_element->right != NULL) {
+ father_of_element = father_of_element->right;
+ while (father_of_element->left != NULL)
+ father_of_element = father_of_element->left;
+ } else {
+ father_of_forefather = father_of_element->up;
+ while (father_of_forefather &&
+ (father_of_forefather->right == father_of_element)) {
+ father_of_element = father_of_forefather;
+ father_of_forefather = father_of_element->up;
+ }
+ father_of_element = father_of_forefather;
+ }
+ }
+#ifdef BTREE_TRACE
+ /* The option -DBTREE_TRACE will give much more information
+ * about the way the process is running, for debugging matters
+ */
+ if (father_of_element != NULL) {
+ printf("\nObject = %s\t", (char *) father_of_element->object);
+ if (father_of_element->up != NULL)
+ printf("Objet du pere = %s\n",
+ (char *) father_of_element->up->object);
+ else
+ printf("Pas de Pere\n");
+ if (father_of_element->left != NULL)
+ printf("Objet du fils gauche = %s\t",
+ (char *) father_of_element->left->object);
+ else
+ printf("Pas de fils gauche\t");
+ if (father_of_element->right != NULL)
+ printf("Objet du fils droit = %s\n",
+ (char *) father_of_element->right->object);
+ else
+ printf("Pas de fils droit\n");
+ printf("Profondeur gauche = %d\t", father_of_element->left_depth);
+ printf("Profondeur droite = %d\n", father_of_element->right_depth);
+ printf(" **************\n");
+ }
+#endif
+ return father_of_element;
+}
+
+#ifdef TEST
+/*****************************************************
+ * This is just a test to show how to handle HTBTree.c
+ */
+main()
+{
+ HTBTree *tree;
+ HTBTElement *next_element;
+
+ tree = HTBTree_new((HTComparer) strcasecomp);
+ HTBTree_add(tree, "hypertext");
+ HTBTree_add(tree, "Addressing");
+ HTBTree_add(tree, "X11");
+ HTBTree_add(tree, "Tools");
+ HTBTree_add(tree, "Proposal.wn");
+ HTBTree_add(tree, "Protocols");
+ HTBTree_add(tree, "NeXT");
+ HTBTree_add(tree, "Daemon");
+ HTBTree_add(tree, "Test");
+ HTBTree_add(tree, "Administration");
+ HTBTree_add(tree, "LineMode");
+ HTBTree_add(tree, "DesignIssues");
+ HTBTree_add(tree, "MarkUp");
+ HTBTree_add(tree, "Macintosh");
+ HTBTree_add(tree, "Proposal.rtf.wn");
+ HTBTree_add(tree, "FIND");
+ HTBTree_add(tree, "Paper");
+ HTBTree_add(tree, "Tcl");
+ HTBTree_add(tree, "Talks");
+ HTBTree_add(tree, "Architecture");
+ HTBTree_add(tree, "VMSHelp");
+ HTBTree_add(tree, "Provider");
+ HTBTree_add(tree, "Archive");
+ HTBTree_add(tree, "SLAC");
+ HTBTree_add(tree, "Project");
+ HTBTree_add(tree, "News");
+ HTBTree_add(tree, "Viola");
+ HTBTree_add(tree, "Users");
+ HTBTree_add(tree, "FAQ");
+ HTBTree_add(tree, "WorkingNotes");
+ HTBTree_add(tree, "Windows");
+ HTBTree_add(tree, "FineWWW");
+ HTBTree_add(tree, "Frame");
+ HTBTree_add(tree, "XMosaic");
+ HTBTree_add(tree, "People");
+ HTBTree_add(tree, "All");
+ HTBTree_add(tree, "Curses");
+ HTBTree_add(tree, "Erwise");
+ HTBTree_add(tree, "Carl");
+ HTBTree_add(tree, "MidasWWW");
+ HTBTree_add(tree, "XPM");
+ HTBTree_add(tree, "MailRobot");
+ HTBTree_add(tree, "Illustrations");
+ HTBTree_add(tree, "VMClient");
+ HTBTree_add(tree, "XPA");
+ HTBTree_add(tree, "Clients.html");
+ HTBTree_add(tree, "Library");
+ HTBTree_add(tree, "CERNLIB_Distribution");
+ HTBTree_add(tree, "libHTML");
+ HTBTree_add(tree, "WindowsPC");
+ HTBTree_add(tree, "tkWWW");
+ HTBTree_add(tree, "tk2.3");
+ HTBTree_add(tree, "CVS-RCS");
+ HTBTree_add(tree, "DecnetSockets");
+ HTBTree_add(tree, "SGMLStream");
+ HTBTree_add(tree, "NextStep");
+ HTBTree_add(tree, "CVSRepository_old");
+ HTBTree_add(tree, "ArthurSecret");
+ HTBTree_add(tree, "CVSROOT");
+ HTBTree_add(tree, "HytelnetGate");
+ HTBTree_add(tree, "cern.www.new.src");
+ HTBTree_add(tree, "Conditions");
+ HTBTree_add(tree, "HTMLGate");
+ HTBTree_add(tree, "Makefile");
+ HTBTree_add(tree, "Newsgroups.html");
+ HTBTree_add(tree, "People.html");
+ HTBTree_add(tree, "Bugs.html");
+ HTBTree_add(tree, "Summary.html");
+ HTBTree_add(tree, "zDesignIssues.wn");
+ HTBTree_add(tree, "HT.draw");
+ HTBTree_add(tree, "HTandCERN.wn");
+ HTBTree_add(tree, "Ideas.wn");
+ HTBTree_add(tree, "MarkUp.wn");
+ HTBTree_add(tree, "Proposal.html");
+ HTBTree_add(tree, "SearchPanel.draw");
+ HTBTree_add(tree, "Comments.wn");
+ HTBTree_add(tree, "Xanadu.html");
+ HTBTree_add(tree, "Storinglinks.html");
+ HTBTree_add(tree, "TheW3Book.html");
+ HTBTree_add(tree, "Talk_Feb-91.html");
+ HTBTree_add(tree, "JFosterEntry.txt");
+ HTBTree_add(tree, "Summary.txt");
+ HTBTree_add(tree, "Bibliography.html");
+ HTBTree_add(tree, "HTandCern.txt");
+ HTBTree_add(tree, "Talk.draw");
+ HTBTree_add(tree, "zDesignNotes.html");
+ HTBTree_add(tree, "Link.html");
+ HTBTree_add(tree, "Status.html");
+ HTBTree_add(tree, "http.txt");
+ HTBTree_add(tree, "People.html~");
+ HTBTree_add(tree, "TAGS");
+ HTBTree_add(tree, "summary.txt");
+ HTBTree_add(tree, "Technical.html");
+ HTBTree_add(tree, "Terms.html");
+ HTBTree_add(tree, "JANETAccess.html");
+ HTBTree_add(tree, "People.txt");
+ HTBTree_add(tree, "README.txt");
+ HTBTree_add(tree, "CodingStandards.html");
+ HTBTree_add(tree, "Copyright.txt");
+ HTBTree_add(tree, "Status_old.html");
+ HTBTree_add(tree, "patches~");
+ HTBTree_add(tree, "RelatedProducts.html");
+ HTBTree_add(tree, "Implementation");
+ HTBTree_add(tree, "History.html");
+ HTBTree_add(tree, "Makefile.bak");
+ HTBTree_add(tree, "Makefile.old");
+ HTBTree_add(tree, "Policy.html");
+ HTBTree_add(tree, "WhatIs.html");
+ HTBTree_add(tree, "TheProject.html");
+ HTBTree_add(tree, "Notation.html");
+ HTBTree_add(tree, "Helping.html");
+ HTBTree_add(tree, "Cyber-WWW.sit.Hqx");
+ HTBTree_add(tree, "Glossary.html");
+ HTBTree_add(tree, "maketags.html");
+ HTBTree_add(tree, "IntroCS.html");
+ HTBTree_add(tree, "Contrib");
+ HTBTree_add(tree, "Help.html");
+ HTBTree_add(tree, "CodeManagExec");
+ HTBTree_add(tree, "HT-0.1draz");
+ HTBTree_add(tree, "Cello");
+ HTBTree_add(tree, "TOPUB");
+ HTBTree_add(tree, "BUILD");
+ HTBTree_add(tree, "BUILDALL");
+ HTBTree_add(tree, "Lynx");
+ HTBTree_add(tree, "ArthurLibrary");
+ HTBTree_add(tree, "RashtyClient");
+ HTBTree_add(tree, "#History.html#");
+ HTBTree_add(tree, "PerlServers");
+ HTBTree_add(tree, "modules");
+ HTBTree_add(tree, "NCSA_httpd");
+ HTBTree_add(tree, "MAIL2HTML");
+ HTBTree_add(tree, "core");
+ HTBTree_add(tree, "EmacsWWW");
+#ifdef BTREE_TRACE
+ printf("\nTreeTopObject=%s\n\n", tree->top->object);
+#endif
+ next_element = HTBTree_next(tree, NULL);
+ while (next_element != NULL) {
+#ifndef BTREE_TRACE
+ printf("The next element is %s\n", next_element->object);
+#endif
+ next_element = HTBTree_next(tree, next_element);
+ }
+ HTBTree_free(tree);
+}
+
+#endif
diff --git a/WWW/Library/Implementation/HTBTree.h b/WWW/Library/Implementation/HTBTree.h
new file mode 100644
index 0000000..a4f78f9
--- /dev/null
+++ b/WWW/Library/Implementation/HTBTree.h
@@ -0,0 +1,104 @@
+/* /Net/dxcern/userd/timbl/hypertext/WWW/Library/Implementation/HTBTree.html
+ BALANCED BINARY TREE FOR SORTING THINGS
+
+ Tree creation, traversal and freeing. User-supplied comparison routine.
+
+ Author: Arthur Secret, CERN. Public domain. Please mail bugs and changes to
+ www-request@info.cern.ch
+
+ part of libWWW
+
+ */
+#ifndef HTBTREE_H
+#define HTBTREE_H 1
+
+#ifndef HTUTILS_H
+#include <HTUtils.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+
+Data structures
+
+ */ typedef struct _HTBTree_element {
+ void *object; /* User object */
+ struct _HTBTree_element *up;
+ struct _HTBTree_element *left;
+ int left_depth;
+ struct _HTBTree_element *right;
+ int right_depth;
+ } HTBTElement;
+
+ typedef int (*HTComparer) (void *a, void *b);
+
+ typedef struct _HTBTree_top {
+ HTComparer compare;
+ struct _HTBTree_element *top;
+ } HTBTree;
+
+/*
+
+Create a binary tree given its discrimination routine
+
+ */
+ extern HTBTree *HTBTree_new(HTComparer comp);
+
+/*
+
+Free storage of the tree but not of the objects
+
+ */
+ extern void HTBTree_free(HTBTree *tree);
+
+/*
+
+Free storage of the tree and of the objects
+
+ */
+ extern void HTBTreeAndObject_free(HTBTree *tree);
+
+/*
+
+Add an object to a binary tree
+
+ */
+
+ extern void HTBTree_add(HTBTree *tree, void *object);
+
+/*
+
+Search an object in a binary tree
+
+ returns Pointer to equivalent object in a tree or NULL if none.
+ */
+
+ extern void *HTBTree_search(HTBTree *tree, void *object);
+
+/*
+
+Find user object for element
+
+ */
+#define HTBTree_object(element) ((element)->object)
+
+/*
+
+Find next element in depth-first order
+
+ ON ENTRY,
+
+ ele if NULL, start with leftmost element. if != 0 give next object to
+ the right.
+
+ returns Pointer to element or NULL if none left.
+
+ */
+ extern HTBTElement *HTBTree_next(HTBTree *tree, HTBTElement *ele);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* HTBTREE_H */
diff --git a/WWW/Library/Implementation/HTCJK.h b/WWW/Library/Implementation/HTCJK.h
new file mode 100644
index 0000000..7edf50b
--- /dev/null
+++ b/WWW/Library/Implementation/HTCJK.h
@@ -0,0 +1,121 @@
+/*
+ * $LynxId: HTCJK.h,v 1.22 2021/07/01 23:51:38 tom Exp $
+ *
+ * CJK character converter HTCJK.h
+ * =======================
+ *
+ * Added 11-Jun-96 by FM, based on jiscode.h for
+ * Yutaka Sato's (ysato@etl.go.jp) SJIS.c, and
+ * Takuya ASADA's (asada@three-a.co.jp) CJK patches.
+ * (see SGML.c).
+ *
+ */
+
+#ifndef HTCJK_H
+#define HTCJK_H
+
+#ifndef HTUTILS_H
+#include <HTUtils.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+ * STATUS CHANGE CODES
+ */
+#define TO_2BCODE '$'
+#define TO_1BCODE '('
+#define TO_KANA '\016'
+#define TO_KANAOUT '\017'
+#define TO_KANJI "\033$B"
+#define TO_HANJI "\033$A"
+#define TO_HANGUL "\033$(C"
+#define TO_ASCII "\033(B"
+
+#define IS_GBK_LO(lo) ((0xA1 <= (lo)) && ((lo) <= 0xFE))
+#define IS_GBK_HI(hi) ((0xA1 <= (hi)) && ((hi) <= 0xF7))
+
+#define IS_SJIS_LO(lo) ((0x40 <= (lo)) && ((lo) != 0x7F) && ((lo) <= 0xFC))
+#define IS_SJIS_HI1(hi) ((0x81 <= (hi)) && ((hi) <= 0x9F)) /* 1st lev. */
+#define IS_SJIS_HI2(hi) ((0xE0 <= (hi)) && ((hi) <= 0xEF)) /* 2nd lev. */
+#define IS_SJIS(hi,lo,in_sjis) (!IS_SJIS_LO(lo) ? 0 : IS_SJIS_HI1(hi) ? (in_sjis=1) : in_sjis && IS_SJIS_HI2(hi))
+#define IS_SJIS_2BYTE(hi,lo) (IS_SJIS_LO(lo) && (IS_SJIS_HI1(hi) || IS_SJIS_HI2(hi)))
+#define IS_SJIS_X0201KANA(lo) ((0xA1 <= (lo)) && ((lo) <= 0xDF))
+
+#define IS_EUC_LOS(lo) ((0x21 <= (lo)) && ((lo) <= 0x7E)) /* standard */
+#define IS_EUC_LOX(lo) ((0xA1 <= (lo)) && ((lo) <= 0xFE)) /* extended */
+#define IS_EUC_HI(hi) ((0xA1 <= (hi)) && ((hi) <= 0xFE))
+#define IS_EUC_X0201KANA(hi,lo) (((hi) == 0x8E) && (0xA1 <= (lo)) && ((lo) <= 0xDF))
+#define IS_EUC(hi,lo) ((IS_EUC_HI(hi) && IS_EUC_LOX(lo)) || IS_EUC_X0201KANA(hi,lo))
+
+#define IS_JAPANESE_2BYTE(hi,lo) (IS_SJIS_2BYTE(hi,lo) || IS_EUC(hi,lo))
+
+#define IS_BIG5_LOS(lo) ((0x40 <= (lo)) && ((lo) <= 0x7E)) /* standard */
+#define IS_BIG5_LOX(lo) ((0xA1 <= (lo)) && ((lo) <= 0xFE)) /* extended */
+#define IS_BIG5_HI(hi) ((0xA1 <= (hi)) && ((hi) <= 0xFE))
+#define IS_BIG5(hi,lo) (IS_BIG5_HI(hi) && (IS_BIG5_LOS(lo) || IS_BIG5_LOX(lo)))
+
+ typedef enum {
+ NOKANJI = 0, EUC, SJIS, JIS
+ } HTkcode;
+ typedef enum {
+ NOCJK = 0, JAPANESE, CHINESE, KOREAN, TAIPEI
+ } HTCJKlang;
+
+ extern HTCJKlang HTCJK;
+
+/*
+ * Function prototypes.
+ */
+ extern void JISx0201TO0208_EUC(unsigned IHI,
+ unsigned ILO,
+ unsigned char *OHI,
+ unsigned char *OLO);
+
+ extern unsigned char *SJIS_TO_JIS1(unsigned HI,
+ unsigned LO,
+ unsigned char *JCODE);
+
+ extern unsigned char *JIS_TO_SJIS1(unsigned HI,
+ unsigned LO,
+ unsigned char *SJCODE);
+
+ extern unsigned char *EUC_TO_SJIS1(unsigned HI,
+ unsigned LO,
+ register unsigned char *SJCODE);
+
+ extern void JISx0201TO0208_SJIS(unsigned I,
+ unsigned char *OHI,
+ unsigned char *OLO);
+
+ extern unsigned char *SJIS_TO_EUC1(unsigned HI,
+ unsigned LO,
+ unsigned char *EUCp);
+
+ extern unsigned char *SJIS_TO_EUC(unsigned char *src,
+ unsigned char *dst);
+
+ extern unsigned char *EUC_TO_SJIS(unsigned char *src,
+ unsigned char *dst);
+
+ extern unsigned char *EUC_TO_JIS(unsigned char *src,
+ unsigned char *dst,
+ const char *toK,
+ const char *toA);
+
+ extern unsigned char *TO_EUC(const unsigned char *jis,
+ unsigned char *euc);
+
+ extern void TO_SJIS(const unsigned char *any,
+ unsigned char *sjis);
+
+ extern void TO_JIS(const unsigned char *any,
+ unsigned char *jis);
+
+ extern char *str_kcode(HTkcode code);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* HTCJK_H */
diff --git a/WWW/Library/Implementation/HTChunk.c b/WWW/Library/Implementation/HTChunk.c
new file mode 100644
index 0000000..6b67011
--- /dev/null
+++ b/WWW/Library/Implementation/HTChunk.c
@@ -0,0 +1,332 @@
+/*
+ * $LynxId: HTChunk.c,v 1.28 2016/11/24 15:29:50 tom Exp $
+ *
+ * Chunk handling: Flexible arrays
+ * ===============================
+ *
+ */
+
+#include <HTUtils.h>
+#include <HTChunk.h>
+
+#include <LYLeaks.h>
+
+/*
+ * Initialize a chunk with a certain allocation unit
+ */
+void HTChunkInit(HTChunk *ch, int grow)
+{
+ ch->data = 0;
+ ch->growby = grow;
+ ch->size = 0;
+ ch->allocated = 0;
+}
+
+/* Create a chunk with a certain allocation unit
+ * --------------
+ */
+HTChunk *HTChunkCreate(int grow)
+{
+ HTChunk *ch = typecalloc(HTChunk);
+
+ if (ch == NULL)
+ outofmem(__FILE__, "creation of chunk");
+
+ HTChunkInit(ch, grow);
+ return ch;
+}
+
+HTChunk *HTChunkCreateMayFail(int grow, int failok)
+{
+ HTChunk *ch = typecalloc(HTChunk);
+
+ if (ch == NULL) {
+ if (!failok) {
+ outofmem(__FILE__, "creation of chunk");
+ } else {
+ return ch;
+ }
+ }
+
+ HTChunkInit(ch, grow);
+ ch->failok = failok;
+ return ch;
+}
+
+/* Create a chunk with a certain allocation unit and ensured size
+ * --------------
+ */
+HTChunk *HTChunkCreate2(int grow, size_t needed)
+{
+ HTChunk *ch = typecalloc(HTChunk);
+
+ if (ch == NULL)
+ outofmem(__FILE__, "HTChunkCreate2");
+
+ HTChunkInit(ch, grow);
+ if (needed-- > 0) {
+ /* Round up */
+ ch->allocated = (int) (needed - (needed % (size_t) ch->growby)
+ + (unsigned) ch->growby);
+ CTRACE((tfp, "HTChunkCreate2: requested %d, allocate %u\n",
+ (int) needed, (unsigned) ch->allocated));
+ ch->data = typecallocn(char, (unsigned) ch->allocated);
+
+ if (!ch->data)
+ outofmem(__FILE__, "HTChunkCreate2 data");
+ }
+ return ch;
+}
+
+/* Clear a chunk of all data
+ * --------------------------
+ */
+void HTChunkClear(HTChunk *ch)
+{
+ FREE(ch->data);
+ ch->size = 0;
+ ch->allocated = 0;
+}
+
+/* Free a chunk (and it's chain, if any)
+ * -------------------------------------
+ */
+void HTChunkFree(HTChunk *ch)
+{
+ HTChunk *next;
+
+ do {
+ next = ch->next;
+ FREE(ch->data);
+ FREE(ch);
+ ch = next;
+ } while (ch != NULL);
+}
+
+/* Realloc the chunk
+ * -----------------
+ */
+BOOL HTChunkRealloc(HTChunk *ch, int growby)
+{
+ char *data;
+
+ ch->allocated = ch->allocated + growby;
+
+ data = (ch->data
+ ? typeRealloc(char, ch->data, ch->allocated)
+ : typecallocn(char, ch->allocated));
+
+ if (data) {
+ ch->data = data;
+ } else if (ch->failok) {
+ HTChunkClear(ch); /* allocation failed, clear all data - kw */
+ return FALSE; /* caller should check ch->allocated - kw */
+ } else {
+ outofmem(__FILE__, "HTChunkRealloc");
+ }
+ return TRUE;
+}
+
+/* Append a character
+ * ------------------
+ */
+void HTChunkPutc(HTChunk *ch, unsigned c)
+{
+ if (ch->size >= ch->allocated) {
+ if (!HTChunkRealloc(ch, ch->growby))
+ return;
+ }
+ ch->data[ch->size++] = (char) c;
+}
+
+/* like above but no realloc: extend to another chunk if necessary */
+HTChunk *HTChunkPutc2(HTChunk *ch, int c)
+{
+ if (ch->size >= ch->allocated) {
+ HTChunk *chunk = HTChunkCreateMayFail(ch->growby, ch->failok);
+
+ ch->next = chunk;
+ ch = chunk;
+ HTChunkPutc(ch, UCH(c));
+ } else {
+ ch->data[ch->size++] = (char) c;
+ }
+ return ch;
+}
+
+/* Ensure a certain size
+ * ---------------------
+ */
+void HTChunkEnsure(HTChunk *ch, int needed)
+{
+ if (needed <= ch->allocated)
+ return;
+ ch->allocated = needed - 1 - ((needed - 1) % ch->growby)
+ + ch->growby; /* Round up */
+ ch->data = (ch->data
+ ? typeRealloc(char, ch->data, ch->allocated)
+ : typecallocn(char, ch->allocated));
+
+ if (ch->data == NULL)
+ outofmem(__FILE__, "HTChunkEnsure");
+}
+
+/*
+ * Append a block of characters.
+ */
+void HTChunkPutb(HTChunk *ch, const char *b, int l)
+{
+ if (l <= 0)
+ return;
+ if (ch->size + l > ch->allocated) {
+ int growby = l - (l % ch->growby) + ch->growby; /* Round up */
+
+ if (!HTChunkRealloc(ch, growby))
+ return;
+ }
+ MemCpy(ch->data + ch->size, b, l);
+ ch->size += l;
+}
+
+/* like above but no realloc: extend to another chunk if necessary */
+HTChunk *HTChunkPutb2(HTChunk *ch, const char *b, int l)
+{
+ if (l <= 0)
+ return ch;
+ if (ch->size + l > ch->allocated) {
+ HTChunk *chunk;
+ int m = ch->allocated - ch->size;
+
+ MemCpy(ch->data + ch->size, b, (unsigned) m);
+ ch->size += m;
+
+ chunk = HTChunkCreateMayFail(ch->growby, ch->failok);
+ ch->next = chunk;
+ ch = chunk;
+ HTChunkPutb(ch, b + m, l - m);
+ } else {
+ MemCpy(ch->data + ch->size, b, (unsigned) l);
+ ch->size += l;
+ }
+ return ch;
+}
+
+#define PUTC(code) ch->data[ch->size++] = (char)(code)
+#define PUTC2(code) ch->data[ch->size++] = (char)(0x80|(0x3f &(code)))
+
+/*
+ * Append a character encoded as UTF-8.
+ */
+void HTChunkPutUtf8Char(HTChunk *ch, UCode_t code)
+{
+ int utflen;
+
+ if (TOASCII(code) < 128)
+ utflen = 1;
+ else if (code < 0x800L) {
+ utflen = 2;
+ } else if (code < 0x10000L) {
+ utflen = 3;
+ } else if (code < 0x200000L) {
+ utflen = 4;
+ } else if (code < 0x4000000L) {
+ utflen = 5;
+ } else if (code <= 0x7fffffffL) {
+ utflen = 6;
+ } else
+ utflen = 0;
+
+ if (ch->size + utflen > ch->allocated) {
+ int growby = (ch->growby >= utflen) ? ch->growby : utflen;
+
+ if (!HTChunkRealloc(ch, growby))
+ return;
+ }
+
+ switch (utflen) {
+ case 0:
+ return;
+ case 1:
+ ch->data[ch->size++] = (char) code;
+ return;
+ case 2:
+ PUTC(0xc0 | (code >> 6));
+ break;
+ case 3:
+ PUTC(0xe0 | (code >> 12));
+ break;
+ case 4:
+ PUTC(0xf0 | (code >> 18));
+ break;
+ case 5:
+ PUTC(0xf8 | (code >> 24));
+ break;
+ case 6:
+ PUTC(0xfc | (code >> 30));
+ break;
+ }
+ switch (utflen) {
+ case 6:
+ PUTC2(code >> 24);
+ /* FALLTHRU */
+ case 5:
+ PUTC2(code >> 18);
+ /* FALLTHRU */
+ case 4:
+ PUTC2(code >> 12);
+ /* FALLTHRU */
+ case 3:
+ PUTC2(code >> 6);
+ /* FALLTHRU */
+ case 2:
+ PUTC2(code);
+ break;
+ }
+}
+
+/* Terminate a chunk
+ * -----------------
+ */
+void HTChunkTerminate(HTChunk *ch)
+{
+ HTChunkPutc(ch, (char) 0);
+}
+
+/* Append a string
+ * ---------------
+ */
+void HTChunkPuts(HTChunk *ch, const char *s)
+{
+ const char *p;
+
+ if (s != NULL) {
+ for (p = s; *p; p++) {
+ if (ch->size >= ch->allocated) {
+ if (!HTChunkRealloc(ch, ch->growby))
+ break;
+ }
+ ch->data[ch->size++] = *p;
+ }
+ }
+}
+
+/* like above but no realloc: extend to another chunk if necessary */
+HTChunk *HTChunkPuts2(HTChunk *ch, const char *s)
+{
+ const char *p;
+
+ if (s != NULL) {
+ for (p = s; *p; p++) {
+ if (ch->size >= ch->allocated) {
+ HTChunk *chunk = HTChunkCreateMayFail(ch->growby, ch->failok);
+
+ ch->next = chunk;
+ ch = chunk;
+ HTChunkPuts(ch, p);
+ break;
+ }
+ ch->data[ch->size++] = *p;
+ }
+ }
+ return ch;
+}
diff --git a/WWW/Library/Implementation/HTChunk.h b/WWW/Library/Implementation/HTChunk.h
new file mode 100644
index 0000000..fa51c99
--- /dev/null
+++ b/WWW/Library/Implementation/HTChunk.h
@@ -0,0 +1,228 @@
+/*
+ * $LynxId: HTChunk.h,v 1.21 2020/01/21 22:02:43 tom Exp $
+ *
+ * HTChunk: Flexible array handling for libwww
+ * CHUNK HANDLING:
+ * FLEXIBLE ARRAYS
+ *
+ * This module implements a flexible array. It is a general utility module. A
+ * chunk is a structure which may be extended. These routines create and
+ * append data to chunks, automatically reallocating them as necessary.
+ *
+ */
+#ifndef HTCHUNK_H
+#define HTCHUNK_H 1
+
+#ifndef HTUTILS_H
+#include <HTUtils.h>
+#endif
+
+#include <UCMap.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+ typedef struct _HTChunk HTChunk;
+
+ struct _HTChunk {
+ int size; /* In bytes */
+ int growby; /* Allocation unit in bytes */
+ int allocated; /* Current size of *data */
+ char *data; /* Pointer to malloc'd area or 0 */
+ int failok; /* allowed to fail without exiting program? */
+ HTChunk *next; /* pointer to the next chunk */
+ };
+
+/*
+ * Initialize a chunk's allocation data and allocation-increment.
+ */
+ extern void HTChunkInit(HTChunk *ch, int grow);
+
+/*
+ *
+ * Create new chunk
+ *
+ * ON ENTRY,
+ *
+ * growby The number of bytes to allocate at a time when the chunk
+ * is later extended. Arbitrary but normally a trade-off
+ * of time vs memory.
+ *
+ * ON EXIT,
+ *
+ * returns A chunk pointer to the new chunk,
+ *
+ */
+
+ extern HTChunk *HTChunkCreate(int growby);
+
+/*
+ * Create a chunk for which an allocation error is not a fatal application
+ * error if failok != 0, but merely resets the chunk. When using a chunk
+ * created this way, the caller should always check whether the contents
+ * are ok each time after data have been appended.
+ * The create call may also fail and will return NULL in that case. - kw
+ */
+ extern HTChunk *HTChunkCreateMayFail(int growby, int failok);
+
+/*
+ * Like HTChunkCreate but with initial allocation - kw
+ *
+ */
+ extern HTChunk *HTChunkCreate2(int growby, size_t needed);
+
+/*
+ *
+ * Free a chunk
+ *
+ * ON ENTRY,
+ *
+ * ch A valid chunk pointer made by HTChunkCreate()
+ *
+ * ON EXIT,
+ *
+ * ch is invalid and may not be used.
+ *
+ */
+
+ extern void HTChunkFree(HTChunk *ch);
+
+/*
+ *
+ * Clear a chunk
+ *
+ * ON ENTRY,
+ *
+ * ch A valid chunk pointer made by HTChunkCreate()
+ *
+ * ON EXIT,
+ *
+ * *ch The size of the chunk is zero.
+ *
+ */
+
+ extern void HTChunkClear(HTChunk *ch);
+
+/*
+ *
+ * Realloc a chunk
+ *
+ * ON ENTRY,
+ *
+ * ch A valid chunk pointer made by HTChunkCreate()
+ *
+ * growby growby
+ *
+ * ON EXIT,
+ *
+ * *ch Expanded by growby
+ *
+ */
+
+ extern BOOL HTChunkRealloc(HTChunk *ch, int growby);
+
+/*
+ *
+ * Ensure a chunk has a certain space in
+ *
+ * ON ENTRY,
+ *
+ * ch A valid chunk pointer made by HTChunkCreate()
+ *
+ * s The size required
+ *
+ * ON EXIT,
+ *
+ * *ch Has size at least s
+ *
+ */
+
+ extern void HTChunkEnsure(HTChunk *ch, int s);
+
+/*
+ *
+ * Append a character to a chunk
+ *
+ * ON ENTRY,
+ *
+ * ch A valid chunk pointer made by HTChunkCreate()
+ *
+ * c The character to be appended
+ *
+ * ON EXIT,
+ *
+ * *ch Is one character bigger
+ *
+ */
+ extern void HTChunkPutc(HTChunk *ch, unsigned c);
+
+ extern void HTChunkPutb(HTChunk *ch, const char *b, int l);
+
+ extern void HTChunkPutUtf8Char(HTChunk *ch, UCode_t code);
+
+/*
+ * Append a string to a chunk
+ *
+ * ON ENTRY,
+ *
+ * ch A valid chunk pointer made by HTChunkCreate()
+ *
+ * str Points to a zero-terminated string to be appended
+ *
+ * ON EXIT,
+ *
+ * *ch Is bigger by strlen(str)
+ *
+ */
+
+ extern void HTChunkPuts(HTChunk *ch, const char *str);
+
+/*
+ *
+ * Append a zero character to a chunk
+ *
+ */
+
+/*
+ *
+ * ON ENTRY,
+ *
+ * ch A valid chunk pointer made by HTChunkCreate()
+ *
+ * ON EXIT,
+ *
+ * *ch Is one character bigger
+ *
+ */
+
+ extern void HTChunkTerminate(HTChunk *ch);
+
+/* like the above but no realloc: extend to another chunk if necessary */
+/*
+ *
+ * Append a character (string, data) to a chunk
+ *
+ * ON ENTRY,
+ *
+ * ch A valid chunk pointer made by HTChunkCreate()
+ *
+ * c The character to be appended
+ *
+ * ON EXIT,
+ *
+ * returns original chunk or a pointer to the new chunk
+ * (original chunk is referenced to the new one
+ * by the field 'next')
+ *
+ */
+ extern HTChunk *HTChunkPutc2(HTChunk *ch, int c);
+ extern HTChunk *HTChunkPuts2(HTChunk *ch, const char *str);
+ extern HTChunk *HTChunkPutb2(HTChunk *ch, const char *b, int l);
+
+/* New pool infrastructure: UNlike the above, store data using alignment */
+ extern HTChunk *HTChunkPutb0(HTChunk *ch, const char *b, int l);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* HTCHUNK_H */
diff --git a/WWW/Library/Implementation/HTDOS.c b/WWW/Library/Implementation/HTDOS.c
new file mode 100644
index 0000000..84bff79
--- /dev/null
+++ b/WWW/Library/Implementation/HTDOS.c
@@ -0,0 +1,241 @@
+/*
+ * $LynxId: HTDOS.c,v 1.40 2013/11/28 11:11:05 tom Exp $
+ * DOS specific routines
+ */
+
+#include <HTUtils.h>
+#include <LYUtils.h>
+#include <HTDOS.h>
+#include <LYStrings.h>
+
+#include <LYLeaks.h>
+
+#ifdef _WINDOWS
+#include <LYGlobalDefs.h>
+#include <HTAlert.h>
+#endif
+
+/*
+ * Make a copy of the source argument in the result, allowing some extra
+ * space so we can append directly onto the result without reallocating.
+ */
+static char *copy_plus(char **result, const char *source)
+{
+ int length = (int) strlen(source);
+ int extra = 10;
+ int n;
+
+ for (n = 0; n < length; ++n) {
+ if (source[n] == ' ')
+ ++extra;
+ }
+
+ HTSprintf0(result, "%-*s", length + extra, source);
+ (*result)[length] = 0;
+ return (*result);
+}
+
+/* PUBLIC HTDOS_wwwName()
+ * CONVERTS DOS Name into WWW Name
+ * ON ENTRY:
+ * dosname DOS file specification (NO NODE)
+ *
+ * ON EXIT:
+ * returns WWW file specification
+ *
+ */
+const char *HTDOS_wwwName(const char *dosname)
+{
+ static char *wwwname = NULL;
+ char *cp_url = copy_plus(&wwwname, dosname);
+ int wwwname_len;
+ char ch;
+
+ while ((ch = *dosname) != '\0') {
+ switch (ch) {
+ case '\\':
+ /* convert dos backslash to unix-style */
+ *cp_url++ = '/';
+ break;
+ case ' ':
+ *cp_url++ = '%';
+ *cp_url++ = '2';
+ *cp_url++ = '0';
+ break;
+ default:
+ *cp_url++ = ch;
+ break;
+ }
+ dosname++;
+ }
+ *cp_url = '\0';
+
+ wwwname_len = (int) strlen(wwwname);
+ if (wwwname_len > 1)
+ cp_url--; /* point last char */
+
+ if (wwwname_len > 3 && *cp_url == '/') {
+ cp_url++;
+ *cp_url = '\0';
+ }
+ return (wwwname);
+}
+
+/*
+ * Convert slashes from Unix to DOS
+ */
+char *HTDOS_slashes(char *path)
+{
+ char *s;
+
+ for (s = path; *s != '\0'; ++s) {
+ if (*s == '/') {
+ *s = '\\';
+ }
+ }
+ return path;
+}
+
+/* PUBLIC HTDOS_name()
+ * CONVERTS WWW name into a DOS name
+ * ON ENTRY:
+ * wwwname WWW file name
+ *
+ * ON EXIT:
+ * returns DOS file specification
+ */
+char *HTDOS_name(const char *wwwname)
+{
+ static char *result = NULL;
+ int joe;
+
+#if defined(SH_EX) /* 2000/03/07 (Tue) 18:32:42 */
+ if (unsafe_filename(wwwname)) {
+ HTUserMsg2("unsafe filename : %s", wwwname);
+ copy_plus(&result, "BAD_LOCAL_FILE_NAME");
+ } else {
+ copy_plus(&result, wwwname);
+ }
+#else
+ copy_plus(&result, wwwname);
+#endif
+#ifdef __DJGPP__
+ if (result[0] == '/'
+ && result[1] == 'd'
+ && result[2] == 'e'
+ && result[3] == 'v'
+ && result[4] == '/'
+ && isalpha(result[5])) {
+ return (result);
+ }
+#endif /* __DJGPP__ */
+
+ (void) HTDOS_slashes(result);
+
+ /* pesky leading slash, rudiment from file://localhost/ */
+ /* the rest of path may be with or without drive letter */
+ if ((result[1] != '\\') && (result[0] == '\\')) {
+ for (joe = 0; (result[joe] = result[joe + 1]) != 0; joe++) ;
+ }
+ /* convert '|' after the drive letter to ':' */
+ if (isalpha(UCH(result[0])) && result[1] == '|') {
+ result[1] = ':';
+ }
+#ifdef _WINDOWS /* 1998/04/02 (Thu) 08:59:48 */
+ if (LYLastPathSep(result) != NULL
+ && !LYIsDosDrive(result)) {
+ char temp_buff[LY_MAXPATH];
+
+ sprintf(temp_buff, "%.3s\\%.*s", windows_drive,
+ (int) (sizeof(temp_buff) - 5), result);
+ StrAllocCopy(result, temp_buff);
+ }
+#endif
+ /*
+ * If we have only a device, add a trailing slash. Otherwise it just
+ * refers to the current directory on the given device.
+ */
+ if (LYLastPathSep(result) == NULL
+ && LYIsDosDrive(result))
+ LYAddPathSep0(result);
+
+ CTRACE((tfp, "HTDOS_name changed `%s' to `%s'\n", wwwname, result));
+ return (result);
+}
+
+#ifdef WIN_EX
+char *HTDOS_short_name(const char *path)
+{
+ static char sbuf[LY_MAXPATH];
+ char *ret;
+ DWORD r;
+
+ if (StrChr(path, '/'))
+ path = HTDOS_name(path);
+ r = GetShortPathName(path, sbuf, sizeof sbuf);
+ if (r >= sizeof(sbuf) || r == 0) {
+ ret = LYStrNCpy(sbuf, path, sizeof(sbuf));
+ } else {
+ ret = sbuf;
+ }
+ return ret;
+}
+#endif
+
+#if defined(DJGPP)
+/*
+ * Poll tcp/ip lib and yield to DPMI-host while nothing in
+ * keyboard buffer (head = tail) (simpler than kbhit).
+ * This is required to be able to finish off dead sockets,
+ * answer pings etc.
+ */
+#include <pc.h>
+#include <dpmi.h>
+#include <libc/farptrgs.h>
+#include <go32.h>
+
+void djgpp_idle_loop(void)
+{
+ while (_farpeekw(_dos_ds, 0x41a) == _farpeekw(_dos_ds, 0x41c)) {
+ tcp_tick(NULL);
+ __dpmi_yield();
+#if defined(USE_SLANG)
+ if (SLang_input_pending(1))
+ break;
+#endif
+ }
+}
+
+/* PUBLIC getxkey()
+ * Replaces libc's getxkey() with polling of tcp/ip
+ * library (WatTcp or Watt-32). *
+ * ON EXIT:
+ * returns extended keypress.
+ */
+
+/* Copyright (C) 1995 DJ Delorie, see COPYING.DJ for details */
+
+int getxkey(void)
+{
+#if defined(DJGPP_KEYHANDLER)
+ __dpmi_regs r;
+
+ djgpp_idle_loop();
+
+ r.h.ah = 0x10;
+ __dpmi_int(0x16, &r);
+
+ if (r.h.al == 0x00)
+ return 0x0100 | r.h.ah;
+ if (r.h.al == 0xe0)
+ return 0x0200 | r.h.ah;
+ return r.h.al;
+
+#elif defined(USE_SLANG)
+ djgpp_idle_loop();
+ return SLkp_getkey();
+#else
+ /* PDcurses uses myGetChar() in LYString.c */
+#endif
+}
+#endif /* DJGPP */
diff --git a/WWW/Library/Implementation/HTDOS.h b/WWW/Library/Implementation/HTDOS.h
new file mode 100644
index 0000000..e1613cb
--- /dev/null
+++ b/WWW/Library/Implementation/HTDOS.h
@@ -0,0 +1,56 @@
+/*
+ * $LynxId: HTDOS.h,v 1.14 2009/09/09 00:16:06 tom Exp $
+ *
+ * DOS specific routines
+ */
+
+#ifndef HTDOS_H
+#define HTDOS_H
+
+#ifndef HTUTILS_H
+#include <HTUtils.h>
+#endif /* HTUTILS_H */
+
+/* PUBLIC HTDOS_wwwName()
+ * CONVERTS DOS Name into WWW Name
+ * ON ENTRY:
+ * dosname DOS file specification (NO NODE)
+ *
+ * ON EXIT:
+ * returns WWW file specification
+ *
+ */
+const char *HTDOS_wwwName(const char *dosname);
+
+/*
+ * Converts Unix slashes to DOS
+ */
+char *HTDOS_slashes(char *path);
+
+/* PUBLIC HTDOS_name()
+ * CONVERTS WWW name into a DOS name
+ * ON ENTRY:
+ * wwwname WWW file name
+ *
+ * ON EXIT:
+ * returns DOS file specification
+ *
+ * Bug: Returns pointer to static -- non-reentrant
+ */
+char *HTDOS_name(const char *wwwname);
+
+#ifdef WIN_EX
+char *HTDOS_short_name(const char *fn);
+
+#else
+#define HTDOS_short_name(fn) fn
+#endif
+
+#ifdef DJGPP
+/*
+ * Poll tcp/ip lib and yield to DPMI-host while nothing in
+ * keyboard buffer (head = tail) (simpler than kbhit).
+ */
+void djgpp_idle_loop(void);
+#endif
+#endif /* HTDOS_H */
diff --git a/WWW/Library/Implementation/HTFTP.c b/WWW/Library/Implementation/HTFTP.c
new file mode 100644
index 0000000..decf559
--- /dev/null
+++ b/WWW/Library/Implementation/HTFTP.c
@@ -0,0 +1,4177 @@
+/*
+ * $LynxId: HTFTP.c,v 1.148 2023/01/05 09:17:15 tom Exp $
+ *
+ * File Transfer Protocol (FTP) Client
+ * for a WorldWideWeb browser
+ * ===================================
+ *
+ * A cache of control connections is kept.
+ *
+ * Note: Port allocation
+ *
+ * It is essential that the port is allocated by the system, rather
+ * than chosen in rotation by us (POLL_PORTS), or the following
+ * problem occurs.
+ *
+ * It seems that an attempt by the server to connect to a port which has
+ * been used recently by a listen on the same socket, or by another
+ * socket this or another process causes a hangup of (almost exactly)
+ * one minute. Therefore, we have to use a rotating port number.
+ * The problem remains that if the application is run twice in quick
+ * succession, it will hang for what remains of a minute.
+ *
+ * Authors
+ * TBL Tim Berners-lee <timbl@info.cern.ch>
+ * DD Denis DeLaRoca 310 825-4580 <CSP1DWD@mvs.oac.ucla.edu>
+ * LM Lou Montulli <montulli@ukanaix.cc.ukans.edu>
+ * FM Foteos Macrides <macrides@sci.wfeb.edu>
+ * History:
+ * 2 May 91 Written TBL, as a part of the WorldWideWeb project.
+ * 15 Jan 92 Bug fix: close() was used for NETCLOSE for control soc
+ * 10 Feb 92 Retry if cached connection times out or breaks
+ * 8 Dec 92 Bug fix 921208 TBL after DD
+ * 17 Dec 92 Anon FTP password now just WWWuser@ suggested by DD
+ * fails on princeton.edu!
+ * 27 Dec 93 (FM) Fixed up so FTP now works with VMS hosts. Path
+ * must be Unix-style and cannot include the device
+ * or top directory.
+ * ?? ??? ?? (LM) Added code to prompt and send passwords for non
+ * anonymous FTP
+ * 25 Mar 94 (LM) Added code to recognize different ftp server types
+ * and code to parse dates and sizes on most hosts.
+ * 27 Mar 93 (FM) Added code for getting dates and sizes on VMS hosts.
+ *
+ * Notes:
+ * Portions Copyright 1994 Trustees of Dartmouth College
+ * Code for recognizing different FTP servers and
+ * parsing "ls -l" output taken from Macintosh Fetch
+ * program with permission from Jim Matthews,
+ * Dartmouth Software Development Team.
+ */
+
+/*
+ * BUGS: @@@ Limit connection cache size!
+ * Error reporting to user.
+ * 400 & 500 errors are ack'ed by user with windows.
+ * Use configuration file for user names
+ *
+ * Note for portability this version does not use select() and
+ * so does not watch the control and data channels at the
+ * same time.
+ */
+
+#include <HTUtils.h>
+
+#include <HTAlert.h>
+
+#include <HTFTP.h> /* Implemented here */
+#include <HTTCP.h>
+#include <HTTP.h>
+#include <HTFont.h>
+
+#define REPEAT_PORT /* Give the port number for each file */
+#define REPEAT_LISTEN /* Close each listen socket and open a new one */
+
+/* define POLL_PORTS If allocation does not work, poll ourselves.*/
+#define LISTEN_BACKLOG 2 /* Number of pending connect requests (TCP) */
+
+#define FIRST_TCP_PORT 1024 /* Region to try for a listening port */
+#define LAST_TCP_PORT 5999
+
+#define LINE_LENGTH 256
+
+#include <HTParse.h>
+#include <HTAnchor.h>
+#include <HTFile.h> /* For HTFileFormat() */
+#include <HTBTree.h>
+#include <HTChunk.h>
+#ifndef IPPORT_FTP
+#define IPPORT_FTP 21
+#endif /* !IPORT_FTP */
+
+#include <LYUtils.h>
+#include <LYGlobalDefs.h>
+#include <LYStrings.h>
+#include <LYLeaks.h>
+
+typedef struct _connection {
+ struct _connection *next; /* Link on list */
+ int socket; /* Socket number for communication */
+ BOOL is_binary; /* Binary mode? */
+} connection;
+
+/* Hypertext object building machinery
+*/
+#include <HTML.h>
+
+/*
+ * socklen_t is the standard, but there are many pre-standard variants.
+ * This ifdef works around a few of those cases.
+ *
+ * Information was obtained from header files on these platforms:
+ * AIX 4.3.2, 5.1
+ * HPUX 10.20, 11.00, 11.11
+ * IRIX64 6.5
+ * Tru64 4.0G, 4.0D, 5.1
+ */
+#if defined(SYS_IRIX64)
+ /* IRIX64 6.5 socket.h may use socklen_t if SGI_SOURCE is not defined */
+# if _NO_XOPEN4 && _NO_XOPEN5
+# define LY_SOCKLEN socklen_t
+# elif _ABIAPI
+# define LY_SOCKLEN int
+# elif _XOPEN5
+# if (_MIPS_SIM != _ABIO32)
+# define LY_SOCKLEN socklen_t
+# else
+# define LY_SOCKLEN int
+# endif
+# else
+# define LY_SOCKLEN size_t
+# endif
+#elif defined(SYS_HPUX)
+# if defined(_XOPEN_SOURCE_EXTENDED) && defined(SO_PROTOTYPE)
+# define LY_SOCKLEN socklen_t
+# else /* HPUX 10.20, etc. */
+# define LY_SOCKLEN int
+# endif
+#elif defined(SYS_TRU64)
+# if defined(_POSIX_PII_SOCKET)
+# define LY_SOCKLEN socklen_t
+# elif defined(_XOPEN_SOURCE_EXTENDED)
+# define LY_SOCKLEN size_t
+# else
+# define LY_SOCKLEN int
+# endif
+#else
+# define LY_SOCKLEN socklen_t
+#endif
+
+#define PUTC(c) (*target->isa->put_character) (target, c)
+#define PUTS(s) (*target->isa->put_string) (target, s)
+#define START(e) (*target->isa->start_element) (target, e, 0, 0, -1, 0)
+#define END(e) (*target->isa->end_element) (target, e, 0)
+#define FREE_TARGET (*target->isa->_free) (target)
+#define ABORT_TARGET (*target->isa->_free) (target)
+
+#define TRACE_ENTRY(tag, entry_info) \
+ CTRACE((tfp, "HTFTP: %s filename: %s date: %s size: %" PRI_off_t "\n", \
+ tag, \
+ entry_info->filename, \
+ NonNull(entry_info->date), \
+ CAST_off_t(entry_info->size)))
+
+struct _HTStructured {
+ const HTStructuredClass *isa;
+ /* ... */
+};
+
+/* Global Variables
+ * ---------------------
+ */
+int HTfileSortMethod = FILE_BY_NAME;
+
+#ifndef DISABLE_FTP /*This disables everything to end-of-file */
+static char ThisYear[8];
+static char LastYear[8];
+static int TheDate;
+static BOOLEAN HaveYears = FALSE;
+
+/* Module-Wide Variables
+ * ---------------------
+ */
+static connection *connections = NULL; /* Linked list of connections */
+static char response_text[LINE_LENGTH + 1]; /* Last response from ftp host */
+static connection *control = NULL; /* Current connection */
+static int data_soc = -1; /* Socket for data transfer =invalid */
+static char *user_entered_password = NULL;
+static char *last_username_and_host = NULL;
+
+/*
+ * Some ftp servers are known to have a broken implementation of RETR. If
+ * asked to retrieve a directory, they get confused and fail subsequent
+ * commands such as CWD and LIST.
+ */
+static int Broken_RETR = FALSE;
+
+/*
+ * Some ftp servers are known to have a broken implementation of EPSV. The
+ * server will hang for a long time when we attempt to connect after issuing
+ * this command.
+ */
+#ifdef INET6
+static int Broken_EPSV = FALSE;
+#endif
+
+typedef enum {
+ GENERIC_SERVER
+ ,MACHTEN_SERVER
+ ,UNIX_SERVER
+ ,VMS_SERVER
+ ,CMS_SERVER
+ ,DCTS_SERVER
+ ,TCPC_SERVER
+ ,PETER_LEWIS_SERVER
+ ,NCSA_SERVER
+ ,WINDOWS_NT_SERVER
+ ,WINDOWS_2K_SERVER
+ ,MS_WINDOWS_SERVER
+ ,MSDOS_SERVER
+ ,APPLESHARE_SERVER
+ ,NETPRESENZ_SERVER
+ ,DLS_SERVER
+} eServerType;
+
+static eServerType server_type = GENERIC_SERVER; /* the type of ftp host */
+static int unsure_type = FALSE; /* sure about the type? */
+static BOOLEAN use_list = FALSE; /* use the LIST command? */
+
+static int interrupted_in_next_data_char = FALSE;
+
+#ifdef POLL_PORTS
+static PortNumber port_number = FIRST_TCP_PORT;
+#endif /* POLL_PORTS */
+
+static BOOL have_socket = FALSE; /* true if master_socket is valid */
+static LYNX_FD master_socket; /* Listening socket = invalid */
+
+static char *port_command; /* Command for setting the port */
+static fd_set open_sockets; /* Mask of active channels */
+static LYNX_FD num_sockets; /* Number of sockets to scan */
+static PortNumber passive_port; /* Port server specified for data */
+
+#define NEXT_CHAR HTGetCharacter() /* Use function in HTFormat.c */
+
+#define DATA_BUFFER_SIZE 2048
+static char data_buffer[DATA_BUFFER_SIZE]; /* Input data buffer */
+static char *data_read_pointer;
+static char *data_write_pointer;
+
+#define NEXT_DATA_CHAR next_data_char()
+static int close_connection(connection * con);
+
+#ifndef HAVE_ATOLL
+off_t LYatoll(const char *value)
+{
+ off_t result = 0;
+
+ while (*value != '\0') {
+ result = (result * 10) + (off_t) (*value++ - '0');
+ }
+ return result;
+}
+#endif
+
+#ifdef LY_FIND_LEAKS
+/*
+ * This function frees module globals. - FM
+ */
+static void free_FTPGlobals(void)
+{
+ FREE(user_entered_password);
+ FREE(last_username_and_host);
+ if (control) {
+ if (control->socket != -1)
+ close_connection(control);
+ FREE(control);
+ }
+}
+#endif /* LY_FIND_LEAKS */
+
+/* PUBLIC HTVMS_name()
+ * CONVERTS WWW name into a VMS name
+ * ON ENTRY:
+ * nn Node Name (optional)
+ * fn WWW file name
+ *
+ * ON EXIT:
+ * returns vms file specification
+ *
+ * Bug: Returns pointer to static -- non-reentrant
+ */
+char *HTVMS_name(const char *nn,
+ const char *fn)
+{
+ /* We try converting the filename into Files-11 syntax. That is, we assume
+ * first that the file is, like us, on a VMS node. We try remote (or
+ * local) DECnet access. Files-11, VMS, VAX and DECnet are trademarks of
+ * Digital Equipment Corporation. The node is assumed to be local if the
+ * hostname WITHOUT DOMAIN matches the local one. @@@
+ */
+ static char *vmsname;
+ char *filename = (char *) malloc(strlen(fn) + 1);
+ char *nodename = (char *) malloc(strlen(nn) + 2 + 1); /* Copies to hack */
+ char *second; /* 2nd slash */
+ char *last; /* last slash */
+
+ const char *hostname = HTHostName();
+
+ if (!filename || !nodename)
+ outofmem(__FILE__, "HTVMSname");
+
+ strcpy(filename, fn);
+ strcpy(nodename, ""); /* On same node? Yes if node names match */
+ if (StrNCmp(nn, "localhost", 9)) {
+ const char *p;
+ const char *q;
+
+ for (p = hostname, q = nn;
+ *p && *p != '.' && *q && *q != '.'; p++, q++) {
+ if (TOUPPER(*p) != TOUPPER(*q)) {
+ char *r;
+
+ strcpy(nodename, nn);
+ r = StrChr(nodename, '.'); /* Mismatch */
+ if (r)
+ *r = '\0'; /* Chop domain */
+ strcat(nodename, "::"); /* Try decnet anyway */
+ break;
+ }
+ }
+ }
+
+ second = StrChr(filename + 1, '/'); /* 2nd slash */
+ last = strrchr(filename, '/'); /* last slash */
+
+ if (!second) { /* Only one slash */
+ HTSprintf0(&vmsname, "%s%s", nodename, filename + 1);
+ } else if (second == last) { /* Exactly two slashes */
+ *second = '\0'; /* Split filename from disk */
+ HTSprintf0(&vmsname, "%s%s:%s", nodename, filename + 1, second + 1);
+ *second = '/'; /* restore */
+ } else { /* More than two slashes */
+ char *p;
+
+ *second = '\0'; /* Split disk from directories */
+ *last = '\0'; /* Split dir from filename */
+ HTSprintf0(&vmsname, "%s%s:[%s]%s",
+ nodename, filename + 1, second + 1, last + 1);
+ *second = *last = '/'; /* restore filename */
+ if ((p = StrChr(vmsname, '[')) != 0) {
+ while (*p != '\0' && *p != ']') {
+ if (*p == '/')
+ *p = '.'; /* Convert dir sep. to dots */
+ ++p;
+ }
+ }
+ }
+ FREE(nodename);
+ FREE(filename);
+ return vmsname;
+}
+
+/* Procedure: Read a character from the data connection
+ * ----------------------------------------------------
+ */
+static int next_data_char(void)
+{
+ int status;
+
+ if (data_read_pointer >= data_write_pointer) {
+ status = NETREAD(data_soc, data_buffer, DATA_BUFFER_SIZE);
+ if (status == HT_INTERRUPTED)
+ interrupted_in_next_data_char = 1;
+ if (status <= 0)
+ return EOF;
+ data_write_pointer = data_buffer + status;
+ data_read_pointer = data_buffer;
+ }
+#ifdef NOT_ASCII
+ {
+ char c = *data_read_pointer++;
+
+ return FROMASCII(c);
+ }
+#else
+ return UCH(*data_read_pointer++);
+#endif /* NOT_ASCII */
+}
+
+/* Close an individual connection
+ *
+ */
+static int close_connection(connection * con)
+{
+ connection *scan;
+ int status;
+
+ CTRACE((tfp, "HTFTP: Closing control socket %d\n", con->socket));
+ status = NETCLOSE(con->socket);
+ if (TRACE && status != 0) {
+#ifdef UNIX
+ CTRACE((tfp, "HTFTP:close_connection: %s", LYStrerror(errno)));
+#else
+ if (con->socket != INVSOC)
+ HTInetStatus("HTFTP:close_connection");
+#endif
+ }
+ con->socket = -1;
+ if (connections == con) {
+ connections = con->next;
+ return status;
+ }
+ for (scan = connections; scan; scan = scan->next) {
+ if (scan->next == con) {
+ scan->next = con->next; /* Unlink */
+ if (control == con)
+ control = (connection *) 0;
+ return status;
+ } /*if */
+ } /* for */
+ return -1; /* very strange -- was not on list. */
+}
+
+static char *help_message_buffer = NULL; /* global :( */
+
+static void init_help_message_cache(void)
+{
+ FREE(help_message_buffer);
+}
+
+static void help_message_cache_add(char *string)
+{
+ if (help_message_buffer)
+ StrAllocCat(help_message_buffer, string);
+ else
+ StrAllocCopy(help_message_buffer, string);
+
+ CTRACE((tfp, "Adding message to help cache: %s\n", string));
+}
+
+static char *help_message_cache_non_empty(void)
+{
+ return (help_message_buffer);
+}
+
+static char *help_message_cache_contents(void)
+{
+ return (help_message_buffer);
+}
+
+/* Send One Command
+ * ----------------
+ *
+ * This function checks whether we have a control connection, and sends
+ * one command if given.
+ *
+ * On entry,
+ * control points to the connection which is established.
+ * cmd points to a command, or is zero to just get the response.
+ *
+ * The command should already be terminated with the CRLF pair.
+ *
+ * On exit,
+ * returns: 1 for success,
+ * or negative for communication failure (in which case
+ * the control connection will be closed).
+ */
+static int write_cmd(const char *cmd)
+{
+ int status;
+
+ if (!control) {
+ CTRACE((tfp, "HTFTP: No control connection set up!!\n"));
+ return HT_NO_CONNECTION;
+ }
+
+ if (cmd) {
+ CTRACE((tfp, " Tx: %s", cmd));
+#ifdef NOT_ASCII
+ {
+ char *p;
+
+ for (p = cmd; *p; p++) {
+ *p = TOASCII(*p);
+ }
+ }
+#endif /* NOT_ASCII */
+ status = (int) NETWRITE(control->socket, cmd, (unsigned) strlen(cmd));
+ if (status < 0) {
+ CTRACE((tfp,
+ "HTFTP: Error %d sending command: closing socket %d\n",
+ status, control->socket));
+ close_connection(control);
+ return status;
+ }
+ }
+ return 1;
+}
+
+/*
+ * For each string in the list, check if it is found in the response text.
+ * If so, return TRUE.
+ */
+static BOOL find_response(HTList *list)
+{
+ BOOL result = FALSE;
+ HTList *p = list;
+ char *value;
+
+ while ((value = (char *) HTList_nextObject(p)) != NULL) {
+ if (LYstrstr(response_text, value)) {
+ result = TRUE;
+ break;
+ }
+ }
+ return result;
+}
+
+/* Execute Command and get Response
+ * --------------------------------
+ *
+ * See the state machine illustrated in RFC959, p57. This implements
+ * one command/reply sequence. It also interprets lines which are to
+ * be continued, which are marked with a "-" immediately after the
+ * status code.
+ *
+ * Continuation then goes on until a line with a matching reply code
+ * an a space after it.
+ *
+ * On entry,
+ * control points to the connection which is established.
+ * cmd points to a command, or is zero to just get the response.
+ *
+ * The command must already be terminated with the CRLF pair.
+ *
+ * On exit,
+ * returns: The first digit of the reply type,
+ * or negative for communication failure.
+ */
+static int response(const char *cmd)
+{
+ int result; /* Three-digit decimal code */
+ int continuation_response = -1;
+ int status;
+
+ if ((status = write_cmd(cmd)) < 0)
+ return status;
+
+ do {
+ char *p = response_text;
+
+ for (;;) {
+ int ich = NEXT_CHAR;
+
+ if (((*p++ = (char) ich) == LF)
+ || (p == &response_text[LINE_LENGTH])) {
+
+ char continuation;
+
+ if (interrupted_in_htgetcharacter) {
+ CTRACE((tfp,
+ "HTFTP: Interrupted in HTGetCharacter, apparently.\n"));
+ NETCLOSE(control->socket);
+ control->socket = -1;
+ return HT_INTERRUPTED;
+ }
+
+ *p = '\0'; /* Terminate the string */
+ CTRACE((tfp, " Rx: %s", response_text));
+
+ /* Check for login or help messages */
+ if (!StrNCmp(response_text, "230-", 4) ||
+ !StrNCmp(response_text, "250-", 4) ||
+ !StrNCmp(response_text, "220-", 4))
+ help_message_cache_add(response_text + 4);
+
+ sscanf(response_text, "%d%c", &result, &continuation);
+ if (continuation_response == -1) {
+ if (continuation == '-') /* start continuation */
+ continuation_response = result;
+ } else { /* continuing */
+ if (continuation_response == result &&
+ continuation == ' ')
+ continuation_response = -1; /* ended */
+ }
+ if (result == 220 && find_response(broken_ftp_retr)) {
+ Broken_RETR = TRUE;
+ CTRACE((tfp, "This server is broken (RETR)\n"));
+ }
+#ifdef INET6
+ if (result == 220 && find_response(broken_ftp_epsv)) {
+ Broken_EPSV = TRUE;
+ CTRACE((tfp, "This server is broken (EPSV)\n"));
+ }
+#endif
+ break;
+ }
+ /* if end of line */
+ if (interrupted_in_htgetcharacter) {
+ CTRACE((tfp,
+ "HTFTP: Interrupted in HTGetCharacter, apparently.\n"));
+ NETCLOSE(control->socket);
+ control->socket = -1;
+ return HT_INTERRUPTED;
+ }
+
+ if (ich == EOF) {
+ CTRACE((tfp, "Error on rx: closing socket %d\n",
+ control->socket));
+ strcpy(response_text, "000 *** TCP read error on response\n");
+ close_connection(control);
+ return -1; /* End of file on response */
+ }
+ } /* Loop over characters */
+
+ } while (continuation_response != -1);
+
+ if (result == 421) {
+ CTRACE((tfp, "HTFTP: They close so we close socket %d\n",
+ control->socket));
+ close_connection(control);
+ return -1;
+ }
+ if ((result == 255 && server_type == CMS_SERVER) &&
+ (0 == strncasecomp(cmd, "CWD", 3) ||
+ 0 == strcasecomp(cmd, "CDUP"))) {
+ /*
+ * Alas, CMS returns 255 on failure to CWD to parent of root. - PG
+ */
+ result = 555;
+ }
+ return result / 100;
+}
+
+static int send_cmd_1(const char *verb)
+{
+ char command[80];
+
+ sprintf(command, "%.*s%c%c", (int) sizeof(command) - 4, verb, CR, LF);
+ return response(command);
+}
+
+static int send_cmd_2(const char *verb, const char *param)
+{
+ char *command = 0;
+ int status;
+
+ HTSprintf0(&command, "%s %s%c%c", verb, param, CR, LF);
+ status = response(command);
+ FREE(command);
+
+ return status;
+}
+
+#define send_cwd(path) send_cmd_2("CWD", path)
+
+/*
+ * This function should try to set the macintosh server into binary mode. Some
+ * servers need an additional letter after the MACB command.
+ */
+static int set_mac_binary(eServerType ServerType)
+{
+ /* try to set mac binary mode */
+ if (ServerType == APPLESHARE_SERVER ||
+ ServerType == NETPRESENZ_SERVER) {
+ /*
+ * Presumably E means "Enable". - KW
+ */
+ return (2 == response("MACB E\r\n"));
+ } else {
+ return (2 == response("MACB\r\n"));
+ }
+}
+
+/* This function gets the current working directory to help
+ * determine what kind of host it is
+ */
+
+static void get_ftp_pwd(eServerType *ServerType, BOOLEAN *UseList)
+{
+ char *cp;
+
+ /* get the working directory (to see what it looks like) */
+ int status = response("PWD\r\n");
+
+ if (status < 0) {
+ return;
+ } else {
+ cp = StrChr(response_text + 5, '"');
+ if (cp)
+ *cp = '\0';
+ if (*ServerType == TCPC_SERVER) {
+ *ServerType = ((response_text[5] == '/') ?
+ NCSA_SERVER : TCPC_SERVER);
+ CTRACE((tfp, "HTFTP: Treating as %s server.\n",
+ ((*ServerType == NCSA_SERVER) ?
+ "NCSA" : "TCPC")));
+ } else if (response_text[5] == '/') {
+ /* path names beginning with / imply Unix,
+ * right?
+ */
+ if (set_mac_binary(*ServerType)) {
+ *ServerType = NCSA_SERVER;
+ CTRACE((tfp, "HTFTP: Treating as NCSA server.\n"));
+ } else {
+ *ServerType = UNIX_SERVER;
+ *UseList = TRUE;
+ CTRACE((tfp, "HTFTP: Treating as Unix server.\n"));
+ }
+ return;
+ } else if (response_text[strlen(response_text) - 1] == ']') {
+ /* path names ending with ] imply VMS, right? */
+ *ServerType = VMS_SERVER;
+ *UseList = TRUE;
+ CTRACE((tfp, "HTFTP: Treating as VMS server.\n"));
+ } else {
+ *ServerType = GENERIC_SERVER;
+ CTRACE((tfp, "HTFTP: Treating as Generic server.\n"));
+ }
+
+ if ((*ServerType == NCSA_SERVER) ||
+ (*ServerType == TCPC_SERVER) ||
+ (*ServerType == PETER_LEWIS_SERVER) ||
+ (*ServerType == NETPRESENZ_SERVER))
+ set_mac_binary(*ServerType);
+ }
+}
+
+/* This function turns MSDOS-like directory output off for
+ * Windows NT servers.
+ */
+
+static void set_unix_dirstyle(eServerType *ServerType, BOOLEAN *UseList)
+{
+ char *cp;
+
+ /* This is a toggle. It seems we have to toggle in order to see
+ * the current state (after toggling), so we may end up toggling
+ * twice. - kw
+ */
+ int status = response("SITE DIRSTYLE\r\n");
+
+ if (status != 2) {
+ *ServerType = GENERIC_SERVER;
+ CTRACE((tfp, "HTFTP: DIRSTYLE failed, treating as Generic server.\n"));
+ return;
+ } else {
+ *UseList = TRUE;
+ /* Expecting one of:
+ * 200 MSDOS-like directory output is off
+ * 200 MSDOS-like directory output is on
+ * The following code doesn't look for the full exact string -
+ * who knows how the wording may change in some future version.
+ * If the first response isn't recognized, we toggle again
+ * anyway, under the assumption that it's more likely that
+ * the MSDOS setting was "off" originally. - kw
+ */
+ cp = strstr(response_text + 4, "MSDOS");
+ if (cp && strstr(cp, " off")) {
+ return; /* already off now. */
+ } else {
+ response("SITE DIRSTYLE\r\n");
+ }
+ }
+}
+
+#define CheckForInterrupt(msg) \
+ if (status == HT_INTERRUPTED) { \
+ CTRACE((tfp, "HTFTP: Interrupted %s.\n", msg)); \
+ _HTProgress(CONNECTION_INTERRUPTED); \
+ NETCLOSE(control->socket); \
+ control->socket = -1; \
+ return HT_INTERRUPTED; \
+ }
+
+/* Get a valid connection to the host
+ * ----------------------------------
+ *
+ * On entry,
+ * arg points to the name of the host in a hypertext address
+ * On exit,
+ * returns <0 if error
+ * socket number if success
+ *
+ * This routine takes care of managing timed-out connections, and
+ * limiting the number of connections in use at any one time.
+ *
+ * It ensures that all connections are logged in if they exist.
+ * It ensures they have the port number transferred.
+ */
+static int get_connection(const char *arg,
+ HTParentAnchor *anchor)
+{
+ int status;
+ char *command = 0;
+ connection *con;
+ char *username = NULL;
+ char *password = NULL;
+ static BOOLEAN firstuse = TRUE;
+
+ if (firstuse) {
+ /*
+ * Set up freeing at exit. - FM
+ */
+#ifdef LY_FIND_LEAKS
+ atexit(free_FTPGlobals);
+#endif
+ firstuse = FALSE;
+ }
+
+ if (control != 0) {
+ connection *next = control->next;
+
+ if (control->socket != -1) {
+ NETCLOSE(control->socket);
+ }
+ memset(con = control, 0, sizeof(*con));
+ con->next = next;
+ } else {
+ con = typecalloc(connection);
+ if (con == NULL)
+ outofmem(__FILE__, "get_connection");
+ }
+ con->socket = -1;
+
+ if (isEmpty(arg)) {
+ free(con);
+ return -1; /* Bad if no name specified */
+ }
+
+ /* Get node name:
+ */
+ CTRACE((tfp, "get_connection(%s)\n", arg));
+ {
+ char *p1 = HTParse(arg, "", PARSE_HOST);
+ char *p2 = strrchr(p1, '@'); /* user? */
+ char *pw = NULL;
+
+ if (p2 != NULL) {
+ username = p1;
+ *p2 = '\0'; /* terminate */
+ p1 = p2 + 1; /* point to host */
+ pw = StrChr(username, ':');
+ if (pw != NULL) {
+ *pw++ = '\0';
+ password = HTUnEscape(pw);
+ }
+ if (*username)
+ HTUnEscape(username);
+
+ /*
+ * If the password doesn't exist then we are going to have to ask
+ * the user for it. The only problem is that we don't want to ask
+ * for it every time, so we will store away in a primitive fashion.
+ */
+ if (!password) {
+ char *tmp = NULL;
+
+ HTSprintf0(&tmp, "%s@%s", username, p1);
+ /*
+ * If the user@host is not equal to the last time through or
+ * user_entered_password has no data then we need to ask the
+ * user for the password.
+ */
+ if (!last_username_and_host ||
+ strcmp(tmp, last_username_and_host) ||
+ !user_entered_password) {
+
+ StrAllocCopy(last_username_and_host, tmp);
+ HTSprintf0(&tmp, gettext("Enter password for user %s@%s:"),
+ username, p1);
+ FREE(user_entered_password);
+ user_entered_password = HTPromptPassword(tmp, NULL);
+
+ } /* else we already know the password */
+ password = user_entered_password;
+ FREE(tmp);
+ }
+ }
+
+ if (!username)
+ FREE(p1);
+ } /* scope of p1 */
+
+ status = HTDoConnect(arg, "FTP", IPPORT_FTP, (int *) &con->socket);
+
+ if (status < 0) {
+ if (status == HT_INTERRUPTED) {
+ CTRACE((tfp, "HTFTP: Interrupted on connect\n"));
+ } else {
+ CTRACE((tfp, "HTFTP: Unable to connect to remote host for `%s'.\n",
+ arg));
+ }
+ if (status == HT_INTERRUPTED) {
+ _HTProgress(CONNECTION_INTERRUPTED);
+ status = HT_NOT_LOADED;
+ } else {
+ HTAlert(gettext("Unable to connect to FTP host."));
+ }
+ if (con->socket != -1) {
+ NETCLOSE(con->socket);
+ }
+
+ FREE(username);
+ if (control == con)
+ control = NULL;
+ FREE(con);
+ return status; /* Bad return */
+ }
+
+ CTRACE((tfp, "FTP connected, socket %d control %p\n",
+ con->socket, (void *) con));
+ control = con; /* Current control connection */
+
+ /* Initialise buffering for control connection */
+ HTInitInput(control->socket);
+ init_help_message_cache(); /* Clear the login message buffer. */
+
+ /* Now we log in Look up username, prompt for pw.
+ */
+ status = response(NULL); /* Get greeting */
+ CheckForInterrupt("at beginning of login");
+
+ server_type = GENERIC_SERVER; /* reset */
+ if (status == 2) { /* Send username */
+ char *cp; /* look at greeting text */
+
+ /* don't gettext() this -- incoming text: */
+ if (strlen(response_text) > 4) {
+ if ((cp = strstr(response_text, " awaits your command")) ||
+ (cp = strstr(response_text, " ready."))) {
+ *cp = '\0';
+ }
+ cp = response_text + 4;
+ if (!strncasecomp(cp, "NetPresenz", 10))
+ server_type = NETPRESENZ_SERVER;
+ } else {
+ cp = response_text;
+ }
+ StrAllocCopy(anchor->server, cp);
+
+ status = send_cmd_2("USER", (username && *username)
+ ? username
+ : "anonymous");
+
+ CheckForInterrupt("while sending username");
+ }
+ if (status == 3) { /* Send password */
+ if (non_empty(password)) {
+ HTSprintf0(&command, "PASS %s%c%c", password, CR, LF);
+ } else {
+ /*
+ * No password was given; use mail-address.
+ */
+ const char *the_address;
+ char *user = NULL;
+ const char *host = NULL;
+ char *cp;
+
+ the_address = anonftp_password;
+ if (isEmpty(the_address))
+ the_address = personal_mail_address;
+ if (isEmpty(the_address))
+ the_address = LYGetEnv("USER");
+ if (isEmpty(the_address))
+ the_address = "WWWuser";
+
+ StrAllocCopy(user, the_address);
+ if ((cp = StrChr(user, '@')) != NULL) {
+ *cp++ = '\0';
+ if (*cp == '\0')
+ host = HTHostName();
+ else
+ host = cp;
+ } else {
+ host = HTHostName();
+ }
+
+ /*
+ * If host is not fully qualified, suppress it
+ * as ftp.uu.net prefers a blank to a bad name
+ */
+ if (!(host) || StrChr(host, '.') == NULL)
+ host = "";
+
+ HTSprintf0(&command, "PASS %s@%s%c%c", user, host, CR, LF);
+ FREE(user);
+ }
+ status = response(command);
+ FREE(command);
+ CheckForInterrupt("while sending password");
+ }
+ FREE(username);
+
+ if (status == 3) {
+ status = send_cmd_1("ACCT noaccount");
+ CheckForInterrupt("while sending password");
+ }
+ if (status != 2) {
+ CTRACE((tfp, "HTFTP: Login fail: %s", response_text));
+ /* if (control->socket > 0) close_connection(control->socket); */
+ return -1; /* Bad return */
+ }
+ CTRACE((tfp, "HTFTP: Logged in.\n"));
+
+ /* Check for host type */
+ if (server_type != NETPRESENZ_SERVER)
+ server_type = GENERIC_SERVER; /* reset */
+ use_list = FALSE; /* reset */
+ if (response("SYST\r\n") == 2) {
+ /* we got a line -- what kind of server are we talking to? */
+ if (StrNCmp(response_text + 4,
+ "UNIX Type: L8 MAC-OS MachTen", 28) == 0) {
+ server_type = MACHTEN_SERVER;
+ use_list = TRUE;
+ CTRACE((tfp, "HTFTP: Treating as MachTen server.\n"));
+
+ } else if (strstr(response_text + 4, "UNIX") != NULL ||
+ strstr(response_text + 4, "Unix") != NULL) {
+ server_type = UNIX_SERVER;
+ unsure_type = FALSE; /* to the best of out knowledge... */
+ use_list = TRUE;
+ CTRACE((tfp, "HTFTP: Treating as Unix server.\n"));
+
+ } else if (strstr(response_text + 4, "MSDOS") != NULL) {
+ server_type = MSDOS_SERVER;
+ use_list = TRUE;
+ CTRACE((tfp, "HTFTP: Treating as MSDOS (Unix emulation) server.\n"));
+
+ } else if (StrNCmp(response_text + 4, "VMS", 3) == 0) {
+ char *tilde = strstr(arg, "/~");
+
+ use_list = TRUE;
+ if (tilde != 0
+ && tilde[2] != 0
+ && strstr(response_text + 4, "MadGoat") != 0) {
+ server_type = UNIX_SERVER;
+ CTRACE((tfp, "HTFTP: Treating VMS as UNIX server.\n"));
+ } else {
+ server_type = VMS_SERVER;
+ CTRACE((tfp, "HTFTP: Treating as VMS server.\n"));
+ }
+
+ } else if ((StrNCmp(response_text + 4, "VM/CMS", 6) == 0) ||
+ (StrNCmp(response_text + 4, "VM ", 3) == 0)) {
+ server_type = CMS_SERVER;
+ use_list = TRUE;
+ CTRACE((tfp, "HTFTP: Treating as CMS server.\n"));
+
+ } else if (StrNCmp(response_text + 4, "DCTS", 4) == 0) {
+ server_type = DCTS_SERVER;
+ CTRACE((tfp, "HTFTP: Treating as DCTS server.\n"));
+
+ } else if (strstr(response_text + 4, "MAC-OS TCP/Connect II") != NULL) {
+ server_type = TCPC_SERVER;
+ CTRACE((tfp, "HTFTP: Looks like a TCPC server.\n"));
+ get_ftp_pwd(&server_type, &use_list);
+ unsure_type = TRUE;
+
+ } else if (server_type == NETPRESENZ_SERVER) { /* already set above */
+ use_list = TRUE;
+ set_mac_binary(server_type);
+ CTRACE((tfp, "HTFTP: Treating as NetPresenz (MACOS) server.\n"));
+
+ } else if (StrNCmp(response_text + 4, "MACOS Peter's Server", 20) == 0) {
+ server_type = PETER_LEWIS_SERVER;
+ use_list = TRUE;
+ set_mac_binary(server_type);
+ CTRACE((tfp, "HTFTP: Treating as Peter Lewis (MACOS) server.\n"));
+
+ } else if (StrNCmp(response_text + 4, "Windows_NT", 10) == 0) {
+ server_type = WINDOWS_NT_SERVER;
+ CTRACE((tfp, "HTFTP: Treating as Window_NT server.\n"));
+ set_unix_dirstyle(&server_type, &use_list);
+
+ } else if (StrNCmp(response_text + 4, "Windows2000", 11) == 0) {
+ server_type = WINDOWS_2K_SERVER;
+ CTRACE((tfp, "HTFTP: Treating as Window_2K server.\n"));
+ set_unix_dirstyle(&server_type, &use_list);
+
+ } else if (StrNCmp(response_text + 4, "MS Windows", 10) == 0) {
+ server_type = MS_WINDOWS_SERVER;
+ use_list = TRUE;
+ CTRACE((tfp, "HTFTP: Treating as MS Windows server.\n"));
+
+ } else if (StrNCmp(response_text + 4,
+ "MACOS AppleShare IP FTP Server", 30) == 0) {
+ server_type = APPLESHARE_SERVER;
+ use_list = TRUE;
+ set_mac_binary(server_type);
+ CTRACE((tfp, "HTFTP: Treating as AppleShare server.\n"));
+
+ } else {
+ server_type = GENERIC_SERVER;
+ CTRACE((tfp, "HTFTP: Ugh! A Generic server.\n"));
+ get_ftp_pwd(&server_type, &use_list);
+ unsure_type = TRUE;
+ }
+ } else {
+ /* SYST fails :( try to get the type from the PWD command */
+ get_ftp_pwd(&server_type, &use_list);
+ }
+
+ return con->socket; /* Good return */
+}
+
+static void reset_master_socket(void)
+{
+ have_socket = FALSE;
+}
+
+static void set_master_socket(int value)
+{
+ have_socket = (BOOLEAN) (value >= 0);
+ if (have_socket)
+ master_socket = (LYNX_FD) value;
+}
+
+/* Close Master (listening) socket
+ * -------------------------------
+ *
+ *
+ */
+static int close_master_socket(void)
+{
+ int status;
+
+ if (have_socket)
+ FD_CLR(master_socket, &open_sockets);
+
+ status = NETCLOSE((int) master_socket);
+ CTRACE((tfp, "HTFTP: Closed master socket %u\n", (unsigned) master_socket));
+
+ reset_master_socket();
+
+ if (status < 0)
+ return HTInetStatus(gettext("close master socket"));
+ else
+ return status;
+}
+
+/* Open a master socket for listening on
+ * -------------------------------------
+ *
+ * When data is transferred, we open a port, and wait for the server to
+ * connect with the data.
+ *
+ * On entry,
+ * have_socket Must be false, if master_socket is not setup already
+ * master_socket Must be negative if not set up already.
+ * On exit,
+ * Returns socket number if good
+ * less than zero if error.
+ * master_socket is socket number if good, else negative.
+ * port_number is valid if good.
+ */
+static int get_listen_socket(void)
+{
+ LY_SOCKADDR soc_A;
+
+#ifdef INET6
+ unsigned short af;
+ LY_SOCKLEN slen;
+#endif /* INET6 */
+ int new_socket; /* Will be master_socket */
+
+ FD_ZERO(&open_sockets); /* Clear our record of open sockets */
+ num_sockets = 0;
+
+ FREE(port_command);
+#ifndef REPEAT_LISTEN
+ if (have_socket)
+ return master_socket; /* Done already */
+#endif /* !REPEAT_LISTEN */
+
+#ifdef INET6
+ /* query address family of control connection */
+ memset(&soc_A, 0, sizeof(soc_A));
+ slen = (LY_SOCKLEN) sizeof(soc_A);
+ if (getsockname(control->socket, SOCKADDR_OF(soc_A), &slen) < 0) {
+ return HTInetStatus("getsockname failed");
+ }
+ af = SOCKADDR_OF(soc_A)->sa_family;
+#endif /* INET6 */
+
+/* Create internet socket
+*/
+#ifdef INET6
+ new_socket = socket(af, SOCK_STREAM, IPPROTO_TCP);
+#else
+ new_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+#endif /* INET6 */
+
+ if (new_socket < 0)
+ return HTInetStatus(gettext("socket for master socket"));
+
+ CTRACE((tfp, "HTFTP: Opened master socket number %d\n", new_socket));
+
+/* Search for a free port.
+*/
+#ifdef INET6
+ memset(&soc_A, 0, sizeof(soc_A));
+ SOCKADDR_OF(soc_A)->sa_family = (unsigned short) af;
+ switch (af) {
+ case AF_INET:
+#ifdef SIN6_LEN
+ SOCKADDR_OF(soc_A)->sa_len = sizeof(struct sockaddr_in);
+#endif /* SIN6_LEN */
+ break;
+ case AF_INET6:
+#ifdef SIN6_LEN
+ SOCKADDR_OF(soc_A)->sa_len = sizeof(struct sockaddr_in6);
+#endif /* SIN6_LEN */
+ break;
+ default:
+ HTInetStatus("AF");
+ }
+#else
+ soc_A.soc_in.sin_family = AF_INET; /* Family = internet, host order */
+ soc_A.soc_in.sin_addr.s_addr = INADDR_ANY; /* Any peer address */
+#endif /* INET6 */
+#ifdef POLL_PORTS
+ {
+ PortNumber old_port_number = port_number;
+
+ for (port_number = (old_port_number + 1);; port_number++) {
+ int status;
+
+ if (port_number > LAST_TCP_PORT)
+ port_number = FIRST_TCP_PORT;
+ if (port_number == old_port_number) {
+ return HTInetStatus("bind");
+ }
+#ifdef INET6
+ soc_A.soc_in.sin_port = htons(port_number);
+#else
+ soc_A.sin_port = htons(port_number);
+#endif /* INET6 */
+#ifdef SOCKS
+ if (socks_flag)
+ if ((status = Rbind(new_socket,
+ SOCKADDR_OF(soc_A),
+ SOCKADDR_LEN(soc_A))) == 0) {
+ break;
+ } else
+#endif /* SOCKS */
+ if ((status = bind(new_socket,
+ SOCKADDR_OF(soc_A),
+ SOCKADDR_LEN(soc_A)
+ )) == 0) {
+ break;
+ }
+ CTRACE((tfp, "TCP bind attempt to port %d yields %d, errno=%d\n",
+ port_number, status, SOCKET_ERRNO));
+ } /* for */
+ }
+#else
+ {
+ int status;
+ LY_SOCKLEN address_length = (LY_SOCKLEN) sizeof(soc_A);
+
+#ifdef SOCKS
+ if (socks_flag)
+ status = Rgetsockname(control->socket,
+ SOCKADDR_OF(soc_A),
+ &address_length);
+ else
+#endif /* SOCKS */
+ status = getsockname(control->socket,
+ SOCKADDR_OF(soc_A),
+ &address_length);
+ if (status < 0) {
+ close(new_socket);
+ return HTInetStatus("getsockname");
+ }
+ CTRACE((tfp, "HTFTP: This host is %s\n",
+ HTInetString((void *) &soc_A.soc_in)));
+
+ soc_A.soc_in.sin_port = 0; /* Unspecified: please allocate */
+#ifdef SOCKS
+ if (socks_flag)
+ status = Rbind(new_socket,
+ SOCKADDR_OF(soc_A),
+ sizeof(soc_A));
+ else
+#endif /* SOCKS */
+ status = bind(new_socket,
+ SOCKADDR_OF(soc_A),
+ SOCKADDR_LEN(soc_A));
+ if (status < 0) {
+ close(new_socket);
+ return HTInetStatus("bind");
+ }
+
+ address_length = sizeof(soc_A);
+#ifdef SOCKS
+ if (socks_flag)
+ status = Rgetsockname(new_socket,
+ SOCKADDR_OF(soc_A),
+ &address_length);
+ else
+#endif /* SOCKS */
+ status = getsockname(new_socket,
+ SOCKADDR_OF(soc_A),
+ &address_length);
+ if (status < 0) {
+ close(new_socket);
+ return HTInetStatus("getsockname");
+ }
+ }
+#endif /* POLL_PORTS */
+
+ CTRACE((tfp, "HTFTP: bound to port %d on %s\n",
+ (int) ntohs(soc_A.soc_in.sin_port),
+ HTInetString((void *) &soc_A.soc_in)));
+
+#ifdef REPEAT_LISTEN
+ if (have_socket)
+ (void) close_master_socket();
+#endif /* REPEAT_LISTEN */
+
+ set_master_socket(new_socket);
+
+/* Now we must find out who we are to tell the other guy
+*/
+ (void) HTHostName(); /* Make address valid - doesn't work */
+#ifdef INET6
+ switch (SOCKADDR_OF(soc_A)->sa_family) {
+ case AF_INET:
+#endif /* INET6 */
+ HTSprintf0(&port_command, "PORT %d,%d,%d,%d,%d,%d%c%c",
+ (int) *((unsigned char *) (&soc_A.soc_in.sin_addr) + 0),
+ (int) *((unsigned char *) (&soc_A.soc_in.sin_addr) + 1),
+ (int) *((unsigned char *) (&soc_A.soc_in.sin_addr) + 2),
+ (int) *((unsigned char *) (&soc_A.soc_in.sin_addr) + 3),
+ (int) *((unsigned char *) (&soc_A.soc_in.sin_port) + 0),
+ (int) *((unsigned char *) (&soc_A.soc_in.sin_port) + 1),
+ CR, LF);
+
+#ifdef INET6
+ break;
+
+ case AF_INET6:
+ {
+ char hostbuf[MAXHOSTNAMELEN];
+ char portbuf[MAXHOSTNAMELEN];
+
+ getnameinfo(SOCKADDR_OF(soc_A),
+ SOCKADDR_LEN(soc_A),
+ hostbuf,
+ (socklen_t) sizeof(hostbuf),
+ portbuf,
+ (socklen_t) sizeof(portbuf),
+ NI_NUMERICHOST | NI_NUMERICSERV);
+ HTSprintf0(&port_command, "EPRT |%d|%s|%s|%c%c", 2, hostbuf, portbuf,
+ CR, LF);
+ break;
+ }
+ default:
+ HTSprintf0(&port_command, "JUNK%c%c", CR, LF);
+ break;
+ }
+#endif /* INET6 */
+ if (port_command == NULL)
+ return -1;
+
+ /* Inform TCP that we will accept connections
+ */
+ {
+ int status;
+
+#ifdef SOCKS
+ if (socks_flag)
+ status = Rlisten((int) master_socket, 1);
+ else
+#endif /* SOCKS */
+ status = listen((int) master_socket, 1);
+ if (status < 0) {
+ reset_master_socket();
+ return HTInetStatus("listen");
+ }
+ }
+ CTRACE((tfp, "TCP: Master socket(), bind() and listen() all OK\n"));
+ FD_SET(master_socket, &open_sockets);
+ if ((master_socket + 1) > num_sockets)
+ num_sockets = master_socket + 1;
+
+ return (int) master_socket; /* Good */
+
+} /* get_listen_socket */
+
+static const char *months[12] =
+{
+ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+};
+
+/* Procedure: Set the current and last year strings and date integer
+ * -----------------------------------------------------------------
+ *
+ * Bug:
+ * This code is for sorting listings by date, if that option
+ * is selected in Lynx, and doesn't take into account time
+ * zones or ensure resetting at midnight, so the sort may not
+ * be perfect, but the actual date isn't changed in the display,
+ * i.e., the date is still correct. - FM
+ */
+static void set_years_and_date(void)
+{
+ char day[8], month[8], date[12];
+ time_t NowTime;
+ int i;
+ char *printable;
+
+ NowTime = time(NULL);
+ printable = ctime(&NowTime);
+ LYStrNCpy(day, printable + 8, 2);
+ if (day[0] == ' ') {
+ day[0] = '0';
+ }
+ LYStrNCpy(month, printable + 4, 3);
+ for (i = 0; i < 12; i++) {
+ if (!strcasecomp(month, months[i])) {
+ break;
+ }
+ }
+ i++;
+ sprintf(date, "9999%02d%.2s", i % 100, day);
+ TheDate = atoi(date);
+ LYStrNCpy(ThisYear, printable + 20, 4);
+ sprintf(LastYear, "%d", (atoi(ThisYear) - 1) % 10000);
+ HaveYears = TRUE;
+}
+
+typedef struct _EntryInfo {
+ char *filename;
+ char *linkname; /* symbolic link, if any */
+ char *type;
+ char *date;
+ off_t size;
+ BOOLEAN display; /* show this entry? */
+#ifdef LONG_LIST
+ unsigned long file_links;
+ char *file_mode;
+ char *file_user;
+ char *file_group;
+#endif
+} EntryInfo;
+
+static void free_entryinfo_struct_contents(EntryInfo *entry_info)
+{
+ if (entry_info) {
+#ifdef LONG_LIST
+ FREE(entry_info->file_mode);
+ FREE(entry_info->file_user);
+ FREE(entry_info->file_group);
+#endif
+ FREE(entry_info->filename);
+ FREE(entry_info->linkname);
+ FREE(entry_info->type);
+ FREE(entry_info->date);
+ }
+ /* don't free the struct */
+}
+
+/*
+ * is_ls_date() --
+ * Return TRUE if s points to a string of the form:
+ * "Sep 1 1990 " or
+ * "Sep 11 11:59 " or
+ * "Dec 12 1989 " or
+ * "FCv 23 1990 " ...
+ */
+static BOOLEAN is_ls_date(char *s)
+{
+ /* must start with three alpha characters */
+ if (!isalpha(UCH(*s++)) || !isalpha(UCH(*s++)) || !isalpha(UCH(*s++)))
+ return FALSE;
+
+ /* space or HT_NON_BREAK_SPACE */
+ if (!(*s == ' ' || *s == HT_NON_BREAK_SPACE)) {
+ return FALSE;
+ }
+ s++;
+
+ /* space or digit */
+ if (!(*s == ' ' || isdigit(UCH(*s)))) {
+ return FALSE;
+ }
+ s++;
+
+ /* digit */
+ if (!isdigit(UCH(*s++)))
+ return FALSE;
+
+ /* space */
+ if (*s++ != ' ')
+ return FALSE;
+
+ /* space or digit */
+ if (!(*s == ' ' || isdigit(UCH(*s)))) {
+ return FALSE;
+ }
+ s++;
+
+ /* digit */
+ if (!isdigit(UCH(*s++)))
+ return FALSE;
+
+ /* colon or digit */
+ if (!(*s == ':' || isdigit(UCH(*s)))) {
+ return FALSE;
+ }
+ s++;
+
+ /* digit */
+ if (!isdigit(UCH(*s++)))
+ return FALSE;
+
+ /* space or digit */
+ if (!(*s == ' ' || isdigit(UCH(*s)))) {
+ return FALSE;
+ }
+ s++;
+
+ /* space */
+ if (*s != ' ')
+ return FALSE;
+
+ return TRUE;
+} /* is_ls_date() */
+
+/*
+ * Extract the name, size, and date from an EPLF line. - 08-06-96 DJB
+ */
+static void parse_eplf_line(char *line,
+ EntryInfo *info)
+{
+ char *cp = line;
+ char ct[26];
+ off_t size;
+ time_t secs;
+ static time_t base; /* time() value on this OS in 1970 */
+ static int flagbase = 0;
+
+ if (!flagbase) {
+ struct tm t;
+
+ t.tm_year = 70;
+ t.tm_mon = 0;
+ t.tm_mday = 0;
+ t.tm_hour = 0;
+ t.tm_min = 0;
+ t.tm_sec = 0;
+ t.tm_isdst = -1;
+ base = mktime(&t); /* could return -1 */
+ flagbase = 1;
+ }
+
+ while (*cp) {
+ switch (*cp) {
+ case '\t':
+ StrAllocCopy(info->filename, cp + 1);
+ return;
+ case 's':
+ size = 0;
+ while (*(++cp) && (*cp != ','))
+ size = (size * 10) + (off_t) (*cp - '0');
+ info->size = size;
+ break;
+ case 'm':
+ secs = 0;
+ while (*(++cp) && (*cp != ','))
+ secs = (secs * 10) + (*cp - '0');
+ secs += base; /* assumes that time_t is #seconds */
+ LYStrNCpy(ct, ctime(&secs), 24);
+ StrAllocCopy(info->date, ct);
+ break;
+ case '/':
+ StrAllocCopy(info->type, ENTRY_IS_DIRECTORY);
+ /* FALLTHRU */
+ default:
+ while (*cp) {
+ if (*cp++ == ',')
+ break;
+ }
+ break;
+ }
+ }
+} /* parse_eplf_line */
+
+/*
+ * Extract the name, size, and date from an ls -l line.
+ */
+static void parse_ls_line(char *line,
+ EntryInfo *entry)
+{
+#ifdef LONG_LIST
+ char *next;
+ char *cp;
+#endif
+ int i, j;
+ off_t base = 1;
+ off_t size_num = 0;
+
+ for (i = (int) strlen(line) - 1;
+ (i > 13) && (!isspace(UCH(line[i])) || !is_ls_date(&line[i - 12]));
+ i--) {
+ ; /* null body */
+ }
+ line[i] = '\0';
+ if (i > 13) {
+ StrAllocCopy(entry->date, &line[i - 12]);
+ /* replace the 4th location with nbsp if it is a space or zero */
+ if (entry->date[4] == ' ' || entry->date[4] == '0')
+ entry->date[4] = HT_NON_BREAK_SPACE;
+ /* make sure year or time is flush right */
+ if (entry->date[11] == ' ') {
+ for (j = 11; j > 6; j--) {
+ entry->date[j] = entry->date[j - 1];
+ }
+ }
+ }
+ j = i - 14;
+ while (isdigit(UCH(line[j]))) {
+ size_num += ((off_t) (line[j] - '0') * base);
+ base *= 10;
+ j--;
+ }
+ entry->size = size_num;
+ StrAllocCopy(entry->filename, &line[i + 1]);
+
+#ifdef LONG_LIST
+ line[j] = '\0';
+
+ /*
+ * Extract the file-permissions, as a string.
+ */
+ if ((cp = StrChr(line, ' ')) != 0) {
+ if ((cp - line) == 10) {
+ *cp = '\0';
+ StrAllocCopy(entry->file_mode, line);
+ *cp = ' ';
+ }
+
+ /*
+ * Next is the link-count.
+ */
+ next = 0;
+ entry->file_links = (unsigned long) strtol(cp, &next, 10);
+ if (next == 0 || *next != ' ') {
+ entry->file_links = 0;
+ next = cp;
+ } else {
+ cp = next;
+ }
+ /*
+ * Next is the user-name.
+ */
+ while (isspace(UCH(*cp)))
+ ++cp;
+ if ((next = StrChr(cp, ' ')) != 0)
+ *next = '\0';
+ if (*cp != '\0')
+ StrAllocCopy(entry->file_user, cp);
+ /*
+ * Next is the group-name (perhaps).
+ */
+ if (next != NULL) {
+ cp = (next + 1);
+ while (isspace(UCH(*cp)))
+ ++cp;
+ if ((next = StrChr(cp, ' ')) != 0)
+ *next = '\0';
+ if (*cp != '\0')
+ StrAllocCopy(entry->file_group, cp);
+ }
+ }
+#endif
+}
+
+/*
+ * Extract the name and size info and whether it refers to a directory from a
+ * LIST line in "dls" format.
+ */
+static void parse_dls_line(char *line,
+ EntryInfo *entry_info,
+ char **pspilledname)
+{
+ short j;
+ int base = 1;
+ off_t size_num = 0;
+ int len;
+ char *cps = NULL;
+
+ /* README 763 Information about this server\0
+ bin/ - \0
+ etc/ = \0
+ ls-lR 0 \0
+ ls-lR.Z 3 \0
+ pub/ = Public area\0
+ usr/ - \0
+ morgan 14 -> ../real/morgan\0
+ TIMIT.mostlikely.Z\0
+ 79215 \0
+ */
+
+ len = (int) strlen(line);
+ if (len == 0) {
+ FREE(*pspilledname);
+ entry_info->display = FALSE;
+ return;
+ }
+ cps = LYSkipNonBlanks(line);
+ if (*cps == '\0') { /* only a filename, save it and return. */
+ StrAllocCopy(*pspilledname, line);
+ entry_info->display = FALSE;
+ return;
+ }
+ if (len < 24 || line[23] != ' ' ||
+ (isspace(UCH(line[0])) && !*pspilledname)) {
+ /* this isn't the expected "dls" format! */
+ if (!isspace(UCH(line[0])))
+ *cps = '\0';
+ if (*pspilledname && !*line) {
+ entry_info->filename = *pspilledname;
+ *pspilledname = NULL;
+ if (entry_info->filename[strlen(entry_info->filename) - 1] == '/')
+ StrAllocCopy(entry_info->type, ENTRY_IS_DIRECTORY);
+ else
+ StrAllocCopy(entry_info->type, "");
+ } else {
+ StrAllocCopy(entry_info->filename, line);
+ if (cps != line && *(cps - 1) == '/')
+ StrAllocCopy(entry_info->type, ENTRY_IS_DIRECTORY);
+ else
+ StrAllocCopy(entry_info->type, "");
+ FREE(*pspilledname);
+ }
+ return;
+ }
+
+ j = 22;
+ if (line[j] == '=' || line[j] == '-') {
+ StrAllocCopy(entry_info->type, ENTRY_IS_DIRECTORY);
+ } else {
+ while (isdigit(UCH(line[j]))) {
+ size_num += (line[j] - '0') * base;
+ base *= 10;
+ j--;
+ }
+ }
+ entry_info->size = size_num;
+
+ cps = LYSkipBlanks(&line[23]);
+ if (!StrNCmp(cps, "-> ", 3) && cps[3] != '\0' && cps[3] != ' ') {
+ StrAllocCopy(entry_info->type, ENTRY_IS_SYMBOLIC_LINK);
+ StrAllocCopy(entry_info->linkname, LYSkipBlanks(cps + 3));
+ entry_info->size = 0; /* don't display size */
+ }
+
+ if (j > 0)
+ line[j] = '\0';
+
+ LYTrimTrailing(line);
+
+ len = (int) strlen(line);
+ if (len == 0 && *pspilledname && **pspilledname) {
+ line = *pspilledname;
+ len = (int) strlen(*pspilledname);
+ }
+ if (len > 0 && line[len - 1] == '/') {
+ /*
+ * It's a dir, remove / and mark it as such.
+ */
+ if (len > 1)
+ line[len - 1] = '\0';
+ if (!entry_info->type)
+ StrAllocCopy(entry_info->type, ENTRY_IS_DIRECTORY);
+ }
+
+ StrAllocCopy(entry_info->filename, line);
+ FREE(*pspilledname);
+} /* parse_dls_line() */
+
+/*
+ * parse_vms_dir_entry()
+ * Format the name, date, and size from a VMS LIST line
+ * into the EntryInfo structure - FM
+ */
+static void parse_vms_dir_entry(char *line,
+ EntryInfo *entry_info)
+{
+ int i, j;
+ off_t ialloc;
+ char *cp, *cpd, *cps, date[16];
+ const char *sp = " ";
+
+ /* Get rid of blank lines, and information lines. Valid lines have the ';'
+ * version number token.
+ */
+ if (!strlen(line) || (cp = StrChr(line, ';')) == NULL) {
+ entry_info->display = FALSE;
+ return;
+ }
+
+ /* Cut out file or directory name at VMS version number. */
+ *cp++ = '\0';
+ StrAllocCopy(entry_info->filename, line);
+
+ /* Cast VMS non-README file and directory names to lowercase. */
+ if (strstr(entry_info->filename, "READ") == NULL) {
+ LYLowerCase(entry_info->filename);
+ i = (int) strlen(entry_info->filename);
+ } else {
+ i = (int) ((strstr(entry_info->filename, "READ")
+ - entry_info->filename)
+ + 4);
+ if (!StrNCmp(&entry_info->filename[i], "ME", 2)) {
+ i += 2;
+ while (entry_info->filename[i] && entry_info->filename[i] != '.') {
+ i++;
+ }
+ } else if (!StrNCmp(&entry_info->filename[i], ".ME", 3)) {
+ i = (int) strlen(entry_info->filename);
+ } else {
+ i = 0;
+ }
+ LYLowerCase(entry_info->filename + i);
+ }
+
+ /* Uppercase terminal .z's or _z's. */
+ if ((--i > 2) &&
+ entry_info->filename[i] == 'z' &&
+ (entry_info->filename[i - 1] == '.' ||
+ entry_info->filename[i - 1] == '_'))
+ entry_info->filename[i] = 'Z';
+
+ /* Convert any tabs in rest of line to spaces. */
+ cps = cp - 1;
+ while ((cps = StrChr(cps + 1, '\t')) != NULL)
+ *cps = ' ';
+
+ /* Collapse serial spaces. */
+ i = 0;
+ j = 1;
+ cps = cp;
+ while (cps[j] != '\0') {
+ if (cps[i] == ' ' && cps[j] == ' ')
+ j++;
+ else
+ cps[++i] = cps[j++];
+ }
+ cps[++i] = '\0';
+
+ /* Set the years and date, if we don't have them yet. * */
+ if (!HaveYears) {
+ set_years_and_date();
+ }
+
+ /* Track down the date. */
+ if ((cpd = StrChr(cp, '-')) != NULL &&
+ strlen(cpd) > 9 && isdigit(UCH(*(cpd - 1))) &&
+ isalpha(UCH(*(cpd + 1))) && *(cpd + 4) == '-') {
+
+ /* Month */
+ *(cpd + 2) = (char) TOLOWER(*(cpd + 2));
+ *(cpd + 3) = (char) TOLOWER(*(cpd + 3));
+ sprintf(date, "%.3s ", cpd + 1);
+
+ /* Day */
+ if (isdigit(UCH(*(cpd - 2))))
+ sprintf(date + 4, "%.2s ", cpd - 2);
+ else
+ sprintf(date + 4, "%c%.1s ", HT_NON_BREAK_SPACE, cpd - 1);
+
+ /* Time or Year */
+ if (!StrNCmp(ThisYear, cpd + 5, 4) &&
+ strlen(cpd) > 15 && *(cpd + 12) == ':') {
+ sprintf(date + 7, "%.5s", cpd + 10);
+ } else {
+ sprintf(date + 7, " %.4s", cpd + 5);
+ }
+
+ StrAllocCopy(entry_info->date, date);
+ }
+
+ /* Track down the size */
+ if ((cpd = StrChr(cp, '/')) != NULL) {
+ /* Appears be in used/allocated format */
+ cps = cpd;
+ while (isdigit(UCH(*(cps - 1))))
+ cps--;
+ if (cps < cpd)
+ *cpd = '\0';
+ entry_info->size = LYatoll(cps);
+ cps = cpd + 1;
+ while (isdigit(UCH(*cps)))
+ cps++;
+ *cps = '\0';
+ ialloc = LYatoll(cpd + 1);
+ /* Check if used is in blocks or bytes */
+ if (entry_info->size <= ialloc)
+ entry_info->size *= 512;
+
+ } else if (strtok(cp, sp) != NULL) {
+ /* We just initialized on the version number */
+ /* Now let's hunt for a lone, size number */
+ while ((cps = strtok(NULL, sp)) != NULL) {
+ cpd = cps;
+ while (isdigit(UCH(*cpd)))
+ cpd++;
+ if (*cpd == '\0') {
+ /* Assume it's blocks */
+ entry_info->size = (LYatoll(cps) * 512);
+ break;
+ }
+ }
+ }
+
+ TRACE_ENTRY("VMS", entry_info);
+ return;
+} /* parse_vms_dir_entry() */
+
+/*
+ * parse_ms_windows_dir_entry() --
+ * Format the name, date, and size from an MS_WINDOWS LIST line into
+ * the EntryInfo structure (assumes Chameleon NEWT format). - FM
+ */
+static void parse_ms_windows_dir_entry(char *line,
+ EntryInfo *entry_info)
+{
+ char *cp = line;
+ char *cps, *cpd, date[16];
+ char *end = line + strlen(line);
+
+ /* Get rid of blank or junk lines. */
+ cp = LYSkipBlanks(cp);
+ if (!(*cp)) {
+ entry_info->display = FALSE;
+ return;
+ }
+
+ /* Cut out file or directory name. */
+ cps = LYSkipNonBlanks(cp);
+ *cps++ = '\0';
+ cpd = cps;
+ StrAllocCopy(entry_info->filename, cp);
+
+ /* Track down the size */
+ if (cps < end) {
+ cps = LYSkipBlanks(cps);
+ cpd = LYSkipNonBlanks(cps);
+ *cpd++ = '\0';
+ if (isdigit(UCH(*cps))) {
+ entry_info->size = LYatoll(cps);
+ } else {
+ StrAllocCopy(entry_info->type, ENTRY_IS_DIRECTORY);
+ }
+ } else {
+ StrAllocCopy(entry_info->type, "");
+ }
+
+ /* Set the years and date, if we don't have them yet. * */
+ if (!HaveYears) {
+ set_years_and_date();
+ }
+
+ /* Track down the date. */
+ if (cpd < end) {
+ cpd = LYSkipBlanks(cpd);
+ if (strlen(cpd) > 17) {
+ *(cpd + 6) = '\0'; /* Month and Day */
+ *(cpd + 11) = '\0'; /* Year */
+ *(cpd + 17) = '\0'; /* Time */
+ if (strcmp(ThisYear, cpd + 7))
+ /* Not this year, so show the year */
+ sprintf(date, "%.6s %.4s", cpd, (cpd + 7));
+ else
+ /* Is this year, so show the time */
+ sprintf(date, "%.6s %.5s", cpd, (cpd + 12));
+ StrAllocCopy(entry_info->date, date);
+ if (entry_info->date[4] == ' ' || entry_info->date[4] == '0') {
+ entry_info->date[4] = HT_NON_BREAK_SPACE;
+ }
+ }
+ }
+
+ TRACE_ENTRY("MS Windows", entry_info);
+ return;
+} /* parse_ms_windows_dir_entry */
+
+/*
+ * parse_windows_nt_dir_entry() --
+ * Format the name, date, and size from a WINDOWS_NT LIST line into
+ * the EntryInfo structure (assumes Chameleon NEWT format). - FM
+ */
+#ifdef NOTDEFINED
+static void parse_windows_nt_dir_entry(char *line,
+ EntryInfo *entry_info)
+{
+ char *cp = line;
+ char *cps, *cpd, date[16];
+ char *end = line + strlen(line);
+ int i;
+
+ /* Get rid of blank or junk lines. */
+ cp = LYSkipBlanks(cp);
+ if (!(*cp)) {
+ entry_info->display = FALSE;
+ return;
+ }
+
+ /* Cut out file or directory name. */
+ cpd = cp;
+ cps = LYSkipNonBlanks(end - 1);
+ cp = (cps + 1);
+ if (!strcmp(cp, ".") || !strcmp(cp, "..")) {
+ entry_info->display = FALSE;
+ return;
+ }
+ StrAllocCopy(entry_info->filename, cp);
+ if (cps < cpd)
+ return;
+ *cp = '\0';
+ end = cp;
+
+ /* Set the years and date, if we don't have them yet. * */
+ if (!HaveYears) {
+ set_years_and_date();
+ }
+
+ /* Cut out the date. */
+ cp = cps = cpd;
+ cps = LYSkipNonBlanks(cps);
+ *cps++ = '\0';
+ if (cps > end) {
+ entry_info->display = FALSE;
+ return;
+ }
+ cps = LYSkipBlanks(cps);
+ cpd = LYSkipNonBlanks(cps);
+ *cps++ = '\0';
+ if (cps > end || cpd == cps || strlen(cpd) < 7) {
+ entry_info->display = FALSE;
+ return;
+ }
+ if (strlen(cp) == 8 &&
+ isdigit(*cp) && isdigit(*(cp + 1)) && *(cp + 2) == '-' &&
+ isdigit(*(cp + 3)) && isdigit(*(cp + 4)) && *(cp + 5) == '-') {
+ *(cp + 2) = '\0'; /* Month */
+ i = atoi(cp) - 1;
+ *(cp + 5) = '\0'; /* Day */
+ sprintf(date, "%.3s %.2s", months[i], (cp + 3));
+ if (date[4] == '0')
+ date[4] = ' ';
+ cp += 6; /* Year */
+ if (strcmp((ThisYear + 2), cp)) {
+ /* Not this year, so show the year */
+ if (atoi(cp) < 70) {
+ sprintf(&date[6], " 20%.2s", cp);
+ } else {
+ sprintf(&date[6], " 19%.2s", cp);
+ }
+ } else {
+ /* Is this year, so show the time */
+ *(cpd + 2) = '\0'; /* Hour */
+ i = atoi(cpd);
+ if (*(cpd + 5) == 'P' || *(cpd + 5) == 'p')
+ i += 12;
+ sprintf(&date[6], " %02d:%.2s", i, (cpd + 3));
+ }
+ StrAllocCopy(entry_info->date, date);
+ if (entry_info->date[4] == ' ' || entry_info->date[4] == '0') {
+ entry_info->date[4] = HT_NON_BREAK_SPACE;
+ }
+ }
+
+ /* Track down the size */
+ if (cps < end) {
+ cps = LYSkipBlanks(cps);
+ cpd = LYSkipNonBlanks(cps);
+ *cpd = '\0';
+ if (isdigit(*cps)) {
+ entry_info->size = LYatoll(cps);
+ } else {
+ StrAllocCopy(entry_info->type, ENTRY_IS_DIRECTORY);
+ }
+ } else {
+ StrAllocCopy(entry_info->type, "");
+ }
+
+ /* Wrap it up */
+ CTRACE((tfp, "HTFTP: Windows NT filename: %s date: %s size: %d\n",
+ entry_info->filename,
+ NonNull(entry_info->date),
+ entry_info->size));
+ return;
+} /* parse_windows_nt_dir_entry */
+#endif /* NOTDEFINED */
+
+/*
+ * parse_cms_dir_entry() --
+ * Format the name, date, and size from a VM/CMS line into
+ * the EntryInfo structure. - FM
+ */
+static void parse_cms_dir_entry(char *line,
+ EntryInfo *entry_info)
+{
+ char *cp = line;
+ char *cps, *cpd, date[16];
+ char *end = line + strlen(line);
+ int RecordLength = 0;
+ int Records = 0;
+ int i;
+
+ /* Get rid of blank or junk lines. */
+ cp = LYSkipBlanks(cp);
+ if (!(*cp)) {
+ entry_info->display = FALSE;
+ return;
+ }
+
+ /* Cut out file or directory name. */
+ cps = LYSkipNonBlanks(cp);
+ *cps++ = '\0';
+ StrAllocCopy(entry_info->filename, cp);
+ if (StrChr(entry_info->filename, '.') != NULL)
+ /* If we already have a dot, we did an NLST. */
+ return;
+ cp = LYSkipBlanks(cps);
+ if (!(*cp)) {
+ /* If we don't have more, we've misparsed. */
+ FREE(entry_info->filename);
+ FREE(entry_info->type);
+ entry_info->display = FALSE;
+ return;
+ }
+ cps = LYSkipNonBlanks(cp);
+ *cps++ = '\0';
+ if ((0 == strcasecomp(cp, "DIR")) && (cp - line) > 17) {
+ /* It's an SFS directory. */
+ StrAllocCopy(entry_info->type, ENTRY_IS_DIRECTORY);
+ entry_info->size = 0;
+ } else {
+ /* It's a file. */
+ cp--;
+ *cp = '.';
+ StrAllocCat(entry_info->filename, cp);
+
+ /* Track down the VM/CMS RECFM or type. */
+ cp = cps;
+ if (cp < end) {
+ cp = LYSkipBlanks(cp);
+ cps = LYSkipNonBlanks(cp);
+ *cps++ = '\0';
+ /* Check cp here, if it's relevant someday. */
+ }
+ }
+
+ /* Track down the record length or dash. */
+ cp = cps;
+ if (cp < end) {
+ cp = LYSkipBlanks(cp);
+ cps = LYSkipNonBlanks(cp);
+ *cps++ = '\0';
+ if (isdigit(UCH(*cp))) {
+ RecordLength = atoi(cp);
+ }
+ }
+
+ /* Track down the number of records or the dash. */
+ cp = cps;
+ if (cps < end) {
+ cp = LYSkipBlanks(cp);
+ cps = LYSkipNonBlanks(cp);
+ *cps++ = '\0';
+ if (isdigit(UCH(*cp))) {
+ Records = atoi(cp);
+ }
+ if (Records > 0 && RecordLength > 0) {
+ /* Compute an approximate size. */
+ entry_info->size = ((off_t) Records * (off_t) RecordLength);
+ }
+ }
+
+ /* Set the years and date, if we don't have them yet. */
+ if (!HaveYears) {
+ set_years_and_date();
+ }
+
+ /* Track down the date. */
+ cpd = cps;
+ if (((cps < end) &&
+ (cps = StrChr(cpd, ':')) != NULL) &&
+ (cps < (end - 3) &&
+ isdigit(UCH(*(cps + 1))) && isdigit(UCH(*(cps + 2))) && *(cps + 3) == ':')) {
+ cps += 3;
+ *cps = '\0';
+ if ((cps - cpd) >= 14) {
+ cpd = (cps - 14);
+ *(cpd + 2) = '\0'; /* Month */
+ *(cpd + 5) = '\0'; /* Day */
+ *(cpd + 8) = '\0'; /* Year */
+ cps -= 5; /* Time */
+ if (*cpd == ' ')
+ *cpd = '0';
+ i = atoi(cpd) - 1;
+ sprintf(date, "%.3s %.2s", months[i], (cpd + 3));
+ if (date[4] == '0')
+ date[4] = ' ';
+ cpd += 6; /* Year */
+ if (strcmp((ThisYear + 2), cpd)) {
+ /* Not this year, so show the year. */
+ if (atoi(cpd) < 70) {
+ sprintf(&date[6], " 20%.2s", cpd);
+ } else {
+ sprintf(&date[6], " 19%.2s", cpd);
+ }
+ } else {
+ /* Is this year, so show the time. */
+ *(cps + 2) = '\0'; /* Hour */
+ i = atoi(cps);
+ sprintf(&date[6], " %02d:%.2s", i, (cps + 3));
+ }
+ StrAllocCopy(entry_info->date, date);
+ if (entry_info->date[4] == ' ' || entry_info->date[4] == '0') {
+ entry_info->date[4] = HT_NON_BREAK_SPACE;
+ }
+ }
+ }
+
+ TRACE_ENTRY("VM/CMS", entry_info);
+ return;
+} /* parse_cms_dir_entry */
+
+/*
+ * Given a line of LIST/NLST output in entry, return results and a file/dir
+ * name in entry_info struct
+ *
+ * If first is true, this is the first name in a directory.
+ */
+static EntryInfo *parse_dir_entry(char *entry,
+ BOOLEAN *first,
+ char **pspilledname)
+{
+ EntryInfo *entry_info;
+ int i;
+ int len;
+ BOOLEAN remove_size = FALSE;
+ char *cp;
+
+ entry_info = typecalloc(EntryInfo);
+
+ if (entry_info == NULL)
+ outofmem(__FILE__, "parse_dir_entry");
+
+ entry_info->display = TRUE;
+
+ switch (server_type) {
+ case DLS_SERVER:
+
+ /*
+ * Interpret and edit LIST output from a Unix server in "dls" format.
+ * This one must have claimed to be Unix in order to get here; if the
+ * first line looks fishy, we revert to Unix and hope that fits better
+ * (this recovery is untested). - kw
+ */
+
+ if (*first) {
+ len = (int) strlen(entry);
+ if (!len || entry[0] == ' ' ||
+ (len >= 24 && entry[23] != ' ') ||
+ (len < 24 && StrChr(entry, ' '))) {
+ server_type = UNIX_SERVER;
+ CTRACE((tfp,
+ "HTFTP: Falling back to treating as Unix server.\n"));
+ } else {
+ *first = FALSE;
+ }
+ }
+
+ if (server_type == DLS_SERVER) {
+ /* if still unchanged... */
+ parse_dls_line(entry, entry_info, pspilledname);
+
+ if (isEmpty(entry_info->filename)) {
+ entry_info->display = FALSE;
+ return (entry_info);
+ }
+ if (!strcmp(entry_info->filename, "..") ||
+ !strcmp(entry_info->filename, "."))
+ entry_info->display = FALSE;
+ if (entry_info->type && *entry_info->type == '\0') {
+ FREE(entry_info->type);
+ return (entry_info);
+ }
+ /*
+ * Goto the bottom and get real type.
+ */
+ break;
+ }
+ /* fall through if server_type changed for *first == TRUE ! */
+ /* FALLTHRU */
+ case UNIX_SERVER:
+ case PETER_LEWIS_SERVER:
+ case MACHTEN_SERVER:
+ case MSDOS_SERVER:
+ case WINDOWS_NT_SERVER:
+ case WINDOWS_2K_SERVER:
+ case APPLESHARE_SERVER:
+ case NETPRESENZ_SERVER:
+ /*
+ * Check for EPLF output (local times).
+ */
+ if (*entry == '+') {
+ parse_eplf_line(entry, entry_info);
+ break;
+ }
+
+ /*
+ * Interpret and edit LIST output from Unix server.
+ */
+ len = (int) strlen(entry);
+ if (*first) {
+ /* don't gettext() this -- incoming text: */
+ if (!strcmp(entry, "can not access directory .")) {
+ /*
+ * Don't reset *first, nothing real will follow. - KW
+ */
+ entry_info->display = FALSE;
+ return (entry_info);
+ }
+ *first = FALSE;
+ if (!StrNCmp(entry, "total ", 6) ||
+ strstr(entry, "not available") != NULL) {
+ entry_info->display = FALSE;
+ return (entry_info);
+ } else if (unsure_type) {
+ /* this isn't really a unix server! */
+ server_type = GENERIC_SERVER;
+ entry_info->display = FALSE;
+ return (entry_info);
+ }
+ }
+
+ /*
+ * Check first character of ls -l output.
+ */
+ if (TOUPPER(entry[0]) == 'D') {
+ /*
+ * It's a directory.
+ */
+ StrAllocCopy(entry_info->type, ENTRY_IS_DIRECTORY);
+ remove_size = TRUE; /* size is not useful */
+ } else if (entry[0] == 'l') {
+ /*
+ * It's a symbolic link, does the user care about knowing if it is
+ * symbolic? I think so since it might be a directory.
+ */
+ StrAllocCopy(entry_info->type, ENTRY_IS_SYMBOLIC_LINK);
+ remove_size = TRUE; /* size is not useful */
+
+ /*
+ * Strip off " -> pathname".
+ */
+ for (i = len - 1; (i > 3) &&
+ (!isspace(UCH(entry[i])) ||
+ (entry[i - 1] != '>') ||
+ (entry[i - 2] != '-') ||
+ (entry[i - 3] != ' ')); i--) ; /* null body */
+ if (i > 3) {
+ entry[i - 3] = '\0';
+ StrAllocCopy(entry_info->linkname, LYSkipBlanks(entry + i));
+ }
+ }
+ /* link */
+ parse_ls_line(entry, entry_info);
+
+ if (!strcmp(entry_info->filename, "..") ||
+ !strcmp(entry_info->filename, "."))
+ entry_info->display = FALSE;
+ /*
+ * Goto the bottom and get real type.
+ */
+ break;
+
+ case VMS_SERVER:
+ /*
+ * Interpret and edit LIST output from VMS server and convert
+ * information lines to zero length.
+ */
+ parse_vms_dir_entry(entry, entry_info);
+
+ /*
+ * Get rid of any junk lines.
+ */
+ if (!entry_info->display)
+ return (entry_info);
+
+ /*
+ * Trim off VMS directory extensions.
+ */
+ len = (int) strlen(entry_info->filename);
+ if ((len > 4) && !strcmp(&entry_info->filename[len - 4], ".dir")) {
+ entry_info->filename[len - 4] = '\0';
+ StrAllocCopy(entry_info->type, ENTRY_IS_DIRECTORY);
+ remove_size = TRUE; /* size is not useful */
+ }
+ /*
+ * Goto the bottom and get real type.
+ */
+ break;
+
+ case MS_WINDOWS_SERVER:
+ /*
+ * Interpret and edit LIST output from MS_WINDOWS server and convert
+ * information lines to zero length.
+ */
+ parse_ms_windows_dir_entry(entry, entry_info);
+
+ /*
+ * Get rid of any junk lines.
+ */
+ if (!entry_info->display)
+ return (entry_info);
+ if (entry_info->type && *entry_info->type == '\0') {
+ FREE(entry_info->type);
+ return (entry_info);
+ }
+ /*
+ * Goto the bottom and get real type.
+ */
+ break;
+
+#ifdef NOTDEFINED
+ case WINDOWS_NT_SERVER:
+ /*
+ * Interpret and edit LIST output from MS_WINDOWS server and convert
+ * information lines to zero length.
+ */
+ parse_windows_nt_dir_entry(entry, entry_info);
+
+ /*
+ * Get rid of any junk lines.
+ */
+ if (!entry_info->display)
+ return (entry_info);
+ if (entry_info->type && *entry_info->type == '\0') {
+ FREE(entry_info->type);
+ return (entry_info);
+ }
+ /*
+ * Goto the bottom and get real type.
+ */
+ break;
+#endif /* NOTDEFINED */
+
+ case CMS_SERVER:
+ {
+ /*
+ * Interpret and edit LIST output from VM/CMS server and convert
+ * any information lines to zero length.
+ */
+ parse_cms_dir_entry(entry, entry_info);
+
+ /*
+ * Get rid of any junk lines.
+ */
+ if (!entry_info->display)
+ return (entry_info);
+ if (entry_info->type && *entry_info->type == '\0') {
+ FREE(entry_info->type);
+ return (entry_info);
+ }
+ /*
+ * Goto the bottom and get real type.
+ */
+ break;
+ }
+
+ case NCSA_SERVER:
+ case TCPC_SERVER:
+ /*
+ * Directories identified by trailing "/" characters.
+ */
+ StrAllocCopy(entry_info->filename, entry);
+ len = (int) strlen(entry);
+ if (entry[len - 1] == '/') {
+ /*
+ * It's a dir, remove / and mark it as such.
+ */
+ entry[len - 1] = '\0';
+ StrAllocCopy(entry_info->type, ENTRY_IS_DIRECTORY);
+ remove_size = TRUE; /* size is not useful */
+ }
+ /*
+ * Goto the bottom and get real type.
+ */
+ break;
+
+ default:
+ /*
+ * We can't tell if it is a directory since we only did an NLST :( List
+ * bad file types anyways? NOT!
+ */
+ StrAllocCopy(entry_info->filename, entry);
+ return (entry_info); /* mostly empty info */
+
+ } /* switch (server_type) */
+
+#ifdef LONG_LIST
+ (void) remove_size;
+#else
+ if (remove_size && entry_info->size) {
+ entry_info->size = 0;
+ }
+#endif
+
+ if (isEmpty(entry_info->filename)) {
+ entry_info->display = FALSE;
+ return (entry_info);
+ }
+ if (strlen(entry_info->filename) > 3) {
+ if (((cp = strrchr(entry_info->filename, '.')) != NULL &&
+ 0 == strncasecomp(cp, ".me", 3)) &&
+ (cp[3] == '\0' || cp[3] == ';')) {
+ /*
+ * Don't treat this as application/x-Troff-me if it's a Unix server
+ * but has the string "read.me", or if it's not a Unix server. -
+ * FM
+ */
+ if ((server_type != UNIX_SERVER) ||
+ (cp > (entry_info->filename + 3) &&
+ 0 == strncasecomp((cp - 4), "read.me", 7))) {
+ StrAllocCopy(entry_info->type, STR_PLAINTEXT);
+ }
+ }
+ }
+
+ /*
+ * Get real types eventually.
+ */
+ if (!entry_info->type) {
+ const char *cp2;
+ HTFormat format;
+ HTAtom *encoding; /* @@ not used at all */
+
+ format = HTFileFormat(entry_info->filename, &encoding, &cp2);
+
+ if (cp2 == NULL) {
+ if (!StrNCmp(HTAtom_name(format), "application", 11)) {
+ cp2 = HTAtom_name(format) + 12;
+ if (!StrNCmp(cp2, "x-", 2))
+ cp2 += 2;
+ } else {
+ cp2 = HTAtom_name(format);
+ }
+ }
+
+ StrAllocCopy(entry_info->type, cp2);
+ }
+
+ return (entry_info);
+}
+
+static void formatDate(char target[16], EntryInfo *entry)
+{
+ char temp[8], month[4];
+ int i;
+
+ /*
+ * Set up for sorting in reverse chronological order. - FM
+ */
+ if (entry->date[9] == ':') {
+ strcpy(target, "9999");
+ LYStrNCpy(temp, &entry->date[7], 5);
+ if (temp[0] == ' ') {
+ temp[0] = '0';
+ }
+ } else {
+ LYStrNCpy(target, &entry->date[8], 4);
+ strcpy(temp, "00:00");
+ }
+ LYStrNCpy(month, entry->date, 3);
+ for (i = 0; i < 12; i++) {
+ if (!strcasecomp(month, months[i])) {
+ break;
+ }
+ }
+ i++;
+ sprintf(month, "%02d", i % 100);
+ strcat(target, month);
+ StrNCat(target, &entry->date[4], 2);
+ if (target[6] == ' ' || target[6] == HT_NON_BREAK_SPACE) {
+ target[6] = '0';
+ }
+
+ /* If no year given, assume last year if it would otherwise be in the
+ * future by more than one day. The one day tolerance is to account for a
+ * possible timezone difference. - kw
+ */
+ if (target[0] == '9' && atoi(target) > TheDate + 1) {
+ for (i = 0; i < 4; i++) {
+ target[i] = LastYear[i];
+ }
+ }
+ strcat(target, temp);
+}
+
+static int compare_EntryInfo_structs(EntryInfo *entry1, EntryInfo *entry2)
+{
+ int status;
+ char date1[16], date2[16];
+ int result = strcmp(entry1->filename, entry2->filename);
+
+ switch (HTfileSortMethod) {
+ case FILE_BY_SIZE:
+ /* both equal or both 0 */
+ if (entry1->size > entry2->size)
+ result = 1;
+ else if (entry1->size < entry2->size)
+ result = -1;
+ break;
+
+ case FILE_BY_TYPE:
+ if (entry1->type && entry2->type) {
+ status = strcasecomp(entry1->type, entry2->type);
+ if (status)
+ result = status;
+ }
+ break;
+
+ case FILE_BY_DATE:
+ if (entry1->date && entry2->date &&
+ strlen(entry1->date) == 12 &&
+ strlen(entry2->date) == 12) {
+ /*
+ * Set the years and date, if we don't have them yet.
+ */
+ if (!HaveYears) {
+ set_years_and_date();
+ }
+ formatDate(date1, entry1);
+ formatDate(date2, entry2);
+ /*
+ * Do the comparison. - FM
+ */
+ status = strcasecomp(date2, date1);
+ if (status)
+ result = status;
+ }
+ break;
+
+ case FILE_BY_NAME:
+ default:
+ break;
+ }
+ return result;
+}
+
+#ifdef LONG_LIST
+static char *FormatStr(char **bufp,
+ char *start,
+ const char *value)
+{
+ char fmt[512];
+
+ if (*start) {
+ sprintf(fmt, "%%%.*ss", (int) sizeof(fmt) - 3, start);
+ HTSprintf(bufp, fmt, value);
+ } else if (*bufp && !(value && *value)) {
+ ;
+ } else if (value) {
+ StrAllocCat(*bufp, value);
+ }
+ return *bufp;
+}
+
+static char *FormatSize(char **bufp,
+ char *start,
+ off_t value)
+{
+ char fmt[512];
+
+ if (*start) {
+ sprintf(fmt, "%%%.*s" PRI_off_t,
+ (int) sizeof(fmt) - DigitsOf(start) - 3, start);
+
+ HTSprintf(bufp, fmt, value);
+ } else {
+ sprintf(fmt, "%" PRI_off_t, CAST_off_t (value));
+
+ StrAllocCat(*bufp, fmt);
+ }
+ return *bufp;
+}
+
+static char *FormatNum(char **bufp,
+ char *start,
+ unsigned long value)
+{
+ char fmt[512];
+
+ if (*start) {
+ sprintf(fmt, "%%%.*sld",
+ (int) sizeof(fmt) - DigitsOf(start) - 3, start);
+ HTSprintf(bufp, fmt, value);
+ } else {
+ sprintf(fmt, "%lu", value);
+ StrAllocCat(*bufp, fmt);
+ }
+ return *bufp;
+}
+
+static void FlushParse(HTStructured * target, char **buf)
+{
+ if (*buf && **buf) {
+ PUTS(*buf);
+ **buf = '\0';
+ }
+}
+
+static void LYListFmtParse(const char *fmtstr,
+ EntryInfo *data,
+ HTStructured * target,
+ char *tail)
+{
+ char c;
+ char *s;
+ char *end;
+ char *start;
+ char *str = NULL;
+ char *buf = NULL;
+ BOOL is_directory = (BOOL) (data->file_mode != 0 &&
+ (TOUPPER(data->file_mode[0]) == 'D'));
+ BOOL is_symlinked = (BOOL) (data->file_mode != 0 &&
+ (TOUPPER(data->file_mode[0]) == 'L'));
+ BOOL remove_size = (BOOL) (is_directory || is_symlinked);
+
+ StrAllocCopy(str, fmtstr);
+ s = str;
+ end = str + strlen(str);
+ while (*s) {
+ start = s;
+ while (*s) {
+ if (*s == '%') {
+ if (*(s + 1) == '%') /* literal % */
+ s++;
+ else
+ break;
+ }
+ s++;
+ }
+ /* s is positioned either at a % or at \0 */
+ *s = '\0';
+ if (s > start) { /* some literal chars. */
+ StrAllocCat(buf, start);
+ }
+ if (s == end)
+ break;
+ start = ++s;
+ while (isdigit(UCH(*s)) || *s == '.' || *s == '-' || *s == ' ' ||
+ *s == '#' || *s == '+' || *s == '\'')
+ s++;
+ c = *s; /* the format char. or \0 */
+ *s = '\0';
+
+ switch (c) {
+ case '\0':
+ StrAllocCat(buf, start);
+ continue;
+
+ case 'A':
+ case 'a': /* anchor */
+ FlushParse(target, &buf);
+ HTDirEntry(target, tail, data->filename);
+ FormatStr(&buf, start, data->filename);
+ PUTS(buf);
+ END(HTML_A);
+ if (buf != 0)
+ *buf = '\0';
+ if (c != 'A' && data->linkname != 0) {
+ PUTS(" -> ");
+ PUTS(data->linkname);
+ }
+ break;
+
+ case 'T': /* MIME type */
+ case 't': /* MIME type description */
+ if (is_directory) {
+ if (c != 'T') {
+ FormatStr(&buf, start, ENTRY_IS_DIRECTORY);
+ } else {
+ FormatStr(&buf, start, "");
+ }
+ } else if (is_symlinked) {
+ if (c != 'T') {
+ FormatStr(&buf, start, ENTRY_IS_SYMBOLIC_LINK);
+ } else {
+ FormatStr(&buf, start, "");
+ }
+ } else {
+ const char *cp2;
+ HTFormat format;
+
+ format = HTFileFormat(data->filename, NULL, &cp2);
+
+ if (c != 'T') {
+ if (cp2 == NULL) {
+ if (!StrNCmp(HTAtom_name(format),
+ "application", 11)) {
+ cp2 = HTAtom_name(format) + 12;
+ if (!StrNCmp(cp2, "x-", 2))
+ cp2 += 2;
+ } else {
+ cp2 = HTAtom_name(format);
+ }
+ }
+ FormatStr(&buf, start, cp2);
+ } else {
+ FormatStr(&buf, start, HTAtom_name(format));
+ }
+ }
+ break;
+
+ case 'd': /* date */
+ if (data->date) {
+ FormatStr(&buf, start, data->date);
+ } else {
+ FormatStr(&buf, start, " * ");
+ }
+ break;
+
+ case 's': /* size in bytes */
+ FormatSize(&buf, start, data->size);
+ break;
+
+ case 'K': /* size in Kilobytes but not for directories */
+ if (remove_size) {
+ FormatStr(&buf, start, "");
+ StrAllocCat(buf, " ");
+ break;
+ }
+ /* FALL THROUGH */
+ case 'k': /* size in Kilobytes */
+ /* FIXME - this is inconsistent with HTFile.c, but historical */
+ if (data->size < 1024) {
+ FormatSize(&buf, start, data->size);
+ StrAllocCat(buf, " bytes");
+ } else {
+ FormatSize(&buf, start, data->size / 1024);
+ StrAllocCat(buf, "Kb");
+ }
+ break;
+
+#ifdef LONG_LIST
+ case 'p': /* unix-style permission bits */
+ FormatStr(&buf, start, NonNull(data->file_mode));
+ break;
+
+ case 'o': /* owner */
+ FormatStr(&buf, start, NonNull(data->file_user));
+ break;
+
+ case 'g': /* group */
+ FormatStr(&buf, start, NonNull(data->file_group));
+ break;
+
+ case 'l': /* link count */
+ FormatNum(&buf, start, data->file_links);
+ break;
+#endif
+
+ case '%': /* literal % with flags/width */
+ FormatStr(&buf, start, "%");
+ break;
+
+ default:
+ fprintf(stderr,
+ "Unknown format character `%c' in list format\n", c);
+ break;
+ }
+
+ s++;
+ }
+ if (buf) {
+ LYTrimTrailing(buf);
+ FlushParse(target, &buf);
+ FREE(buf);
+ }
+ PUTC('\n');
+ FREE(str);
+}
+#endif /* LONG_LIST */
+
+/* Read a directory into an hypertext object from the data socket
+ * --------------------------------------------------------------
+ *
+ * On entry,
+ * anchor Parent anchor to link the this node to
+ * address Address of the directory
+ * On exit,
+ * returns HT_LOADED if OK
+ * <0 if error.
+ */
+static int read_directory(HTParentAnchor *parent,
+ const char *address,
+ HTFormat format_out,
+ HTStream *sink)
+{
+ int status;
+ BOOLEAN WasInterrupted = FALSE;
+ HTStructured *target = HTML_new(parent, format_out, sink);
+ char *filename = HTParse(address, "", PARSE_PATH + PARSE_PUNCTUATION);
+ EntryInfo *entry_info;
+ BOOLEAN first = TRUE;
+ char *lastpath = NULL; /* prefix for link, either "" (for root) or xxx */
+ BOOL tildeIsTop = FALSE;
+
+#ifndef LONG_LIST
+ char string_buffer[64];
+#endif
+
+ _HTProgress(gettext("Receiving FTP directory."));
+
+ /*
+ * Force the current Date and Year (TheDate, ThisYear, and LastYear) to be
+ * recalculated for each directory request. Otherwise we have a problem
+ * with long-running sessions assuming the wrong date for today. - kw
+ */
+ HaveYears = FALSE;
+ /*
+ * Check whether we always want the home directory treated as Welcome. -
+ * FM
+ */
+ if (server_type == VMS_SERVER)
+ tildeIsTop = TRUE;
+
+ /*
+ * This should always come back FALSE, since the flag is set only for local
+ * directory listings if LONG_LIST was defined on compilation, but we could
+ * someday set up an equivalent listing for Unix ftp servers. - FM
+ */
+ (void) HTDirTitles(target, parent, format_out, tildeIsTop);
+
+ data_read_pointer = data_write_pointer = data_buffer;
+
+ if (*filename == '\0') { /* Empty filename: use root. */
+ StrAllocCopy(lastpath, "/");
+ } else if (!strcmp(filename, "/")) { /* Root path. */
+ StrAllocCopy(lastpath, "/foo/..");
+ } else {
+ char *p = strrchr(filename, '/'); /* Find the lastslash. */
+ char *cp;
+
+ if (server_type == CMS_SERVER) {
+ StrAllocCopy(lastpath, filename); /* Use absolute path for CMS. */
+ } else {
+ StrAllocCopy(lastpath, p + 1); /* Take slash off the beginning. */
+ }
+ if ((cp = strrchr(lastpath, ';')) != NULL) { /* Trim type= param. */
+ if (!strncasecomp((cp + 1), "type=", 5)) {
+ if (TOUPPER(*(cp + 6)) == 'D' ||
+ TOUPPER(*(cp + 6)) == 'A' ||
+ TOUPPER(*(cp + 6)) == 'I')
+ *cp = '\0';
+ }
+ }
+ }
+ FREE(filename);
+
+ {
+ HTBTree *bt = HTBTree_new((HTComparer) compare_EntryInfo_structs);
+ int ic;
+ HTChunk *chunk = HTChunkCreate(128);
+ int BytesReceived = 0;
+ int BytesReported = 0;
+ char NumBytes[64];
+ char *spilledname = NULL;
+
+ PUTC('\n'); /* prettier LJM */
+ for (ic = 0; ic != EOF;) { /* For each entry in the directory */
+ HTChunkClear(chunk);
+
+ if (HTCheckForInterrupt()) {
+ CTRACE((tfp,
+ "read_directory: interrupted after %d bytes\n",
+ BytesReceived));
+ WasInterrupted = TRUE;
+ if (BytesReceived) {
+ goto unload_btree; /* unload btree */
+ } else {
+ ABORT_TARGET;
+ HTBTreeAndObject_free(bt);
+ FREE(spilledname);
+ HTChunkFree(chunk);
+ return HT_INTERRUPTED;
+ }
+ }
+
+ /* read directory entry
+ */
+ interrupted_in_next_data_char = FALSE;
+ for (;;) { /* Read in one line as filename */
+ ic = NEXT_DATA_CHAR;
+ AgainForMultiNet:
+ if (interrupted_in_next_data_char) {
+ CTRACE((tfp,
+ "read_directory: interrupted_in_next_data_char after %d bytes\n",
+ BytesReceived));
+ WasInterrupted = TRUE;
+ if (BytesReceived) {
+ goto unload_btree; /* unload btree */
+ } else {
+ ABORT_TARGET;
+ HTBTreeAndObject_free(bt);
+ FREE(spilledname);
+ HTChunkFree(chunk);
+ return HT_INTERRUPTED;
+ }
+ } else if ((char) ic == CR || (char) ic == LF) { /* Terminator? */
+ if (chunk->size != 0) { /* got some text */
+ /* Deal with MultiNet's wrapping of long lines */
+ if (server_type == VMS_SERVER) {
+ /* Deal with MultiNet's wrapping of long lines - F.M. */
+ if (data_read_pointer < data_write_pointer &&
+ *(data_read_pointer + 1) == ' ')
+ data_read_pointer++;
+ else if (data_read_pointer >= data_write_pointer) {
+ status = NETREAD(data_soc, data_buffer,
+ DATA_BUFFER_SIZE);
+ if (status == HT_INTERRUPTED) {
+ interrupted_in_next_data_char = 1;
+ goto AgainForMultiNet;
+ }
+ if (status <= 0) {
+ ic = EOF;
+ break;
+ }
+ data_write_pointer = data_buffer + status;
+ data_read_pointer = data_buffer;
+ if (*data_read_pointer == ' ')
+ data_read_pointer++;
+ else
+ break;
+ } else
+ break;
+ } else
+ break; /* finish getting one entry */
+ }
+ } else if (ic == EOF) {
+ break; /* End of file */
+ } else {
+ HTChunkPutc(chunk, UCH(ic));
+ }
+ }
+ HTChunkTerminate(chunk);
+
+ BytesReceived += chunk->size;
+ if (BytesReceived > BytesReported + 1024) {
+#ifdef _WINDOWS
+ sprintf(NumBytes, gettext("Transferred %d bytes (%5d)"),
+ BytesReceived, ws_read_per_sec);
+#else
+ sprintf(NumBytes, TRANSFERRED_X_BYTES, BytesReceived);
+#endif
+ HTProgress(NumBytes);
+ BytesReported = BytesReceived;
+ }
+
+ if (ic == EOF && chunk->size == 1)
+ /* 1 means empty: includes terminating 0 */
+ break;
+ CTRACE((tfp, "HTFTP: Line in %s is %s\n",
+ lastpath, chunk->data));
+
+ entry_info = parse_dir_entry(chunk->data, &first, &spilledname);
+ if (entry_info->display) {
+ FREE(spilledname);
+ CTRACE((tfp, "Adding file to BTree: %s\n",
+ entry_info->filename));
+ HTBTree_add(bt, entry_info);
+ } else {
+ free_entryinfo_struct_contents(entry_info);
+ FREE(entry_info);
+ }
+
+ } /* next entry */
+
+ unload_btree:
+
+ HTChunkFree(chunk);
+ FREE(spilledname);
+
+ /* print out the handy help message if it exists :) */
+ if (help_message_cache_non_empty()) {
+ START(HTML_PRE);
+ START(HTML_HR);
+ PUTC('\n');
+ PUTS(help_message_cache_contents());
+ init_help_message_cache(); /* to free memory */
+ START(HTML_HR);
+ PUTC('\n');
+ } else {
+ START(HTML_PRE);
+ PUTC('\n');
+ }
+
+ /* Run through tree printing out in order
+ */
+ {
+#ifndef LONG_LIST
+#ifdef SH_EX /* 1997/10/18 (Sat) 14:14:28 */
+ char *p, name_buff[256];
+ int name_len, dot_len;
+
+#define FNAME_WIDTH 30
+#define FILE_GAP 1
+
+#endif
+ int i;
+#endif
+ HTBTElement *ele;
+
+ for (ele = HTBTree_next(bt, NULL);
+ ele != NULL;
+ ele = HTBTree_next(bt, ele)) {
+ entry_info = (EntryInfo *) HTBTree_object(ele);
+
+#ifdef LONG_LIST
+ LYListFmtParse(ftp_format,
+ entry_info,
+ target,
+ lastpath);
+#else
+ if (entry_info->date) {
+ PUTS(entry_info->date);
+ PUTS(" ");
+ } else {
+ PUTS(" * ");
+ }
+
+ if (entry_info->type) {
+ for (i = 0; entry_info->type[i] != '\0' && i < 16; i++)
+ PUTC(entry_info->type[i]);
+ for (; i < 17; i++)
+ PUTC(' ');
+ }
+ /* start the anchor */
+ HTDirEntry(target, lastpath, entry_info->filename);
+#ifdef SH_EX /* 1997/10/18 (Sat) 16:00 */
+ name_len = strlen(entry_info->filename);
+
+ sprintf(name_buff, "%-*s", FNAME_WIDTH, entry_info->filename);
+
+ if (name_len < FNAME_WIDTH) {
+ dot_len = FNAME_WIDTH - FILE_GAP - name_len;
+ if (dot_len > 0) {
+ p = name_buff + name_len + 1;
+ while (dot_len-- > 0)
+ *p++ = '.';
+ }
+ } else {
+ name_buff[FNAME_WIDTH] = '\0';
+ }
+
+ PUTS(name_buff);
+#else
+ PUTS(entry_info->filename);
+#endif
+ END(HTML_A);
+
+ if (entry_info->size) {
+#ifdef SH_EX /* 1998/02/02 (Mon) 16:34:52 */
+ if (entry_info->size < 1024)
+ sprintf(string_buffer, "%6ld bytes",
+ entry_info->size);
+ else
+ sprintf(string_buffer, "%6ld Kb",
+ entry_info->size / 1024);
+#else
+ if (entry_info->size < 1024)
+ sprintf(string_buffer, " %lu bytes",
+ (unsigned long) entry_info->size);
+ else
+ sprintf(string_buffer, " %luKb",
+ (unsigned long) entry_info->size / 1024);
+#endif
+ PUTS(string_buffer);
+ } else if (entry_info->linkname != 0) {
+ PUTS(" -> ");
+ PUTS(entry_info->linkname);
+ }
+
+ PUTC('\n'); /* end of this entry */
+#endif
+
+ free_entryinfo_struct_contents(entry_info);
+ }
+ }
+ END(HTML_PRE);
+ END(HTML_BODY);
+ FREE_TARGET;
+ HTBTreeAndObject_free(bt);
+ }
+
+ FREE(lastpath);
+
+ if (WasInterrupted || data_soc != -1) { /* should always be true */
+ /*
+ * Without closing the data socket first, the response(NULL) later may
+ * hang. Some servers expect the client to fin/ack the close of the
+ * data connection before proceeding with the conversation on the
+ * control connection. - kw
+ */
+ CTRACE((tfp, "HTFTP: Closing data socket %d\n", data_soc));
+ status = NETCLOSE(data_soc);
+ if (status == -1)
+ HTInetStatus("close"); /* Comment only */
+ data_soc = -1;
+ }
+
+ if (WasInterrupted || HTCheckForInterrupt()) {
+ _HTProgress(TRANSFER_INTERRUPTED);
+ }
+ return HT_LOADED;
+}
+
+/*
+ * Setup an FTP connection.
+ */
+static int setup_connection(const char *name,
+ HTParentAnchor *anchor)
+{
+ int retry; /* How many times tried? */
+ int status = HT_NO_CONNECTION;
+
+ CTRACE((tfp, "setup_connection(%s)\n", name));
+
+ /* set use_list to NOT since we don't know what kind of server
+ * this is yet. And set the type to GENERIC
+ */
+ use_list = FALSE;
+ server_type = GENERIC_SERVER;
+ Broken_RETR = FALSE;
+
+#ifdef INET6
+ Broken_EPSV = FALSE;
+#endif
+
+ for (retry = 0; retry < 2; retry++) { /* For timed out/broken connections */
+ status = get_connection(name, anchor);
+ if (status < 0) {
+ break;
+ }
+
+ if (!ftp_local_passive) {
+ status = get_listen_socket();
+ if (status < 0) {
+ NETCLOSE(control->socket);
+ control->socket = -1;
+#ifdef INET6
+ if (have_socket)
+ (void) close_master_socket();
+#else
+ close_master_socket();
+#endif /* INET6 */
+ /* HT_INTERRUPTED would fall through, if we could interrupt
+ somehow in the middle of it, which we currently can't. */
+ break;
+ }
+#ifdef REPEAT_PORT
+ /* Inform the server of the port number we will listen on
+ */
+ status = response(port_command);
+ FREE(port_command);
+ if (status == HT_INTERRUPTED) {
+ CTRACE((tfp, "HTFTP: Interrupted in response (port_command)\n"));
+ _HTProgress(CONNECTION_INTERRUPTED);
+ NETCLOSE(control->socket);
+ control->socket = -1;
+ close_master_socket();
+ status = HT_INTERRUPTED;
+ break;
+ }
+ if (status != 2) { /* Could have timed out */
+ if (status < 0)
+ continue; /* try again - net error */
+ status = -status; /* bad reply */
+ break;
+ }
+ CTRACE((tfp, "HTFTP: Port defined.\n"));
+#endif /* REPEAT_PORT */
+ } else { /* Tell the server to be passive */
+ char *command = NULL;
+ const char *p = "?";
+ int h0, h1, h2, h3, p0, p1; /* Parts of reply */
+
+#ifdef INET6
+ char dst[LINE_LENGTH + 1];
+#endif
+
+ data_soc = status;
+
+#ifdef INET6
+ /* see RFC 2428 */
+ if (Broken_EPSV)
+ status = 1;
+ else
+ status = send_cmd_1(p = "EPSV");
+ if (status < 0) /* retry or Bad return */
+ continue;
+ else if (status != 2) {
+ status = send_cmd_1(p = "PASV");
+ if (status < 0) { /* retry or Bad return */
+ continue;
+ } else if (status != 2) {
+ status = -status; /* bad reply */
+ break;
+ }
+ }
+
+ if (strcmp(p, "PASV") == 0) {
+ for (p = response_text; *p && *p != ','; p++) {
+ ; /* null body */
+ }
+
+ while (--p > response_text && '0' <= *p && *p <= '9') {
+ ; /* null body */
+ }
+ status = sscanf(p + 1, "%d,%d,%d,%d,%d,%d",
+ &h0, &h1, &h2, &h3, &p0, &p1);
+ if (status < 4) {
+ fprintf(tfp, "HTFTP: PASV reply has no inet address!\n");
+ status = HT_NO_CONNECTION;
+ break;
+ }
+ passive_port = (PortNumber) ((p0 << 8) + p1);
+ sprintf(dst, "%d.%d.%d.%d", h0, h1, h2, h3);
+ } else if (strcmp(p, "EPSV") == 0) {
+ char c0, c1, c2, c3;
+ LY_SOCKADDR ss;
+ LY_SOCKLEN sslen;
+
+ /*
+ * EPSV bla (|||port|)
+ */
+ for (p = response_text; *p && !isspace(UCH(*p)); p++) {
+ ; /* null body */
+ }
+ for ( /*nothing */ ;
+ *p && *p != '(';
+ p++) { /*) */
+ ; /* null body */
+ }
+ status = sscanf(p, "(%c%c%c%d%c)", &c0, &c1, &c2, &p0, &c3);
+ if (status != 5) {
+ fprintf(tfp, "HTFTP: EPSV reply has invalid format!\n");
+ status = HT_NO_CONNECTION;
+ break;
+ }
+ passive_port = (PortNumber) p0;
+
+ sslen = (LY_SOCKLEN) sizeof(ss);
+ if (getpeername(control->socket, SOCKADDR_OF(ss), &sslen) < 0) {
+ fprintf(tfp, "HTFTP: getpeername(control) failed\n");
+ status = HT_NO_CONNECTION;
+ break;
+ }
+ if (getnameinfo(SOCKADDR_OF(ss),
+ sslen,
+ dst,
+ (socklen_t) sizeof(dst),
+ NULL, 0, NI_NUMERICHOST)) {
+ fprintf(tfp, "HTFTP: getnameinfo failed\n");
+ status = HT_NO_CONNECTION;
+ break;
+ }
+ }
+#else
+ status = send_cmd_1("PASV");
+ if (status != 2) {
+ if (status < 0)
+ continue; /* retry or Bad return */
+ status = -status; /* bad reply */
+ break;
+ }
+ for (p = response_text; *p && *p != ','; p++) {
+ ; /* null body */
+ }
+
+ while (--p > response_text && '0' <= *p && *p <= '9') {
+ ; /* null body */
+ }
+
+ status = sscanf(p + 1, "%d,%d,%d,%d,%d,%d",
+ &h0, &h1, &h2, &h3, &p0, &p1);
+ if (status < 4) {
+ fprintf(tfp, "HTFTP: PASV reply has no inet address!\n");
+ status = HT_NO_CONNECTION;
+ break;
+ }
+ passive_port = (PortNumber) ((p0 << 8) + p1);
+#endif /* INET6 */
+ CTRACE((tfp, "HTFTP: Server is listening on port %d\n",
+ passive_port));
+
+ /* Open connection for data: */
+
+#ifdef INET6
+ HTSprintf0(&command, "%s//%s:%d/", STR_FTP_URL, dst, passive_port);
+#else
+ HTSprintf0(&command, "%s//%d.%d.%d.%d:%d/",
+ STR_FTP_URL, h0, h1, h2, h3, passive_port);
+#endif
+ status = HTDoConnect(command, "FTP data", passive_port, &data_soc);
+ FREE(command);
+
+ if (status < 0) {
+ (void) HTInetStatus(gettext("connect for data"));
+ NETCLOSE(data_soc);
+ break;
+ }
+
+ CTRACE((tfp, "FTP data connected, socket %d\n", data_soc));
+ }
+ status = 0;
+ break; /* No more retries */
+
+ } /* for retries */
+ CTRACE((tfp, "setup_connection returns %d\n", status));
+ return status;
+}
+
+/* Retrieve File from Server
+ * -------------------------
+ *
+ * On entry,
+ * name WWW address of a file: document, including hostname
+ * On exit,
+ * returns Socket number for file if good.
+ * <0 if bad.
+ */
+int HTFTPLoad(const char *name,
+ HTParentAnchor *anchor,
+ HTFormat format_out,
+ HTStream *sink)
+{
+ BOOL isDirectory = NO;
+ HTAtom *encoding = NULL;
+ int status, final_status;
+ int outstanding = 1; /* outstanding control connection responses
+
+ that we are willing to wait for, if we
+ get to the point of reading data - kw */
+ HTFormat format;
+
+ CTRACE((tfp, "HTFTPLoad(%s) %s connection\n",
+ name,
+ (ftp_local_passive
+ ? "passive"
+ : "normal")));
+
+ HTReadProgress((off_t) 0, (off_t) 0);
+
+ status = setup_connection(name, anchor);
+ if (status < 0)
+ return status; /* Failed with this code */
+
+ /* Ask for the file:
+ */
+ {
+ char *filename = HTParse(name, "", PARSE_PATH + PARSE_PUNCTUATION);
+ char *fname = filename; /* Save for subsequent free() */
+ char *vmsname = NULL;
+ BOOL binary;
+ const char *type = NULL;
+ char *types = NULL;
+ char *cp;
+
+ if (server_type == CMS_SERVER) {
+ /* If the unescaped path has a %2f, reject it as illegal. - FM */
+ if (((cp = strstr(filename, "%2")) != NULL) &&
+ TOUPPER(cp[2]) == 'F') {
+ FREE(fname);
+ init_help_message_cache(); /* to free memory */
+ NETCLOSE(control->socket);
+ control->socket = -1;
+ CTRACE((tfp,
+ "HTFTP: Rejecting path due to illegal escaped slash.\n"));
+ return -1;
+ }
+ }
+
+ if (!*filename) {
+ StrAllocCopy(filename, "/");
+ type = "D";
+ } else if ((type = types = strrchr(filename, ';')) != NULL) {
+ /*
+ * Check and trim the type= parameter. - FM
+ */
+ if (!strncasecomp((type + 1), "type=", 5)) {
+ switch (TOUPPER(*(type + 6))) {
+ case 'D':
+ *types = '\0';
+ type = "D";
+ break;
+ case 'A':
+ *types = '\0';
+ type = "A";
+ break;
+ case 'I':
+ *types = '\0';
+ type = "I";
+ break;
+ default:
+ type = "";
+ break;
+ }
+ if (!*filename) {
+ *filename = '/';
+ *(filename + 1) = '\0';
+ }
+ }
+ if (*type != '\0') {
+ CTRACE((tfp, "HTFTP: type=%s\n", type));
+ }
+ }
+ HTUnEscape(filename);
+ CTRACE((tfp, "HTFTP: UnEscaped %s\n", filename));
+ if (filename[1] == '~') {
+ /*
+ * Check if translation of HOME as tilde is supported,
+ * and adjust filename if so. - FM
+ */
+ char *cp2 = NULL;
+ char *fn = NULL;
+
+ if ((cp2 = StrChr((filename + 1), '/')) != NULL) {
+ *cp2 = '\0';
+ }
+ status = send_cmd_1("PWD");
+ if (status == 2 && response_text[5] == '/') {
+ status = send_cwd(filename + 1);
+ if (status == 2) {
+ StrAllocCopy(fn, (filename + 1));
+ if (cp2) {
+ *cp2 = '/';
+ if (fn[strlen(fn) - 1] != '/') {
+ StrAllocCat(fn, cp2);
+ } else {
+ StrAllocCat(fn, (cp2 + 1));
+ }
+ cp2 = NULL;
+ }
+ FREE(fname);
+ fname = filename = fn;
+ }
+ }
+ if (cp2) {
+ *cp2 = '/';
+ }
+ }
+ if (strlen(filename) > 3) {
+ char *cp2;
+
+ if (((cp2 = strrchr(filename, '.')) != NULL &&
+ 0 == strncasecomp(cp2, ".me", 3)) &&
+ (cp2[3] == '\0' || cp2[3] == ';')) {
+ /*
+ * Don't treat this as application/x-Troff-me if it's a Unix
+ * server but has the string "read.me", or if it's not a Unix
+ * server. - FM
+ */
+ if ((server_type != UNIX_SERVER) ||
+ (cp2 > (filename + 3) &&
+ 0 == strncasecomp((cp2 - 4), "read.me", 7))) {
+ *cp2 = '\0';
+ format = HTFileFormat(filename, &encoding, NULL);
+ *cp2 = '.';
+ } else {
+ format = HTFileFormat(filename, &encoding, NULL);
+ }
+ } else {
+ format = HTFileFormat(filename, &encoding, NULL);
+ }
+ } else {
+ format = HTFileFormat(filename, &encoding, NULL);
+ }
+ format = HTCharsetFormat(format, anchor, -1);
+ binary = (BOOL) (encoding != WWW_ENC_8BIT &&
+ encoding != WWW_ENC_7BIT);
+ if (!binary &&
+ /*
+ * Force binary if we're in source, download or dump mode and this is
+ * not a VM/CMS server, so we don't get CRLF instead of LF (or CR) for
+ * newlines in text files. Can't do this for VM/CMS or we'll get raw
+ * EBCDIC. - FM
+ */
+ (format_out == WWW_SOURCE ||
+ format_out == WWW_DOWNLOAD ||
+ format_out == WWW_DUMP) &&
+ (server_type != CMS_SERVER))
+ binary = TRUE;
+ if (!binary && type && *type == 'I') {
+ /*
+ * Force binary if we had ;type=I - FM
+ */
+ binary = TRUE;
+ } else if (binary && type && *type == 'A') {
+ /*
+ * Force ASCII if we had ;type=A - FM
+ */
+ binary = FALSE;
+ }
+ if (binary != control->is_binary) {
+ /*
+ * Act on our setting if not already set. - FM
+ */
+ const char *mode = binary ? "I" : "A";
+
+ status = send_cmd_2("TYPE", mode);
+ if (status != 2) {
+ init_help_message_cache(); /* to free memory */
+ return ((status < 0) ? status : -status);
+ }
+ control->is_binary = binary;
+ }
+ switch (server_type) {
+ /*
+ * Handle what for Lynx are special case servers, e.g., for which
+ * we respect RFC 1738, or which have known conflicts in suffix
+ * mappings. - FM
+ */
+ case VMS_SERVER:
+ {
+ char *cp1, *cp2;
+ BOOL included_device = FALSE;
+ BOOL found_tilde = FALSE;
+
+ /* Accept only Unix-style filename */
+ if (StrChr(filename, ':') != NULL ||
+ StrChr(filename, '[') != NULL) {
+ FREE(fname);
+ init_help_message_cache(); /* to free memory */
+ NETCLOSE(control->socket);
+ control->socket = -1;
+ CTRACE((tfp,
+ "HTFTP: Rejecting path due to non-Unix-style syntax.\n"));
+ return -1;
+ }
+ /* Handle any unescaped "/%2F" path */
+ if (!StrNCmp(filename, "//", 2)) {
+ int i;
+
+ included_device = TRUE;
+ for (i = 0; filename[(i + 1)]; i++)
+ filename[i] = filename[(i + 1)];
+ filename[i] = '\0';
+ CTRACE((tfp, "HTFTP: Trimmed '%s'\n", filename));
+ cp = HTVMS_name("", filename);
+ CTRACE((tfp, "HTFTP: VMSized '%s'\n", cp));
+ if ((cp1 = strrchr(cp, ']')) != NULL) {
+ strcpy(filename, ++cp1);
+ CTRACE((tfp, "HTFTP: Filename '%s'\n", filename));
+ *cp1 = '\0';
+ status = send_cwd(cp);
+ if (status != 2) {
+ char *dotslash = 0;
+
+ if ((cp1 = StrChr(cp, '[')) != NULL) {
+ *cp1++ = '\0';
+ status = send_cwd(cp);
+ if (status != 2) {
+ FREE(fname);
+ init_help_message_cache(); /* to free memory */
+ NETCLOSE(control->socket);
+ control->socket = -1;
+ return ((status < 0) ? status : -status);
+ }
+ HTSprintf0(&dotslash, "[.%s", cp1);
+ status = send_cwd(dotslash);
+ FREE(dotslash);
+ if (status != 2) {
+ FREE(fname);
+ init_help_message_cache(); /* to free memory */
+ NETCLOSE(control->socket);
+ control->socket = -1;
+ return ((status < 0) ? status : -status);
+ }
+ } else {
+ FREE(fname);
+ init_help_message_cache(); /* to free memory */
+ NETCLOSE(control->socket);
+ control->socket = -1;
+ return ((status < 0) ? status : -status);
+ }
+ }
+ } else if ((cp1 = StrChr(cp, ':')) != NULL &&
+ StrChr(cp, '[') == NULL &&
+ StrChr(cp, ']') == NULL) {
+ cp1++;
+ if (*cp1 != '\0') {
+ int cplen = (int) (cp1 - cp);
+
+ strcpy(filename, cp1);
+ CTRACE((tfp, "HTFTP: Filename '%s'\n", filename));
+ HTSprintf0(&vmsname, "%.*s[%s]", cplen, cp, filename);
+ status = send_cwd(vmsname);
+ if (status != 2) {
+ HTSprintf(&vmsname, "%.*s[000000]", cplen, cp);
+ status = send_cwd(vmsname);
+ if (status != 2) {
+ HTSprintf(&vmsname, "%.*s", cplen, cp);
+ status = send_cwd(vmsname);
+ if (status != 2) {
+ FREE(fname);
+ init_help_message_cache();
+ NETCLOSE(control->socket);
+ control->socket = -1;
+ return ((status < 0) ? status : -status);
+ }
+ }
+ } else {
+ HTSprintf0(&vmsname, "000000");
+ filename = vmsname;
+ }
+ }
+ } else if (0 == strcmp(cp, (filename + 1))) {
+ status = send_cwd(cp);
+ if (status != 2) {
+ HTSprintf0(&vmsname, "%s:", cp);
+ status = send_cwd(vmsname);
+ if (status != 2) {
+ FREE(fname);
+ init_help_message_cache(); /* to free memory */
+ NETCLOSE(control->socket);
+ control->socket = -1;
+ return ((status < 0) ? status : -status);
+ }
+ }
+ HTSprintf0(&vmsname, "000000");
+ filename = vmsname;
+ }
+ }
+ /* Trim trailing slash if filename is not the top directory */
+ if (strlen(filename) > 1 && filename[strlen(filename) - 1] == '/')
+ filename[strlen(filename) - 1] = '\0';
+
+#ifdef MAINTAIN_CONNECTION /* Don't need this if always new connection - F.M. */
+ if (!included_device) {
+ /* Get the current default VMS device:[directory] */
+ status = send_cmd_1("PWD");
+ if (status != 2) {
+ FREE(fname);
+ init_help_message_cache(); /* to free memory */
+ NETCLOSE(control->socket);
+ control->socket = -1;
+ return ((status < 0) ? status : -status);
+ }
+ /* Go to the VMS account's top directory */
+ if ((cp = StrChr(response_text, '[')) != NULL &&
+ (cp1 = strrchr(response_text, ']')) != NULL) {
+ char *tmp = 0;
+ unsigned len = 4;
+
+ StrAllocCopy(tmp, cp);
+ if ((cp2 = StrChr(cp, '.')) != NULL && cp2 < cp1) {
+ len += (cp2 - cp);
+ } else {
+ len += (cp1 - cp);
+ }
+ tmp[len] = 0;
+ StrAllocCat(tmp, "]");
+
+ status = send_cwd(tmp);
+ FREE(tmp);
+
+ if (status != 2) {
+ FREE(fname);
+ init_help_message_cache(); /* to free memory */
+ NETCLOSE(control->socket);
+ control->socket = -1;
+ return ((status < 0) ? status : -status);
+ }
+ }
+ }
+#endif /* MAINTAIN_CONNECTION */
+
+ /* If we want the VMS account's top directory, list it now */
+ if (!(strcmp(filename, "/~")) ||
+ (included_device && 0 == strcmp(filename, "000000")) ||
+ (strlen(filename) == 1 && *filename == '/')) {
+ isDirectory = YES;
+ status = send_cmd_1("LIST");
+ FREE(fname);
+ if (status != 1) {
+ /* Action not started */
+ init_help_message_cache(); /* to free memory */
+ NETCLOSE(control->socket);
+ control->socket = -1;
+ return ((status < 0) ? status : -status);
+ }
+ /* Big goto! */
+ goto listen;
+ }
+ /* Otherwise, go to appropriate directory and doctor filename */
+ if (!StrNCmp(filename, "/~", 2)) {
+ filename += 2;
+ found_tilde = TRUE;
+ }
+ CTRACE((tfp, "check '%s' to translate x/y/ to [.x.y]\n", filename));
+ if (!included_device &&
+ (cp = StrChr(filename, '/')) != NULL &&
+ (cp1 = strrchr(cp, '/')) != NULL &&
+ (cp1 - cp) > 1) {
+ char *tmp = 0;
+
+ HTSprintf0(&tmp, "[.%.*s]", (int) (cp1 - cp - 1), cp + 1);
+
+ CTRACE((tfp, "change path '%s'\n", tmp));
+ while ((cp2 = strrchr(tmp, '/')) != NULL)
+ *cp2 = '.';
+ CTRACE((tfp, "...to path '%s'\n", tmp));
+
+ status = send_cwd(tmp);
+ FREE(tmp);
+
+ if (status != 2) {
+ FREE(fname);
+ init_help_message_cache(); /* to free memory */
+ NETCLOSE(control->socket);
+ control->socket = -1;
+ return ((status < 0) ? status : -status);
+ }
+ filename = cp1 + 1;
+ } else {
+ if (!included_device && !found_tilde) {
+ filename += 1;
+ }
+ }
+ break;
+ }
+ case CMS_SERVER:
+ {
+ /*
+ * If we want the CMS account's top directory, or a base SFS or
+ * anonymous directory path (i.e., without a slash), list it
+ * now. FM
+ */
+ if ((strlen(filename) == 1 && *filename == '/') ||
+ ((0 == strncasecomp((filename + 1), "vmsysu:", 7)) &&
+ (cp = StrChr((filename + 1), '.')) != NULL &&
+ StrChr(cp, '/') == NULL) ||
+ (0 == strncasecomp(filename + 1, "anonymou.", 9) &&
+ StrChr(filename + 1, '/') == NULL)) {
+ if (filename[1] != '\0') {
+ status = send_cwd(filename + 1);
+ if (status != 2) {
+ /* Action not started */
+ init_help_message_cache(); /* to free memory */
+ NETCLOSE(control->socket);
+ control->socket = -1;
+ return ((status < 0) ? status : -status);
+ }
+ }
+ isDirectory = YES;
+ if (use_list)
+ status = send_cmd_1("LIST");
+ else
+ status = send_cmd_1("NLST");
+ FREE(fname);
+ if (status != 1) {
+ /* Action not started */
+ init_help_message_cache(); /* to free memory */
+ NETCLOSE(control->socket);
+ control->socket = -1;
+ return ((status < 0) ? status : -status);
+ }
+ /* Big goto! */
+ goto listen;
+ }
+ filename++;
+
+ /* Otherwise, go to appropriate directory and adjust filename */
+ while ((cp = StrChr(filename, '/')) != NULL) {
+ *cp++ = '\0';
+ status = send_cwd(filename);
+ if (status == 2) {
+ if (*cp == '\0') {
+ isDirectory = YES;
+ if (use_list)
+ status = send_cmd_1("LIST");
+ else
+ status = send_cmd_1("NLST");
+ FREE(fname);
+ if (status != 1) {
+ /* Action not started */
+ init_help_message_cache(); /* to free memory */
+ NETCLOSE(control->socket);
+ control->socket = -1;
+ return ((status < 0) ? status : -status);
+ }
+ /* Clear any messages from the login directory */
+ init_help_message_cache();
+ /* Big goto! */
+ goto listen;
+ }
+ filename = cp;
+ }
+ }
+ break;
+ }
+ default:
+ /* Shift for any unescaped "/%2F" path */
+ if (!StrNCmp(filename, "//", 2))
+ filename++;
+ break;
+ }
+ /*
+ * Act on a file or listing request, or try to figure out which we're
+ * dealing with if we don't know yet. - FM
+ */
+ if (!(type) || (type && *type != 'D')) {
+ /*
+ * If we are retrieving a file we will (except for CMS) use
+ * binary mode, which lets us use the size command supported by
+ * ftp servers which implement RFC 3659. Knowing the size lets
+ * us in turn display ETA in the progress message -TD
+ */
+ if (control->is_binary) {
+ int code;
+
+ status = send_cmd_2("SIZE", filename);
+ if (status == 2) {
+#if !defined(HAVE_LONG_LONG) && defined(GUESS_PRI_off_t)
+ long size;
+
+ if (sscanf(response_text, "%d %ld", &code, &size) == 2) {
+ anchor->content_length = (off_t) size;
+ }
+#else
+ off_t size;
+ if (sscanf(response_text, "%d %" SCN_off_t, &code, &size)
+ == 2) {
+ anchor->content_length = size;
+ }
+#endif
+ }
+ }
+ status = send_cmd_2("RETR", filename);
+ if (status >= 5) {
+ int check;
+
+ if (Broken_RETR) {
+ CTRACE((tfp, "{{reconnecting...\n"));
+ close_connection(control);
+ check = setup_connection(name, anchor);
+ CTRACE((tfp, "...done }}reconnecting\n"));
+ if (check < 0)
+ return check;
+ }
+ }
+ } else {
+ status = 5; /* Failed status set as flag. - FM */
+ }
+ if (status != 1) { /* Failed : try to CWD to it */
+ /* Clear any login messages if this isn't the login directory */
+ if (strcmp(filename, "/"))
+ init_help_message_cache();
+
+ status = send_cwd(filename);
+ if (status == 2) { /* Succeeded : let's NAME LIST it */
+ isDirectory = YES;
+ if (use_list)
+ status = send_cmd_1("LIST");
+ else
+ status = send_cmd_1("NLST");
+ }
+ }
+ FREE(fname);
+ FREE(vmsname);
+ if (status != 1) {
+ init_help_message_cache(); /* to free memory */
+ NETCLOSE(control->socket);
+ control->socket = -1;
+ if (status < 0)
+ return status;
+ else
+ return -status;
+ }
+ }
+
+ listen:
+ if (!ftp_local_passive) {
+ /* Wait for the connection */
+ LY_SOCKADDR soc_A;
+ LY_SOCKLEN soc_addrlen = (LY_SOCKLEN) sizeof(soc_A);
+
+#ifdef SOCKS
+ if (socks_flag)
+ status = Raccept((int) master_socket,
+ SOCKADDR_OF(soc_A),
+ &soc_addrlen);
+ else
+#endif /* SOCKS */
+ status = accept((int) master_socket,
+ SOCKADDR_OF(soc_A),
+ &soc_addrlen);
+ if (status < 0) {
+ init_help_message_cache(); /* to free memory */
+ return HTInetStatus("accept");
+ }
+ CTRACE((tfp, "TCP: Accepted new socket %d\n", status));
+ data_soc = status;
+ }
+
+ if (isDirectory) {
+ if (server_type == UNIX_SERVER && !unsure_type &&
+ !strcmp(response_text,
+ "150 Opening ASCII mode data connection for /bin/dl.\n")) {
+ CTRACE((tfp, "HTFTP: Treating as \"dls\" server.\n"));
+ server_type = DLS_SERVER;
+ }
+ final_status = read_directory(anchor, name, format_out, sink);
+ if (final_status > 0) {
+ if (server_type != CMS_SERVER)
+ if (outstanding-- > 0) {
+ status = response(NULL);
+ if (status < 0 ||
+ (status == 2 && !StrNCmp(response_text, "221", 3)))
+ outstanding = 0;
+ }
+ } else { /* HT_INTERRUPTED */
+ /* User may have pressed 'z' to give up because no
+ packets got through, so let's not make them wait
+ any longer - kw */
+ outstanding = 0;
+ }
+
+ if (data_soc != -1) { /* normally done in read_directory */
+ CTRACE((tfp, "HTFTP: Closing data socket %d\n", data_soc));
+ status = NETCLOSE(data_soc);
+ if (status == -1)
+ HTInetStatus("close"); /* Comment only */
+ }
+ status = final_status;
+ } else {
+ int rv;
+ char *FileName = HTParse(name, "", PARSE_PATH + PARSE_PUNCTUATION);
+
+ /* Clear any login messages */
+ init_help_message_cache();
+
+ /* Fake a Content-Encoding for compressed files. - FM */
+ HTUnEscape(FileName);
+ if (!IsUnityEnc(encoding)) {
+ /*
+ * We already know from the call to HTFileFormat above that this is
+ * a compressed file, no need to look at the filename again. - kw
+ */
+ StrAllocCopy(anchor->content_type, format->name);
+ StrAllocCopy(anchor->content_encoding, HTAtom_name(encoding));
+ format = HTAtom_for("www/compressed");
+
+ } else {
+ int rootlen;
+ CompressFileType cft = HTCompressFileType(FileName, "._-", &rootlen);
+
+ if (cft != cftNone) {
+ FileName[rootlen] = '\0';
+ format = HTFileFormat(FileName, &encoding, NULL);
+ format = HTCharsetFormat(format, anchor, -1);
+ StrAllocCopy(anchor->content_type, format->name);
+ format = HTAtom_for("www/compressed");
+ }
+
+ switch (cft) {
+ case cftCompress:
+ StrAllocCopy(anchor->content_encoding, "x-compress");
+ break;
+ case cftGzip:
+ StrAllocCopy(anchor->content_encoding, "x-gzip");
+ break;
+ case cftDeflate:
+ StrAllocCopy(anchor->content_encoding, "x-deflate");
+ break;
+ case cftBzip2:
+ StrAllocCopy(anchor->content_encoding, "x-bzip2");
+ break;
+ case cftBrotli:
+ StrAllocCopy(anchor->content_encoding, "x-brotli");
+ break;
+ case cftNone:
+ break;
+ }
+ }
+ FREE(FileName);
+
+ _HTProgress(gettext("Receiving FTP file."));
+ rv = HTParseSocket(format, format_out, anchor, data_soc, sink);
+
+ HTInitInput(control->socket);
+ /* Reset buffering to control connection DD 921208 */
+
+ if (rv < 0) {
+ if (rv == -2) /* weird error, don't expect much response */
+ outstanding--;
+ else if (rv == HT_INTERRUPTED || rv == -1)
+ /* User may have pressed 'z' to give up because no
+ packets got through, so let's not make them wait
+ longer - kw */
+ outstanding = 0;
+ CTRACE((tfp, "HTFTP: Closing data socket %d\n", data_soc));
+ status = NETCLOSE(data_soc);
+ } else {
+ status = 2; /* data_soc already closed in HTCopy - kw */
+ }
+
+ if (status < 0 && rv != HT_INTERRUPTED && rv != -1) {
+ (void) HTInetStatus("close"); /* Comment only */
+ } else {
+ if (rv != HT_LOADED && outstanding--) {
+ status = response(NULL); /* Pick up final reply */
+ if (status != 2 && rv != HT_INTERRUPTED && rv != -1) {
+ data_soc = -1; /* invalidate it */
+ init_help_message_cache(); /* to free memory */
+ return HTLoadError(sink, 500, response_text);
+ } else if (status == 2 && !StrNCmp(response_text, "221", 3)) {
+ outstanding = 0;
+ }
+ }
+ }
+ final_status = HT_LOADED;
+ }
+ while (outstanding-- > 0 &&
+ (status > 0)) {
+ status = response(NULL);
+ if (status == 2 && !StrNCmp(response_text, "221", 3))
+ break;
+ }
+ data_soc = -1; /* invalidate it */
+ CTRACE((tfp, "HTFTPLoad: normal end; "));
+ if (control->socket < 0) {
+ CTRACE((tfp, "control socket is %d\n", control->socket));
+ } else {
+ CTRACE((tfp, "closing control socket %d\n", control->socket));
+ status = NETCLOSE(control->socket);
+ if (status == -1)
+ HTInetStatus("control connection close"); /* Comment only */
+ }
+ control->socket = -1;
+ init_help_message_cache(); /* to free memory */
+ /* returns HT_LOADED (always for file if we get here) or error */
+ return final_status;
+} /* open_file_read */
+
+/*
+ * This function frees any user entered password, so that
+ * it must be entered again for a future request. - FM
+ */
+void HTClearFTPPassword(void)
+{
+ /*
+ * Need code to check cached documents from non-anonymous ftp accounts and
+ * do something to ensure that they no longer can be accessed without a new
+ * retrieval. - FM
+ */
+
+ /*
+ * Now free the current user entered password, if any. - FM
+ */
+ FREE(user_entered_password);
+}
+
+#endif /* ifndef DISABLE_FTP */
diff --git a/WWW/Library/Implementation/HTFTP.h b/WWW/Library/Implementation/HTFTP.h
new file mode 100644
index 0000000..a903bbb
--- /dev/null
+++ b/WWW/Library/Implementation/HTFTP.h
@@ -0,0 +1,70 @@
+/* FTP access module for libwww
+ FTP ACCESS FUNCTIONS
+
+ This isn't really a valid protocol module -- it is lumped together with HTFile . That
+ could be changed easily.
+
+ Author: Tim Berners-Lee. Public Domain. Please mail changes to timbl@info.cern.ch
+
+ */
+#ifndef HTFTP_H
+#define HTFTP_H
+
+#include <HTAnchor.h>
+#include <HTStream.h>
+#include <HTParse.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+#define FILE_BY_NAME 0
+#define FILE_BY_TYPE 1
+#define FILE_BY_SIZE 2
+#define FILE_BY_DATE 3
+ extern int HTfileSortMethod; /* specifies the method of sorting */
+
+/* PUBLIC HTVMS_name()
+ * CONVERTS WWW name into a VMS name
+ * ON ENTRY:
+ * nn Node Name (optional)
+ * fn WWW file name
+ *
+ * ON EXIT:
+ * returns vms file specification
+ *
+ * Bug: Returns pointer to static -- non-reentrant
+ */
+ extern char *HTVMS_name(const char *nn,
+ const char *fn);
+
+/*
+
+Retrieve File from Server
+
+ ON EXIT,
+
+ returns Socket number for file if good.<0 if bad.
+
+ */
+ extern int HTFTPLoad(const char *name,
+ HTParentAnchor *anchor,
+ HTFormat format_out,
+ HTStream *sink);
+
+/*
+ * This function frees any user entered password, so that
+ * it must be entered again for a future request. - FM
+ */
+ extern void HTClearFTPPassword(void);
+
+/*
+
+Return Host Name
+
+ */
+ extern const char *HTHostName(void);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/WWW/Library/Implementation/HTFWriter.h b/WWW/Library/Implementation/HTFWriter.h
new file mode 100644
index 0000000..015ea15
--- /dev/null
+++ b/WWW/Library/Implementation/HTFWriter.h
@@ -0,0 +1,30 @@
+/* File Writer for libwww
+ C FILE WRITER
+
+ It is useful to have both FWriter and Writer for environments in which fdopen() doesn't
+ exist for example.
+
+ */
+#ifndef HTFWRITE_H
+#define HTFWRITE_H
+
+#include <HTStream.h>
+#include <HTFormat.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+ extern HTStream *HTFWriter_new(FILE *fp);
+
+ extern HTStream *HTSaveAndExecute(HTPresentation *pres,
+ HTParentAnchor *anchor, /* Not used */
+ HTStream *sink);
+
+ extern HTStream *HTSaveLocally(HTPresentation *pres,
+ HTParentAnchor *anchor, /* Not used */
+ HTStream *sink);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* HTFWRITE_H */
diff --git a/WWW/Library/Implementation/HTFile.c b/WWW/Library/Implementation/HTFile.c
new file mode 100644
index 0000000..8fdaa2c
--- /dev/null
+++ b/WWW/Library/Implementation/HTFile.c
@@ -0,0 +1,3395 @@
+/*
+ * $LynxId: HTFile.c,v 1.158 2022/07/25 23:52:05 tom Exp $
+ *
+ * File Access HTFile.c
+ * ===========
+ *
+ * This is unix-specific code in general, with some VMS bits.
+ * These are routines for file access used by browsers.
+ * Development of this module for Unix DIRED_SUPPORT in Lynx
+ * regrettably has has been conducted in a manner with now
+ * creates a major impediment for hopes of adapting Lynx to
+ * a newer version of the library.
+ *
+ * History:
+ * Feb 91 Written Tim Berners-Lee CERN/CN
+ * Apr 91 vms-vms access included using DECnet syntax
+ * 26 Jun 92 (JFG) When running over DECnet, suppressed FTP.
+ * Fixed access bug for relative names on VMS.
+ * Sep 93 (MD) Access to VMS files allows sharing.
+ * 15 Nov 93 (MD) Moved HTVMSname to HTVMSUTILS.C
+ * 27 Dec 93 (FM) FTP now works with VMS hosts.
+ * FTP path must be Unix-style and cannot include
+ * the device or top directory.
+ */
+
+#include <HTUtils.h>
+
+#ifndef VMS
+#if defined(DOSPATH)
+#undef LONG_LIST
+#define LONG_LIST /* Define this for long style unix listings (ls -l),
+ the actual style is configurable from lynx.cfg */
+#endif
+/* #define NO_PARENT_DIR_REFERENCE */
+/* Define this for no parent links */
+#endif /* !VMS */
+
+#if defined(DOSPATH)
+#define HAVE_READDIR 1
+#define USE_DIRENT
+#endif
+
+#if defined(USE_DOS_DRIVES)
+#include <HTDOS.h>
+#endif
+
+#include <HTFile.h> /* Implemented here */
+
+#ifdef VMS
+#include <stat.h>
+#endif /* VMS */
+
+#if defined (USE_ZLIB) || defined (USE_BZLIB)
+#include <GridText.h>
+#endif
+
+#define MULTI_SUFFIX ".multi" /* Extension for scanning formats */
+
+#include <HTParse.h>
+#include <HTTCP.h>
+#ifndef DECNET
+#include <HTFTP.h>
+#endif /* !DECNET */
+#include <HTAnchor.h>
+#include <HTAtom.h>
+#include <HTAAProt.h>
+#include <HTFWriter.h>
+#include <HTInit.h>
+#include <HTBTree.h>
+#include <HTAlert.h>
+#include <HTCJK.h>
+#include <UCDefs.h>
+#include <UCMap.h>
+#include <UCAux.h>
+
+#include <LYexit.h>
+#include <LYCharSets.h>
+#include <LYGlobalDefs.h>
+#include <LYStrings.h>
+#include <LYUtils.h>
+
+#ifdef USE_PRETTYSRC
+# include <LYPrettySrc.h>
+#endif
+
+#include <LYLeaks.h>
+
+typedef struct _HTSuffix {
+ char *suffix;
+ HTAtom *rep;
+ HTAtom *encoding;
+ char *desc;
+ float quality;
+} HTSuffix;
+
+typedef struct {
+ struct stat file_info;
+ char sort_tags;
+ char file_name[1]; /* on the end of the struct, since its length varies */
+} DIRED;
+
+#ifndef NGROUPS
+#ifdef NGROUPS_MAX
+#define NGROUPS NGROUPS_MAX
+#else
+#define NGROUPS 32
+#endif /* NGROUPS_MAX */
+#endif /* NGROUPS */
+
+#ifndef GETGROUPS_T
+#define GETGROUPS_T int
+#endif
+
+#include <HTML.h> /* For directory object building */
+
+#define PUTC(c) (*target->isa->put_character)(target, c)
+#define PUTS(s) (*target->isa->put_string)(target, s)
+#define START(e) (*target->isa->start_element)(target, e, 0, 0, -1, 0)
+#define END(e) (*target->isa->end_element)(target, e, 0)
+#define MAYBE_END(e) if (HTML_dtd.tags[e].contents != SGML_EMPTY) \
+ (*target->isa->end_element)(target, e, 0)
+#define FREE_TARGET (*target->isa->_free)(target)
+#define ABORT_TARGET (*targetClass._abort)(target, NULL);
+
+struct _HTStructured {
+ const HTStructuredClass *isa;
+ /* ... */
+};
+
+/*
+ * Controlling globals.
+ */
+int HTDirAccess = HT_DIR_OK;
+
+#ifdef DIRED_SUPPORT
+int HTDirReadme = HT_DIR_README_NONE;
+
+#else
+int HTDirReadme = HT_DIR_README_TOP;
+#endif /* DIRED_SUPPORT */
+
+static const char *HTMountRoot = "/Net/"; /* Where to find mounts */
+
+#ifdef VMS
+static const char *HTCacheRoot = "/WWW$SCRATCH"; /* Where to cache things */
+
+#else
+static const char *HTCacheRoot = "/tmp/W3_Cache_"; /* Where to cache things */
+#endif /* VMS */
+
+static char s_no_suffix[] = "*";
+static char s_unknown_suffix[] = "*.*";
+
+/*
+ * Suffix registration.
+ */
+static HTList *HTSuffixes = 0;
+
+static HTSuffix no_suffix =
+{
+ s_no_suffix, NULL, NULL, NULL, 1.0
+};
+
+static HTSuffix unknown_suffix =
+{
+ s_unknown_suffix, NULL, NULL, NULL, 1.0
+};
+
+/* To free up the suffixes at program exit.
+ * ----------------------------------------
+ */
+#ifdef LY_FIND_LEAKS
+static void free_suffixes(void);
+#endif
+
+#define FindSearch(filename) strchr(filename, '?')
+
+#ifdef LONG_LIST
+static char *FormatStr(char **bufp,
+ char *start,
+ const char *entry)
+{
+ char fmt[512];
+
+ if (*start) {
+ sprintf(fmt, "%%%.*ss", (int) sizeof(fmt) - 3, start);
+ HTSprintf0(bufp, fmt, entry);
+ } else if (*bufp && !(entry && *entry)) {
+ **bufp = '\0';
+ } else if (entry) {
+ StrAllocCopy(*bufp, entry);
+ }
+ return *bufp;
+}
+
+static char *FormatSize(char **bufp,
+ char *start,
+ off_t entry)
+{
+ char fmt[512];
+
+ if (*start) {
+ sprintf(fmt, "%%%.*s" PRI_off_t,
+ (int) sizeof(fmt) - DigitsOf(start) - 3, start);
+
+ HTSprintf0(bufp, fmt, entry);
+ } else {
+ sprintf(fmt, "%" PRI_off_t, CAST_off_t (entry));
+
+ StrAllocCopy(*bufp, fmt);
+ }
+ return *bufp;
+}
+
+static char *FormatNum(char **bufp,
+ char *start,
+ int entry)
+{
+ char fmt[512];
+
+ if (*start) {
+ sprintf(fmt, "%%%.*sd", (int) sizeof(fmt) - 3, start);
+ HTSprintf0(bufp, fmt, entry);
+ } else {
+ sprintf(fmt, "%d", entry);
+ StrAllocCopy(*bufp, fmt);
+ }
+ return *bufp;
+}
+
+static void LYListFmtParse(const char *fmtstr,
+ DIRED * data,
+ char *file,
+ HTStructured * target,
+ char *tail)
+{
+ char c;
+ char *s;
+ char *end;
+ char *start;
+ char *str = NULL;
+ char *buf = NULL;
+ char tmp[LY_MAXPATH];
+ char type;
+
+#ifndef NOUSERS
+ const char *name;
+#endif
+ time_t now;
+ char *datestr;
+
+#ifdef S_IFLNK
+ int len;
+#endif
+#define SEC_PER_YEAR (60 * 60 * 24 * 365)
+
+#ifdef _WINDOWS /* 1998/01/06 (Tue) 21:20:53 */
+ static const char *pbits[] =
+ {
+ "---", "--x", "-w-", "-wx",
+ "r--", "r-x", "rw-", "rwx",
+ 0};
+
+#define PBIT(a, n, s) pbits[((a) >> (n)) & 0x7]
+
+#else
+ static const char *pbits[] =
+ {"---", "--x", "-w-", "-wx",
+ "r--", "r-x", "rw-", "rwx", 0};
+ static const char *psbits[] =
+ {"--S", "--s", "-wS", "-ws",
+ "r-S", "r-s", "rwS", "rws", 0};
+
+#define PBIT(a, n, s) (s) ? psbits[((a) >> (n)) & 0x7] : \
+ pbits[((a) >> (n)) & 0x7]
+#endif
+#if defined(S_ISVTX) && !defined(_WINDOWS)
+ static const char *ptbits[] =
+ {"--T", "--t", "-wT", "-wt",
+ "r-T", "r-t", "rwT", "rwt", 0};
+
+#define PTBIT(a, s) (s) ? ptbits[(a) & 0x7] : pbits[(a) & 0x7]
+#else
+#define PTBIT(a, s) PBIT(a, 0, 0)
+#endif
+
+ if (data->file_info.st_mode == 0)
+ fmtstr = " %a"; /* can't stat so just do anchor */
+
+ StrAllocCopy(str, fmtstr);
+ s = str;
+ end = str + strlen(str);
+ while (*s) {
+ start = s;
+ while (*s) {
+ if (*s == '%') {
+ if (*(s + 1) == '%') /* literal % */
+ s++;
+ else
+ break;
+ }
+ s++;
+ }
+ /* s is positioned either at a % or at \0 */
+ *s = '\0';
+ if (s > start) { /* some literal chars. */
+ PUTS(start);
+ }
+ if (s == end)
+ break;
+ start = ++s;
+ while (isdigit(UCH(*s)) || *s == '.' || *s == '-' || *s == ' ' ||
+ *s == '#' || *s == '+' || *s == '\'')
+ s++;
+ c = *s; /* the format char. or \0 */
+ *s = '\0';
+
+ switch (c) {
+ case '\0':
+ PUTS(start);
+ continue;
+
+ case 'A':
+ case 'a': /* anchor */
+ HTDirEntry(target, tail, data->file_name);
+ FormatStr(&buf, start, data->file_name);
+ PUTS(buf);
+ END(HTML_A);
+ *buf = '\0';
+#ifdef S_IFLNK
+ if (c != 'A' && S_ISLNK(data->file_info.st_mode) &&
+ (len = (int) readlink(file, tmp, sizeof(tmp) - 1)) >= 0) {
+ PUTS(" -> ");
+ tmp[len] = '\0';
+ PUTS(tmp);
+ }
+#endif
+ break;
+
+ case 'T': /* MIME type */
+ case 't': /* MIME type description */
+ if (S_ISDIR(data->file_info.st_mode)) {
+ if (c != 'T') {
+ FormatStr(&buf, start, ENTRY_IS_DIRECTORY);
+ } else {
+ FormatStr(&buf, start, "");
+ }
+ } else {
+ const char *cp2;
+ HTFormat format;
+
+ format = HTFileFormat(file, NULL, &cp2);
+
+ if (c != 'T') {
+ if (cp2 == NULL) {
+ if (!StrNCmp(HTAtom_name(format),
+ "application", 11)) {
+ cp2 = HTAtom_name(format) + 12;
+ if (!StrNCmp(cp2, "x-", 2))
+ cp2 += 2;
+ } else {
+ cp2 = HTAtom_name(format);
+ }
+ }
+ FormatStr(&buf, start, cp2);
+ } else {
+ FormatStr(&buf, start, HTAtom_name(format));
+ }
+ }
+ break;
+
+ case 'd': /* date */
+ now = time(0);
+ datestr = ctime(&data->file_info.st_mtime);
+ if ((now - data->file_info.st_mtime) < SEC_PER_YEAR / 2)
+ /*
+ * MMM DD HH:MM
+ */
+ sprintf(tmp, "%.12s", datestr + 4);
+ else
+ /*
+ * MMM DD YYYY
+ */
+ sprintf(tmp, "%.7s %.4s ", datestr + 4,
+ datestr + 20);
+ FormatStr(&buf, start, tmp);
+ break;
+
+ case 's': /* size in bytes */
+ FormatSize(&buf, start, data->file_info.st_size);
+ break;
+
+ case 'K': /* size in Kilobytes but not for directories */
+ if (S_ISDIR(data->file_info.st_mode)) {
+ FormatStr(&buf, start, "");
+ StrAllocCat(buf, " ");
+ break;
+ }
+ /* FALL THROUGH */
+ case 'k': /* size in Kilobytes */
+ FormatSize(&buf, start, ((data->file_info.st_size + 1023) / 1024));
+ StrAllocCat(buf, "K");
+ break;
+
+ case 'p': /* unix-style permission bits */
+ switch (data->file_info.st_mode & S_IFMT) {
+#if defined(_MSC_VER) && defined(_S_IFIFO)
+ case _S_IFIFO:
+ type = 'p';
+ break;
+#else
+ case S_IFIFO:
+ type = 'p';
+ break;
+#endif
+ case S_IFCHR:
+ type = 'c';
+ break;
+ case S_IFDIR:
+ type = 'd';
+ break;
+ case S_IFREG:
+ type = '-';
+ break;
+#ifdef S_IFBLK
+ case S_IFBLK:
+ type = 'b';
+ break;
+#endif
+#ifdef S_IFLNK
+ case S_IFLNK:
+ type = 'l';
+ break;
+#endif
+#ifdef S_IFSOCK
+# ifdef S_IFIFO /* some older machines (e.g., apollo) have a conflict */
+# if S_IFIFO != S_IFSOCK
+ case S_IFSOCK:
+ type = 's';
+ break;
+# endif
+# else
+ case S_IFSOCK:
+ type = 's';
+ break;
+# endif
+#endif /* S_IFSOCK */
+ default:
+ type = '?';
+ break;
+ }
+#ifdef _WINDOWS
+ sprintf(tmp, "%c%s", type,
+ PBIT(data->file_info.st_mode, 6, data->file_info.st_mode & S_IRWXU));
+#else
+ sprintf(tmp, "%c%s%s%s", type,
+ PBIT(data->file_info.st_mode, 6, data->file_info.st_mode & S_ISUID),
+ PBIT(data->file_info.st_mode, 3, data->file_info.st_mode & S_ISGID),
+ PTBIT(data->file_info.st_mode, data->file_info.st_mode & S_ISVTX));
+#endif
+ FormatStr(&buf, start, tmp);
+ break;
+
+ case 'o': /* owner */
+#ifndef NOUSERS
+ name = HTAA_UidToName((int) data->file_info.st_uid);
+ if (*name) {
+ FormatStr(&buf, start, name);
+ } else {
+ FormatNum(&buf, start, (int) data->file_info.st_uid);
+ }
+#endif
+ break;
+
+ case 'g': /* group */
+#ifndef NOUSERS
+ name = HTAA_GidToName((int) data->file_info.st_gid);
+ if (*name) {
+ FormatStr(&buf, start, name);
+ } else {
+ FormatNum(&buf, start, (int) data->file_info.st_gid);
+ }
+#endif
+ break;
+
+ case 'l': /* link count */
+ FormatNum(&buf, start, (int) data->file_info.st_nlink);
+ break;
+
+ case '%': /* literal % with flags/width */
+ FormatStr(&buf, start, "%");
+ break;
+
+ default:
+ fprintf(stderr,
+ "Unknown format character `%c' in list format\n", c);
+ break;
+ }
+ if (buf)
+ PUTS(buf);
+
+ s++;
+ }
+ FREE(buf);
+ PUTC('\n');
+ FREE(str);
+}
+#endif /* LONG_LIST */
+
+/* Define the representation associated with a file suffix.
+ * --------------------------------------------------------
+ *
+ * Calling this with suffix set to "*" will set the default
+ * representation.
+ * Calling this with suffix set to "*.*" will set the default
+ * representation for unknown suffix files which contain a ".".
+ *
+ * The encoding parameter can give a trivial (8bit, 7bit, binary)
+ * or real (gzip, compress) encoding.
+ *
+ * If filename suffix is already defined with the same encoding
+ * its previous definition is overridden.
+ */
+void HTSetSuffix5(const char *suffix,
+ const char *representation,
+ const char *encoding,
+ const char *desc,
+ double value)
+{
+ HTSuffix *suff;
+ BOOL trivial_enc = (BOOL) IsUnityEncStr(encoding);
+
+ if (strcmp(suffix, s_no_suffix) == 0)
+ suff = &no_suffix;
+ else if (strcmp(suffix, s_unknown_suffix) == 0)
+ suff = &unknown_suffix;
+ else {
+ HTList *cur = HTSuffixes;
+
+ while (NULL != (suff = (HTSuffix *) HTList_nextObject(cur))) {
+ if (suff->suffix && 0 == strcmp(suff->suffix, suffix) &&
+ ((trivial_enc && IsUnityEnc(suff->encoding)) ||
+ (!trivial_enc && !IsUnityEnc(suff->encoding) &&
+ strcmp(encoding, HTAtom_name(suff->encoding)) == 0)))
+ break;
+ }
+ if (!suff) { /* Not found -- create a new node */
+ suff = typecalloc(HTSuffix);
+ if (suff == NULL)
+ outofmem(__FILE__, "HTSetSuffix");
+
+ if (!HTSuffixes) {
+ HTSuffixes = HTList_new();
+#ifdef LY_FIND_LEAKS
+ atexit(free_suffixes);
+#endif
+ }
+
+ HTList_addObject(HTSuffixes, suff);
+
+ StrAllocCopy(suff->suffix, suffix);
+ }
+ }
+
+ if (representation)
+ suff->rep = HTAtom_for(representation);
+
+ /*
+ * Memory leak fixed.
+ * 05-28-94 Lynx 2-3-1 Garrett Arch Blythe
+ * Invariant code removed.
+ */
+ suff->encoding = HTAtom_for(encoding);
+
+ StrAllocCopy(suff->desc, desc);
+
+ suff->quality = (float) value;
+}
+
+#ifdef LY_FIND_LEAKS
+/*
+ * Purpose: Free all added suffixes.
+ * Arguments: void
+ * Return Value: void
+ * Remarks/Portability/Dependencies/Restrictions:
+ * To be used at program exit.
+ * Revision History:
+ * 05-28-94 created Lynx 2-3-1 Garrett Arch Blythe
+ */
+static void free_suffixes(void)
+{
+ HTSuffix *suff = NULL;
+
+ /*
+ * Loop through all suffixes.
+ */
+ while (!HTList_isEmpty(HTSuffixes)) {
+ /*
+ * Free off each item and its members if need be.
+ */
+ suff = (HTSuffix *) HTList_removeLastObject(HTSuffixes);
+ FREE(suff->suffix);
+ FREE(suff->desc);
+ FREE(suff);
+ }
+ /*
+ * Free off the list itself.
+ */
+ HTList_delete(HTSuffixes);
+ HTSuffixes = NULL;
+}
+#endif /* LY_FIND_LEAKS */
+
+/* Make the cache file name for a W3 document.
+ * -------------------------------------------
+ * Make up a suitable name for saving the node in
+ *
+ * E.g. /tmp/WWW_Cache_news/1234@cernvax.cern.ch
+ * /tmp/WWW_Cache_http/crnvmc/FIND/xx.xxx.xx
+ *
+ * On exit:
+ * Returns a malloc'ed string which must be freed by the caller.
+ */
+char *HTCacheFileName(const char *name)
+{
+ char *acc_method = HTParse(name, "", PARSE_ACCESS);
+ char *host = HTParse(name, "", PARSE_HOST);
+ char *path = HTParse(name, "", PARSE_PATH + PARSE_PUNCTUATION);
+ char *result = NULL;
+
+ HTSprintf0(&result, "%s/WWW/%s/%s%s", HTCacheRoot, acc_method, host, path);
+
+ FREE(path);
+ FREE(acc_method);
+ FREE(host);
+ return result;
+}
+
+/* Open a file for write, creating the path.
+ * -----------------------------------------
+ */
+#ifdef NOT_IMPLEMENTED
+static int HTCreatePath(const char *path)
+{
+ return -1;
+}
+#endif /* NOT_IMPLEMENTED */
+
+/* Convert filename from URL-path syntax to local path format
+ * ----------------------------------------------------------
+ * Input name is assumed to be the URL-path of a local file
+ * URL, i.e. what comes after the "file://localhost".
+ * '#'-fragments to be treated as such must already be stripped.
+ * If expand_all is FALSE, unescape only escaped '/'. - kw
+ *
+ * On exit:
+ * Returns a malloc'ed string which must be freed by the caller.
+ */
+char *HTURLPath_toFile(const char *name,
+ int expand_all,
+ int is_remote GCC_UNUSED)
+{
+ char *path = NULL;
+ char *result = NULL;
+
+ StrAllocCopy(path, name);
+ if (expand_all)
+ HTUnEscape(path); /* Interpret all % signs */
+ else
+ HTUnEscapeSome(path, "/"); /* Interpret % signs for path delims */
+
+ CTRACE((tfp, "URLPath `%s' means path `%s'\n", name, path));
+#if defined(USE_DOS_DRIVES)
+ StrAllocCopy(result, is_remote ? path : HTDOS_name(path));
+#else
+ StrAllocCopy(result, path);
+#endif
+
+ FREE(path);
+
+ return result;
+}
+/* Convert filenames between local and WWW formats.
+ * ------------------------------------------------
+ * Make up a suitable name for saving the node in
+ *
+ * E.g. $(HOME)/WWW/news/1234@cernvax.cern.ch
+ * $(HOME)/WWW/http/crnvmc/FIND/xx.xxx.xx
+ *
+ * On exit:
+ * Returns a malloc'ed string which must be freed by the caller.
+ */
+/* NOTE: Don't use this function if you know that the input is a URL path
+ rather than a full URL, use HTURLPath_toFile instead. Otherwise
+ this function will return the wrong thing for some unusual
+ paths (like ones containing "//", possibly escaped). - kw
+*/
+char *HTnameOfFile_WWW(const char *name,
+ int WWW_prefix,
+ int expand_all)
+{
+ char *acc_method = HTParse(name, "", PARSE_ACCESS);
+ char *host = HTParse(name, "", PARSE_HOST);
+ char *path = HTParse(name, "", PARSE_PATH + PARSE_PUNCTUATION);
+ const char *home;
+ char *result = NULL;
+
+ if (expand_all) {
+ HTUnEscape(path); /* Interpret all % signs */
+ } else
+ HTUnEscapeSome(path, "/"); /* Interpret % signs for path delims */
+
+ if (0 == strcmp(acc_method, "file") /* local file */
+ ||!*acc_method) { /* implicitly local? */
+ if ((0 == strcasecomp(host, HTHostName())) ||
+ (0 == strcasecomp(host, "localhost")) || !*host) {
+ CTRACE((tfp, "Node `%s' means path `%s'\n", name, path));
+ StrAllocCopy(result, HTSYS_name(path));
+ } else if (WWW_prefix) {
+ HTSprintf0(&result, "%s%s%s", "/Net/", host, path);
+ CTRACE((tfp, "Node `%s' means file `%s'\n", name, result));
+ } else {
+ StrAllocCopy(result, path);
+ }
+ } else if (WWW_prefix) { /* other access */
+#ifdef VMS
+ if ((home = LYGetEnv("HOME")) == NULL)
+ home = HTCacheRoot;
+ else
+ home = HTVMS_wwwName(home);
+#else
+#if defined(_WINDOWS) /* 1997/10/16 (Thu) 20:42:51 */
+ home = Home_Dir();
+#else
+ home = LYGetEnv("HOME");
+#endif
+ if (home == NULL)
+ home = "/tmp";
+#endif /* VMS */
+ HTSprintf0(&result, "%s/WWW/%s/%s%s", home, acc_method, host, path);
+ } else {
+ StrAllocCopy(result, path);
+ }
+
+ FREE(host);
+ FREE(path);
+ FREE(acc_method);
+
+ CTRACE((tfp, "HTnameOfFile_WWW(%s,%d,%d) = %s\n",
+ name, WWW_prefix, expand_all, result));
+
+ return result;
+}
+
+/* Make a WWW name from a full local path name.
+ * --------------------------------------------
+ *
+ * Bugs:
+ * At present, only the names of two network root nodes are hand-coded
+ * in and valid for the NeXT only. This should be configurable in
+ * the general case.
+ */
+char *WWW_nameOfFile(const char *name)
+{
+ char *result = NULL;
+
+#ifdef NeXT
+ if (0 == StrNCmp("/private/Net/", name, 13)) {
+ HTSprintf0(&result, "%s//%s", STR_FILE_URL, name + 13);
+ } else
+#endif /* NeXT */
+ if (0 == StrNCmp(HTMountRoot, name, 5)) {
+ HTSprintf0(&result, "%s//%s", STR_FILE_URL, name + 5);
+ } else {
+ HTSprintf0(&result, "%s//%s%s", STR_FILE_URL, HTHostName(), name);
+ }
+ CTRACE((tfp, "File `%s'\n\tmeans node `%s'\n", name, result));
+ return result;
+}
+
+/* Determine a suitable suffix, given the representation.
+ * ------------------------------------------------------
+ *
+ * On entry,
+ * rep is the atomized MIME style representation
+ * enc is an encoding, trivial (8bit, binary, etc.) or gzip etc.
+ *
+ * On exit:
+ * Returns a pointer to a suitable suffix string if one has been
+ * found, else "".
+ */
+const char *HTFileSuffix(HTAtom *rep,
+ const char *enc)
+{
+ HTSuffix *suff;
+
+#ifdef FNAMES_8_3
+ HTSuffix *first_found = NULL;
+#endif
+ BOOL trivial_enc;
+ int n;
+ int i;
+
+#define NO_INIT /* don't init anymore since I do it in Lynx at startup */
+#ifndef NO_INIT
+ if (!HTSuffixes)
+ HTFileInit();
+#endif /* !NO_INIT */
+
+ trivial_enc = (BOOL) IsUnityEncStr(enc);
+ n = HTList_count(HTSuffixes);
+ for (i = 0; i < n; i++) {
+ suff = (HTSuffix *) HTList_objectAt(HTSuffixes, i);
+ if (suff->rep == rep &&
+#if defined(VMS) || defined(FNAMES_8_3)
+ /* Don't return a suffix whose first char is a dot, and which
+ has more dots or asterisks after that, for
+ these systems - kw */
+ (!suff->suffix || !suff->suffix[0] || suff->suffix[0] != '.' ||
+ (StrChr(suff->suffix + 1, '.') == NULL &&
+ StrChr(suff->suffix + 1, '*') == NULL)) &&
+#endif
+ ((trivial_enc && IsUnityEnc(suff->encoding)) ||
+ (!trivial_enc && !IsUnityEnc(suff->encoding) &&
+ strcmp(enc, HTAtom_name(suff->encoding)) == 0))) {
+#ifdef FNAMES_8_3
+ if (suff->suffix && (strlen(suff->suffix) <= 4)) {
+ /*
+ * If length of suffix (including dot) is 4 or smaller, return
+ * this one even if we found a longer one earlier - kw
+ */
+ return suff->suffix;
+ } else if (!first_found) {
+ first_found = suff; /* remember this one */
+ }
+#else
+ return suff->suffix; /* OK -- found */
+#endif
+ }
+ }
+#ifdef FNAMES_8_3
+ if (first_found)
+ return first_found->suffix;
+#endif
+ return ""; /* Dunno */
+}
+
+/*
+ * Trim version from VMS filenames to avoid confusing comparisons.
+ */
+#ifdef VMS
+static const char *VMS_trim_version(const char *filename)
+{
+ const char *result = filename;
+ const char *version = StrChr(filename, ';');
+
+ if (version != 0) {
+ static char *stripped;
+
+ StrAllocCopy(stripped, filename);
+ stripped[version - filename] = '\0';
+ result = (const char *) stripped;
+ }
+ return result;
+}
+#define VMS_DEL_VERSION(name) name = VMS_trim_version(name)
+#else
+#define VMS_DEL_VERSION(name) /* nothing */
+#endif
+
+/* Determine file format from file name.
+ * -------------------------------------
+ *
+ * This version will return the representation and also set
+ * a variable for the encoding.
+ *
+ * Encoding may be a unity encoding (binary, 8bit, etc.) or
+ * a content-coding like gzip, compress.
+ *
+ * It will handle for example x.txt, x.txt,Z, x.Z
+ */
+HTFormat HTFileFormat(const char *filename,
+ HTAtom **pencoding,
+ const char **pdesc)
+{
+ HTSuffix *suff;
+ int n;
+ int i;
+ int lf;
+ char *search;
+
+ VMS_DEL_VERSION(filename);
+
+ if ((search = FindSearch(filename)) != 0) {
+ char *newname = NULL;
+ HTFormat result;
+
+ StrAllocCopy(newname, filename);
+ newname[((const char *) search) - filename] = '\0';
+ result = HTFileFormat(newname, pencoding, pdesc);
+ free(newname);
+ return result;
+ }
+
+ if (pencoding)
+ *pencoding = NULL;
+ if (pdesc)
+ *pdesc = NULL;
+ if (LYforce_HTML_mode) {
+ if (pencoding)
+ *pencoding = WWW_ENC_8BIT;
+ return WWW_HTML;
+ }
+#ifndef NO_INIT
+ if (!HTSuffixes)
+ HTFileInit();
+#endif /* !NO_INIT */
+ lf = (int) strlen(filename);
+ n = HTList_count(HTSuffixes);
+ for (i = 0; i < n; i++) {
+ int ls;
+
+ suff = (HTSuffix *) HTList_objectAt(HTSuffixes, i);
+ ls = (int) strlen(suff->suffix);
+ if ((ls <= lf) && 0 == strcasecomp(suff->suffix, filename + lf - ls)) {
+ int j;
+
+ if (pencoding)
+ *pencoding = suff->encoding;
+ if (pdesc)
+ *pdesc = suff->desc;
+ if (suff->rep) {
+ return suff->rep; /* OK -- found */
+ }
+ for (j = 0; j < n; j++) { /* Got encoding, need representation */
+ int ls2;
+
+ suff = (HTSuffix *) HTList_objectAt(HTSuffixes, j);
+ ls2 = (int) strlen(suff->suffix);
+ if ((ls + ls2 <= lf) &&
+ !strncasecomp(suff->suffix,
+ filename + lf - ls - ls2, ls2)) {
+ if (suff->rep) {
+ if (pdesc && !(*pdesc))
+ *pdesc = suff->desc;
+ if (pencoding && IsUnityEnc(*pencoding) &&
+ *pencoding != WWW_ENC_7BIT &&
+ !IsUnityEnc(suff->encoding))
+ *pencoding = suff->encoding;
+ return suff->rep;
+ }
+ }
+ }
+
+ }
+ }
+
+ /* defaults tree */
+
+ suff = (StrChr(filename, '.')
+ ? (unknown_suffix.rep
+ ? &unknown_suffix
+ : &no_suffix)
+ : &no_suffix);
+
+ /*
+ * Set default encoding unless found with suffix already.
+ */
+ if (pencoding && !*pencoding) {
+ *pencoding = (suff->encoding
+ ? suff->encoding
+ : HTAtom_for("binary"));
+ }
+ return suff->rep ? suff->rep : WWW_BINARY;
+}
+
+/* Revise the file format in relation to the Lynx charset. - FM
+ * -------------------------------------------------------
+ *
+ * This checks the format associated with an anchor for
+ * an extended MIME Content-Type, and if a charset is
+ * indicated, sets Lynx up for proper handling in relation
+ * to the currently selected character set. - FM
+ */
+HTFormat HTCharsetFormat(HTFormat format,
+ HTParentAnchor *anchor,
+ int default_LYhndl)
+{
+ char *cp = NULL, *cp1, *cp2, *cp3 = NULL, *cp4;
+ BOOL chartrans_ok = FALSE;
+ int chndl = -1;
+ const char *format_name = format->name;
+
+ FREE(anchor->charset);
+ if (format_name == 0)
+ format_name = "";
+ StrAllocCopy(cp, format_name);
+ LYLowerCase(cp);
+ if (((cp1 = StrChr(cp, ';')) != NULL) &&
+ (cp2 = strstr(cp1, "charset")) != NULL) {
+ CTRACE((tfp, "HTCharsetFormat: Extended MIME Content-Type is %s\n",
+ format_name));
+ cp2 += 7;
+ while (*cp2 == ' ' || *cp2 == '=')
+ cp2++;
+ StrAllocCopy(cp3, cp2); /* copy to mutilate more */
+ for (cp4 = cp3; (*cp4 != '\0' && *cp4 != '"' &&
+ *cp4 != ';' && *cp4 != ':' &&
+ !WHITE(*cp4)); cp4++) {
+ ; /* do nothing */
+ }
+ *cp4 = '\0';
+ cp4 = cp3;
+ chndl = UCGetLYhndl_byMIME(cp3);
+ if (UCCanTranslateFromTo(chndl, current_char_set)) {
+ chartrans_ok = YES;
+ *cp1 = '\0';
+ format = HTAtom_for(cp);
+ StrAllocCopy(anchor->charset, cp4);
+ HTAnchor_setUCInfoStage(anchor, chndl,
+ UCT_STAGE_MIME,
+ UCT_SETBY_MIME);
+ } else if (chndl < 0) {
+ /*
+ * Got something but we don't recognize it.
+ */
+ chndl = UCLYhndl_for_unrec;
+ if (chndl < 0)
+ /*
+ * UCLYhndl_for_unrec not defined :-( fallback to
+ * UCLYhndl_for_unspec which always valid.
+ */
+ chndl = UCLYhndl_for_unspec; /* always >= 0 */
+ if (UCCanTranslateFromTo(chndl, current_char_set)) {
+ chartrans_ok = YES;
+ HTAnchor_setUCInfoStage(anchor, chndl,
+ UCT_STAGE_MIME,
+ UCT_SETBY_DEFAULT);
+ }
+ }
+ if (chartrans_ok) {
+ LYUCcharset *p_in = HTAnchor_getUCInfoStage(anchor,
+ UCT_STAGE_MIME);
+ LYUCcharset *p_out = HTAnchor_setUCInfoStage(anchor,
+ current_char_set,
+ UCT_STAGE_HTEXT,
+ UCT_SETBY_DEFAULT);
+
+ if (!p_out) {
+ /*
+ * Try again.
+ */
+ p_out = HTAnchor_getUCInfoStage(anchor, UCT_STAGE_HTEXT);
+ }
+ if (!strcmp(p_in->MIMEname, "x-transparent")) {
+ HTPassEightBitRaw = TRUE;
+ HTAnchor_setUCInfoStage(anchor,
+ HTAnchor_getUCLYhndl(anchor,
+ UCT_STAGE_HTEXT),
+ UCT_STAGE_MIME,
+ UCT_SETBY_DEFAULT);
+ }
+ if (!strcmp(p_out->MIMEname, "x-transparent")) {
+ HTPassEightBitRaw = TRUE;
+ HTAnchor_setUCInfoStage(anchor,
+ HTAnchor_getUCLYhndl(anchor,
+ UCT_STAGE_MIME),
+ UCT_STAGE_HTEXT,
+ UCT_SETBY_DEFAULT);
+ }
+ if (p_in->enc != UCT_ENC_CJK) {
+ HTCJK = NOCJK;
+ if (!(p_in->codepoints &
+ UCT_CP_SUBSETOF_LAT1) &&
+ chndl == current_char_set) {
+ HTPassEightBitRaw = TRUE;
+ }
+ } else if (p_out->enc == UCT_ENC_CJK) {
+ Set_HTCJK(p_in->MIMEname, p_out->MIMEname);
+ }
+ } else {
+ /*
+ * Cannot translate. If according to some heuristic the given
+ * charset and the current display character both are likely to be
+ * like ISO-8859 in structure, pretend we have some kind of match.
+ */
+ BOOL given_is_8859 = (BOOL) (!StrNCmp(cp4, "iso-8859-", 9) &&
+ isdigit(UCH(cp4[9])));
+ BOOL given_is_8859like = (BOOL) (given_is_8859 ||
+ !StrNCmp(cp4, "windows-", 8) ||
+ !StrNCmp(cp4, "cp12", 4) ||
+ !StrNCmp(cp4, "cp-12", 5));
+ BOOL given_and_display_8859like = (BOOL) (given_is_8859like &&
+ (strstr(LYchar_set_names[current_char_set],
+ "ISO-8859") ||
+ strstr(LYchar_set_names[current_char_set],
+ "windows-")));
+
+ if (given_and_display_8859like) {
+ *cp1 = '\0';
+ format = HTAtom_for(cp);
+ }
+ if (given_is_8859) {
+ cp1 = &cp4[10];
+ while (*cp1 &&
+ isdigit(UCH(*cp1)))
+ cp1++;
+ *cp1 = '\0';
+ }
+ if (given_and_display_8859like) {
+ StrAllocCopy(anchor->charset, cp4);
+ HTPassEightBitRaw = TRUE;
+ }
+ HTAlert(*cp4 ? cp4 : anchor->charset);
+ }
+ FREE(cp3);
+ } else if (cp1 != NULL) {
+ /*
+ * No charset parameter is present. Ignore all other parameters, as we
+ * do when charset is present. - FM
+ */
+ *cp1 = '\0';
+ format = HTAtom_for(cp);
+ }
+ FREE(cp);
+
+ /*
+ * Set up defaults, if needed. - FM
+ */
+ if (!chartrans_ok && !anchor->charset && default_LYhndl >= 0) {
+ HTAnchor_setUCInfoStage(anchor, default_LYhndl,
+ UCT_STAGE_MIME,
+ UCT_SETBY_DEFAULT);
+ }
+ HTAnchor_copyUCInfoStage(anchor,
+ UCT_STAGE_PARSER,
+ UCT_STAGE_MIME,
+ -1);
+
+ return format;
+}
+
+/* Get various pieces of meta info from file name.
+ * -----------------------------------------------
+ *
+ * LYGetFileInfo fills in information that can be determined without
+ * an actual (new) access to the filesystem, based on current suffix
+ * and character set configuration. If the file has been loaded and
+ * parsed before (with the same URL generated here!) and the anchor
+ * is still around, some results may be influenced by that (in
+ * particular, charset info from a META tag - this is not actually
+ * tested!).
+ * The caller should not keep pointers to the returned objects around
+ * for too long, the valid lifetimes vary. In particular, the returned
+ * charset string should be copied if necessary. If return of the
+ * file_anchor is requested, that one can be used to retrieve
+ * additional bits of info that are stored in the anchor object and
+ * are not covered here; as usual, don't keep pointers to the
+ * file_anchor longer than necessary since the object may disappear
+ * through HTuncache_current_document or at the next document load.
+ * - kw
+ */
+void LYGetFileInfo(const char *filename,
+ HTParentAnchor **pfile_anchor,
+ HTFormat *pformat,
+ HTAtom **pencoding,
+ const char **pdesc,
+ const char **pcharset,
+ int *pfile_cs)
+{
+ char *Afn;
+ char *Aname = NULL;
+ HTFormat format;
+ HTAtom *myEnc = NULL;
+ HTParentAnchor *file_anchor;
+ const char *file_csname;
+ int file_cs;
+
+ /*
+ * Convert filename to URL. Note that it is always supposed to be a
+ * filename, not maybe-filename-maybe-URL, so we don't use
+ * LYFillLocalFileURL and LYEnsureAbsoluteURL. - kw
+ */
+ Afn = HTEscape(filename, URL_PATH);
+ LYLocalFileToURL(&Aname, Afn);
+ file_anchor = HTAnchor_findSimpleAddress(Aname);
+
+ format = HTFileFormat(filename, &myEnc, pdesc);
+ format = HTCharsetFormat(format, file_anchor, UCLYhndl_HTFile_for_unspec);
+ file_cs = HTAnchor_getUCLYhndl(file_anchor, UCT_STAGE_MIME);
+ file_csname = file_anchor->charset;
+ if (!file_csname) {
+ if (file_cs >= 0)
+ file_csname = LYCharSet_UC[file_cs].MIMEname;
+ else
+ file_csname = "display character set";
+ }
+ CTRACE((tfp, "GetFileInfo: '%s' is a%s %s %s file, charset=%s (%d).\n",
+ filename,
+ ((myEnc && *HTAtom_name(myEnc) == '8') ? "n" : myEnc ? "" :
+ *HTAtom_name(format) == 'a' ? "n" : ""),
+ myEnc ? HTAtom_name(myEnc) : "",
+ HTAtom_name(format),
+ file_csname,
+ file_cs));
+ FREE(Afn);
+ FREE(Aname);
+ if (pfile_anchor)
+ *pfile_anchor = file_anchor;
+ if (pformat)
+ *pformat = format;
+ if (pencoding)
+ *pencoding = myEnc;
+ if (pcharset)
+ *pcharset = file_csname;
+ if (pfile_cs)
+ *pfile_cs = file_cs;
+}
+
+/* Determine value from file name.
+ * -------------------------------
+ *
+ */
+float HTFileValue(const char *filename)
+{
+ HTSuffix *suff;
+ int n;
+ int i;
+ int lf = (int) strlen(filename);
+
+#ifndef NO_INIT
+ if (!HTSuffixes)
+ HTFileInit();
+#endif /* !NO_INIT */
+ n = HTList_count(HTSuffixes);
+ for (i = 0; i < n; i++) {
+ int ls;
+
+ suff = (HTSuffix *) HTList_objectAt(HTSuffixes, i);
+ ls = (int) strlen(suff->suffix);
+ if ((ls <= lf) && 0 == strcmp(suff->suffix, filename + lf - ls)) {
+ CTRACE((tfp, "File: Value of %s is %.3f\n",
+ filename, suff->quality));
+ return suff->quality; /* OK -- found */
+ }
+ }
+ return (float) 0.3; /* Dunno! */
+}
+
+/*
+ * Determine compression type from file name, by looking at its suffix.
+ * Sets as side-effect a pointer to the "dot" that begins the suffix.
+ */
+CompressFileType HTCompressFileType(const char *filename,
+ const char *dots,
+ int *rootlen)
+{
+ CompressFileType result = cftNone;
+ char *search;
+
+ if ((search = FindSearch(filename)) != 0) {
+ char *newname = NULL;
+
+ StrAllocCopy(newname, filename);
+ newname[((const char *) search) - filename] = '\0';
+ result = HTCompressFileType(newname, dots, rootlen);
+ free(newname);
+ } else {
+ size_t len;
+ const char *ftype;
+
+ VMS_DEL_VERSION(filename);
+ len = strlen(filename);
+ ftype = filename + len;
+
+ if ((len > 3)
+ && !strcasecomp((ftype - 2), "br")
+ && StrChr(dots, ftype[-3]) != 0) {
+ result = cftBrotli;
+ ftype -= 3;
+ } else if ((len > 4)
+ && !strcasecomp((ftype - 3), "bz2")
+ && StrChr(dots, ftype[-4]) != 0) {
+ result = cftBzip2;
+ ftype -= 4;
+ } else if ((len > 3)
+ && !strcasecomp((ftype - 2), "gz")
+ && StrChr(dots, ftype[-3]) != 0) {
+ result = cftGzip;
+ ftype -= 3;
+ } else if ((len > 3)
+ && !strcasecomp((ftype - 2), "zz")
+ && StrChr(dots, ftype[-3]) != 0) {
+ result = cftDeflate;
+ ftype -= 3;
+ } else if ((len > 2)
+ && !strcmp((ftype - 1), "Z")
+ && StrChr(dots, ftype[-2]) != 0) {
+ result = cftCompress;
+ ftype -= 2;
+ }
+
+ *rootlen = (int) (ftype - filename);
+
+ CTRACE((tfp, "HTCompressFileType(%s) returns %d:%s\n",
+ filename, (int) result, filename + *rootlen));
+ }
+ return result;
+}
+
+/*
+ * Determine expected file-suffix from the compression method.
+ */
+const char *HTCompressTypeToSuffix(CompressFileType method)
+{
+ const char *result = "";
+
+ switch (method) {
+ default:
+ case cftNone:
+ result = "";
+ break;
+ case cftGzip:
+ result = ".gz";
+ break;
+ case cftCompress:
+ result = ".Z";
+ break;
+ case cftBzip2:
+ result = ".bz2";
+ break;
+ case cftDeflate:
+ result = ".zz";
+ break;
+ case cftBrotli:
+ result = ".br";
+ break;
+ }
+ return result;
+}
+
+/*
+ * Determine compression encoding from the compression method.
+ */
+const char *HTCompressTypeToEncoding(CompressFileType method)
+{
+ const char *result = NULL;
+
+ switch (method) {
+ default:
+ case cftNone:
+ result = NULL;
+ break;
+ case cftGzip:
+ result = "gzip";
+ break;
+ case cftCompress:
+ result = "compress";
+ break;
+ case cftBzip2:
+ result = "bzip2";
+ break;
+ case cftDeflate:
+ result = "deflate";
+ break;
+ case cftBrotli:
+ result = "brotli";
+ break;
+ }
+ return result;
+}
+
+/*
+ * Check if the token from "Content-Encoding" corresponds to a compression
+ * type. RFC 2068 (and cut/paste into RFC 2616) lists these:
+ * gzip
+ * compress
+ * deflate
+ * as well as "identity" (but that does nothing).
+ */
+CompressFileType HTEncodingToCompressType(const char *coding)
+{
+ CompressFileType result = cftNone;
+
+ if (coding == NULL) {
+ result = cftNone;
+ } else if (!strcasecomp(coding, "gzip") ||
+ !strcasecomp(coding, "x-gzip")) {
+ result = cftGzip;
+ } else if (!strcasecomp(coding, "compress") ||
+ !strcasecomp(coding, "x-compress")) {
+ result = cftCompress;
+ } else if (!strcasecomp(coding, "bzip2") ||
+ !strcasecomp(coding, "x-bzip2")) {
+ result = cftBzip2;
+ } else if (!strcasecomp(coding, "br") || /* actual */
+ !strcasecomp(coding, "brotli") || /* expected */
+ !strcasecomp(coding, "x-brotli")) {
+ result = cftBrotli;
+ } else if (!strcasecomp(coding, "deflate") ||
+ !strcasecomp(coding, "x-deflate")) {
+ result = cftDeflate;
+ }
+ return result;
+}
+
+CompressFileType HTContentTypeToCompressType(const char *ct)
+{
+ CompressFileType method = cftNone;
+
+ if (ct == NULL) {
+ method = cftNone;
+ } else if (!strncasecomp(ct, "application/gzip", 16) ||
+ !strncasecomp(ct, "application/x-gzip", 18)) {
+ method = cftGzip;
+ } else if (!strncasecomp(ct, "application/compress", 20) ||
+ !strncasecomp(ct, "application/x-compress", 22)) {
+ method = cftCompress;
+ } else if (!strncasecomp(ct, "application/bzip2", 17) ||
+ !strncasecomp(ct, "application/x-bzip2", 19)) {
+ method = cftBzip2;
+ } else if (!strncasecomp(ct, "application/br", 14) ||
+ !strncasecomp(ct, "application/brotli", 18) ||
+ !strncasecomp(ct, "application/x-brotli", 20)) {
+ method = cftBrotli;
+ }
+ return method;
+}
+
+/*
+ * Check the anchor's content_type and content_encoding elements for a gzip or
+ * Unix compressed file -FM, TD
+ */
+CompressFileType HTContentToCompressType(HTParentAnchor *anchor)
+{
+ CompressFileType method = cftNone;
+ const char *ct = HTAnchor_content_type(anchor);
+ const char *ce = HTAnchor_content_encoding(anchor);
+
+ if (ct != 0) {
+ method = HTContentTypeToCompressType(ct);
+ } else if (ce != 0) {
+ method = HTEncodingToCompressType(ce);
+ }
+ return method;
+}
+
+/* Determine write access to a file.
+ * ---------------------------------
+ *
+ * On exit:
+ * Returns YES if file can be accessed and can be written to.
+ *
+ * Bugs:
+ * 1. No code for non-unix systems.
+ * 2. Isn't there a quicker way?
+ */
+BOOL HTEditable(const char *filename GCC_UNUSED)
+{
+#ifndef NO_GROUPS
+ GETGROUPS_T groups[NGROUPS];
+ uid_t myUid;
+ int ngroups; /* The number of groups */
+ struct stat fileStatus;
+ int i;
+
+ if (stat(filename, &fileStatus)) /* Get details of filename */
+ return NO; /* Can't even access file! */
+
+ ngroups = getgroups(NGROUPS, groups); /* Groups to which I belong */
+ myUid = geteuid(); /* Get my user identifier */
+
+ if (TRACE) {
+ int i2;
+
+ fprintf(tfp,
+ "File mode is 0%o, uid=%d, gid=%d. My uid=%d, %d groups (",
+ (unsigned int) fileStatus.st_mode,
+ (int) fileStatus.st_uid,
+ (int) fileStatus.st_gid,
+ (int) myUid,
+ (int) ngroups);
+ for (i2 = 0; i2 < ngroups; i2++)
+ fprintf(tfp, " %d", (int) groups[i2]);
+ fprintf(tfp, ")\n");
+ }
+
+ if (fileStatus.st_mode & 0002) /* I can write anyway? */
+ return YES;
+
+ if ((fileStatus.st_mode & 0200) /* I can write my own file? */
+ &&(fileStatus.st_uid == myUid))
+ return YES;
+
+ if (fileStatus.st_mode & 0020) /* Group I am in can write? */
+ {
+ for (i = 0; i < ngroups; i++) {
+ if (groups[i] == fileStatus.st_gid)
+ return YES;
+ }
+ }
+ CTRACE((tfp, "\tFile is not editable.\n"));
+#endif /* NO_GROUPS */
+ return NO; /* If no excuse, can't do */
+}
+
+/* Make a save stream.
+ * -------------------
+ *
+ * The stream must be used for writing back the file.
+ * @@@ no backup done
+ */
+HTStream *HTFileSaveStream(HTParentAnchor *anchor)
+{
+ const char *addr = anchor->address;
+ char *localname = HTLocalName(addr);
+ FILE *fp = fopen(localname, BIN_W);
+
+ FREE(localname);
+ if (!fp)
+ return NULL;
+
+ return HTFWriter_new(fp);
+}
+
+/* Output one directory entry.
+ * ---------------------------
+ */
+void HTDirEntry(HTStructured * target, const char *tail, const char *entry)
+{
+ char *relative = NULL;
+ char *stripped = NULL;
+ char *escaped = NULL;
+ int len;
+
+ if (entry == NULL)
+ entry = "";
+ StrAllocCopy(escaped, entry);
+ LYTrimPathSep(escaped);
+ if (strcmp(escaped, "..") != 0) {
+ stripped = escaped;
+ escaped = HTEscape(stripped, URL_XPALPHAS);
+ if (((len = (int) strlen(escaped)) > 2) &&
+ escaped[(len - 3)] == '%' &&
+ escaped[(len - 2)] == '2' &&
+ TOUPPER(escaped[(len - 1)]) == 'F') {
+ escaped[(len - 3)] = '\0';
+ }
+ }
+
+ if (isEmpty(tail)) {
+ /*
+ * Handle extra slash at end of path.
+ */
+ HTStartAnchor(target, NULL, (escaped[0] != '\0' ? escaped : "/"));
+ } else {
+ /*
+ * If empty tail, gives absolute ref below.
+ */
+ relative = 0;
+ HTSprintf0(&relative, "%s%s%s",
+ tail,
+ (*escaped != '\0' ? "/" : ""),
+ escaped);
+ HTStartAnchor(target, NULL, relative);
+ FREE(relative);
+ }
+ FREE(stripped);
+ FREE(escaped);
+}
+
+static BOOL view_structured(HTFormat format_out)
+{
+ BOOL result = FALSE;
+
+#ifdef USE_PRETTYSRC
+ if (psrc_view
+ || (format_out == WWW_DUMP))
+ result = TRUE;
+#else
+ if (format_out == WWW_SOURCE)
+ result = TRUE;
+#endif
+ return result;
+}
+
+/*
+ * Write a DOCTYPE to the given stream if we happen to want to see the
+ * source view, or are dumping source. This is not needed when the source
+ * is not visible, since the document is rendered from a HTStructured object.
+ */
+void HTStructured_doctype(HTStructured * target, HTFormat format_out)
+{
+ if (view_structured(format_out))
+ PUTS(LYNX_DOCTYPE "\n");
+}
+
+void HTStructured_meta(HTStructured * target, HTFormat format_out)
+{
+ if (view_structured(format_out))
+ PUTS("<meta http-equiv=\"Content-Type\" content=\"" STR_HTML
+ "; charset=iso-8859-1\">\n");
+}
+/* Output parent directory entry.
+ * ------------------------------
+ *
+ * This gives the TITLE and H1 header, and also a link
+ * to the parent directory if appropriate.
+ *
+ * On exit:
+ * Returns TRUE if an "Up to <parent>" link was not created
+ * for a readable local directory because LONG_LIST is defined
+ * and NO_PARENT_DIR_REFERENCE is not defined, so that the
+ * calling function should use LYListFmtParse() to create a link
+ * to the parent directory. Otherwise, it returns FALSE. - FM
+ */
+BOOL HTDirTitles(HTStructured * target, HTParentAnchor *anchor,
+ HTFormat format_out,
+ int tildeIsTop)
+{
+ const char *logical = anchor->address;
+ char *path = HTParse(logical, "", PARSE_PATH + PARSE_PUNCTUATION);
+ char *current;
+ char *cp = NULL;
+ BOOL need_parent_link = FALSE;
+ int i;
+
+#if defined(USE_DOS_DRIVES)
+ BOOL local_link = (strlen(logical) > 18
+ && !strncasecomp(logical, "file://localhost/", 17)
+ && LYIsDosDrive(logical + 17));
+ BOOL is_remote = !local_link;
+
+#else
+#define is_remote TRUE
+#endif
+
+ /*
+ * Check tildeIsTop for treating home directory as Welcome (assume the
+ * tilde is not followed by a username). - FM
+ */
+ if (tildeIsTop && !StrNCmp(path, "/~", 2)) {
+ if (path[2] == '\0') {
+ path[1] = '\0';
+ } else {
+ for (i = 0; path[(i + 2)]; i++) {
+ path[i] = path[(i + 2)];
+ }
+ path[i] = '\0';
+ }
+ }
+
+ /*
+ * Trim out the ;type= parameter, if present. - FM
+ */
+ if ((cp = strrchr(path, ';')) != NULL) {
+ if (!strncasecomp((cp + 1), "type=", 5)) {
+ if (TOUPPER(*(cp + 6)) == 'D' ||
+ TOUPPER(*(cp + 6)) == 'A' ||
+ TOUPPER(*(cp + 6)) == 'I')
+ *cp = '\0';
+ }
+ cp = NULL;
+ }
+ current = LYPathLeaf(path); /* last part or "" */
+
+ {
+ char *printable = NULL;
+
+#ifdef DIRED_SUPPORT
+ printable = HTURLPath_toFile(((!strncasecomp(path, "/%2F", 4)) /* "//" ? */
+ ? (path + 1)
+ : path),
+ TRUE,
+ is_remote);
+ if (0 == strncasecomp(printable, "/vmsysu:", 8) ||
+ 0 == strncasecomp(printable, "/anonymou.", 10)) {
+ StrAllocCopy(cp, (printable + 1));
+ StrAllocCopy(printable, cp);
+ FREE(cp);
+ }
+#else
+ StrAllocCopy(printable, current);
+ HTUnEscape(printable);
+#endif /* DIRED_SUPPORT */
+
+ HTStructured_doctype(target, format_out);
+
+ START(HTML_HEAD);
+ PUTC('\n');
+ START(HTML_TITLE);
+ PUTS(*printable ? printable : WELCOME_MSG);
+ PUTS(SEGMENT_DIRECTORY);
+ END(HTML_TITLE);
+ PUTC('\n');
+ HTStructured_meta(target, format_out);
+ END(HTML_HEAD);
+ PUTC('\n');
+
+ START(HTML_BODY);
+ PUTC('\n');
+
+#ifdef DIRED_SUPPORT
+ START(HTML_H2);
+ PUTS(*printable ? SEGMENT_CURRENT_DIR : "");
+ PUTS(*printable ? printable : WELCOME_MSG);
+ END(HTML_H2);
+ PUTC('\n');
+#else
+ START(HTML_H1);
+ PUTS(*printable ? printable : WELCOME_MSG);
+ END(HTML_H1);
+ PUTC('\n');
+#endif /* DIRED_SUPPORT */
+ if (((0 == strncasecomp(printable, "vmsysu:", 7)) &&
+ (cp = StrChr(printable, '.')) != NULL &&
+ StrChr(cp, '/') == NULL) ||
+ (0 == strncasecomp(printable, "anonymou.", 9) &&
+ StrChr(printable, '/') == NULL)) {
+ FREE(printable);
+ FREE(path);
+ return (need_parent_link);
+ }
+ FREE(printable);
+ }
+
+#ifndef NO_PARENT_DIR_REFERENCE
+ /*
+ * Make link back to parent directory.
+ */
+ if (current - path > 0
+ && LYIsPathSep(current[-1])
+ && current[0] != '\0') { /* was a slash AND something else too */
+ char *parent = NULL;
+ char *relative = NULL;
+
+ current[-1] = '\0';
+ parent = strrchr(path, '/'); /* penultimate slash */
+
+ if ((parent &&
+ (!strcmp(parent, "/..") ||
+ !strncasecomp(parent, "/%2F", 4))) ||
+ !strncasecomp(current, "%2F", 3)) {
+ FREE(path);
+ return (need_parent_link);
+ }
+
+ relative = 0;
+ HTSprintf0(&relative, "%s/..", current);
+
+#if defined(DOSPATH) || defined(__EMX__)
+ if (local_link) {
+ if (parent != 0 && strlen(parent) == 3) {
+ StrAllocCat(relative, "/.");
+ }
+ } else
+#endif
+
+#if !defined (VMS)
+ {
+ /*
+ * On Unix, if it's not ftp and the directory cannot be read, don't
+ * put out a link.
+ *
+ * On VMS, this problem is dealt with internally by
+ * HTVMSBrowseDir().
+ */
+ DIR *dp = NULL;
+
+ if (LYisLocalFile(logical)) {
+ /*
+ * We need an absolute file path for the opendir. We also need
+ * to unescape for this test. Don't worry about %2F now, they
+ * presumably have been dealt with above, and shouldn't appear
+ * for local files anyway... Assume OS / filesystem will just
+ * ignore superfluous slashes. - KW
+ */
+ char *fullparentpath = NULL;
+
+ /*
+ * Path has been shortened above.
+ */
+ StrAllocCopy(fullparentpath, *path ? path : "/");
+
+ /*
+ * Guard against weirdness.
+ */
+ if (0 == strcmp(current, "..")) {
+ StrAllocCat(fullparentpath, "/../..");
+ } else if (0 == strcmp(current, ".")) {
+ StrAllocCat(fullparentpath, "/..");
+ }
+
+ HTUnEscape(fullparentpath);
+ if ((dp = opendir(fullparentpath)) == NULL) {
+ FREE(fullparentpath);
+ FREE(relative);
+ FREE(path);
+ return (need_parent_link);
+ }
+ closedir(dp);
+ FREE(fullparentpath);
+#ifdef LONG_LIST
+ need_parent_link = TRUE;
+ FREE(path);
+ FREE(relative);
+ return (need_parent_link);
+#endif /* LONG_LIST */
+ }
+ }
+#endif /* !VMS */
+ HTStartAnchor(target, "", relative);
+ FREE(relative);
+
+ PUTS(SEGMENT_UP_TO);
+ if (parent) {
+ if ((0 == strcmp(current, ".")) ||
+ (0 == strcmp(current, ".."))) {
+ /*
+ * Should not happen, but if it does, at least avoid giving
+ * misleading info. - KW
+ */
+ PUTS("..");
+ } else {
+ char *printable = NULL;
+
+ StrAllocCopy(printable, parent + 1);
+ HTUnEscape(printable);
+ PUTS(printable);
+ FREE(printable);
+ }
+ } else {
+ PUTC('/');
+ }
+ END(HTML_A);
+ PUTC('\n');
+ }
+#endif /* !NO_PARENT_DIR_REFERENCE */
+
+ FREE(path);
+ return (need_parent_link);
+}
+
+#if defined HAVE_READDIR
+/* Send README file.
+ * -----------------
+ *
+ * If a README file exists, then it is inserted into the document here.
+ */
+static void do_readme(HTStructured * target, const char *localname)
+{
+ FILE *fp;
+ char *readme_file_name = NULL;
+ int ch;
+
+ HTSprintf0(&readme_file_name, "%s/%s", localname, HT_DIR_README_FILE);
+
+ fp = fopen(readme_file_name, "r");
+
+ if (fp) {
+ START(HTML_PRE);
+ while ((ch = fgetc(fp)) != EOF) {
+ PUTC((char) ch);
+ }
+ END(HTML_PRE);
+ HTDisplayPartial();
+ fclose(fp);
+ }
+ FREE(readme_file_name);
+}
+
+#define DIRED_BLOK(obj) (((DIRED *)(obj))->sort_tags)
+#define DIRED_NAME(obj) (((DIRED *)(obj))->file_name)
+
+#define NM_cmp(a,b) ((a) < (b) ? -1 : ((a) > (b) ? 1 : 0))
+
+#if defined(LONG_LIST) && defined(DIRED_SUPPORT)
+static const char *file_type(const char *path)
+{
+ const char *type;
+
+ while (*path == '.')
+ ++path;
+ type = StrChr(path, '.');
+ if (type == NULL)
+ type = "";
+ return type;
+}
+#endif /* LONG_LIST && DIRED_SUPPORT */
+
+static int dired_cmp(void *a, void *b)
+{
+ DIRED *p = (DIRED *) a;
+ DIRED *q = (DIRED *) b;
+ int code = p->sort_tags - q->sort_tags;
+
+#if defined(LONG_LIST) && defined(DIRED_SUPPORT)
+ if (code == 0) {
+ switch (dir_list_order) {
+ case ORDER_BY_SIZE:
+ code = -NM_cmp(p->file_info.st_size, q->file_info.st_size);
+ break;
+ case ORDER_BY_DATE:
+ code = -NM_cmp(p->file_info.st_mtime, q->file_info.st_mtime);
+ break;
+ case ORDER_BY_MODE:
+ code = NM_cmp(p->file_info.st_mode, q->file_info.st_mode);
+ break;
+ case ORDER_BY_USER:
+ code = NM_cmp(p->file_info.st_uid, q->file_info.st_uid);
+ break;
+ case ORDER_BY_GROUP:
+ code = NM_cmp(p->file_info.st_gid, q->file_info.st_gid);
+ break;
+ case ORDER_BY_TYPE:
+ code = AS_cmp(file_type(p->file_name), file_type(q->file_name));
+ break;
+ default:
+ code = 0;
+ break;
+ }
+ }
+#endif /* LONG_LIST && DIRED_SUPPORT */
+ if (code == 0)
+ code = AS_cmp(p->file_name, q->file_name);
+#if 0
+ CTRACE((tfp, "dired_cmp(%d) ->%d\n\t%c:%s (%s)\n\t%c:%s (%s)\n",
+ dir_list_order,
+ code,
+ p->sort_tags, p->file_name, file_type(p->file_name),
+ q->sort_tags, q->file_name, file_type(q->file_name)));
+#endif
+ return code;
+}
+
+static int print_local_dir(DIR *dp, char *localname,
+ HTParentAnchor *anchor,
+ HTFormat format_out,
+ HTStream *sink)
+{
+ HTStructured *target; /* HTML object */
+ HTBTree *bt;
+ HTStructuredClass targetClass;
+ STRUCT_DIRENT *dirbuf;
+ char *pathname = NULL;
+ char *tail = NULL;
+ const char *p;
+ char *tmpfilename = NULL;
+ BOOL need_parent_link = FALSE;
+ BOOL preformatted = FALSE;
+ int status;
+ struct stat *actual_info;
+
+#ifdef DISP_PARTIAL
+ int num_of_entries = 0; /* lines counter */
+#endif
+
+#ifdef S_IFLNK
+ struct stat link_info;
+#endif
+
+ CTRACE((tfp, "print_local_dir() started\n"));
+
+ pathname = HTParse(anchor->address, "",
+ PARSE_PATH + PARSE_PUNCTUATION);
+
+ if ((p = strrchr(pathname, '/')) == NULL)
+ p = "/";
+ StrAllocCopy(tail, (p + 1));
+ FREE(pathname);
+
+ if (UCLYhndl_HTFile_for_unspec >= 0) {
+ HTAnchor_setUCInfoStage(anchor,
+ UCLYhndl_HTFile_for_unspec,
+ UCT_STAGE_PARSER,
+ UCT_SETBY_DEFAULT);
+ }
+
+ target = HTML_new(anchor, format_out, sink);
+ targetClass = *target->isa; /* Copy routine entry points */
+
+ /*
+ * The need_parent_link flag will be set if an "Up to <parent>" link was
+ * not created for a readable parent in HTDirTitles() because LONG_LIST is
+ * defined and NO_PARENT_DIR_REFERENCE is not defined so that need we to
+ * create the link via an LYListFmtParse() call. - FM
+ */
+ need_parent_link = HTDirTitles(target, anchor, format_out, FALSE);
+
+#ifdef DIRED_SUPPORT
+ if (!isLYNXCGI(anchor->address)) {
+ HTAnchor_setFormat(anchor, WWW_DIRED);
+ lynx_edit_mode = TRUE;
+ }
+#endif /* DIRED_SUPPORT */
+ if (HTDirReadme == HT_DIR_README_TOP)
+ do_readme(target, localname);
+
+ bt = HTBTree_new(dired_cmp);
+
+ _HTProgress(READING_DIRECTORY);
+ status = HT_LOADED; /* assume we don't get interrupted */
+ while ((dirbuf = readdir(dp)) != NULL) {
+ /*
+ * While there are directory entries to be read...
+ */
+ DIRED *data = NULL;
+
+#ifdef STRUCT_DIRENT__D_INO
+ if (dirbuf->d_ino == 0)
+ /*
+ * If the entry is not being used, skip it.
+ */
+ continue;
+#endif
+ /*
+ * Skip self, parent if handled in HTDirTitles() or if
+ * NO_PARENT_DIR_REFERENCE is not defined, and any dot files if
+ * no_dotfiles is set or show_dotfiles is not set. - FM
+ */
+ if (!strcmp(dirbuf->d_name, ".") /* self */ ||
+ (!strcmp(dirbuf->d_name, "..") /* parent */ &&
+ need_parent_link == FALSE) ||
+ ((strcmp(dirbuf->d_name, "..")) &&
+ (dirbuf->d_name[0] == '.' &&
+ (no_dotfiles || !show_dotfiles))))
+ continue;
+
+ StrAllocCopy(tmpfilename, localname);
+ /*
+ * If filename is not root directory, add trailing separator.
+ */
+ LYAddPathSep(&tmpfilename);
+
+ StrAllocCat(tmpfilename, dirbuf->d_name);
+ data = (DIRED *) malloc(sizeof(DIRED) + strlen(dirbuf->d_name) + 4);
+ if (data == NULL) {
+ status = HT_PARTIAL_CONTENT;
+ break;
+ }
+ LYTrimPathSep(tmpfilename);
+
+ actual_info = &(data->file_info);
+#ifdef S_IFLNK
+ if (lstat(tmpfilename, actual_info) < 0) {
+ actual_info->st_mode = 0;
+ } else {
+ if (S_ISLNK(actual_info->st_mode)) {
+ actual_info = &link_info;
+ if (stat(tmpfilename, actual_info) < 0)
+ actual_info->st_mode = 0;
+ }
+ }
+#else
+ if (stat(tmpfilename, actual_info) < 0)
+ actual_info->st_mode = 0;
+#endif
+
+ strcpy(data->file_name, dirbuf->d_name);
+#ifndef DIRED_SUPPORT
+ if (S_ISDIR(actual_info->st_mode)) {
+ data->sort_tags = 'D';
+ } else {
+ data->sort_tags = 'F';
+ /* D & F to have first directories, then files */
+ }
+#else
+ if (S_ISDIR(actual_info->st_mode)) {
+ if (dir_list_style == MIXED_STYLE) {
+ data->sort_tags = ' ';
+ LYAddPathSep0(data->file_name);
+ } else if (!strcmp(dirbuf->d_name, "..")) {
+ data->sort_tags = 'A';
+ } else {
+ data->sort_tags = 'D';
+ }
+ } else if (dir_list_style == MIXED_STYLE) {
+ data->sort_tags = ' ';
+ } else if (dir_list_style == FILES_FIRST) {
+ data->sort_tags = 'C';
+ /* C & D to have first files, then directories */
+ } else {
+ data->sort_tags = 'F';
+ }
+#endif /* !DIRED_SUPPORT */
+ /*
+ * Sort dirname in the tree bt.
+ */
+ HTBTree_add(bt, data);
+
+#ifdef DISP_PARTIAL
+ /* optimize for expensive operation: */
+ if (num_of_entries % (partial_threshold > 0 ?
+ partial_threshold : display_lines) == 0) {
+ if (HTCheckForInterrupt()) {
+ status = HT_PARTIAL_CONTENT;
+ break;
+ }
+ }
+ num_of_entries++;
+#endif /* DISP_PARTIAL */
+
+ } /* end while directory entries left to read */
+
+ if (status != HT_PARTIAL_CONTENT)
+ _HTProgress(OPERATION_OK);
+ else
+ CTRACE((tfp, "Reading the directory interrupted by user\n"));
+
+ /*
+ * Run through tree printing out in order.
+ */
+ {
+ HTBTElement *next_element = HTBTree_next(bt, NULL);
+
+ /* pick up the first element of the list */
+ int num_of_entries_output = 0; /* lines counter */
+
+ char state;
+
+ /* I for initial (.. file),
+ D for directory file,
+ F for file */
+
+#ifdef DIRED_SUPPORT
+ char test;
+#endif /* DIRED_SUPPORT */
+ state = 'I';
+
+ while (next_element != NULL) {
+ DIRED *entry;
+
+#ifndef DISP_PARTIAL
+ if (num_of_entries_output % HTMAX(display_lines, 10) == 0) {
+ if (HTCheckForInterrupt()) {
+ _HTProgress(TRANSFER_INTERRUPTED);
+ status = HT_PARTIAL_CONTENT;
+ break;
+ }
+ }
+#endif
+ StrAllocCopy(tmpfilename, localname);
+ /*
+ * If filename is not root directory.
+ */
+ LYAddPathSep(&tmpfilename);
+
+ entry = (DIRED *) (HTBTree_object(next_element));
+ /*
+ * Append the current entry's filename to the path.
+ */
+ StrAllocCat(tmpfilename, entry->file_name);
+ HTSimplify(tmpfilename, LYIsPathSep(*tmpfilename));
+ /*
+ * Output the directory entry.
+ */
+ if (strcmp(DIRED_NAME(HTBTree_object(next_element)), "..")) {
+#ifdef DIRED_SUPPORT
+ test =
+ (char) (DIRED_BLOK(HTBTree_object(next_element))
+ == 'D' ? 'D' : 'F');
+ if (state != test) {
+#ifndef LONG_LIST
+ if (dir_list_style == FILES_FIRST) {
+ if (state == 'F') {
+ END(HTML_DIR);
+ PUTC('\n');
+ }
+ } else if (dir_list_style != MIXED_STYLE)
+ if (state == 'D') {
+ END(HTML_DIR);
+ PUTC('\n');
+ }
+#endif /* !LONG_LIST */
+ state =
+ (char) (DIRED_BLOK(HTBTree_object(next_element))
+ == 'D' ? 'D' : 'F');
+ if (preformatted) {
+ END(HTML_PRE);
+ PUTC('\n');
+ preformatted = FALSE;
+ }
+ START(HTML_H2);
+ if (dir_list_style != MIXED_STYLE) {
+ START(HTML_EM);
+ PUTS(state == 'D'
+ ? LABEL_SUBDIRECTORIES
+ : LABEL_FILES);
+ END(HTML_EM);
+ }
+ END(HTML_H2);
+ PUTC('\n');
+#ifndef LONG_LIST
+ START(HTML_DIR);
+ PUTC('\n');
+#endif /* !LONG_LIST */
+ }
+#else
+ if (state != DIRED_BLOK(HTBTree_object(next_element))) {
+#ifndef LONG_LIST
+ if (state == 'D') {
+ END(HTML_DIR);
+ PUTC('\n');
+ }
+#endif /* !LONG_LIST */
+ state =
+ (char) (DIRED_BLOK(HTBTree_object(next_element))
+ == 'D' ? 'D' : 'F');
+ if (preformatted) {
+ END(HTML_PRE);
+ PUTC('\n');
+ preformatted = FALSE;
+ }
+ START(HTML_H2);
+ START(HTML_EM);
+ PUTS(state == 'D'
+ ? LABEL_SUBDIRECTORIES
+ : LABEL_FILES);
+ END(HTML_EM);
+ END(HTML_H2);
+ PUTC('\n');
+#ifndef LONG_LIST
+ START(HTML_DIR);
+ PUTC('\n');
+#endif /* !LONG_LIST */
+ }
+#endif /* DIRED_SUPPORT */
+#ifndef LONG_LIST
+ START(HTML_LI);
+#endif /* !LONG_LIST */
+ }
+ if (!preformatted) {
+ START(HTML_PRE);
+ PUTC('\n');
+ preformatted = TRUE;
+ }
+#ifdef LONG_LIST
+ LYListFmtParse(list_format, entry, tmpfilename, target, tail);
+#else
+ HTDirEntry(target, tail, entry->file_name);
+ PUTS(entry->file_name);
+ END(HTML_A);
+ MAYBE_END(HTML_LI);
+ PUTC('\n');
+#endif /* LONG_LIST */
+
+ next_element = HTBTree_next(bt, next_element);
+ /* pick up the next element of the list;
+ if none, return NULL */
+
+ /* optimize for expensive operation: */
+#ifdef DISP_PARTIAL
+ if (num_of_entries_output %
+ ((partial_threshold > 0)
+ ? partial_threshold
+ : display_lines) == 0) {
+ /* num_of_entries, num_of_entries_output... */
+ HTDisplayPartial();
+
+ if (HTCheckForInterrupt()) {
+ _HTProgress(TRANSFER_INTERRUPTED);
+ status = HT_PARTIAL_CONTENT;
+ break;
+ }
+ }
+ num_of_entries_output++;
+#endif /* DISP_PARTIAL */
+
+ } /* end while next_element */
+
+ if (status == HT_LOADED) {
+ if (state == 'I') {
+ START(HTML_P);
+ PUTS("Empty Directory");
+ }
+#ifndef LONG_LIST
+ else
+ END(HTML_DIR);
+#endif /* !LONG_LIST */
+ }
+ } /* end printing out the tree in order */
+ if (preformatted) {
+ END(HTML_PRE);
+ PUTC('\n');
+ }
+ END(HTML_BODY);
+ PUTC('\n');
+
+ FREE(tmpfilename);
+ FREE(tail);
+ HTBTreeAndObject_free(bt);
+
+ if (status == HT_LOADED) {
+ if (HTDirReadme == HT_DIR_README_BOTTOM)
+ do_readme(target, localname);
+ FREE_TARGET;
+ } else {
+ ABORT_TARGET;
+ }
+ HTFinishDisplayPartial();
+ return status; /* document loaded, maybe partial */
+}
+#endif /* HAVE_READDIR */
+
+#ifndef VMS
+int HTStat(const char *filename,
+ struct stat *data)
+{
+ int result = -1;
+ size_t len = strlen(filename);
+
+ if (len != 0 && LYIsPathSep(filename[len - 1])) {
+ char *temp_name = NULL;
+
+ HTSprintf0(&temp_name, "%s.", filename);
+ result = HTStat(temp_name, data);
+ FREE(temp_name);
+ } else {
+ result = stat(filename, data);
+#ifdef _WINDOWS
+ /*
+ * Someone claims that stat() doesn't give the proper result for a
+ * directory on Windows.
+ */
+ if (result == -1
+ && access(filename, 0) == 0) {
+ data->st_mode = S_IFDIR;
+ result = 0;
+ }
+#endif
+ }
+ return result;
+}
+#endif
+
+#if defined(USE_ZLIB) || defined(USE_BZLIB)
+static BOOL sniffStream(FILE *fp, char *buffer, size_t needed)
+{
+ long offset = ftell(fp);
+ BOOL result = FALSE;
+
+ if (offset >= 0) {
+ if (fread(buffer, sizeof(char), needed, fp) == needed) {
+ result = TRUE;
+ }
+ if (fseek(fp, offset, SEEK_SET) < 0) {
+ CTRACE((tfp, "error seeking in stream\n"));
+ result = FALSE;
+ }
+ }
+ return result;
+}
+#endif
+
+#ifdef USE_ZLIB
+static BOOL isGzipStream(FILE *fp)
+{
+ char buffer[3];
+ BOOL result;
+
+ if (sniffStream(fp, buffer, sizeof(buffer))
+ && !MemCmp(buffer, "\037\213", sizeof(buffer) - 1)) {
+ result = TRUE;
+ } else {
+ CTRACE((tfp, "not a gzip-stream\n"));
+ result = FALSE;
+ }
+ return result;
+}
+
+/*
+ * Strictly speaking, DEFLATE has no header bytes. But decode what we can,
+ * (to eliminate the one "reserved" pattern) and provide a trace. See RFC-1951
+ * discussion of BFINAL and BTYPE.
+ */
+static BOOL isDeflateStream(FILE *fp)
+{
+ char buffer[3];
+ BOOL result = FALSE;
+
+ if (sniffStream(fp, buffer, sizeof(buffer))) {
+ int bit1 = ((buffer[0] >> 0) & 1);
+ int bit2 = ((buffer[0] >> 1) & 1);
+ int bit3 = ((buffer[0] >> 2) & 1);
+ int btype = ((bit3 << 1) + bit2);
+
+ if (!MemCmp(buffer, "\170\234", sizeof(buffer) - 1)) {
+ result = TRUE;
+ CTRACE((tfp, "isDeflate: assume zlib-wrapped deflate\n"));
+ } else if (btype == 3) {
+ CTRACE((tfp, "isDeflate: not a deflate-stream\n"));
+ } else {
+ CTRACE((tfp, "isDeflate: %send block, %s compression\n",
+ (bit1 ? "" : "non-"),
+ (btype == 0
+ ? "no"
+ : (btype == 1
+ ? "static Huffman"
+ : "dynamic Huffman"))));
+ result = TRUE;
+ }
+ }
+ return result;
+}
+#endif
+
+#ifdef USE_BZLIB
+static BOOL isBzip2Stream(FILE *fp)
+{
+ char buffer[6];
+ BOOL result;
+
+ if (sniffStream(fp, buffer, sizeof(buffer))
+ && !MemCmp(buffer, "BZh", 3)
+ && isdigit(UCH(buffer[3]))
+ && isdigit(UCH(buffer[4]))) {
+ result = TRUE;
+ } else {
+ CTRACE((tfp, "not a bzip2-stream\n"));
+ result = FALSE;
+ }
+ return result;
+}
+#endif
+
+#ifdef VMS
+#define FOPEN_MODE(bin) "r", "shr=put", "shr=upd"
+#define DOT_STRING "._-" /* FIXME: should we check if suffix is after ']' or ':' ? */
+#else
+#define FOPEN_MODE(bin) (bin ? BIN_R : "r")
+#define DOT_STRING "."
+#endif
+
+#ifdef USE_BROTLI
+static FILE *brotli_open(const char *localname, const char *mode)
+{
+ CTRACE((tfp, "brotli_open file=%s, mode=%s\n", localname, mode));
+ return fopen(localname, mode);
+}
+#endif
+
+static int decompressAndParse(HTParentAnchor *anchor,
+ HTFormat format_out,
+ HTStream *sink,
+ char *nodename GCC_UNUSED,
+ char *filename,
+ HTAtom *myEncoding,
+ HTFormat format,
+ int *statusp)
+{
+ HTAtom *encoding = 0;
+
+#ifdef USE_ZLIB
+ FILE *zzfp = 0;
+ gzFile gzfp = 0;
+#endif /* USE_ZLIB */
+#ifdef USE_BZLIB
+ BZFILE *bzfp = 0;
+#endif /* USE_BZLIB */
+#ifdef USE_BROTLI
+ FILE *brfp = 0;
+#endif /* USE_BROTLI */
+#if defined(USE_ZLIB) || defined(USE_BZLIB)
+ CompressFileType internal_decompress = cftNone;
+ BOOL failed_decompress = NO;
+#endif
+ int rootlen = 0;
+ char *localname = filename;
+ int bin;
+ FILE *fp;
+ int result = FALSE;
+
+#ifdef VMS
+ /*
+ * Assume that the file is in Unix-style syntax if it contains a '/' after
+ * the leading one. @@
+ */
+ localname = (StrChr(localname + 1, '/')
+ ? HTVMS_name(nodename, localname)
+ : localname + 1);
+#endif /* VMS */
+
+ bin = HTCompressFileType(filename, ".", &rootlen) != cftNone;
+ fp = fopen(localname, FOPEN_MODE(bin));
+
+#ifdef VMS
+ /*
+ * If the file wasn't VMS syntax, then perhaps it is Ultrix.
+ */
+ if (!fp) {
+ char *ultrixname = 0;
+
+ CTRACE((tfp, "HTLoadFile: Can't open as %s\n", localname));
+ HTSprintf0(&ultrixname, "%s::\"%s\"", nodename, filename);
+ fp = fopen(ultrixname, FOPEN_MODE(bin));
+ if (!fp) {
+ CTRACE((tfp, "HTLoadFile: Can't open as %s\n", ultrixname));
+ }
+ FREE(ultrixname);
+ }
+#endif /* VMS */
+ CTRACE((tfp, "HTLoadFile: Opening `%s' gives %p\n", localname, (void *) fp));
+ if (fp) { /* Good! */
+ if (HTEditable(localname)) {
+ HTAtom *put = HTAtom_for("PUT");
+ HTList *methods = HTAnchor_methods(anchor);
+
+ if (HTList_indexOf(methods, put) == (-1)) {
+ HTList_addObject(methods, put);
+ }
+ }
+ /*
+ * Fake a Content-Encoding for compressed files. - FM
+ */
+ if (!IsUnityEnc(myEncoding)) {
+ /*
+ * We already know from the call to HTFileFormat that
+ * this is a compressed file, no need to look at the filename
+ * again. - kw
+ */
+#if defined(USE_ZLIB) || defined(USE_BZLIB)
+ CompressFileType method = HTEncodingToCompressType(HTAtom_name(myEncoding));
+#endif
+
+#define isDOWNLOAD(m) (strcmp(format_out->name, STR_DOWNLOAD) && (method == m))
+#ifdef USE_ZLIB
+ if (isDOWNLOAD(cftGzip)) {
+ if (isGzipStream(fp)) {
+ fclose(fp);
+ fp = 0;
+ gzfp = gzopen(localname, BIN_R);
+
+ CTRACE((tfp, "HTLoadFile: gzopen of `%s' gives %p\n",
+ localname, (void *) gzfp));
+ }
+ internal_decompress = cftGzip;
+ } else if (isDOWNLOAD(cftDeflate)) {
+ if (isDeflateStream(fp)) {
+ zzfp = fp;
+ fp = 0;
+
+ CTRACE((tfp, "HTLoadFile: zzopen of `%s' gives %p\n",
+ localname, (void *) zzfp));
+ }
+ internal_decompress = cftDeflate;
+ } else
+#endif /* USE_ZLIB */
+#ifdef USE_BZLIB
+ if (isDOWNLOAD(cftBzip2)) {
+ if (isBzip2Stream(fp)) {
+ fclose(fp);
+ fp = 0;
+ bzfp = BZ2_bzopen(localname, BIN_R);
+
+ CTRACE((tfp, "HTLoadFile: bzopen of `%s' gives %p\n",
+ localname, bzfp));
+ }
+ internal_decompress = cftBzip2;
+ } else
+#endif /* USE_BZLIB */
+#ifdef USE_BROTLI
+ if (isDOWNLOAD(cftBrotli)) {
+ fclose(fp);
+ fp = 0;
+ brfp = brotli_open(localname, BIN_R);
+
+ CTRACE((tfp, "HTLoadFile: brotli_open of `%s' gives %p\n",
+ localname, (void *) brfp));
+ internal_decompress = cftBrotli;
+ } else
+#endif /* USE_BROTLI */
+ {
+ StrAllocCopy(anchor->content_type, format->name);
+ StrAllocCopy(anchor->content_encoding, HTAtom_name(myEncoding));
+ format = HTAtom_for("www/compressed");
+ }
+ } else {
+ CompressFileType cft = HTCompressFileType(localname, DOT_STRING, &rootlen);
+
+ if (cft != cftNone) {
+ char *cp = NULL;
+
+ StrAllocCopy(cp, localname);
+ cp[rootlen] = '\0';
+ format = HTFileFormat(cp, &encoding, NULL);
+ FREE(cp);
+ format = HTCharsetFormat(format, anchor,
+ UCLYhndl_HTFile_for_unspec);
+ StrAllocCopy(anchor->content_type, format->name);
+ }
+
+ switch (cft) {
+ case cftCompress:
+ StrAllocCopy(anchor->content_encoding, "x-compress");
+ format = HTAtom_for("www/compressed");
+ break;
+ case cftDeflate:
+ StrAllocCopy(anchor->content_encoding, "x-deflate");
+#ifdef USE_ZLIB
+ if (strcmp(format_out->name, STR_DOWNLOAD) != 0) {
+ if (isDeflateStream(fp)) {
+ zzfp = fp;
+ fp = 0;
+
+ CTRACE((tfp, "HTLoadFile: zzopen of `%s' gives %p\n",
+ localname, (void *) zzfp));
+ }
+ internal_decompress = cftDeflate;
+ }
+#else /* USE_ZLIB */
+ format = HTAtom_for("www/compressed");
+#endif /* USE_ZLIB */
+ break;
+ case cftGzip:
+ StrAllocCopy(anchor->content_encoding, "x-gzip");
+#ifdef USE_ZLIB
+ if (strcmp(format_out->name, STR_DOWNLOAD) != 0) {
+ if (isGzipStream(fp)) {
+ fclose(fp);
+ fp = 0;
+ gzfp = gzopen(localname, BIN_R);
+
+ CTRACE((tfp, "HTLoadFile: gzopen of `%s' gives %p\n",
+ localname, (void *) gzfp));
+ }
+ internal_decompress = cftGzip;
+ }
+#else /* USE_ZLIB */
+ format = HTAtom_for("www/compressed");
+#endif /* USE_ZLIB */
+ break;
+ case cftBzip2:
+ StrAllocCopy(anchor->content_encoding, "x-bzip2");
+#ifdef USE_BZLIB
+ if (strcmp(format_out->name, STR_DOWNLOAD) != 0) {
+ if (isBzip2Stream(fp)) {
+ fclose(fp);
+ fp = 0;
+ bzfp = BZ2_bzopen(localname, BIN_R);
+
+ CTRACE((tfp, "HTLoadFile: bzopen of `%s' gives %p\n",
+ localname, bzfp));
+ }
+ internal_decompress = cftBzip2;
+ }
+#else /* USE_BZLIB */
+ format = HTAtom_for("www/compressed");
+#endif /* USE_BZLIB */
+ break;
+ case cftBrotli:
+ StrAllocCopy(anchor->content_encoding, "x-brotli");
+#ifdef USE_BROTLI
+ if (strcmp(format_out->name, STR_DOWNLOAD) != 0) {
+ fclose(fp);
+ fp = 0;
+ brfp = brotli_open(localname, BIN_R);
+
+ CTRACE((tfp, "HTLoadFile: brotli_open of `%s' gives %p\n",
+ localname, (void *) brfp));
+ internal_decompress = cftBrotli;
+ }
+#else /* USE_BROTLI */
+ format = HTAtom_for("www/compressed");
+#endif /* USE_BROTLI */
+ break;
+ case cftNone:
+ break;
+ }
+ }
+#if defined(USE_ZLIB) || defined(USE_BZLIB)
+ if (internal_decompress != cftNone) {
+ switch (internal_decompress) {
+#ifdef USE_ZLIB
+ case cftDeflate:
+ failed_decompress = (BOOLEAN) (zzfp == NULL);
+ break;
+ case cftCompress:
+ case cftGzip:
+ failed_decompress = (BOOLEAN) (gzfp == NULL);
+ break;
+#endif
+#ifdef USE_BZLIB
+ case cftBzip2:
+ failed_decompress = (BOOLEAN) (bzfp == NULL);
+ break;
+#endif
+#ifdef USE_BROTLI
+ case cftBrotli:
+ failed_decompress = (BOOLEAN) (brfp == NULL);
+ break;
+#endif
+ default:
+ failed_decompress = YES;
+ break;
+ }
+ if (failed_decompress) {
+ *statusp = HTLoadError(NULL,
+ -(HT_ERROR),
+ FAILED_OPEN_COMPRESSED_FILE);
+ } else {
+ char *sugfname = NULL;
+
+ if (anchor->SugFname) {
+ StrAllocCopy(sugfname, anchor->SugFname);
+ } else {
+ char *anchor_path = HTParse(anchor->address, "",
+ PARSE_PATH + PARSE_PUNCTUATION);
+ char *lastslash;
+
+ HTUnEscape(anchor_path);
+ lastslash = strrchr(anchor_path, '/');
+ if (lastslash)
+ StrAllocCopy(sugfname, lastslash + 1);
+ FREE(anchor_path);
+ }
+ FREE(anchor->content_encoding);
+ if (sugfname && *sugfname)
+ HTCheckFnameForCompression(&sugfname, anchor,
+ TRUE);
+ if (sugfname && *sugfname)
+ StrAllocCopy(anchor->SugFname, sugfname);
+ FREE(sugfname);
+#ifdef USE_BROTLI
+ if (brfp)
+ *statusp = HTParseBrFile(format, format_out,
+ anchor,
+ brfp, sink);
+#endif
+#ifdef USE_BZLIB
+ if (bzfp)
+ *statusp = HTParseBzFile(format, format_out,
+ anchor,
+ bzfp, sink);
+#endif
+#ifdef USE_ZLIB
+ if (gzfp)
+ *statusp = HTParseGzFile(format, format_out,
+ anchor,
+ gzfp, sink);
+ else if (zzfp)
+ *statusp = HTParseZzFile(format, format_out,
+ anchor,
+ zzfp, sink);
+#endif
+ }
+ } else
+#endif /* USE_ZLIB || USE_BZLIB */
+ {
+ *statusp = HTParseFile(format, format_out, anchor, fp, sink);
+ }
+ if (fp != 0) {
+ fclose(fp);
+ fp = 0;
+ }
+ result = TRUE;
+ } /* If successful open */
+ return result;
+}
+
+/* Load a document.
+ * ----------------
+ *
+ * On entry:
+ * addr must point to the fully qualified hypertext reference.
+ * This is the physical address of the file
+ *
+ * On exit:
+ * returns <0 Error has occurred.
+ * HTLOADED OK
+ *
+ */
+int HTLoadFile(const char *addr,
+ HTParentAnchor *anchor,
+ HTFormat format_out,
+ HTStream *sink)
+{
+ char *filename = NULL;
+ char *acc_method = NULL;
+ HTFormat format;
+ char *nodename = NULL;
+ char *newname = NULL; /* Simplified name of file */
+ HTAtom *myEncoding = NULL; /* enc of this file, may be gzip etc. */
+ int status = -1;
+
+#ifndef DISABLE_FTP
+ char *ftp_newhost;
+#endif
+
+#ifdef VMS
+ struct stat stat_info;
+#endif /* VMS */
+
+ /*
+ * Reduce the filename to a basic form (hopefully unique!).
+ */
+ StrAllocCopy(newname, addr);
+ filename = HTParse(newname, "", PARSE_PATH | PARSE_PUNCTUATION);
+ nodename = HTParse(newname, "", PARSE_HOST);
+
+ /*
+ * If access is ftp, or file is on another host, invoke ftp now.
+ */
+ acc_method = HTParse(newname, "", PARSE_ACCESS);
+ if (strcmp("ftp", acc_method) == 0 ||
+ (!LYSameHostname("localhost", nodename) &&
+ !LYSameHostname(nodename, HTHostName()))) {
+ status = -1;
+ FREE(newname);
+ FREE(filename);
+ FREE(nodename);
+ FREE(acc_method);
+#ifndef DISABLE_FTP
+ ftp_newhost = HTParse(addr, "", PARSE_HOST);
+ if (strcmp(ftp_lasthost, ftp_newhost))
+ ftp_local_passive = ftp_passive;
+
+ status = HTFTPLoad(addr, anchor, format_out, sink);
+
+ if (ftp_passive == ftp_local_passive) {
+ if ((status >= 400) || (status < 0)) {
+ ftp_local_passive = (BOOLEAN) !ftp_passive;
+ status = HTFTPLoad(addr, anchor, format_out, sink);
+ }
+ }
+
+ free(ftp_lasthost);
+ ftp_lasthost = ftp_newhost;
+#endif /* DISABLE_FTP */
+ return status;
+ } else {
+ FREE(newname);
+ FREE(acc_method);
+ }
+#if defined(VMS) || defined(USE_DOS_DRIVES)
+ HTUnEscape(filename);
+#endif /* VMS */
+
+ /*
+ * Determine the format and encoding mapped to any suffix.
+ */
+ if (anchor->content_type && anchor->content_encoding) {
+ /*
+ * If content_type and content_encoding are BOTH already set in the
+ * anchor object, we believe it and don't try to derive format and
+ * encoding from the filename. - kw
+ */
+ format = HTAtom_for(anchor->content_type);
+ myEncoding = HTAtom_for(anchor->content_encoding);
+ } else {
+ int default_UCLYhndl = UCLYhndl_HTFile_for_unspec;
+
+ if (force_old_UCLYhndl_on_reload) {
+ force_old_UCLYhndl_on_reload = FALSE;
+ default_UCLYhndl = forced_UCLYhdnl;
+ }
+
+ format = HTFileFormat(filename, &myEncoding, NULL);
+
+ /*
+ * Check the format for an extended MIME charset value, and act on it
+ * if present. Otherwise, assume what is indicated by the last
+ * parameter (fallback will effectively be UCLYhndl_for_unspec, by
+ * default ISO-8859-1). - kw
+ */
+ format = HTCharsetFormat(format, anchor, default_UCLYhndl);
+ }
+
+#ifdef VMS
+ /*
+ * Check to see if the 'filename' is in fact a directory. If it is create
+ * a new hypertext object containing a list of files and subdirectories
+ * contained in the directory. All of these are links to the directories
+ * or files listed.
+ */
+ if (HTStat(filename, &stat_info) == -1) {
+ CTRACE((tfp, "HTLoadFile: Can't stat %s\n", filename));
+ } else {
+ if (S_ISDIR(stat_info.st_mode)) {
+ if (HTDirAccess == HT_DIR_FORBID) {
+ FREE(filename);
+ FREE(nodename);
+ return HTLoadError(sink, 403, DISALLOWED_DIR_SCAN);
+ }
+
+ if (HTDirAccess == HT_DIR_SELECTIVE) {
+ char *enable_file_name = NULL;
+
+ HTSprintf0(&enable_file_name, "%s/%s", filename, HT_DIR_ENABLE_FILE);
+ if (HTStat(enable_file_name, &stat_info) == -1) {
+ FREE(filename);
+ FREE(nodename);
+ FREE(enable_file_name);
+ return HTLoadError(sink, 403, DISALLOWED_SELECTIVE_ACCESS);
+ }
+ }
+
+ FREE(filename);
+ FREE(nodename);
+ return HTVMSBrowseDir(addr, anchor, format_out, sink);
+ }
+ }
+
+ if (decompressAndParse(anchor,
+ format_out,
+ sink,
+ nodename,
+ filename,
+ myEncoding,
+ format,
+ &status)) {
+ FREE(nodename);
+ FREE(filename);
+ return status;
+ }
+ FREE(filename);
+
+#else /* not VMS: */
+
+ FREE(filename);
+
+ /*
+ * For unix, we try to translate the name into the name of a transparently
+ * mounted file.
+ *
+ * Not allowed in secure (HTClientHost) situations. TBL 921019
+ */
+#ifndef NO_UNIX_IO
+ /* Need protection here for telnet server but not httpd server. */
+
+ if (!HTSecure) { /* try local file system */
+ char *localname = HTLocalName(addr);
+ struct stat dir_info;
+
+#ifdef HAVE_READDIR
+ /*
+ * Multiformat handling.
+ *
+ * If needed, scan directory to find a good file. Bug: We don't stat
+ * the file to find the length.
+ */
+ if ((strlen(localname) > strlen(MULTI_SUFFIX)) &&
+ (0 == strcmp(localname + strlen(localname) - strlen(MULTI_SUFFIX),
+ MULTI_SUFFIX))) {
+ DIR *dp = 0;
+ BOOL forget_multi = NO;
+
+ STRUCT_DIRENT *dirbuf;
+ float best = (float) NO_VALUE_FOUND; /* So far best is bad */
+ HTFormat best_rep = NULL; /* Set when rep found */
+ HTAtom *best_enc = NULL;
+ char *best_name = NULL; /* Best dir entry so far */
+
+ char *base = strrchr(localname, '/');
+ size_t baselen = 0;
+
+ if (!base || base == localname) {
+ forget_multi = YES;
+ } else {
+ *base++ = '\0'; /* Just got directory name */
+ baselen = strlen(base) - strlen(MULTI_SUFFIX);
+ base[baselen] = '\0'; /* Chop off suffix */
+
+ dp = opendir(localname);
+ }
+ if (forget_multi || !dp) {
+ FREE(localname);
+ FREE(nodename);
+ return HTLoadError(sink, 500, FAILED_DIR_SCAN);
+ }
+
+ while ((dirbuf = readdir(dp)) != NULL) {
+ /*
+ * While there are directory entries to be read...
+ */
+#ifdef STRUCT_DIRENT__D_INO
+ if (dirbuf->d_ino == 0)
+ continue; /* if the entry is not being used, skip it */
+#endif
+ if (strlen(dirbuf->d_name) > baselen && /* Match? */
+ !StrNCmp(dirbuf->d_name, base, baselen)) {
+ HTAtom *enc;
+ HTFormat rep = HTFileFormat(dirbuf->d_name, &enc, NULL);
+ float filevalue = HTFileValue(dirbuf->d_name);
+ float value = HTStackValue(rep, format_out,
+ filevalue,
+ 0L /* @@@@@@ */ );
+
+ if (value <= 0.0) {
+ int rootlen = 0;
+ const char *atomname = NULL;
+ CompressFileType cft =
+ HTCompressFileType(dirbuf->d_name, ".", &rootlen);
+ char *cp = NULL;
+
+ enc = NULL;
+ if (cft != cftNone) {
+ StrAllocCopy(cp, dirbuf->d_name);
+ cp[rootlen] = '\0';
+ format = HTFileFormat(cp, NULL, NULL);
+ FREE(cp);
+ value = HTStackValue(format, format_out,
+ filevalue, 0L);
+ }
+ switch (cft) {
+ case cftCompress:
+ atomname = "application/x-compressed";
+ break;
+ case cftGzip:
+ atomname = "application/x-gzip";
+ break;
+ case cftDeflate:
+ atomname = "application/x-deflate";
+ break;
+ case cftBzip2:
+ atomname = "application/x-bzip2";
+ break;
+ case cftBrotli:
+ atomname = "application/x-brotli";
+ break;
+ case cftNone:
+ break;
+ }
+
+ if (atomname != NULL) {
+ value = HTStackValue(format, format_out,
+ filevalue, 0L);
+ if (value <= 0.0) {
+ format = HTAtom_for(atomname);
+ value = HTStackValue(format, format_out,
+ filevalue, 0L);
+ }
+ if (value <= 0.0) {
+ format = HTAtom_for("www/compressed");
+ value = HTStackValue(format, format_out,
+ filevalue, 0L);
+ }
+ }
+ }
+ if (value < NO_VALUE_FOUND) {
+ CTRACE((tfp,
+ "HTLoadFile: value of presenting %s is %f\n",
+ HTAtom_name(rep), value));
+ if (value > best) {
+ best_rep = rep;
+ best_enc = enc;
+ best = value;
+ StrAllocCopy(best_name, dirbuf->d_name);
+ }
+ } /* if best so far */
+ }
+ /* if match */
+ } /* end while directory entries left to read */
+ closedir(dp);
+
+ if (best_rep) {
+ format = best_rep;
+ myEncoding = best_enc;
+ base[-1] = '/'; /* Restore directory name */
+ base[0] = '\0';
+ StrAllocCat(localname, best_name);
+ FREE(best_name);
+ } else { /* If not found suitable file */
+ FREE(localname);
+ FREE(nodename);
+ return HTLoadError(sink, 403, FAILED_NO_REPRESENTATION);
+ }
+ /*NOTREACHED */
+ }
+ /* if multi suffix */
+ /*
+ * Check to see if the 'localname' is in fact a directory. If it is
+ * create a new hypertext object containing a list of files and
+ * subdirectories contained in the directory. All of these are links
+ * to the directories or files listed. NB This assumes the existence
+ * of a type 'STRUCT_DIRENT', which will hold the directory entry, and
+ * a type 'DIR' which is used to point to the current directory being
+ * read.
+ */
+#if defined(USE_DOS_DRIVES)
+ if (strlen(localname) == 2 && LYIsDosDrive(localname))
+ LYAddPathSep(&localname);
+#endif
+ if (HTStat(localname, &dir_info) == -1) /* get file information */
+ {
+ /* if can't read file information */
+ CTRACE((tfp, "HTLoadFile: can't stat %s\n", localname));
+
+ } else { /* Stat was OK */
+
+ if (S_ISDIR(dir_info.st_mode)) {
+ /*
+ * If localname is a directory.
+ */
+ DIR *dp;
+ struct stat file_info;
+
+ CTRACE((tfp, "%s is a directory\n", localname));
+
+ /*
+ * Check directory access. Selective access means only those
+ * directories containing a marker file can be browsed.
+ */
+ if (HTDirAccess == HT_DIR_FORBID) {
+ FREE(localname);
+ FREE(nodename);
+ return HTLoadError(sink, 403, DISALLOWED_DIR_SCAN);
+ }
+
+ if (HTDirAccess == HT_DIR_SELECTIVE) {
+ char *enable_file_name = NULL;
+
+ HTSprintf0(&enable_file_name, "%s/%s", localname, HT_DIR_ENABLE_FILE);
+ if (stat(enable_file_name, &file_info) != 0) {
+ FREE(localname);
+ FREE(nodename);
+ FREE(enable_file_name);
+ return HTLoadError(sink, 403, DISALLOWED_SELECTIVE_ACCESS);
+ }
+ }
+
+ CTRACE((tfp, "Opening directory %s\n", localname));
+ dp = opendir(localname);
+ if (!dp) {
+ FREE(localname);
+ FREE(nodename);
+ return HTLoadError(sink, 403, FAILED_DIR_UNREADABLE);
+ }
+
+ /*
+ * Directory access is allowed and possible.
+ */
+
+ status = print_local_dir(dp, localname,
+ anchor, format_out, sink);
+ closedir(dp);
+ FREE(localname);
+ FREE(nodename);
+ return status; /* document loaded, maybe partial */
+
+ }
+ /* end if localname is a directory */
+ if (S_ISREG(dir_info.st_mode)) {
+#ifdef LONG_MAX
+ if (dir_info.st_size <= LONG_MAX)
+#endif
+ anchor->content_length = (long) dir_info.st_size;
+ }
+
+ } /* end if file stat worked */
+
+/* End of directory reading section
+*/
+#endif /* HAVE_READDIR */
+ if (decompressAndParse(anchor,
+ format_out,
+ sink,
+ nodename,
+ localname,
+ myEncoding,
+ format,
+ &status)) {
+ FREE(nodename);
+ FREE(localname);
+ return status;
+ }
+ FREE(localname);
+ } /* local unix file system */
+#endif /* !NO_UNIX_IO */
+#endif /* VMS */
+
+#ifndef DECNET
+ /*
+ * Now, as transparently mounted access has failed, we try FTP.
+ */
+ {
+ /*
+ * Deal with case-sensitivity differences on VMS versus Unix.
+ */
+#ifdef VMS
+ if (strcasecomp(nodename, HTHostName()) != 0)
+#else
+ if (strcmp(nodename, HTHostName()) != 0)
+#endif /* VMS */
+ {
+ status = -1;
+ FREE(nodename);
+ if (StrNCmp(addr, "file://localhost", 16)) {
+ /* never go to ftp site when URL
+ * is file://localhost
+ */
+#ifndef DISABLE_FTP
+ status = HTFTPLoad(addr, anchor, format_out, sink);
+#endif /* DISABLE_FTP */
+ }
+ return status;
+ }
+ FREE(nodename);
+ }
+#endif /* !DECNET */
+
+ /*
+ * All attempts have failed.
+ */
+ {
+ CTRACE((tfp, "Can't open `%s', errno=%d\n", addr, SOCKET_ERRNO));
+
+ return HTLoadError(sink, 403, FAILED_FILE_UNREADABLE);
+ }
+}
+
+static const char *program_paths[pp_Last];
+
+/*
+ * Given a program number, return its path
+ */
+const char *HTGetProgramPath(ProgramPaths code)
+{
+ const char *result = NULL;
+
+ if (code > ppUnknown && code < pp_Last)
+ result = program_paths[code];
+ return result;
+}
+
+/*
+ * Store a program's path. The caller must allocate the string used for 'path',
+ * since HTInitProgramPaths() may free it.
+ */
+void HTSetProgramPath(ProgramPaths code, const char *path)
+{
+ if (code > ppUnknown && code < pp_Last) {
+ program_paths[code] = isEmpty(path) ? 0 : path;
+ }
+}
+
+/*
+ * Reset the list of known program paths to the ones that are compiled-in
+ */
+void HTInitProgramPaths(BOOL init)
+{
+ ProgramPaths code;
+ int n;
+ const char *path;
+ const char *test;
+
+ for (n = (int) ppUnknown + 1; n < (int) pp_Last; ++n) {
+ switch (code = (ProgramPaths) n) {
+#ifdef BROTLI_PATH
+ case ppBROTLI:
+ path = BROTLI_PATH;
+ break;
+#endif
+#ifdef BZIP2_PATH
+ case ppBZIP2:
+ path = BZIP2_PATH;
+ break;
+#endif
+#ifdef CHMOD_PATH
+ case ppCHMOD:
+ path = CHMOD_PATH;
+ break;
+#endif
+#ifdef COMPRESS_PATH
+ case ppCOMPRESS:
+ path = COMPRESS_PATH;
+ break;
+#endif
+#ifdef COPY_PATH
+ case ppCOPY:
+ path = COPY_PATH;
+ break;
+#endif
+#ifdef CSWING_PATH
+ case ppCSWING:
+ path = CSWING_PATH;
+ break;
+#endif
+#ifdef GZIP_PATH
+ case ppGZIP:
+ path = GZIP_PATH;
+ break;
+#endif
+#ifdef INFLATE_PATH
+ case ppINFLATE:
+ path = INFLATE_PATH;
+ break;
+#endif
+#ifdef INSTALL_PATH
+ case ppINSTALL:
+ path = INSTALL_PATH;
+ break;
+#endif
+#ifdef MKDIR_PATH
+ case ppMKDIR:
+ path = MKDIR_PATH;
+ break;
+#endif
+#ifdef MV_PATH
+ case ppMV:
+ path = MV_PATH;
+ break;
+#endif
+#ifdef RLOGIN_PATH
+ case ppRLOGIN:
+ path = RLOGIN_PATH;
+ break;
+#endif
+#ifdef RM_PATH
+ case ppRM:
+ path = RM_PATH;
+ break;
+#endif
+#ifdef RMDIR_PATH
+ case ppRMDIR:
+ path = RMDIR_PATH;
+ break;
+#endif
+#ifdef SETFONT_PATH
+ case ppSETFONT:
+ path = SETFONT_PATH;
+ break;
+#endif
+#ifdef TAR_PATH
+ case ppTAR:
+ path = TAR_PATH;
+ break;
+#endif
+#ifdef TELNET_PATH
+ case ppTELNET:
+ path = TELNET_PATH;
+ break;
+#endif
+#ifdef TN3270_PATH
+ case ppTN3270:
+ path = TN3270_PATH;
+ break;
+#endif
+#ifdef TOUCH_PATH
+ case ppTOUCH:
+ path = TOUCH_PATH;
+ break;
+#endif
+#ifdef UNCOMPRESS_PATH
+ case ppUNCOMPRESS:
+ path = UNCOMPRESS_PATH;
+ break;
+#endif
+#ifdef UNZIP_PATH
+ case ppUNZIP:
+ path = UNZIP_PATH;
+ break;
+#endif
+#ifdef UUDECODE_PATH
+ case ppUUDECODE:
+ path = UUDECODE_PATH;
+ break;
+#endif
+#ifdef ZCAT_PATH
+ case ppZCAT:
+ path = ZCAT_PATH;
+ break;
+#endif
+#ifdef ZIP_PATH
+ case ppZIP:
+ path = ZIP_PATH;
+ break;
+#endif
+ default:
+ path = NULL;
+ break;
+ }
+ test = HTGetProgramPath(code);
+ if (test != NULL && test != path) {
+ free(DeConst(test));
+ }
+ if (init) {
+ HTSetProgramPath(code, path);
+ }
+ }
+}
+
+/*
+ * Protocol descriptors
+ */
+#ifdef GLOBALDEF_IS_MACRO
+#define _HTFILE_C_1_INIT { "ftp", HTLoadFile, 0 }
+GLOBALDEF(HTProtocol, HTFTP, _HTFILE_C_1_INIT);
+#define _HTFILE_C_2_INIT { "file", HTLoadFile, HTFileSaveStream }
+GLOBALDEF(HTProtocol, HTFile, _HTFILE_C_2_INIT);
+#else
+GLOBALDEF HTProtocol HTFTP =
+{"ftp", HTLoadFile, 0};
+GLOBALDEF HTProtocol HTFile =
+{"file", HTLoadFile, HTFileSaveStream};
+#endif /* GLOBALDEF_IS_MACRO */
diff --git a/WWW/Library/Implementation/HTFile.h b/WWW/Library/Implementation/HTFile.h
new file mode 100644
index 0000000..0bdfb79
--- /dev/null
+++ b/WWW/Library/Implementation/HTFile.h
@@ -0,0 +1,368 @@
+/*
+ * $LynxId: HTFile.h,v 1.35 2021/07/29 22:54:21 tom Exp $
+ * File access in libwww
+ * FILE ACCESS
+ *
+ * These are routines for local file access used by WWW browsers and servers.
+ * Implemented by HTFile.c.
+ *
+ * If the file is not a local file, then we pass it on to HTFTP in case it
+ * can be reached by FTP.
+ */
+#ifndef HTFILE_H
+#define HTFILE_H
+
+#include <HTFormat.h>
+#include <HTAccess.h>
+
+#ifndef HTML_H
+#include <HTML.h> /* SCW */
+#endif /* HTML_H */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+ * Controlling globals
+ *
+ * These flags control how directories and files are represented as
+ * hypertext, and are typically set by the application from command
+ * line options, etc.
+ */ extern int HTDirAccess;
+ /* Directory access level */
+
+#define HT_DIR_FORBID 0 /* Altogether forbidden */
+#define HT_DIR_SELECTIVE 1 /* If HT_DIR_ENABLE_FILE exists */
+#define HT_DIR_OK 2 /* Any accessible directory */
+
+#define HT_DIR_ENABLE_FILE ".www_browsable" /* If exists, can browse */
+
+ extern int HTDirReadme; /* Include readme files in listing? */
+
+ /* Values: */
+#define HT_DIR_README_NONE 0 /* No */
+#define HT_DIR_README_TOP 1 /* Yes, first */
+#define HT_DIR_README_BOTTOM 2 /* Yes, at the end */
+
+#define HT_DIR_README_FILE "README"
+
+/*
+ * Convert filenames between local and WWW formats
+ */
+ extern char *HTURLPath_toFile(const char *name, int expand_all, int is_remote);
+ extern char *HTnameOfFile_WWW(const char *name, int WWW_prefix, int expand_all);
+
+#define HTLocalName(name) HTnameOfFile_WWW(name,TRUE,TRUE)
+#define HTfullURL_toFile(name) HTnameOfFile_WWW(name,FALSE,TRUE)
+#define HTpartURL_toFile(name) HTnameOfFile_WWW(name,FALSE,FALSE)
+
+/*
+ * Make a WWW name from a full local path name
+ */
+ extern char *WWW_nameOfFile(const char *name);
+
+/*
+ * Generate the name of a cache file
+ */
+ extern char *HTCacheFileName(const char *name);
+
+/*
+ * Generate fragments of HTML for source-view:
+ */
+ extern void HTStructured_doctype(HTStructured * target, HTFormat format_out);
+
+ extern void HTStructured_meta(HTStructured * target, HTFormat format_out);
+/*
+ * Output directory titles
+ *
+ * This is (like the next one) used by HTFTP. It is common code to generate
+ * the title and heading 1 and the parent directory link for any anchor.
+ *
+ * changed to return TRUE if parent directory link was generated,
+ * FALSE otherwise - KW
+ */
+ extern BOOL HTDirTitles(HTStructured * target, HTParentAnchor *anchor,
+ HTFormat format_out,
+ int tildeIsTop);
+
+/*
+ * Check existence.
+ */
+ extern int HTStat(const char *filename,
+ struct stat *data);
+
+/* Load a document.
+ * ----------------
+ */
+ extern int HTLoadFile(const char *addr,
+ HTParentAnchor *anchor,
+ HTFormat format_out,
+ HTStream *sink);
+
+/*
+ * Output a directory entry
+ *
+ * This is used by HTFTP.c for example -- it is a common routine for
+ * generating a linked directory entry.
+ */
+ extern void HTDirEntry(HTStructured * target, /* in which to put the linked text */ const char *tail, /* last part of directory name */
+ const char *entry); /* name of this entry */
+
+/*
+ * HTSetSuffix: Define the representation for a file suffix
+ *
+ * This defines a mapping between local file suffixes and file content
+ * types and encodings.
+ *
+ * ON ENTRY,
+ *
+ * suffix includes the "." if that is important (normally, yes!)
+ *
+ * representation is MIME-style content-type
+ *
+ * encoding is MIME-style content-transfer-encoding
+ * (8bit, 7bit, etc) or HTTP-style content-encoding
+ * (gzip, compress etc.)
+ *
+ * quality an a priori judgement of the quality of such files
+ * (0.0..1.0)
+ *
+ * HTSetSuffix5 has one more parameter for a short description of the type
+ * which is otherwise derived from the representation:
+ *
+ * desc is a short textual description, or NULL
+ *
+ * Examples: HTSetSuffix(".ps", "application/postscript", "8bit", 1.0);
+ * Examples: HTSetSuffix(".psz", "application/postscript", "gzip", 1.0);
+ * A MIME type could also indicate a non-trivial encoding on its own
+ * ("application/x-compressed-tar"), but in that case don't use encoding
+ * to also indicate it but use "binary" etc.
+ */
+ extern void HTSetSuffix5(const char *suffix,
+ const char *representation,
+ const char *encoding,
+ const char *desc,
+ double quality);
+
+#define HTSetSuffix(suff,rep,enc,q) HTSetSuffix5(suff, rep, enc, NULL, q)
+
+/*
+ * HTFileFormat: Get Representation and Encoding from file name.
+ *
+ * ON EXIT,
+ *
+ * return The represntation it imagines the file is in.
+ *
+ * *pEncoding The encoding (binary, 7bit, etc). See HTSetSuffix.
+ */
+ extern HTFormat HTFileFormat(const char *filename,
+ HTAtom **pEncoding,
+ const char **pDesc);
+
+/*
+ * HTCharsetFormat: Revise the file format in relation to the Lynx charset.
+ *
+ * This checks the format associated with an anchor for
+ * for an extended MIME Content-Type, and if a charset is
+ * indicated, sets Lynx up for proper handling in relation
+ * to the currently selected character set. - FM
+ */
+ extern HTFormat HTCharsetFormat(HTFormat format,
+ HTParentAnchor *anchor,
+ int default_LYhndl);
+
+/* Get various pieces of meta info from file name.
+ * -----------------------------------------------
+ *
+ * LYGetFileInfo fills in information that can be determined without
+ * an actual (new) access to the filesystem, based on current suffix
+ * and character set configuration. If the file has been loaded and
+ * parsed before (with the same URL generated here!) and the anchor
+ * is still around, some results may be influenced by that (in
+ * particular, charset info from a META tag - this is not actually
+ * tested!).
+ * The caller should not keep pointers to the returned objects around
+ * for too long, the valid lifetimes vary. In particular, the returned
+ * charset string should be copied if necessary. If return of the
+ * file_anchor is requested, that one can be used to retrieve
+ * additional bits of info that are stored in the anchor object and
+ * are not covered here; as usual, don't keep pointers to the
+ * file_anchor longer than necessary since the object may disappear
+ * through HTuncache_current_document or at the next document load.
+ * - kw
+ */
+ extern void LYGetFileInfo(const char *filename,
+ HTParentAnchor **pfile_anchor,
+ HTFormat *pformat,
+ HTAtom **pencoding,
+ const char **pdesc,
+ const char **pcharset,
+ int *pfile_cs);
+
+/*
+ * Determine file value from file name.
+ */
+ extern float HTFileValue(const char *filename);
+
+/*
+ * Known compression types.
+ */
+ typedef enum {
+ cftNone
+ ,cftCompress
+ ,cftGzip
+ ,cftBzip2
+ ,cftDeflate
+ ,cftBrotli
+ } CompressFileType;
+
+/*
+ * Determine compression type from file name, by looking at its suffix.
+ */
+ extern CompressFileType HTCompressFileType(const char *filename,
+ const char *dots,
+ int *rootlen);
+
+/*
+ * Determine compression type from the content-encoding.
+ */
+ extern CompressFileType HTEncodingToCompressType(const char *encoding);
+/*
+ * Determine compression type from the content-encoding.
+ */
+ extern CompressFileType HTContentTypeToCompressType(const char *ct);
+/*
+ * Determine compression type from the content-type and/or content-encoding.
+ */
+ extern CompressFileType HTContentToCompressType(HTParentAnchor *anchor);
+/*
+ * Determine compression encoding from the compression method.
+ */
+ extern const char *HTCompressTypeToEncoding(CompressFileType method);
+/*
+ * Determine expected file-suffix from the compression method.
+ */
+ extern const char *HTCompressTypeToSuffix(CompressFileType method);
+/*
+ * Determine write access to a file.
+ *
+ * ON EXIT,
+ *
+ * return value YES if file can be accessed and can be written to.
+ *
+ * BUGS
+ *
+ * Isn't there a quicker way?
+ */
+
+#if defined(HAVE_CONFIG_H)
+
+#ifndef HAVE_GETGROUPS
+#define NO_GROUPS
+#endif
+
+#else
+
+#ifdef VMS
+#define NO_GROUPS
+#endif /* VMS */
+#ifdef NO_UNIX_IO
+#define NO_GROUPS
+#endif /* NO_UNIX_IO */
+#ifdef PCNFS
+#define NO_GROUPS
+#endif /* PCNFS */
+#ifdef NOUSERS
+#define NO_GROUPS
+#endif /* PCNFS */
+
+#endif /* HAVE_CONFIG_H */
+
+ extern BOOL HTEditable(const char *filename);
+
+/* Make a save stream.
+ * -------------------
+ */
+ extern HTStream *HTFileSaveStream(HTParentAnchor *anchor);
+
+/*
+ * Determine a suitable suffix, given the representation.
+ *
+ * ON ENTRY,
+ *
+ * rep is the atomized MIME style representation
+ * enc is an encoding (8bit, binary, gzip, compress,..)
+ *
+ * ON EXIT,
+ *
+ * returns a pointer to a suitable suffix string if one has
+ * been found, else NULL.
+ */
+ extern const char *HTFileSuffix(HTAtom *rep,
+ const char *enc);
+
+/*
+ * Enumerate external programs that lynx may assume exists. Unlike those
+ * given in download scripts, etc., lynx would really like to know their
+ * absolute paths, for better security.
+ */
+ typedef enum {
+ ppUnknown = 0
+ ,ppBROTLI
+ ,ppBZIP2
+ ,ppCHMOD
+ ,ppCOMPRESS
+ ,ppCOPY
+ ,ppCSWING
+ ,ppGZIP
+ ,ppINFLATE
+ ,ppINSTALL
+ ,ppMKDIR
+ ,ppMV
+ ,ppRLOGIN
+ ,ppRM
+ ,ppRMDIR
+ ,ppSETFONT
+ ,ppTAR
+ ,ppTELNET
+ ,ppTN3270
+ ,ppTOUCH
+ ,ppUNCOMPRESS
+ ,ppUNZIP
+ ,ppUUDECODE
+ ,ppZCAT
+ ,ppZIP
+ ,pp_Last
+ } ProgramPaths;
+
+/*
+ * Given a program number, return its path
+ */
+ extern const char *HTGetProgramPath(ProgramPaths code);
+
+/*
+ * Store a program's path
+ */
+ extern void HTSetProgramPath(ProgramPaths code,
+ const char *path);
+
+/*
+ * Reset the list of known program paths to the ones that are compiled-in
+ */
+ extern void HTInitProgramPaths(BOOL init);
+
+/*
+ * The Protocols
+ */
+#ifdef GLOBALREF_IS_MACRO
+ extern GLOBALREF (HTProtocol, HTFTP);
+ extern GLOBALREF (HTProtocol, HTFile);
+
+#else
+ GLOBALREF HTProtocol HTFTP, HTFile;
+#endif /* GLOBALREF_IS_MACRO */
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* HTFILE_H */
diff --git a/WWW/Library/Implementation/HTFinger.c b/WWW/Library/Implementation/HTFinger.c
new file mode 100644
index 0000000..566761a
--- /dev/null
+++ b/WWW/Library/Implementation/HTFinger.c
@@ -0,0 +1,418 @@
+/*
+ * $LynxId: HTFinger.c,v 1.31 2013/11/28 11:27:50 tom Exp $
+ *
+ * FINGER ACCESS HTFinger.c
+ * =============
+ * Authors:
+ * ARB Andrew Brooks
+ *
+ * History:
+ * 21 Apr 94 First version (ARB, from HTNews.c by TBL)
+ * 12 Mar 96 Made the URL and command buffering secure from
+ * stack modifications, beautified the HTLoadFinger()
+ * and response() functions, and added support for the
+ * following URL formats for sending a "", "/w",
+ * "username[@host]", or "/w username[@host]" command
+ * to the server:
+ * finger://host
+ * finger://host/
+ * finger://host/%2fw
+ * finger://host/%2fw%20username[@host]
+ * finger://host/w/username[@host]
+ * finger://host/username[@host]
+ * finger://host/username[@host]/w
+ * finger://username@host
+ * finger://username@host/
+ * finger://username@host/w
+ * 15 Mar 96 Added support for port 79 gtype 0 gopher URLs
+ * relayed from HTLoadGopher. - FM
+ */
+
+#include <HTUtils.h>
+
+#ifndef DISABLE_FINGER
+
+#include <HTAlert.h>
+#include <HTML.h>
+#include <HTParse.h>
+#include <HTFormat.h>
+#include <HTTCP.h>
+#include <HTString.h>
+#include <HTFinger.h>
+
+#include <LYUtils.h>
+#include <LYLeaks.h>
+
+#define FINGER_PORT 79 /* See rfc742 */
+#define BIG 1024 /* Bug */
+
+#define PUTC(c) (*targetClass.put_character)(target, c)
+#define PUTS(s) (*targetClass.put_string)(target, s)
+#define START(e) (*targetClass.start_element)(target, e, 0, 0, -1, 0)
+#define END(e) (*targetClass.end_element)(target, e, 0)
+#define FREE_TARGET (*targetClass._free)(target)
+#define NEXT_CHAR HTGetCharacter()
+
+/* Module-wide variables
+*/
+static int finger_fd; /* Socket for FingerHost */
+
+struct _HTStructured {
+ const HTStructuredClass *isa; /* For gopher streams */
+ /* ... */
+};
+
+static HTStructured *target; /* The output sink */
+static HTStructuredClass targetClass; /* Copy of fn addresses */
+
+/* Initialisation for this module
+ * ------------------------------
+ */
+static BOOL initialized = NO;
+static BOOL initialize(void)
+{
+ finger_fd = -1; /* Disconnected */
+ return YES;
+}
+
+/* Start anchor element
+ * --------------------
+ */
+static void start_anchor(const char *href)
+{
+ BOOL present[HTML_A_ATTRIBUTES];
+ const char *value[HTML_A_ATTRIBUTES];
+
+ {
+ int i;
+
+ for (i = 0; i < HTML_A_ATTRIBUTES; i++)
+ present[i] = (BOOL) (i == HTML_A_HREF);
+ }
+ ((const char **) value)[HTML_A_HREF] = href;
+ (*targetClass.start_element) (target, HTML_A, present,
+ (const char **) value, -1, 0);
+
+}
+
+/* Send Finger Command line to remote host & Check Response
+ * --------------------------------------------------------
+ *
+ * On entry,
+ * command points to the command to be sent, including CRLF, or is null
+ * pointer if no command to be sent.
+ * On exit,
+ * Negative status indicates transmission error, socket closed.
+ * Positive status is a Finger status.
+ */
+
+static int response(char *command,
+ char *sitename,
+ HTParentAnchor *anAnchor,
+ HTFormat format_out,
+ HTStream *sink)
+{
+ int status;
+ int length = (int) strlen(command);
+ int ch, i;
+ char line[BIG], *l, *cmd = NULL;
+ char *p = line, *href = NULL;
+
+ if (length == 0)
+ return (-1);
+
+ /* Set up buffering.
+ */
+ HTInitInput(finger_fd);
+
+ /* Send the command.
+ */
+ CTRACE((tfp, "HTFinger command to be sent: %s", command));
+ status = (int) NETWRITE(finger_fd, (char *) command, (unsigned) length);
+ if (status < 0) {
+ CTRACE((tfp, "HTFinger: Unable to send command. Disconnecting.\n"));
+ NETCLOSE(finger_fd);
+ finger_fd = -1;
+ return status;
+ }
+ /* if bad status */
+ /* Make a hypertext object with an anchor list.
+ */
+ target = HTML_new(anAnchor, format_out, sink);
+ targetClass = *target->isa; /* Copy routine entry points */
+
+ /* Create the results report.
+ */
+ CTRACE((tfp, "HTFinger: Reading finger information\n"));
+ START(HTML_HTML);
+ PUTC('\n');
+ START(HTML_HEAD);
+ PUTC('\n');
+ START(HTML_TITLE);
+ PUTS("Finger server on ");
+ PUTS(sitename);
+ END(HTML_TITLE);
+ PUTC('\n');
+ END(HTML_HEAD);
+ PUTC('\n');
+ START(HTML_BODY);
+ PUTC('\n');
+ START(HTML_H1);
+ PUTS("Finger server on ");
+ START(HTML_EM);
+ PUTS(sitename);
+ END(HTML_EM);
+ PUTS(": ");
+ StrAllocCopy(cmd, command);
+ for (i = ((int) strlen(cmd) - 1); i >= 0; i--) {
+ if (cmd[i] == LF || cmd[i] == CR) {
+ cmd[i] = '\0';
+ } else {
+ break;
+ }
+ }
+ PUTS(cmd);
+ FREE(cmd);
+ END(HTML_H1);
+ PUTC('\n');
+ START(HTML_PRE);
+
+ while ((ch = NEXT_CHAR) != EOF) {
+
+ if (interrupted_in_htgetcharacter) {
+ CTRACE((tfp,
+ "HTFinger: Interrupted in HTGetCharacter, apparently.\n"));
+ _HTProgress(CONNECTION_INTERRUPTED);
+ goto end_html;
+ }
+
+ if (ch != LF) {
+ *p = (char) ch; /* Put character in line */
+ if (p < &line[BIG - 1]) {
+ p++;
+ }
+ } else {
+ *p = '\0'; /* Terminate line */
+ /*
+ * OK we now have a line.
+ * Load it as 'l' and parse it.
+ */
+ p = l = line;
+ while (*l) {
+ if (StrNCmp(l, STR_NEWS_URL, LEN_NEWS_URL) &&
+ StrNCmp(l, "snews://", 8) &&
+ StrNCmp(l, "nntp://", 7) &&
+ StrNCmp(l, "snewspost:", 10) &&
+ StrNCmp(l, "snewsreply:", 11) &&
+ StrNCmp(l, "newspost:", 9) &&
+ StrNCmp(l, "newsreply:", 10) &&
+ StrNCmp(l, "ftp://", 6) &&
+ StrNCmp(l, "file:/", 6) &&
+ StrNCmp(l, "finger://", 9) &&
+ StrNCmp(l, "http://", 7) &&
+ StrNCmp(l, "https://", 8) &&
+ StrNCmp(l, "wais://", 7) &&
+ StrNCmp(l, STR_MAILTO_URL, LEN_MAILTO_URL) &&
+ StrNCmp(l, "cso://", 6) &&
+ StrNCmp(l, "gopher://", 9))
+ PUTC(*l++);
+ else {
+ StrAllocCopy(href, l);
+ start_anchor(strtok(href, " \r\n\t,>)\""));
+ while (*l && !StrChr(" \r\n\t,>)\"", *l))
+ PUTC(*l++);
+ END(HTML_A);
+ FREE(href);
+ }
+ }
+ PUTC('\n');
+ }
+ }
+ NETCLOSE(finger_fd);
+ finger_fd = -1;
+
+ end_html:
+ END(HTML_PRE);
+ PUTC('\n');
+ END(HTML_BODY);
+ PUTC('\n');
+ END(HTML_HTML);
+ PUTC('\n');
+ FREE_TARGET;
+ return (0);
+}
+
+/* Load by name HTLoadFinger
+ * ============
+ */
+int HTLoadFinger(const char *arg,
+ HTParentAnchor *anAnchor,
+ HTFormat format_out,
+ HTStream *stream)
+{
+ static char empty[1];
+
+ char *username, *sitename; /* Fields extracted from URL */
+ char *slash, *at_sign; /* Fields extracted from URL */
+ char *command, *str, *param; /* Buffers */
+ int port; /* Port number from URL */
+ int status; /* tcp return */
+ int result = HT_LOADED;
+ BOOL IsGopherURL = FALSE;
+ const char *p1 = arg;
+
+ CTRACE((tfp, "HTFinger: Looking for %s\n", (arg ? arg : "NULL")));
+
+ if (!(arg && *arg)) {
+ HTAlert(COULD_NOT_LOAD_DATA);
+ return HT_NOT_LOADED; /* Ignore if no name */
+ }
+
+ if (!initialized)
+ initialized = initialize();
+ if (!initialized) {
+ HTAlert(gettext("Could not set up finger connection."));
+ return HT_NOT_LOADED; /* FAIL */
+ }
+
+ /* Set up the host and command fields.
+ */
+ if (!strncasecomp(arg, "finger://", 9)) {
+ p1 = arg + 9; /* Skip "finger://" prefix */
+ } else if (!strncasecomp(arg, "gopher://", 9)) {
+ p1 = arg + 9; /* Skip "gopher://" prefix */
+ IsGopherURL = TRUE;
+ }
+
+ param = 0;
+ sitename = StrAllocCopy(param, p1);
+ if (param == 0) {
+ HTAlert(COULD_NOT_LOAD_DATA);
+ return HT_NOT_LOADED;
+ } else if ((slash = StrChr(sitename, '/')) != NULL) {
+ *slash++ = '\0';
+ HTUnEscape(slash);
+ if (IsGopherURL) {
+ if (*slash != '0') {
+ HTAlert(COULD_NOT_LOAD_DATA);
+ return HT_NOT_LOADED; /* FAIL */
+ }
+ *slash++ = '\0';
+ }
+ }
+
+ if ((at_sign = StrChr(sitename, '@')) != NULL) {
+ if (IsGopherURL) {
+ HTAlert(COULD_NOT_LOAD_DATA);
+ return HT_NOT_LOADED; /* FAIL */
+ } else {
+ *at_sign++ = '\0';
+ username = sitename;
+ sitename = at_sign;
+ HTUnEscape(username);
+ }
+ } else if (slash) {
+ username = slash;
+ } else {
+ username = empty;
+ }
+
+ if (*sitename == '\0') {
+ HTAlert(gettext("Could not load data (no sitename in finger URL)"));
+ result = HT_NOT_LOADED; /* Ignore if no name */
+ } else if (HTParsePort(sitename, &port) != NULL) {
+ if (port != 79) {
+ HTAlert(gettext("Invalid port number - will only use port 79!"));
+ result = HT_NOT_LOADED; /* Ignore if wrong port */
+ }
+ }
+
+ if (result == HT_LOADED) {
+ /* Load the string for making a connection/
+ */
+ str = 0;
+ HTSprintf0(&str, "lose://%s/", sitename);
+
+ /* Load the command for the finger server.
+ */
+ command = 0;
+ if (at_sign && slash) {
+ if (*slash == 'w' || *slash == 'W') {
+ HTSprintf0(&command, "/w %s%c%c", username, CR, LF);
+ } else {
+ HTSprintf0(&command, "%s%c%c", username, CR, LF);
+ }
+ } else if (at_sign) {
+ HTSprintf0(&command, "%s%c%c", username, CR, LF);
+ } else if (*username == '/') {
+ if ((slash = StrChr((username + 1), '/')) != NULL) {
+ *slash = ' ';
+ }
+ HTSprintf0(&command, "%s%c%c", username, CR, LF);
+ } else if ((*username == 'w' || *username == 'W') &&
+ *(username + 1) == '/') {
+ if (*username + 2 != '\0') {
+ *(username + 1) = ' ';
+ } else {
+ *(username + 1) = '\0';
+ }
+ HTSprintf0(&command, "/%s%c%c", username, CR, LF);
+ } else if ((*username == 'w' || *username == 'W') &&
+ *(username + 1) == '\0') {
+ HTSprintf0(&command, "/%s%c%c", username, CR, LF);
+ } else if ((slash = StrChr(username, '/')) != NULL) {
+ *slash++ = '\0';
+ if (*slash == 'w' || *slash == 'W') {
+ HTSprintf0(&command, "/w %s%c%c", username, CR, LF);
+ } else {
+ HTSprintf0(&command, "%s%c%c", username, CR, LF);
+ }
+ } else {
+ HTSprintf0(&command, "%s%c%c", username, CR, LF);
+ }
+
+ /* Now, let's get a stream setup up from the FingerHost:
+ * CONNECTING to finger host
+ */
+ CTRACE((tfp, "HTFinger: doing HTDoConnect on '%s'\n", str));
+ status = HTDoConnect(str, "finger", FINGER_PORT, &finger_fd);
+ CTRACE((tfp, "HTFinger: Done DoConnect; status %d\n", status));
+
+ if (status == HT_INTERRUPTED) {
+ /* Interrupt cleanly */
+ CTRACE((tfp,
+ "HTFinger: Interrupted on connect; recovering cleanly.\n"));
+ HTProgress(CONNECTION_INTERRUPTED);
+ result = HT_NOT_LOADED;
+ } else if (status < 0) {
+ NETCLOSE(finger_fd);
+ finger_fd = -1;
+ CTRACE((tfp, "HTFinger: Unable to connect to finger host.\n"));
+ HTAlert(gettext("Could not access finger host."));
+ result = HT_NOT_LOADED; /* FAIL */
+ } else {
+ CTRACE((tfp, "HTFinger: Connected to finger host '%s'.\n", str));
+
+ /* Send the command, and process response if successful.
+ */
+ if (response(command, sitename, anAnchor, format_out, stream) != 0) {
+ HTAlert(gettext("No response from finger server."));
+ result = HT_NOT_LOADED;
+ }
+ }
+ FREE(str);
+ FREE(command);
+ }
+ FREE(param);
+ return result;
+}
+
+#ifdef GLOBALDEF_IS_MACRO
+#define _HTFINGER_C_1_INIT { "finger", HTLoadFinger, NULL }
+GLOBALDEF(HTProtocol, HTFinger, _HTFINGER_C_1_INIT);
+#else
+GLOBALDEF HTProtocol HTFinger =
+{"finger", HTLoadFinger, NULL};
+#endif /* GLOBALDEF_IS_MACRO */
+
+#endif /* not DISABLE_FINGER */
diff --git a/WWW/Library/Implementation/HTFinger.h b/WWW/Library/Implementation/HTFinger.h
new file mode 100644
index 0000000..071d43b
--- /dev/null
+++ b/WWW/Library/Implementation/HTFinger.h
@@ -0,0 +1,30 @@
+/* Finger protocol module for the WWW library */
+/* History:
+ * 21 Apr 94 Andrew Brooks
+ */
+
+#ifndef HTFINGER_H
+#define HTFINGER_H
+
+#include <HTAccess.h>
+#include <HTAnchor.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+#ifdef GLOBALREF_IS_MACRO
+ extern GLOBALREF (HTProtocol, HTFinger);
+
+#else
+ GLOBALREF HTProtocol HTFinger;
+#endif /* GLOBALREF_IS_MACRO */
+
+ extern int HTLoadFinger(const char *arg,
+ HTParentAnchor *anAnchor,
+ HTFormat format_out,
+ HTStream *stream);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* HTFINGER_H */
diff --git a/WWW/Library/Implementation/HTFormat.c b/WWW/Library/Implementation/HTFormat.c
new file mode 100644
index 0000000..a830387
--- /dev/null
+++ b/WWW/Library/Implementation/HTFormat.c
@@ -0,0 +1,2181 @@
+/*
+ * $LynxId: HTFormat.c,v 1.96 2022/03/31 23:39:38 tom Exp $
+ *
+ * Manage different file formats HTFormat.c
+ * =============================
+ *
+ * Bugs:
+ * Not reentrant.
+ *
+ * Assumes the incoming stream is ASCII, rather than a local file
+ * format, and so ALWAYS converts from ASCII on non-ASCII machines.
+ * Therefore, non-ASCII machines can't read local files.
+ *
+ */
+
+#define HTSTREAM_INTERNAL 1
+
+#include <HTUtils.h>
+
+/* Implements:
+*/
+#include <HTFormat.h>
+
+static float HTMaxSecs = 1e10; /* No effective limit */
+
+#ifdef UNIX
+#ifdef NeXT
+#define PRESENT_POSTSCRIPT "open %s; /bin/rm -f %s\n"
+#else
+#define PRESENT_POSTSCRIPT "(ghostview %s ; /bin/rm -f %s)&\n"
+ /* Full pathname would be better! */
+#endif /* NeXT */
+#endif /* UNIX */
+
+#include <HTML.h>
+#include <HTMLDTD.h>
+#include <HText.h>
+#include <HTAlert.h>
+#include <HTList.h>
+#include <HTInit.h>
+#include <HTTCP.h>
+#include <HTTP.h>
+/* Streams and structured streams which we use:
+*/
+#include <HTFWriter.h>
+#include <HTPlain.h>
+#include <SGML.h>
+#include <HTMLGen.h>
+
+#include <LYexit.h>
+#include <LYUtils.h>
+#include <GridText.h>
+#include <LYGlobalDefs.h>
+#include <LYLeaks.h>
+
+#ifdef DISP_PARTIAL
+#include <LYMainLoop.h>
+#endif
+
+#ifdef USE_BROTLI
+#include <brotli/decode.h>
+#endif
+
+BOOL HTOutputSource = NO; /* Flag: shortcut parser to stdout */
+
+/* this version used by the NetToText stream */
+struct _HTStream {
+ const HTStreamClass *isa;
+ BOOL had_cr;
+ HTStream *sink;
+};
+
+/* Presentation methods
+ * --------------------
+ */
+HTList *HTPresentations = NULL;
+HTPresentation *default_presentation = NULL;
+
+/*
+ * To free off the presentation list.
+ */
+#ifdef LY_FIND_LEAKS
+static void HTFreePresentations(void);
+#endif
+
+/* Define a presentation system command for a content-type
+ * -------------------------------------------------------
+ */
+void HTSetPresentation(const char *representation,
+ const char *command,
+ const char *testcommand,
+ double quality,
+ double secs,
+ double secs_per_byte,
+ long int maxbytes,
+ AcceptMedia media)
+{
+ HTPresentation *pres = typecalloc(HTPresentation);
+
+ if (pres == NULL)
+ outofmem(__FILE__, "HTSetPresentation");
+
+ assert(representation != NULL);
+
+ CTRACE2(TRACE_CFG,
+ (tfp,
+ "HTSetPresentation rep=%s, command=%s, test=%s, qual=%f\n",
+ NonNull(representation),
+ NonNull(command),
+ NonNull(testcommand),
+ quality));
+
+ pres->rep = HTAtom_for(representation);
+ pres->rep_out = WWW_PRESENT; /* Fixed for now ... :-) */
+ pres->converter = HTSaveAndExecute; /* Fixed for now ... */
+ pres->quality = (float) quality;
+ pres->secs = (float) secs;
+ pres->secs_per_byte = (float) secs_per_byte;
+ pres->maxbytes = maxbytes;
+ pres->get_accept = 0;
+ pres->accept_opt = media;
+
+ pres->command = NULL;
+ StrAllocCopy(pres->command, command);
+
+ pres->testcommand = NULL;
+ StrAllocCopy(pres->testcommand, testcommand);
+
+ /*
+ * Memory leak fixed.
+ * 05-28-94 Lynx 2-3-1 Garrett Arch Blythe
+ */
+ if (!HTPresentations) {
+ HTPresentations = HTList_new();
+#ifdef LY_FIND_LEAKS
+ atexit(HTFreePresentations);
+#endif
+ }
+
+ if (strcmp(representation, "*") == 0) {
+ FREE(default_presentation);
+ default_presentation = pres;
+ } else {
+ HTList_addObject(HTPresentations, pres);
+ }
+}
+
+/* Define a built-in function for a content-type
+ * ---------------------------------------------
+ */
+void HTSetConversion(const char *representation_in,
+ const char *representation_out,
+ HTConverter *converter,
+ double quality,
+ double secs,
+ double secs_per_byte,
+ long int maxbytes,
+ AcceptMedia media)
+{
+ HTPresentation *pres = typecalloc(HTPresentation);
+
+ if (pres == NULL)
+ outofmem(__FILE__, "HTSetConversion");
+
+ CTRACE2(TRACE_CFG,
+ (tfp,
+ "HTSetConversion rep_in=%s, rep_out=%s, qual=%f\n",
+ NonNull(representation_in),
+ NonNull(representation_out),
+ quality));
+
+ pres->rep = HTAtom_for(representation_in);
+ pres->rep_out = HTAtom_for(representation_out);
+ pres->converter = converter;
+ pres->command = NULL;
+ pres->testcommand = NULL;
+ pres->quality = (float) quality;
+ pres->secs = (float) secs;
+ pres->secs_per_byte = (float) secs_per_byte;
+ pres->maxbytes = maxbytes;
+ pres->get_accept = TRUE;
+ pres->accept_opt = media;
+
+ /*
+ * Memory Leak fixed.
+ * 05-28-94 Lynx 2-3-1 Garrett Arch Blythe
+ */
+ if (!HTPresentations) {
+ HTPresentations = HTList_new();
+#ifdef LY_FIND_LEAKS
+ atexit(HTFreePresentations);
+#endif
+ }
+
+ HTList_addObject(HTPresentations, pres);
+}
+
+#ifdef LY_FIND_LEAKS
+/*
+ * Purpose: Free the presentation list.
+ * Arguments: void
+ * Return Value: void
+ * Remarks/Portability/Dependencies/Restrictions:
+ * Made to clean up Lynx's bad leakage.
+ * Revision History:
+ * 05-28-94 created Lynx 2-3-1 Garrett Arch Blythe
+ */
+static void HTFreePresentations(void)
+{
+ HTPresentation *pres = NULL;
+
+ /*
+ * Loop through the list.
+ */
+ while (!HTList_isEmpty(HTPresentations)) {
+ /*
+ * Free off each item. May also need to free off it's items, but not
+ * sure as of yet.
+ */
+ pres = (HTPresentation *) HTList_removeLastObject(HTPresentations);
+ FREE(pres->command);
+ FREE(pres->testcommand);
+ FREE(pres);
+ }
+ /*
+ * Free the list itself.
+ */
+ HTList_delete(HTPresentations);
+ HTPresentations = NULL;
+}
+#endif /* LY_FIND_LEAKS */
+
+/* File buffering
+ * --------------
+ *
+ * The input file is read using the macro which can read from
+ * a socket or a file.
+ * The input buffer size, if large will give greater efficiency and
+ * release the server faster, and if small will save space on PCs etc.
+ */
+#define INPUT_BUFFER_SIZE 4096 /* Tradeoff */
+static char input_buffer[INPUT_BUFFER_SIZE];
+static char *input_pointer;
+static char *input_limit;
+static int input_file_number;
+
+/* Set up the buffering
+ *
+ * These routines are public because they are in fact needed by
+ * many parsers, and on PCs and Macs we should not duplicate
+ * the static buffer area.
+ */
+void HTInitInput(int file_number)
+{
+ input_file_number = file_number;
+ input_pointer = input_limit = input_buffer;
+}
+
+int interrupted_in_htgetcharacter = 0;
+int HTGetCharacter(void)
+{
+ char ch;
+
+ interrupted_in_htgetcharacter = 0;
+ do {
+ if (input_pointer >= input_limit) {
+ int status = NETREAD(input_file_number,
+ input_buffer, INPUT_BUFFER_SIZE);
+
+ if (status <= 0) {
+ if (status == 0)
+ return EOF;
+ if (status == HT_INTERRUPTED) {
+ CTRACE((tfp, "HTFormat: Interrupted in HTGetCharacter\n"));
+ interrupted_in_htgetcharacter = 1;
+ return EOF;
+ }
+ CTRACE((tfp, "HTFormat: File read error %d\n", status));
+ return EOF; /* -1 is returned by UCX
+ at end of HTTP link */
+ }
+ input_pointer = input_buffer;
+ input_limit = input_buffer + status;
+ }
+ ch = *input_pointer++;
+ } while (ch == (char) 13); /* Ignore ASCII carriage return */
+
+ return FROMASCII(UCH(ch));
+}
+
+#ifdef USE_SSL
+int HTGetSSLCharacter(void *handle)
+{
+ char ch;
+
+ interrupted_in_htgetcharacter = 0;
+ if (!handle)
+ return (char) EOF;
+ do {
+ if (input_pointer >= input_limit) {
+ int status = SSL_read((SSL *) handle,
+ input_buffer, INPUT_BUFFER_SIZE);
+
+ if (status <= 0) {
+ if (status == 0)
+ return (char) EOF;
+ if (status == HT_INTERRUPTED) {
+ CTRACE((tfp,
+ "HTFormat: Interrupted in HTGetSSLCharacter\n"));
+ interrupted_in_htgetcharacter = 1;
+ return (char) EOF;
+ }
+ CTRACE((tfp, "HTFormat: SSL_read error %d\n", status));
+ return (char) EOF; /* -1 is returned by UCX
+ at end of HTTP link */
+ }
+ input_pointer = input_buffer;
+ input_limit = input_buffer + status;
+ }
+ ch = *input_pointer++;
+ } while (ch == (char) 13); /* Ignore ASCII carriage return */
+
+ return FROMASCII(ch);
+}
+#endif /* USE_SSL */
+
+/* Match maintype to any MIME type starting with maintype, for example:
+ * image/gif should match image
+ */
+static int half_match(char *trial_type, char *target)
+{
+ char *cp = StrChr(trial_type, '/');
+
+ /* if no '/' or no '*' */
+ if (!cp || *(cp + 1) != '*')
+ return 0;
+
+ CTRACE((tfp, "HTFormat: comparing %s and %s for half match\n",
+ trial_type, target));
+
+ /* main type matches */
+ if (!StrNCmp(trial_type, target, ((cp - trial_type) - 1)))
+ return 1;
+
+ return 0;
+}
+
+/*
+ * Evaluate a deferred mailcap test command, i.e.,. one that substitutes the
+ * document's charset or other values in %{name} format.
+ */
+static BOOL failsMailcap(HTPresentation *pres, HTParentAnchor *anchor)
+{
+ if (pres->testcommand != NULL &&
+ anchor != NULL &&
+ anchor->content_type_params != NULL) {
+ if (LYTestMailcapCommand(pres->testcommand,
+ anchor->content_type_params) != 0)
+ return TRUE;
+ }
+ return FALSE;
+}
+
+#define WWW_WILDCARD_REP_OUT HTAtom_for("*")
+
+/* Look up a presentation
+ * ----------------------
+ *
+ * If fill_in is NULL, only look for an exact match.
+ * If a wildcard match is made, *fill_in is used to store
+ * a possibly modified presentation, and a pointer to it is
+ * returned. For an exact match, a pointer to the presentation
+ * in the HTPresentations list is returned. Returns NULL if
+ * nothing found. - kw
+ *
+ */
+static HTPresentation *HTFindPresentation(HTFormat rep_in,
+ HTFormat rep_out,
+ HTPresentation *fill_in,
+ HTParentAnchor *anchor)
+{
+#undef THIS_FUNC
+#define THIS_FUNC "HTFindPresentation"
+ HTAtom *wildcard = NULL; /* = HTAtom_for("*"); lookup when needed - kw */
+ int n;
+ int i;
+ HTPresentation *pres;
+ HTPresentation *match;
+ HTPresentation *strong_wildcard_match = 0;
+ HTPresentation *weak_wildcard_match = 0;
+ HTPresentation *last_default_match = 0;
+ HTPresentation *strong_subtype_wildcard_match = 0;
+
+ CTRACE((tfp, THIS_FUNC ": Looking up presentation for %s to %s\n",
+ HTAtom_name(rep_in), HTAtom_name(rep_out)));
+
+ n = HTList_count(HTPresentations);
+ for (i = 0; i < n; i++) {
+ pres = (HTPresentation *) HTList_objectAt(HTPresentations, i);
+ if (pres->rep == rep_in) {
+ if (pres->rep_out == rep_out) {
+ if (failsMailcap(pres, anchor))
+ continue;
+ CTRACE((tfp, THIS_FUNC ": found exact match: %s -> %s\n",
+ HTAtom_name(pres->rep),
+ HTAtom_name(pres->rep_out)));
+ return pres;
+
+ } else if (!fill_in) {
+ continue;
+ } else {
+ if (!wildcard)
+ wildcard = WWW_WILDCARD_REP_OUT;
+ if (pres->rep_out == wildcard) {
+ if (failsMailcap(pres, anchor))
+ continue;
+ if (!strong_wildcard_match)
+ strong_wildcard_match = pres;
+ /* otherwise use the first one */
+ CTRACE((tfp, THIS_FUNC
+ ": found strong wildcard match: %s -> %s\n",
+ HTAtom_name(pres->rep),
+ HTAtom_name(pres->rep_out)));
+ }
+ }
+
+ } else if (!fill_in) {
+ continue;
+
+ } else if (half_match(HTAtom_name(pres->rep),
+ HTAtom_name(rep_in))) {
+ if (pres->rep_out == rep_out) {
+ if (failsMailcap(pres, anchor))
+ continue;
+ if (!strong_subtype_wildcard_match)
+ strong_subtype_wildcard_match = pres;
+ /* otherwise use the first one */
+ CTRACE((tfp, THIS_FUNC
+ ": found strong subtype wildcard match: %s -> %s\n",
+ HTAtom_name(pres->rep),
+ HTAtom_name(pres->rep_out)));
+ }
+ }
+
+ if (pres->rep == WWW_SOURCE) {
+ if (pres->rep_out == rep_out) {
+ if (failsMailcap(pres, anchor))
+ continue;
+ if (!weak_wildcard_match)
+ weak_wildcard_match = pres;
+ /* otherwise use the first one */
+ CTRACE((tfp,
+ THIS_FUNC ": found weak wildcard match: %s\n",
+ HTAtom_name(pres->rep_out)));
+
+ } else if (!last_default_match) {
+ if (!wildcard)
+ wildcard = WWW_WILDCARD_REP_OUT;
+ if (pres->rep_out == wildcard) {
+ if (failsMailcap(pres, anchor))
+ continue;
+ last_default_match = pres;
+ /* otherwise use the first one */
+ }
+ }
+ }
+ }
+
+ match = (strong_subtype_wildcard_match
+ ? strong_subtype_wildcard_match
+ : (strong_wildcard_match
+ ? strong_wildcard_match
+ : (weak_wildcard_match
+ ? weak_wildcard_match
+ : last_default_match)));
+
+ if (match) {
+ *fill_in = *match; /* Specific instance */
+ fill_in->rep = rep_in; /* yuk */
+ fill_in->rep_out = rep_out; /* yuk */
+ return fill_in;
+ }
+
+ return NULL;
+#undef THIS_FUNC
+}
+
+/* Create a filter stack
+ * ---------------------
+ *
+ * If a wildcard match is made, a temporary HTPresentation
+ * structure is made to hold the destination format while the
+ * new stack is generated. This is just to pass the out format to
+ * MIME so far. Storing the format of a stream in the stream might
+ * be a lot neater.
+ *
+ */
+HTStream *HTStreamStack(HTFormat rep_in,
+ HTFormat rep_out,
+ HTStream *sink,
+ HTParentAnchor *anchor)
+{
+#undef THIS_FUNC
+#define THIS_FUNC "HTStreamStack"
+ HTPresentation temp;
+ HTPresentation *match;
+ HTStream *result;
+
+ CTRACE((tfp, THIS_FUNC ": Constructing stream stack for %s to %s (%s)\n",
+ HTAtom_name(rep_in),
+ HTAtom_name(rep_out),
+ NONNULL(anchor->content_type_params)));
+
+ if (rep_out == rep_in) {
+ result = sink;
+
+ } else if ((match = HTFindPresentation(rep_in, rep_out, &temp, anchor))) {
+ if (match == &temp) {
+ CTRACE((tfp, THIS_FUNC ": Using %s\n", HTAtom_name(temp.rep_out)));
+ } else {
+ CTRACE((tfp, THIS_FUNC ": found exact match: %s -> %s\n",
+ HTAtom_name(match->rep),
+ HTAtom_name(match->rep_out)));
+ }
+ result = (*match->converter) (match, anchor, sink);
+ } else {
+ result = NULL;
+ }
+ if (TRACE) {
+ if (result && result->isa && result->isa->name) {
+ CTRACE((tfp, THIS_FUNC ": Returning \"%s\"\n", result->isa->name));
+ } else if (result) {
+ CTRACE((tfp, THIS_FUNC ": Returning *unknown* stream!\n"));
+ } else {
+ CTRACE((tfp, THIS_FUNC ": Returning NULL!\n"));
+ CTRACE_FLUSH(tfp); /* a crash may be imminent... - kw */
+ }
+ }
+ return result;
+#undef THIS_FUNC
+}
+
+/* Put a presentation near start of list
+ * -------------------------------------
+ *
+ * Look up a presentation (exact match only) and, if found, reorder
+ * it to the start of the HTPresentations list. - kw
+ */
+void HTReorderPresentation(HTFormat rep_in,
+ HTFormat rep_out)
+{
+ HTPresentation *match;
+
+ if ((match = HTFindPresentation(rep_in, rep_out, NULL, NULL))) {
+ HTList_removeObject(HTPresentations, match);
+ HTList_addObject(HTPresentations, match);
+ }
+}
+
+/*
+ * Setup 'get_accept' flag to denote presentations that are not redundant,
+ * and will be listed in "Accept:" header.
+ */
+void HTFilterPresentations(void)
+{
+ int i, j;
+ int n = HTList_count(HTPresentations);
+ HTPresentation *p, *q;
+ BOOL matched;
+ char *s, *t;
+
+ CTRACE((tfp, "HTFilterPresentations (AcceptMedia %#x)\n", LYAcceptMedia));
+ for (i = 0; i < n; i++) {
+ p = (HTPresentation *) HTList_objectAt(HTPresentations, i);
+ s = HTAtom_name(p->rep);
+
+ p->get_accept = FALSE;
+ if ((LYAcceptMedia & p->accept_opt) != 0
+ && p->rep_out == WWW_PRESENT
+ && p->rep != WWW_SOURCE
+ && strcasecomp(s, "www/mime")
+ && strcasecomp(s, "www/compressed")
+ && p->quality <= 1.0 && p->quality >= 0.0) {
+ matched = TRUE;
+ for (j = 0; j < i; j++) {
+ q = (HTPresentation *) HTList_objectAt(HTPresentations, j);
+ t = HTAtom_name(q->rep);
+
+ if (!strcasecomp(s, t)) {
+ matched = FALSE;
+ CTRACE((tfp, " match %s %s\n", s, t));
+ break;
+ }
+ }
+ p->get_accept = matched;
+ }
+ }
+}
+
+/* Find the cost of a filter stack
+ * -------------------------------
+ *
+ * Must return the cost of the same stack which HTStreamStack would set up.
+ *
+ * On entry,
+ * length The size of the data to be converted
+ */
+float HTStackValue(HTFormat rep_in,
+ HTFormat rep_out,
+ double initial_value,
+ long int length)
+{
+ HTAtom *wildcard = WWW_WILDCARD_REP_OUT;
+
+ CTRACE((tfp, "HTFormat: Evaluating stream stack for %s worth %.3f to %s\n",
+ HTAtom_name(rep_in), initial_value, HTAtom_name(rep_out)));
+
+ if (rep_out == WWW_SOURCE || rep_out == rep_in)
+ return 0.0;
+
+ {
+ int n = HTList_count(HTPresentations);
+ int i;
+ HTPresentation *pres;
+
+ for (i = 0; i < n; i++) {
+ pres = (HTPresentation *) HTList_objectAt(HTPresentations, i);
+ if (pres->rep == rep_in &&
+ (pres->rep_out == rep_out || pres->rep_out == wildcard)) {
+ float value = (float) (initial_value * pres->quality);
+
+ if (HTMaxSecs > 0.0)
+ value = (value
+ - ((float) length * pres->secs_per_byte
+ + pres->secs)
+ / HTMaxSecs);
+ return value;
+ }
+ }
+ }
+
+ return (float) -1e30; /* Really bad */
+
+}
+
+/* Display the page while transfer in progress
+ * -------------------------------------------
+ *
+ * Repaint the page only when necessary.
+ * This is a traverse call for HText_pageDisplay() - it works!.
+ *
+ */
+void HTDisplayPartial(void)
+{
+#ifdef DISP_PARTIAL
+ if (display_partial) {
+ /*
+ * HText_getNumOfLines() = "current" number of complete lines received
+ * NumOfLines_partial = number of lines at the moment of last repaint.
+ * (we update NumOfLines_partial only when we repaint the display.)
+ *
+ * display_partial could only be enabled in HText_new() so a new
+ * HTMainText object available - all HText_ functions use it, lines
+ * counter HText_getNumOfLines() in particular.
+ *
+ * Otherwise HTMainText holds info from the previous document and we
+ * may repaint it instead of the new one: prev doc scrolled to the
+ * first line (=Newline_partial) is not good looking :-) 23 Aug 1998
+ * Leonid Pauzner
+ *
+ * So repaint the page only when necessary:
+ */
+ int Newline_partial = LYGetNewline();
+
+ if (((Newline_partial + display_lines) - 1 > NumOfLines_partial)
+ /* current page not complete... */
+ && (partial_threshold > 0 ?
+ ((Newline_partial + partial_threshold) - 1 <=
+ HText_getNumOfLines()) :
+ ((Newline_partial + display_lines) - 1 <= HText_getNumOfLines()))
+ /*
+ * Originally we rendered by increments of 2 lines,
+ * but that got annoying on slow network connections.
+ * Then we switched to full-pages. Now it's configurable.
+ * If partial_threshold <= 0, then it's a full page
+ */
+ ) {
+ if (LYMainLoop_pageDisplay(Newline_partial))
+ NumOfLines_partial = HText_getNumOfLines();
+ }
+ }
+#else /* nothing */
+#endif /* DISP_PARTIAL */
+}
+
+/* Put this as early as possible, OK just after HTDisplayPartial() */
+void HTFinishDisplayPartial(void)
+{
+#ifdef DISP_PARTIAL
+ /*
+ * End of incremental rendering stage here.
+ */
+ display_partial = FALSE;
+#endif /* DISP_PARTIAL */
+}
+
+/* Push data from a socket down a stream
+ * -------------------------------------
+ *
+ * This routine is responsible for creating and PRESENTING any
+ * graphic (or other) objects described by the file.
+ *
+ * The file number given is assumed to be a TELNET stream, i.e., containing
+ * CRLF at the end of lines which need to be stripped to LF for unix
+ * when the format is textual.
+ *
+ * State of socket and target stream on entry:
+ * socket (file_number) assumed open,
+ * target (sink) assumed valid.
+ *
+ * Return values:
+ * HT_INTERRUPTED Interruption or error after some data received.
+ * -2 Unexpected disconnect before any data received.
+ * -1 Interruption or error before any data received, or
+ * (UNIX) other read error before any data received, or
+ * download cancelled.
+ * HT_LOADED Normal close of socket (end of file indication
+ * received), or
+ * unexpected disconnect after some data received, or
+ * other read error after some data received, or
+ * (not UNIX) other read error before any data received.
+ *
+ * State of socket and target stream on return depends on return value:
+ * HT_INTERRUPTED socket still open, target aborted.
+ * -2 socket still open, target stream still valid.
+ * -1 socket still open, target aborted.
+ * otherwise socket closed, target stream still valid.
+ */
+int HTCopy(HTParentAnchor *anchor,
+ int file_number,
+ void *handle GCC_UNUSED,
+ HTStream *sink)
+{
+ HTStreamClass targetClass;
+ BOOL suppress_readprogress = NO;
+ off_t limit = anchor ? anchor->content_length : 0;
+ off_t bytes = 0;
+ off_t header_length = 0;
+ int rv = 0;
+
+ /* Push the data down the stream
+ */
+ targetClass = *(sink->isa); /* Copy pointers to procedures */
+
+ /*
+ * Push binary from socket down sink
+ *
+ * This operation could be put into a main event loop
+ */
+ HTReadProgress(bytes, (off_t) 0);
+ for (;;) {
+ int status;
+
+ if (LYCancelDownload) {
+ LYCancelDownload = FALSE;
+ (*targetClass._abort) (sink, NULL);
+ rv = -1;
+ goto finished;
+ }
+
+ if (HTCheckForInterrupt()) {
+ _HTProgress(TRANSFER_INTERRUPTED);
+ (*targetClass._abort) (sink, NULL);
+ if (bytes)
+ rv = HT_INTERRUPTED;
+ else
+ rv = -1;
+ goto finished;
+ }
+#ifdef USE_SSL
+ if (handle)
+ status = SSL_read((SSL *) handle, input_buffer, INPUT_BUFFER_SIZE);
+ else
+ status = NETREAD(file_number, input_buffer, INPUT_BUFFER_SIZE);
+#else
+ status = NETREAD(file_number, input_buffer, INPUT_BUFFER_SIZE);
+#endif /* USE_SSL */
+ if (status <= 0) {
+ if (status == 0) {
+ break;
+ } else if (status == HT_INTERRUPTED) {
+ _HTProgress(TRANSFER_INTERRUPTED);
+ (*targetClass._abort) (sink, NULL);
+ if (bytes)
+ rv = HT_INTERRUPTED;
+ else
+ rv = -1;
+ goto finished;
+ } else if (SOCKET_ERRNO == ENOTCONN ||
+#ifdef _WINDOWS /* 1997/11/10 (Mon) 16:57:18 */
+ SOCKET_ERRNO == ETIMEDOUT ||
+#endif
+ SOCKET_ERRNO == ECONNRESET ||
+ SOCKET_ERRNO == EPIPE) {
+ /*
+ * Arrrrgh, HTTP 0/1 compatibility problem, maybe.
+ */
+ if (bytes <= 0) {
+ /*
+ * Don't have any data, so let the calling function decide
+ * what to do about it. - FM
+ */
+ rv = -2;
+ goto finished;
+ } else {
+#ifdef UNIX
+ /*
+ * Treat what we've received already as the complete
+ * transmission, but not without giving the user an alert.
+ * I don't know about all the different TCP stacks for VMS
+ * etc., so this is currently only for UNIX. - kw
+ */
+ HTInetStatus("NETREAD");
+ HTAlert("Unexpected server disconnect.");
+ CTRACE((tfp,
+ "HTCopy: Unexpected server disconnect. Treating as completed.\n"));
+#else /* !UNIX */
+ /*
+ * Treat what we've gotten already as the complete
+ * transmission. - FM
+ */
+ CTRACE((tfp,
+ "HTCopy: Unexpected server disconnect. Treating as completed.\n"));
+ status = 0;
+#endif /* UNIX */
+ }
+#ifdef UNIX
+ } else { /* status < 0 and other errno */
+ /*
+ * Treat what we've received already as the complete
+ * transmission, but not without giving the user an alert. I
+ * don't know about all the different TCP stacks for VMS etc.,
+ * so this is currently only for UNIX. - kw
+ */
+ HTInetStatus("NETREAD");
+ HTAlert("Unexpected read error.");
+ if (bytes) {
+ (void) NETCLOSE(file_number);
+ rv = HT_LOADED;
+ } else {
+ (*targetClass._abort) (sink, NULL);
+ rv = -1;
+ }
+ goto finished;
+#endif
+ }
+ break;
+ }
+
+ /*
+ * Suppress ReadProgress messages when collecting a redirection
+ * message, at least initially (unless/until anchor->content_type gets
+ * changed, probably by the MIME message parser). That way messages
+ * put up by the HTTP module or elsewhere can linger in the statusline
+ * for a while. - kw
+ */
+ suppress_readprogress = (BOOL) (anchor && anchor->content_type &&
+ !strcmp(anchor->content_type,
+ "message/x-http-redirection"));
+#ifdef NOT_ASCII
+ {
+ char *p;
+
+ for (p = input_buffer; p < input_buffer + status; p++) {
+ *p = FROMASCII(*p);
+ }
+ }
+#endif /* NOT_ASCII */
+
+ header_length = anchor != 0 ? anchor->header_length : 0;
+
+ (*targetClass.put_block) (sink, input_buffer, status);
+ if (anchor != 0 && anchor->inHEAD) {
+ if (!suppress_readprogress) {
+ statusline(gettext("Reading headers..."));
+ }
+ CTRACE((tfp, "HTCopy read %" PRI_off_t " header bytes\n",
+ CAST_off_t (anchor->header_length)));
+ } else {
+ /*
+ * If header-length is increased at this point, that is due to
+ * HTMIME, which detects the end of the server headers. There
+ * may be additional (non-header) data in that block.
+ */
+ if (anchor != 0 && (anchor->header_length > header_length)) {
+ int header = (int) (anchor->header_length - header_length);
+
+ CTRACE((tfp, "HTCopy read %" PRI_off_t " header bytes "
+ "(%d extra vs %d total)\n",
+ CAST_off_t (anchor->header_length),
+ header, status));
+ if (status > header) {
+ bytes += (status - header);
+ }
+ } else {
+ bytes += status;
+ }
+ if (!suppress_readprogress) {
+ HTReadProgress(bytes, limit);
+ }
+ HTDisplayPartial();
+ }
+
+ /* a few buggy implementations do not close the connection properly
+ * and will hang if we try to read past the declared content-length.
+ */
+ if (limit > 0 && bytes >= limit)
+ break;
+ } /* next bufferload */
+ if (anchor != 0) {
+ CTRACE((tfp, "HTCopy copied %"
+ PRI_off_t " actual, %"
+ PRI_off_t " limit\n", CAST_off_t (bytes), CAST_off_t (limit)));
+ anchor->actual_length = bytes;
+ }
+
+ _HTProgress(TRANSFER_COMPLETE);
+ (void) NETCLOSE(file_number);
+ rv = HT_LOADED;
+
+ finished:
+ HTFinishDisplayPartial();
+ return (rv);
+}
+
+/* Push data from a file pointer down a stream
+ * -------------------------------------
+ *
+ * This routine is responsible for creating and PRESENTING any
+ * graphic (or other) objects described by the file.
+ *
+ *
+ * State of file and target stream on entry:
+ * FILE* (fp) assumed open,
+ * target (sink) assumed valid.
+ *
+ * Return values:
+ * HT_INTERRUPTED Interruption after some data read.
+ * HT_PARTIAL_CONTENT Error after some data read.
+ * -1 Error before any data read.
+ * HT_LOADED Normal end of file indication on reading.
+ *
+ * State of file and target stream on return:
+ * always fp still open, target stream still valid.
+ */
+int HTFileCopy(FILE *fp, HTStream *sink)
+{
+ HTStreamClass targetClass;
+ int status;
+ off_t bytes;
+ int rv = HT_OK;
+
+ /* Push the data down the stream
+ */
+ targetClass = *(sink->isa); /* Copy pointers to procedures */
+
+ /* Push binary from socket down sink
+ */
+ HTReadProgress(bytes = 0, (off_t) 0);
+ for (;;) {
+ status = (int) fread(input_buffer,
+ (size_t) 1,
+ (size_t) INPUT_BUFFER_SIZE, fp);
+ if (status == 0) { /* EOF or error */
+ if (ferror(fp) == 0) {
+ rv = HT_LOADED;
+ break;
+ }
+ CTRACE((tfp, "HTFormat: Read error, read returns %d\n",
+ ferror(fp)));
+ if (bytes) {
+ rv = HT_PARTIAL_CONTENT;
+ } else {
+ rv = -1;
+ }
+ break;
+ }
+
+ (*targetClass.put_block) (sink, input_buffer, status);
+ bytes += status;
+ HTReadProgress(bytes, (off_t) 0);
+ /* Suppress last screen update in partial mode - a regular update under
+ * control of mainloop() should follow anyway. - kw
+ */
+#ifdef DISP_PARTIAL
+ if (display_partial && bytes != HTMainAnchor->content_length)
+ HTDisplayPartial();
+#endif
+
+ if (HTCheckForInterrupt()) {
+ _HTProgress(TRANSFER_INTERRUPTED);
+ if (bytes) {
+ rv = HT_INTERRUPTED;
+ } else {
+ rv = -1;
+ }
+ break;
+ }
+ } /* next bufferload */
+
+ HTFinishDisplayPartial();
+ return rv;
+}
+
+#ifdef USE_SOURCE_CACHE
+/* Push data from an HTChunk down a stream
+ * ---------------------------------------
+ *
+ * This routine is responsible for creating and PRESENTING any
+ * graphic (or other) objects described by the file.
+ *
+ * State of memory and target stream on entry:
+ * HTChunk* (chunk) and target (sink) assumed valid.
+ *
+ * Return values:
+ * HT_LOADED All data sent.
+ * HT_INTERRUPTED Interruption after some data read.
+ *
+ * State of memory and target stream on return:
+ * always chunk unchanged, target stream still valid.
+ */
+int HTMemCopy(HTChunk *chunk, HTStream *sink)
+{
+ HTStreamClass targetClass;
+ off_t bytes;
+ int rv = HT_OK;
+
+ targetClass = *(sink->isa);
+ HTReadProgress(bytes = 0, (off_t) 0);
+ for (; chunk != NULL; chunk = chunk->next) {
+
+ /* Push the data down the stream a piece at a time, in case we're
+ * running a large document on a slow machine.
+ */
+ (*targetClass.put_block) (sink, chunk->data, chunk->size);
+ bytes += chunk->size;
+
+ HTReadProgress(bytes, (off_t) 0);
+ HTDisplayPartial();
+
+ if (HTCheckForInterrupt()) {
+ _HTProgress(TRANSFER_INTERRUPTED);
+ if (bytes) {
+ rv = HT_INTERRUPTED;
+ } else {
+ rv = -1;
+ }
+ break;
+ }
+ }
+
+ HTFinishDisplayPartial();
+ return rv;
+}
+#endif
+
+#ifdef USE_ZLIB
+/* Push data from a gzip file pointer down a stream
+ * -------------------------------------
+ *
+ * This routine is responsible for creating and PRESENTING any
+ * graphic (or other) objects described by the file.
+ *
+ *
+ * State of file and target stream on entry:
+ * gzFile (gzfp) assumed open (should have gzipped content),
+ * target (sink) assumed valid.
+ *
+ * Return values:
+ * HT_INTERRUPTED Interruption after some data read.
+ * HT_PARTIAL_CONTENT Error after some data read.
+ * -1 Error before any data read.
+ * HT_LOADED Normal end of file indication on reading.
+ *
+ * State of file and target stream on return:
+ * always gzfp still open, target stream still valid.
+ */
+static int HTGzFileCopy(gzFile gzfp, HTStream *sink)
+{
+ HTStreamClass targetClass;
+ int status;
+ off_t bytes;
+ int gzerrnum;
+ int rv = HT_OK;
+
+ /* Push the data down the stream
+ */
+ targetClass = *(sink->isa); /* Copy pointers to procedures */
+
+ /* read and inflate gzip'd file, and push binary down sink
+ */
+ HTReadProgress(bytes = 0, (off_t) 0);
+ for (;;) {
+ status = gzread(gzfp, input_buffer, INPUT_BUFFER_SIZE);
+ if (status <= 0) { /* EOF or error */
+ if (status == 0) {
+ rv = HT_LOADED;
+ break;
+ }
+ CTRACE((tfp, "HTGzFileCopy: Read error, gzread returns %d\n",
+ status));
+ CTRACE((tfp, "gzerror : %s\n",
+ gzerror(gzfp, &gzerrnum)));
+ if (TRACE) {
+ if (gzerrnum == Z_ERRNO)
+ perror("gzerror ");
+ }
+ if (bytes) {
+ rv = HT_PARTIAL_CONTENT;
+ } else {
+ rv = -1;
+ }
+ break;
+ }
+
+ (*targetClass.put_block) (sink, input_buffer, status);
+ bytes += status;
+ HTReadProgress(bytes, (off_t) -1);
+ HTDisplayPartial();
+
+ if (HTCheckForInterrupt()) {
+ _HTProgress(TRANSFER_INTERRUPTED);
+ rv = HT_INTERRUPTED;
+ break;
+ }
+ } /* next bufferload */
+
+ HTFinishDisplayPartial();
+ return rv;
+}
+
+#ifndef HAVE_ZERROR
+#define zError(s) LynxZError(s)
+static const char *zError(int status)
+{
+ static char result[80];
+
+ sprintf(result, "zlib error %d", status);
+ return result;
+}
+#endif
+
+/* Push data from a deflate file pointer down a stream
+ * -------------------------------------
+ *
+ * This routine is responsible for creating and PRESENTING any
+ * graphic (or other) objects described by the file. The code is
+ * loosely based on the inflate.c file from w3m.
+ *
+ *
+ * State of file and target stream on entry:
+ * FILE (zzfp) assumed open (should have deflated content),
+ * target (sink) assumed valid.
+ *
+ * Return values:
+ * HT_INTERRUPTED Interruption after some data read.
+ * HT_PARTIAL_CONTENT Error after some data read.
+ * -1 Error before any data read.
+ * HT_LOADED Normal end of file indication on reading.
+ *
+ * State of file and target stream on return:
+ * always zzfp still open, target stream still valid.
+ */
+static int HTZzFileCopy(FILE *zzfp, HTStream *sink)
+{
+#undef THIS_FUNC
+#define THIS_FUNC "HTZzFileCopy"
+ static char dummy_head[1 + 1] =
+ {
+ 0x8 + 0x7 * 0x10,
+ (((0x8 + 0x7 * 0x10) * 0x100 + 30) / 31 * 31) & 0xFF,
+ };
+
+ z_stream s;
+ HTStreamClass targetClass;
+ off_t bytes;
+ int rv = HT_OK;
+ char output_buffer[INPUT_BUFFER_SIZE];
+ int status;
+ int flush;
+ int retry = 0;
+ int len = 0;
+
+ /* Push the data down the stream
+ */
+ targetClass = *(sink->isa); /* Copy pointers to procedures */
+
+ memset(&s, 0, sizeof(s));
+ status = inflateInit(&s);
+ if (status != Z_OK) {
+ CTRACE((tfp, THIS_FUNC " inflateInit() %s\n", zError(status)));
+ exit_immediately(EXIT_FAILURE);
+ }
+ s.avail_in = 0;
+ s.next_out = (Bytef *) output_buffer;
+ s.avail_out = sizeof(output_buffer);
+ flush = Z_NO_FLUSH;
+
+ /* read and inflate deflate'd file, and push binary down sink
+ */
+ HTReadProgress(bytes = 0, (off_t) 0);
+ for (;;) {
+ if (s.avail_in == 0) {
+ s.next_in = (Bytef *) input_buffer;
+ s.avail_in = (uInt) fread(input_buffer,
+ (size_t) 1,
+ (size_t) INPUT_BUFFER_SIZE, zzfp);
+ len = (int) s.avail_in;
+ }
+ status = inflate(&s, flush);
+ if (status == Z_STREAM_END || status == Z_BUF_ERROR) {
+ len = (int) sizeof(output_buffer) - (int) s.avail_out;
+ if (len > 0) {
+ (*targetClass.put_block) (sink, output_buffer, len);
+ bytes += len;
+ HTReadProgress(bytes, (off_t) -1);
+ HTDisplayPartial();
+ }
+ rv = HT_LOADED;
+ break;
+ } else if (status == Z_DATA_ERROR && !retry++) {
+ status = inflateReset(&s);
+ if (status != Z_OK) {
+ CTRACE((tfp, THIS_FUNC " inflateReset() %s\n", zError(status)));
+ rv = -1;
+ break;
+ }
+ s.next_in = (Bytef *) dummy_head;
+ s.avail_in = sizeof(dummy_head);
+ (void) inflate(&s, flush);
+ s.next_in = (Bytef *) input_buffer;
+ s.avail_in = (unsigned) len;
+ continue;
+ } else if (status != Z_OK) {
+ CTRACE((tfp, THIS_FUNC " inflate() %s\n", zError(status)));
+ rv = bytes ? HT_PARTIAL_CONTENT : -1;
+ break;
+ } else if (s.avail_out == 0) {
+ len = sizeof(output_buffer);
+ s.next_out = (Bytef *) output_buffer;
+ s.avail_out = sizeof(output_buffer);
+
+ (*targetClass.put_block) (sink, output_buffer, len);
+ bytes += len;
+ HTReadProgress(bytes, (off_t) -1);
+ HTDisplayPartial();
+
+ if (HTCheckForInterrupt()) {
+ _HTProgress(TRANSFER_INTERRUPTED);
+ rv = bytes ? HT_INTERRUPTED : -1;
+ break;
+ }
+ }
+ retry = 1;
+ } /* next bufferload */
+
+ inflateEnd(&s);
+ HTFinishDisplayPartial();
+ return rv;
+#undef THIS_FUNC
+}
+#endif /* USE_ZLIB */
+
+#ifdef USE_BZLIB
+/* Push data from a bzip file pointer down a stream
+ * -------------------------------------
+ *
+ * This routine is responsible for creating and PRESENTING any
+ * graphic (or other) objects described by the file.
+ *
+ *
+ * State of file and target stream on entry:
+ * BZFILE (bzfp) assumed open (should have bzipped content),
+ * target (sink) assumed valid.
+ *
+ * Return values:
+ * HT_INTERRUPTED Interruption after some data read.
+ * HT_PARTIAL_CONTENT Error after some data read.
+ * -1 Error before any data read.
+ * HT_LOADED Normal end of file indication on reading.
+ *
+ * State of file and target stream on return:
+ * always bzfp still open, target stream still valid.
+ */
+static int HTBzFileCopy(BZFILE * bzfp, HTStream *sink)
+{
+ HTStreamClass targetClass;
+ int status;
+ off_t bytes;
+ int bzerrnum;
+ int rv = HT_OK;
+
+ /* Push the data down the stream
+ */
+ targetClass = *(sink->isa); /* Copy pointers to procedures */
+
+ /* read and inflate bzip'd file, and push binary down sink
+ */
+ HTReadProgress(bytes = 0, (off_t) 0);
+ for (;;) {
+ status = BZ2_bzread(bzfp, input_buffer, INPUT_BUFFER_SIZE);
+ if (status <= 0) { /* EOF or error */
+ if (status == 0) {
+ rv = HT_LOADED;
+ break;
+ }
+ CTRACE((tfp, "HTBzFileCopy: Read error, bzread returns %d\n",
+ status));
+ CTRACE((tfp, "bzerror : %s\n",
+ BZ2_bzerror(bzfp, &bzerrnum)));
+ if (bytes) {
+ rv = HT_PARTIAL_CONTENT;
+ } else {
+ rv = -1;
+ }
+ break;
+ }
+
+ (*targetClass.put_block) (sink, input_buffer, status);
+ bytes += status;
+ HTReadProgress(bytes, (off_t) -1);
+ HTDisplayPartial();
+
+ if (HTCheckForInterrupt()) {
+ _HTProgress(TRANSFER_INTERRUPTED);
+ rv = HT_INTERRUPTED;
+ break;
+ }
+ } /* next bufferload */
+
+ HTFinishDisplayPartial();
+ return rv;
+}
+#endif /* USE_BZLIB */
+
+#ifdef USE_BROTLI
+/* Push data from a brotli file pointer down a stream
+ * -------------------------------------
+ *
+ * This routine is responsible for creating and PRESENTING any
+ * graphic (or other) objects described by the file.
+ *
+ *
+ * State of file and target stream on entry:
+ * BZFILE (bzfp) assumed open (should have bzipped content),
+ * target (sink) assumed valid.
+ *
+ * Return values:
+ * HT_INTERRUPTED Interruption after some data read.
+ * HT_PARTIAL_CONTENT Error after some data read.
+ * -1 Error before any data read.
+ * HT_LOADED Normal end of file indication on reading.
+ *
+ * State of file and target stream on return:
+ * always bzfp still open, target stream still valid.
+ */
+static int HTBrFileCopy(FILE *brfp, HTStream *sink)
+{
+#undef THIS_FUNC
+#define THIS_FUNC "HTBrFileCopy"
+ HTStreamClass targetClass;
+ int status;
+ off_t bytes;
+ int rv = HT_OK;
+ BrotliDecoderResult status2 = BROTLI_DECODER_RESULT_ERROR;
+
+ char *brotli_buffer = NULL;
+ char *normal_buffer = NULL;
+ size_t brotli_size;
+ size_t brotli_limit = 0;
+ size_t brotli_offset = brotli_limit;
+ size_t normal_size;
+ size_t normal_limit = 0;
+
+ /* Push the data down the stream
+ */
+ targetClass = *(sink->isa); /* Copy pointers to procedures */
+
+ /* read and inflate brotli'd file, and push binary down sink
+ */
+ HTReadProgress(bytes = 0, (off_t) 0);
+ /*
+ * first, read all of the brotli'd file into memory, to work with the
+ * library's limitations.
+ */
+ for (;;) {
+ size_t input_chunk = INPUT_BUFFER_SIZE;
+
+ brotli_offset = brotli_limit;
+ brotli_limit += input_chunk;
+ brotli_buffer = realloc(brotli_buffer, brotli_limit);
+ if (brotli_buffer == NULL)
+ outofmem(__FILE__, THIS_FUNC);
+ status = (int) fread(brotli_buffer + brotli_offset, sizeof(char),
+ input_chunk, brfp);
+
+ if (status <= 0) { /* EOF or error */
+ if (status == 0) {
+ rv = HT_LOADED;
+ break;
+ }
+ CTRACE((tfp, THIS_FUNC ": Read error, fread returns %d\n", status));
+ if (bytes) {
+ if (!feof(brfp))
+ rv = HT_PARTIAL_CONTENT;
+ } else {
+ rv = -1;
+ }
+ break;
+ }
+ bytes += status;
+ }
+
+ /*
+ * next, unless we encountered an error (and have no data), try
+ * decompressing with increasing output buffer sizes until the brotli
+ * library succeeds.
+ */
+ if (bytes > 0) {
+ do {
+ if (normal_limit == 0)
+ normal_limit = (10 * brotli_limit) + INPUT_BUFFER_SIZE;
+ else
+ normal_limit *= 2;
+ normal_buffer = realloc(normal_buffer, normal_limit);
+ if (normal_buffer == NULL)
+ outofmem(__FILE__, THIS_FUNC);
+ brotli_size = (size_t) bytes;
+ normal_size = normal_limit;
+ status2 = BrotliDecoderDecompress(brotli_size,
+ (uint8_t *) brotli_buffer,
+ &normal_size,
+ (uint8_t *) normal_buffer);
+ /*
+ * brotli library should return
+ * BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT,
+ * but actually returns
+ * BROTLI_DECODER_RESULT_ERROR
+ *
+ * Accommodate possible improvements...
+ */
+ } while (status2 != BROTLI_DECODER_RESULT_SUCCESS);
+ }
+
+ /*
+ * finally, pump that data into the output stream.
+ */
+ if (status2 == BROTLI_DECODER_RESULT_SUCCESS) {
+ CTRACE((tfp, THIS_FUNC ": decompressed %ld -> %ld (1:%.1f)\n",
+ brotli_size, normal_size,
+ (double) normal_size / (double) brotli_size));
+ (*targetClass.put_block) (sink, normal_buffer, (int) normal_size);
+ bytes += status;
+ HTReadProgress(bytes, (off_t) -1);
+ HTDisplayPartial();
+
+ if (HTCheckForInterrupt()) {
+ _HTProgress(TRANSFER_INTERRUPTED);
+ rv = HT_INTERRUPTED;
+ }
+ }
+ free(brotli_buffer);
+ free(normal_buffer);
+
+ /* next bufferload */
+ HTFinishDisplayPartial();
+ return rv;
+#undef THIS_FUNC
+}
+#endif /* USE_BZLIB */
+
+/* Push data from a socket down a stream STRIPPING CR
+ * --------------------------------------------------
+ *
+ * This routine is responsible for creating and PRESENTING any
+ * graphic (or other) objects described by the socket.
+ *
+ * The file number given is assumed to be a TELNET stream ie containing
+ * CRLF at the end of lines which need to be stripped to LF for unix
+ * when the format is textual.
+ *
+ */
+void HTCopyNoCR(HTParentAnchor *anchor GCC_UNUSED,
+ int file_number,
+ HTStream *sink)
+{
+ HTStreamClass targetClass;
+ int character;
+
+ /* Push the data, ignoring CRLF, down the stream
+ */
+ targetClass = *(sink->isa); /* Copy pointers to procedures */
+
+ /*
+ * Push text from telnet socket down sink
+ *
+ * @@@@@ To push strings could be faster? (especially is we cheat and
+ * don't ignore CR! :-}
+ */
+ HTInitInput(file_number);
+ for (;;) {
+ character = HTGetCharacter();
+ if (character == EOF)
+ break;
+ (*targetClass.put_character) (sink, (char) character);
+ }
+}
+
+/* Parse a socket given format and file number
+ *
+ * This routine is responsible for creating and PRESENTING any
+ * graphic (or other) objects described by the file.
+ *
+ * The file number given is assumed to be a TELNET stream ie containing
+ * CRLF at the end of lines which need to be stripped to LF for unix
+ * when the format is textual.
+ *
+ * State of socket and target stream on entry:
+ * socket (file_number) assumed open,
+ * target (sink) usually NULL (will call stream stack).
+ *
+ * Return values:
+ * HT_INTERRUPTED Interruption or error after some data received.
+ * -501 Stream stack failed (cannot present or convert).
+ * -2 Unexpected disconnect before any data received.
+ * -1 Stream stack failed (cannot present or convert), or
+ * Interruption or error before any data received, or
+ * (UNIX) other read error before any data received, or
+ * download cancelled.
+ * HT_LOADED Normal close of socket (end of file indication
+ * received), or
+ * unexpected disconnect after some data received, or
+ * other read error after some data received, or
+ * (not UNIX) other read error before any data received.
+ *
+ * State of socket and target stream on return depends on return value:
+ * HT_INTERRUPTED socket still open, target aborted.
+ * -501 socket still open, target stream NULL.
+ * -2 socket still open, target freed.
+ * -1 socket still open, target stream aborted or NULL.
+ * otherwise socket closed, target stream freed.
+ */
+int HTParseSocket(HTFormat rep_in,
+ HTFormat format_out,
+ HTParentAnchor *anchor,
+ int file_number,
+ HTStream *sink)
+{
+ HTStream *stream;
+ HTStreamClass targetClass;
+ int rv;
+
+ stream = HTStreamStack(rep_in, format_out, sink, anchor);
+
+ if (!stream) {
+ char *buffer = 0;
+
+ if (LYCancelDownload) {
+ LYCancelDownload = FALSE;
+ return -1;
+ }
+ HTSprintf0(&buffer, CANNOT_CONVERT_I_TO_O,
+ HTAtom_name(rep_in), HTAtom_name(format_out));
+ CTRACE((tfp, "HTFormat: %s\n", buffer));
+ rv = HTLoadError(sink, 501, buffer); /* returns -501 */
+ FREE(buffer);
+ } else {
+ /*
+ * Push the data, don't worry about CRLF we can strip them later.
+ */
+ targetClass = *(stream->isa); /* Copy pointers to procedures */
+ rv = HTCopy(anchor, file_number, NULL, stream);
+ if (rv != -1 && rv != HT_INTERRUPTED)
+ (*targetClass._free) (stream);
+ }
+ return rv;
+ /* Originally: full: HT_LOADED; partial: HT_INTERRUPTED; no bytes: -1 */
+}
+
+/* Parse a file given format and file pointer
+ *
+ * This routine is responsible for creating and PRESENTING any
+ * graphic (or other) objects described by the file.
+ *
+ * The file number given is assumed to be a TELNET stream ie containing
+ * CRLF at the end of lines which need to be stripped to \n for unix
+ * when the format is textual.
+ *
+ * State of file and target stream on entry:
+ * FILE* (fp) assumed open,
+ * target (sink) usually NULL (will call stream stack).
+ *
+ * Return values:
+ * -501 Stream stack failed (cannot present or convert).
+ * -1 Download cancelled.
+ * HT_NO_DATA Error before any data read.
+ * HT_PARTIAL_CONTENT Interruption or error after some data read.
+ * HT_LOADED Normal end of file indication on reading.
+ *
+ * State of file and target stream on return:
+ * always fp still open; target freed, aborted, or NULL.
+ */
+int HTParseFile(HTFormat rep_in,
+ HTFormat format_out,
+ HTParentAnchor *anchor,
+ FILE *fp,
+ HTStream *sink)
+{
+ HTStream *stream;
+ HTStreamClass targetClass;
+ int rv;
+ int result;
+
+ if (fp == NULL) {
+ result = HT_LOADED;
+ } else {
+ stream = HTStreamStack(rep_in, format_out, sink, anchor);
+
+ if (!stream || !stream->isa) {
+ char *buffer = 0;
+
+ if (LYCancelDownload) {
+ LYCancelDownload = FALSE;
+ result = -1;
+ } else {
+ HTSprintf0(&buffer, CANNOT_CONVERT_I_TO_O,
+ HTAtom_name(rep_in), HTAtom_name(format_out));
+ CTRACE((tfp, "HTFormat(in HTParseFile): %s\n", buffer));
+ rv = HTLoadError(sink, 501, buffer);
+ FREE(buffer);
+ result = rv;
+ }
+ } else {
+
+ /*
+ * Push the data down the stream
+ *
+ * @@ Bug: This decision ought to be made based on "encoding"
+ * rather than on content-type. @@@ When we handle encoding. The
+ * current method smells anyway.
+ */
+ targetClass = *(stream->isa); /* Copy pointers to procedures */
+ rv = HTFileCopy(fp, stream);
+ if (rv == -1 || rv == HT_INTERRUPTED) {
+ (*targetClass._abort) (stream, NULL);
+ } else {
+ (*targetClass._free) (stream);
+ }
+
+ if (rv == -1) {
+ result = HT_NO_DATA;
+ } else if (rv == HT_INTERRUPTED || (rv > 0 && rv != HT_LOADED)) {
+ result = HT_PARTIAL_CONTENT;
+ } else {
+ result = HT_LOADED;
+ }
+ }
+ }
+ return result;
+}
+
+#ifdef USE_SOURCE_CACHE
+/* Parse a document in memory given format and memory block pointer
+ *
+ * This routine is responsible for creating and PRESENTING any
+ * graphic (or other) objects described by the file.
+ *
+ * State of memory and target stream on entry:
+ * HTChunk* (chunk) assumed valid,
+ * target (sink) usually NULL (will call stream stack).
+ *
+ * Return values:
+ * -501 Stream stack failed (cannot present or convert).
+ * HT_LOADED All data sent.
+ *
+ * State of memory and target stream on return:
+ * always chunk unchanged; target freed, aborted, or NULL.
+ */
+int HTParseMem(HTFormat rep_in,
+ HTFormat format_out,
+ HTParentAnchor *anchor,
+ HTChunk *chunk,
+ HTStream *sink)
+{
+ HTStream *stream;
+ HTStreamClass targetClass;
+ int rv;
+ int result;
+
+ stream = HTStreamStack(rep_in, format_out, sink, anchor);
+ if (!stream || !stream->isa) {
+ char *buffer = 0;
+
+ HTSprintf0(&buffer, CANNOT_CONVERT_I_TO_O,
+ HTAtom_name(rep_in), HTAtom_name(format_out));
+ CTRACE((tfp, "HTFormat(in HTParseMem): %s\n", buffer));
+ rv = HTLoadError(sink, 501, buffer);
+ FREE(buffer);
+ result = rv;
+ } else {
+
+ /* Push the data down the stream
+ */
+ targetClass = *(stream->isa);
+ (void) HTMemCopy(chunk, stream);
+ (*targetClass._free) (stream);
+ result = HT_LOADED;
+ }
+ return result;
+}
+#endif
+
+#ifdef USE_ZLIB
+static int HTCloseGzFile(gzFile gzfp)
+{
+ int gzres;
+
+ if (gzfp == NULL)
+ return 0;
+ gzres = gzclose(gzfp);
+ if (TRACE) {
+ if (gzres == Z_ERRNO) {
+ perror("gzclose ");
+ } else if (gzres != Z_OK) {
+ CTRACE((tfp, "gzclose : error number %d\n", gzres));
+ }
+ }
+ return (gzres);
+}
+
+/* HTParseGzFile
+ *
+ * State of file and target stream on entry:
+ * gzFile (gzfp) assumed open,
+ * target (sink) usually NULL (will call stream stack).
+ *
+ * Return values:
+ * -501 Stream stack failed (cannot present or convert).
+ * -1 Download cancelled.
+ * HT_NO_DATA Error before any data read.
+ * HT_PARTIAL_CONTENT Interruption or error after some data read.
+ * HT_LOADED Normal end of file indication on reading.
+ *
+ * State of file and target stream on return:
+ * always gzfp closed; target freed, aborted, or NULL.
+ */
+int HTParseGzFile(HTFormat rep_in,
+ HTFormat format_out,
+ HTParentAnchor *anchor,
+ gzFile gzfp,
+ HTStream *sink)
+{
+ HTStream *stream;
+ HTStreamClass targetClass;
+ int rv;
+ int result;
+
+ stream = HTStreamStack(rep_in, format_out, sink, anchor);
+
+ if (!stream || !stream->isa) {
+ char *buffer = 0;
+
+ HTCloseGzFile(gzfp);
+ if (LYCancelDownload) {
+ LYCancelDownload = FALSE;
+ result = -1;
+ } else {
+ HTSprintf0(&buffer, CANNOT_CONVERT_I_TO_O,
+ HTAtom_name(rep_in), HTAtom_name(format_out));
+ CTRACE((tfp, "HTFormat(in HTParseGzFile): %s\n", buffer));
+ rv = HTLoadError(sink, 501, buffer);
+ FREE(buffer);
+ result = rv;
+ }
+ } else {
+
+ /*
+ * Push the data down the stream
+ *
+ * @@ Bug: This decision ought to be made based on "encoding" rather than
+ * on content-type. @@@ When we handle encoding. The current method
+ * smells anyway.
+ */
+ targetClass = *(stream->isa); /* Copy pointers to procedures */
+ rv = HTGzFileCopy(gzfp, stream);
+ if (rv == -1 || rv == HT_INTERRUPTED) {
+ (*targetClass._abort) (stream, NULL);
+ } else {
+ (*targetClass._free) (stream);
+ }
+
+ HTCloseGzFile(gzfp);
+ if (rv == -1) {
+ result = HT_NO_DATA;
+ } else if (rv == HT_INTERRUPTED || (rv > 0 && rv != HT_LOADED)) {
+ result = HT_PARTIAL_CONTENT;
+ } else {
+ result = HT_LOADED;
+ }
+ }
+ return result;
+}
+
+/* HTParseZzFile
+ *
+ * State of file and target stream on entry:
+ * FILE (zzfp) assumed open,
+ * target (sink) usually NULL (will call stream stack).
+ *
+ * Return values:
+ * -501 Stream stack failed (cannot present or convert).
+ * -1 Download cancelled.
+ * HT_NO_DATA Error before any data read.
+ * HT_PARTIAL_CONTENT Interruption or error after some data read.
+ * HT_LOADED Normal end of file indication on reading.
+ *
+ * State of file and target stream on return:
+ * always zzfp closed; target freed, aborted, or NULL.
+ */
+int HTParseZzFile(HTFormat rep_in,
+ HTFormat format_out,
+ HTParentAnchor *anchor,
+ FILE *zzfp,
+ HTStream *sink)
+{
+ HTStream *stream;
+ HTStreamClass targetClass;
+ int rv;
+ int result;
+
+ stream = HTStreamStack(rep_in, format_out, sink, anchor);
+
+ if (!stream || !stream->isa) {
+ char *buffer = 0;
+
+ fclose(zzfp);
+ if (LYCancelDownload) {
+ LYCancelDownload = FALSE;
+ result = -1;
+ } else {
+ HTSprintf0(&buffer, CANNOT_CONVERT_I_TO_O,
+ HTAtom_name(rep_in), HTAtom_name(format_out));
+ CTRACE((tfp, "HTFormat(in HTParseGzFile): %s\n", buffer));
+ rv = HTLoadError(sink, 501, buffer);
+ FREE(buffer);
+ result = rv;
+ }
+ } else {
+
+ /*
+ * Push the data down the stream
+ *
+ * @@ Bug: This decision ought to be made based on "encoding" rather than
+ * on content-type. @@@ When we handle encoding. The current method
+ * smells anyway.
+ */
+ targetClass = *(stream->isa); /* Copy pointers to procedures */
+ rv = HTZzFileCopy(zzfp, stream);
+ if (rv == -1 || rv == HT_INTERRUPTED) {
+ (*targetClass._abort) (stream, NULL);
+ } else {
+ (*targetClass._free) (stream);
+ }
+
+ fclose(zzfp);
+ if (rv == -1) {
+ result = HT_NO_DATA;
+ } else if (rv == HT_INTERRUPTED || (rv > 0 && rv != HT_LOADED)) {
+ result = HT_PARTIAL_CONTENT;
+ } else {
+ result = HT_LOADED;
+ }
+ }
+ return result;
+}
+#endif /* USE_ZLIB */
+
+#ifdef USE_BZLIB
+static void HTCloseBzFile(BZFILE * bzfp)
+{
+ if (bzfp)
+ BZ2_bzclose(bzfp);
+}
+
+/* HTParseBzFile
+ *
+ * State of file and target stream on entry:
+ * bzFile (bzfp) assumed open,
+ * target (sink) usually NULL (will call stream stack).
+ *
+ * Return values:
+ * -501 Stream stack failed (cannot present or convert).
+ * -1 Download cancelled.
+ * HT_NO_DATA Error before any data read.
+ * HT_PARTIAL_CONTENT Interruption or error after some data read.
+ * HT_LOADED Normal end of file indication on reading.
+ *
+ * State of file and target stream on return:
+ * always bzfp closed; target freed, aborted, or NULL.
+ */
+int HTParseBzFile(HTFormat rep_in,
+ HTFormat format_out,
+ HTParentAnchor *anchor,
+ BZFILE * bzfp,
+ HTStream *sink)
+{
+ HTStream *stream;
+ HTStreamClass targetClass;
+ int rv;
+ int result;
+
+ stream = HTStreamStack(rep_in, format_out, sink, anchor);
+
+ if (!stream || !stream->isa) {
+ char *buffer = 0;
+
+ HTCloseBzFile(bzfp);
+ if (LYCancelDownload) {
+ LYCancelDownload = FALSE;
+ result = -1;
+ } else {
+ HTSprintf0(&buffer, CANNOT_CONVERT_I_TO_O,
+ HTAtom_name(rep_in), HTAtom_name(format_out));
+ CTRACE((tfp, "HTFormat(in HTParseBzFile): %s\n", buffer));
+ rv = HTLoadError(sink, 501, buffer);
+ FREE(buffer);
+ result = rv;
+ }
+ } else {
+
+ /*
+ * Push the data down the stream
+ *
+ * @@ Bug: This decision ought to be made based on "encoding" rather than
+ * on content-type. @@@ When we handle encoding. The current method
+ * smells anyway.
+ */
+ targetClass = *(stream->isa); /* Copy pointers to procedures */
+ rv = HTBzFileCopy(bzfp, stream);
+ if (rv == -1 || rv == HT_INTERRUPTED) {
+ (*targetClass._abort) (stream, NULL);
+ } else {
+ (*targetClass._free) (stream);
+ }
+
+ HTCloseBzFile(bzfp);
+ if (rv == -1) {
+ result = HT_NO_DATA;
+ } else if (rv == HT_INTERRUPTED || (rv > 0 && rv != HT_LOADED)) {
+ result = HT_PARTIAL_CONTENT;
+ } else {
+ result = HT_LOADED;
+ }
+ }
+ return result;
+}
+#endif /* USE_BZLIB */
+
+#ifdef USE_BROTLI
+/* HTParseBrFile
+ *
+ * State of file and target stream on entry:
+ * FILE* (brfp) assumed open,
+ * target (sink) usually NULL (will call stream stack).
+ *
+ * Return values:
+ * -501 Stream stack failed (cannot present or convert).
+ * -1 Download cancelled.
+ * HT_NO_DATA Error before any data read.
+ * HT_PARTIAL_CONTENT Interruption or error after some data read.
+ * HT_LOADED Normal end of file indication on reading.
+ *
+ * State of file and target stream on return:
+ * always brfp closed; target freed, aborted, or NULL.
+ */
+int HTParseBrFile(HTFormat rep_in,
+ HTFormat format_out,
+ HTParentAnchor *anchor,
+ FILE *brfp,
+ HTStream *sink)
+{
+#undef THIS_FUNC
+#define THIS_FUNC "HTParseBrFile"
+ HTStream *stream;
+ HTStreamClass targetClass;
+ int rv;
+ int result;
+
+ stream = HTStreamStack(rep_in, format_out, sink, anchor);
+
+ if (!stream || !stream->isa) {
+ char *buffer = 0;
+
+ fclose(brfp);
+ if (LYCancelDownload) {
+ LYCancelDownload = FALSE;
+ result = -1;
+ } else {
+ HTSprintf0(&buffer, CANNOT_CONVERT_I_TO_O,
+ HTAtom_name(rep_in), HTAtom_name(format_out));
+ CTRACE((tfp, "HTFormat(in " THIS_FUNC "): %s\n", buffer));
+ rv = HTLoadError(sink, 501, buffer);
+ FREE(buffer);
+ result = rv;
+ }
+ } else {
+
+ /*
+ * Push the data down the stream
+ *
+ * @@ Bug: This decision ought to be made based on "encoding" rather than
+ * on content-type. @@@ When we handle encoding. The current method
+ * smells anyway.
+ */
+ targetClass = *(stream->isa); /* Copy pointers to procedures */
+ rv = HTBrFileCopy(brfp, stream);
+ if (rv == -1 || rv == HT_INTERRUPTED) {
+ (*targetClass._abort) (stream, NULL);
+ } else {
+ (*targetClass._free) (stream);
+ }
+
+ fclose(brfp);
+ if (rv == -1) {
+ result = HT_NO_DATA;
+ } else if (rv == HT_INTERRUPTED || (rv > 0 && rv != HT_LOADED)) {
+ result = HT_PARTIAL_CONTENT;
+ } else {
+ result = HT_LOADED;
+ }
+ }
+ return result;
+#undef THIS_FUNC
+}
+#endif /* USE_BROTLI */
+
+/* Converter stream: Network Telnet to internal character text
+ * -----------------------------------------------------------
+ *
+ * The input is assumed to be in ASCII, with lines delimited
+ * by (13,10) pairs, These pairs are converted into (CR,LF)
+ * pairs in the local representation. The (CR,LF) sequence
+ * when found is changed to a '\n' character, the internal
+ * C representation of a new line.
+ */
+
+static void NetToText_put_character(HTStream *me, int net_char)
+{
+ char c = (char) FROMASCII(net_char);
+
+ if (me->had_cr) {
+ if (c == LF) {
+ me->sink->isa->put_character(me->sink, '\n'); /* Newline */
+ me->had_cr = NO;
+ return;
+ } else {
+ me->sink->isa->put_character(me->sink, CR); /* leftover */
+ }
+ }
+ me->had_cr = (BOOL) (c == CR);
+ if (!me->had_cr)
+ me->sink->isa->put_character(me->sink, c); /* normal */
+}
+
+static void NetToText_put_string(HTStream *me, const char *s)
+{
+ const char *p;
+
+ for (p = s; *p; p++)
+ NetToText_put_character(me, *p);
+}
+
+static void NetToText_put_block(HTStream *me, const char *s, int l)
+{
+ const char *p;
+
+ for (p = s; p < (s + l); p++)
+ NetToText_put_character(me, *p);
+}
+
+static void NetToText_free(HTStream *me)
+{
+ (me->sink->isa->_free) (me->sink); /* Close rest of pipe */
+ FREE(me);
+}
+
+static void NetToText_abort(HTStream *me, HTError e)
+{
+ me->sink->isa->_abort(me->sink, e); /* Abort rest of pipe */
+ FREE(me);
+}
+
+/* The class structure
+*/
+static HTStreamClass NetToTextClass =
+{
+ "NetToText",
+ NetToText_free,
+ NetToText_abort,
+ NetToText_put_character,
+ NetToText_put_string,
+ NetToText_put_block
+};
+
+/* The creation method
+*/
+HTStream *HTNetToText(HTStream *sink)
+{
+ HTStream *me = typecalloc(HTStream);
+
+ if (me == NULL)
+ outofmem(__FILE__, "NetToText");
+
+ me->isa = &NetToTextClass;
+
+ me->had_cr = NO;
+ me->sink = sink;
+ return me;
+}
+
+static HTStream HTBaseStreamInstance; /* Made static */
+
+/*
+ * ERROR STREAM
+ * ------------
+ * There is only one error stream shared by anyone who wants a
+ * generic error returned from all stream methods.
+ */
+static void HTErrorStream_put_character(HTStream *me GCC_UNUSED, int c GCC_UNUSED)
+{
+ LYCancelDownload = TRUE;
+}
+
+static void HTErrorStream_put_string(HTStream *me GCC_UNUSED, const char *s)
+{
+ if (s && *s)
+ LYCancelDownload = TRUE;
+}
+
+static void HTErrorStream_write(HTStream *me GCC_UNUSED, const char *s, int l)
+{
+ if (l && s)
+ LYCancelDownload = TRUE;
+}
+
+static void HTErrorStream_free(HTStream *me GCC_UNUSED)
+{
+ return;
+}
+
+static void HTErrorStream_abort(HTStream *me GCC_UNUSED, HTError e GCC_UNUSED)
+{
+ return;
+}
+
+static const HTStreamClass HTErrorStreamClass =
+{
+ "ErrorStream",
+ HTErrorStream_free,
+ HTErrorStream_abort,
+ HTErrorStream_put_character,
+ HTErrorStream_put_string,
+ HTErrorStream_write
+};
+
+HTStream *HTErrorStream(void)
+{
+ CTRACE((tfp, "ErrorStream. Created\n"));
+ HTBaseStreamInstance.isa = &HTErrorStreamClass; /* The rest is random */
+ return &HTBaseStreamInstance;
+}
diff --git a/WWW/Library/Implementation/HTFormat.h b/WWW/Library/Implementation/HTFormat.h
new file mode 100644
index 0000000..835daef
--- /dev/null
+++ b/WWW/Library/Implementation/HTFormat.h
@@ -0,0 +1,588 @@
+/*
+ * $LynxId: HTFormat.h,v 1.42 2022/04/01 07:54:14 tom Exp $
+ *
+ * HTFormat: The format manager in the WWW Library
+ * MANAGE DIFFERENT DOCUMENT FORMATS
+ *
+ * Here we describe the functions of the HTFormat module which handles conversion between
+ * different data representations. (In MIME parlance, a representation is known as a
+ * content-type. In WWW the term "format" is often used as it is shorter).
+ *
+ * This module is implemented by HTFormat.c. This hypertext document is used to generate
+ * the HTFormat.h include file. Part of the WWW library.
+ */
+#ifndef HTFORMAT_H
+#define HTFORMAT_H
+
+#include <HTStream.h>
+#include <HTAtom.h>
+#include <HTList.h>
+#include <HTAnchor.h>
+
+#ifdef USE_SOURCE_CACHE
+#include <HTChunk.h>
+#endif
+
+#ifdef USE_BZLIB
+#include <bzlib.h>
+#endif
+
+#ifdef USE_ZLIB
+#include <zlib.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+
+ These macros (which used to be constants) define some basic internally
+ referenced representations. The www/xxx ones are of course not MIME
+ standard.
+
+ www/source is an output format which leaves the input untouched. It is
+ useful for diagnostics, and for users who want to see the original, whatever
+ it is.
+
+ */
+/* Internal ones */
+#define STR_SOURCE "www/source"
+ extern HTAtom *WWW_SOURCE; /* calculated once, heavy used */
+
+/*
+
+ www/present represents the user's perception of the document. If you
+ convert to www/present, you present the material to the user.
+
+ */
+#define STR_PRESENT "www/present"
+#define WWW_PRESENT HTAtom_for(STR_PRESENT) /* The user's perception */
+
+#define WWW_DEBUG HTAtom_for("www/debug")
+/*
+
+ WWW_DEBUG represents the user's perception of debug information, for example
+ sent as a HTML document in a HTTP redirection message.
+
+ */
+
+/*
+
+ The message/rfc822 format means a MIME message or a plain text message with
+ no MIME header. This is what is returned by an HTTP server.
+
+ */
+#define WWW_MIME HTAtom_for("www/mime") /* A MIME message */
+
+/*
+ For parsing only the header. - kw
+ */
+#define WWW_MIME_HEAD HTAtom_for("message/x-rfc822-head")
+
+/*
+
+ www/print is like www/present except it represents a printed copy.
+
+ */
+#define WWW_PRINT HTAtom_for("www/print") /* A printed copy */
+
+/*
+
+ www/unknown is a really unknown type. Some default action is appropriate.
+
+ */
+#define WWW_UNKNOWN HTAtom_for("www/unknown")
+
+#ifdef DIRED_SUPPORT
+/*
+ www/dired signals directory edit mode.
+*/
+#define WWW_DIRED HTAtom_for("www/dired")
+#endif
+
+/*
+ * Miscellaneous internal MIME types.
+ */
+#define STR_DOWNLOAD "www/download"
+#define WWW_DOWNLOAD HTAtom_for(STR_DOWNLOAD)
+
+#define STR_DUMP "www/dump"
+#define WWW_DUMP HTAtom_for(STR_DUMP)
+
+/*
+
+ These are regular MIME types. HTML is assumed to be added by the W3 code.
+ application/octet-stream was mistakenly application/binary in earlier libwww
+ versions (pre 2.11).
+
+ */
+#define STR_BINARY "application/octet-stream"
+#define STR_PLAINTEXT "text/plain"
+#define STR_HTML "text/html"
+
+#define WWW_BINARY HTAtom_for(STR_BINARY)
+#define WWW_PLAINTEXT HTAtom_for(STR_PLAINTEXT)
+#define WWW_HTML HTAtom_for(STR_HTML)
+
+#define WWW_POSTSCRIPT HTAtom_for("application/postscript")
+#define WWW_RICHTEXT HTAtom_for("application/rtf")
+#define WWW_AUDIO HTAtom_for("audio/basic")
+
+ typedef HTAtom *HTEncoding;
+
+/*
+ * The following are values for the MIME types:
+ */
+#define WWW_ENC_7BIT HTAtom_for("7bit")
+#define WWW_ENC_8BIT HTAtom_for("8bit")
+#define WWW_ENC_BINARY HTAtom_for("binary")
+
+/*
+ * We also add
+ */
+#define WWW_ENC_COMPRESS HTAtom_for("compress")
+
+/*
+ * Does a string designate a real encoding, or is it just
+ * a "dummy" as for example 7bit, 8bit, and binary?
+ */
+#define IsUnityEncStr(senc) \
+ ((senc)==NULL || *(senc)=='\0' || !strcmp(senc,"identity") ||\
+ !strcmp(senc,"8bit") || !strcmp(senc,"binary") || !strcmp(senc,"7bit"))
+
+#define IsUnityEnc(enc) \
+ ((enc)==NULL || (enc)==HTAtom_for("identity") ||\
+ (enc)==WWW_ENC_8BIT || (enc)==WWW_ENC_BINARY || (enc)==WWW_ENC_7BIT)
+
+/*
+
+The HTPresentation and HTConverter types
+
+ This HTPresentation structure represents a possible conversion algorithm
+ from one format to another. It includes a pointer to a conversion routine.
+ The conversion routine returns a stream to which data should be fed. See
+ also HTStreamStack which scans the list of registered converters and calls
+ one. See the initialisation module for a list of conversion routines.
+
+ */
+ typedef struct _HTPresentation HTPresentation;
+
+ typedef HTStream *HTConverter (HTPresentation *pres,
+ HTParentAnchor *anchor,
+ HTStream *sink);
+
+ struct _HTPresentation {
+ HTAtom *rep; /* representation name atomized */
+ HTAtom *rep_out; /* resulting representation */
+ HTConverter *converter; /* routine to gen the stream stack */
+ char *command; /* MIME-format command string */
+ char *testcommand; /* MIME-format test string */
+ float quality; /* Between 0 (bad) and 1 (good) */
+ float secs;
+ float secs_per_byte;
+ off_t maxbytes;
+ BOOL get_accept; /* list in "Accept:" for GET */
+ int accept_opt; /* matches against LYAcceptMedia */
+ };
+
+/*
+
+ The list of presentations is kept by this module. It is also scanned by
+ modules which want to know the set of formats supported. for example.
+
+ */
+ extern HTList *HTPresentations;
+
+/*
+
+ The default presentation is used when no other is appropriate
+
+ */
+ extern HTPresentation *default_presentation;
+
+/*
+ * Options used for "Content-Type" string
+ */
+ typedef enum {
+ contentBINARY = 0
+ ,contentTEXT
+ ,contentHTML
+ } ContentType;
+
+/*
+ * Options used for "Accept:" string
+ */
+ typedef enum {
+ /* make the components powers of two so we can add them */
+ mediaINT = 1 /* internal types predefined in HTInit.c */
+ ,mediaEXT = 2 /* external types predefined in HTInit.c */
+ ,mediaCFG = 4 /* types, e.g., viewers, from lynx.cfg */
+ ,mediaUSR = 8 /* user's mime-types, etc. */
+ ,mediaSYS = 16 /* system's mime-types, etc. */
+ /* these are useful flavors for the options menu */
+ ,mediaOpt1 = mediaINT
+ ,mediaOpt2 = mediaINT + mediaCFG
+ ,mediaOpt3 = mediaINT + mediaCFG + mediaUSR
+ ,mediaOpt4 = mediaINT + mediaCFG + mediaUSR + mediaSYS
+ /* this is the flavor from pre-2.8.6 */
+ ,mediaALL = mediaINT + mediaEXT + mediaCFG + mediaUSR + mediaSYS
+ } AcceptMedia;
+
+/*
+ * Options used for "Accept-Encoding:" string
+ */
+ typedef enum {
+ encodingNONE = 0
+ ,encodingGZIP = 1
+ ,encodingDEFLATE = 2
+ ,encodingCOMPRESS = 4
+ ,encodingBZIP2 = 8
+ ,encodingBROTLI = 16
+ ,encodingALL = (encodingGZIP
+ + encodingDEFLATE
+ + encodingCOMPRESS
+ + encodingBZIP2
+ + encodingBROTLI)
+ } AcceptEncoding;
+
+/*
+
+HTSetPresentation: Register a system command to present a format
+
+ ON ENTRY,
+
+ rep is the MIME - style format name
+
+ command is the MAILCAP - style command template
+
+ testcommand is the MAILCAP - style testcommand template
+
+ quality A degradation faction 0..1.0
+
+ secs A limit on the time user will wait (0.0 for infinity)
+ secs_per_byte
+
+ maxbytes A limit on the length acceptable as input (0 infinite)
+
+ media Used in filtering presentation types for "Accept:"
+
+ */
+ extern void HTSetPresentation(const char *representation,
+ const char *command,
+ const char *testcommand,
+ double quality,
+ double secs,
+ double secs_per_byte,
+ long int maxbytes,
+ AcceptMedia media
+ );
+
+/*
+
+HTSetConversion: Register a conversion routine
+
+ ON ENTRY,
+
+ rep_in is the content-type input
+
+ rep_out is the resulting content-type
+
+ converter is the routine to make the stream to do it
+
+ */
+
+ extern void HTSetConversion(const char *rep_in,
+ const char *rep_out,
+ HTConverter *converter,
+ double quality,
+ double secs,
+ double secs_per_byte,
+ long int maxbytes,
+ AcceptMedia media
+ );
+
+/*
+
+HTStreamStack: Create a stack of streams
+
+ This is the routine which actually sets up the conversion. It currently
+ checks only for direct conversions, but multi-stage conversions are forseen.
+ It takes a stream into which the output should be sent in the final format,
+ builds the conversion stack, and returns a stream into which the data in the
+ input format should be fed. The anchor is passed because hypertxet objects
+ load information into the anchor object which represents them.
+
+ */
+ extern HTStream *HTStreamStack(HTFormat format_in,
+ HTFormat format_out,
+ HTStream *stream_out,
+ HTParentAnchor *anchor);
+
+/*
+HTReorderPresentation: put presentation near head of list
+
+ Look up a presentation (exact match only) and, if found, reorder it to the
+ start of the HTPresentations list. - kw
+ */
+
+ extern void HTReorderPresentation(HTFormat format_in,
+ HTFormat format_out);
+
+/*
+ * Setup 'get_accept' flag to denote presentations that are not redundant,
+ * and will be listed in "Accept:" header.
+ */
+ extern void HTFilterPresentations(void);
+
+/*
+
+HTStackValue: Find the cost of a filter stack
+
+ Must return the cost of the same stack which HTStreamStack would set up.
+
+ ON ENTRY,
+
+ format_in The format of the data to be converted
+
+ format_out The format required
+
+ initial_value The intrinsic "value" of the data before conversion on a scale
+ from 0 to 1
+
+ length The number of bytes expected in the input format
+
+ */
+ extern float HTStackValue(HTFormat format_in,
+ HTFormat rep_out,
+ double initial_value,
+ long int length);
+
+#define NO_VALUE_FOUND -1e20 /* returned if none found */
+
+/* Display the page while transfer in progress
+ * -------------------------------------------
+ *
+ * Repaint the page only when necessary.
+ * This is a traverse call for HText_pageDispaly() - it works!.
+ *
+ */
+ extern void HTDisplayPartial(void);
+
+ extern void HTFinishDisplayPartial(void);
+
+/*
+
+HTCopy: Copy a socket to a stream
+
+ This is used by the protocol engines to send data down a stream, typically
+ one which has been generated by HTStreamStack.
+
+ */
+ extern int HTCopy(HTParentAnchor *anchor,
+ int file_number,
+ void *handle,
+ HTStream *sink);
+
+/*
+
+HTFileCopy: Copy a file to a stream
+
+ This is used by the protocol engines to send data down a stream, typically
+ one which has been generated by HTStreamStack. It is currently called by
+ HTParseFile
+
+ */
+ extern int HTFileCopy(FILE *fp,
+ HTStream *sink);
+
+#ifdef USE_SOURCE_CACHE
+/*
+
+HTMemCopy: Copy a memory chunk to a stream
+
+ This is used by the protocol engines to send data down a stream, typically
+ one which has been generated by HTStreamStack. It is currently called by
+ HTParseMem
+
+ */
+ extern int HTMemCopy(HTChunk *chunk,
+ HTStream *sink);
+#endif
+
+/*
+
+HTCopyNoCR: Copy a socket to a stream, stripping CR characters.
+
+ It is slower than HTCopy .
+
+ */
+
+ extern void HTCopyNoCR(HTParentAnchor *anchor,
+ int file_number,
+ HTStream *sink);
+
+/*
+
+Clear input buffer and set file number
+
+ This routine and the one below provide simple character input from sockets.
+ (They are left over from the older architecture and may not be used very
+ much.) The existence of a common routine and buffer saves memory space in
+ small implementations.
+
+ */
+ extern void HTInitInput(int file_number);
+
+/*
+
+Get next character from buffer
+
+ */
+ extern int interrupted_in_htgetcharacter;
+ extern int HTGetCharacter(void);
+
+/*
+
+HTParseSocket: Parse a socket given its format
+
+ This routine is called by protocol modules to load an object. uses
+ HTStreamStack and the copy routines above. Returns HT_LOADED if successful,
+ <0 if not.
+
+ */
+ extern int HTParseSocket(HTFormat format_in,
+ HTFormat format_out,
+ HTParentAnchor *anchor,
+ int file_number,
+ HTStream *sink);
+
+/*
+
+HTParseFile: Parse a File through a file pointer
+
+ This routine is called by protocols modules to load an object. uses
+ HTStreamStack and HTFileCopy. Returns HT_LOADED if successful, can also
+ return HT_PARTIAL_CONTENT, HT_NO_DATA, or other <0 for failure.
+
+ */
+ extern int HTParseFile(HTFormat format_in,
+ HTFormat format_out,
+ HTParentAnchor *anchor,
+ FILE *fp,
+ HTStream *sink);
+
+#ifdef USE_SOURCE_CACHE
+/*
+
+HTParseMem: Parse a document in memory
+
+ This routine is called by protocols modules to load an object. uses
+ HTStreamStack and HTMemCopy. Returns HT_LOADED if successful, can also
+ return <0 for failure.
+
+ */
+ extern int HTParseMem(HTFormat format_in,
+ HTFormat format_out,
+ HTParentAnchor *anchor,
+ HTChunk *chunk,
+ HTStream *sink);
+#endif
+
+#ifdef USE_ZLIB
+/*
+HTParseGzFile: Parse a gzip'ed File through a file pointer
+
+ This routine is called by protocols modules to load an object. uses
+ HTStreamStack and HTGzFileCopy. Returns HT_LOADED if successful, can also
+ return HT_PARTIAL_CONTENT, HT_NO_DATA, or other <0 for failure.
+ */
+ extern int HTParseGzFile(HTFormat format_in,
+ HTFormat format_out,
+ HTParentAnchor *anchor,
+ gzFile gzfp,
+ HTStream *sink);
+
+/*
+HTParseZzFile: Parse a deflate'd File through a file pointer
+
+ This routine is called by protocols modules to load an object. uses
+ HTStreamStack and HTZzFileCopy. Returns HT_LOADED if successful, can also
+ return HT_PARTIAL_CONTENT, HT_NO_DATA, or other <0 for failure.
+ */
+ extern int HTParseZzFile(HTFormat format_in,
+ HTFormat format_out,
+ HTParentAnchor *anchor,
+ FILE *zzfp,
+ HTStream *sink);
+
+#endif /* USE_ZLIB */
+
+#ifdef USE_BZLIB
+/*
+HTParseBzFile: Parse a bzip2'ed File through a file pointer
+
+ This routine is called by protocols modules to load an object. uses
+ HTStreamStack and HTBzFileCopy. Returns HT_LOADED if successful, can also
+ return HT_PARTIAL_CONTENT, HT_NO_DATA, or other <0 for failure.
+ */
+ extern int HTParseBzFile(HTFormat format_in,
+ HTFormat format_out,
+ HTParentAnchor *anchor,
+ BZFILE * bzfp,
+ HTStream *sink);
+
+#endif /* USE_BZLIB */
+
+#ifdef USE_BROTLI
+/*
+HTParseBrFile: Parse a brotli'ed File through a file pointer
+
+ This routine is called by protocols modules to load an object. uses
+ HTStreamStack and HTBrFileCopy. Returns HT_LOADED if successful, can also
+ return HT_PARTIAL_CONTENT, HT_NO_DATA, or other <0 for failure.
+ */
+ extern int HTParseBrFile(HTFormat format_in,
+ HTFormat format_out,
+ HTParentAnchor *anchor,
+ FILE *brfp,
+ HTStream *sink);
+
+#endif /* USE_BROTLI */
+
+/*
+
+HTNetToText: Convert Net ASCII to local representation
+
+ This is a filter stream suitable for taking text from a socket and passing
+ it into a stream which expects text in the local C representation. It does
+ ASCII and newline conversion. As usual, pass its output stream to it when
+ creating it.
+
+ */
+ extern HTStream *HTNetToText(HTStream *sink);
+
+/*
+
+HTFormatInit: Set up default presentations and conversions
+
+ These are defined in HTInit.c or HTSInit.c if these have been replaced. If
+ you don't call this routine, and you don't define any presentations, then
+ this routine will automatically be called the first time a conversion is
+ needed. However, if you explicitly add some conversions (eg using
+ HTLoadRules) then you may want also to explicitly call this to get the
+ defaults as well.
+
+ */
+ extern void HTFormatInit(void);
+
+/*
+
+Epilogue
+
+ */
+ extern BOOL HTOutputSource; /* Flag: shortcut parser */
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* HTFORMAT_H */
diff --git a/WWW/Library/Implementation/HTGopher.c b/WWW/Library/Implementation/HTGopher.c
new file mode 100644
index 0000000..0314179
--- /dev/null
+++ b/WWW/Library/Implementation/HTGopher.c
@@ -0,0 +1,2071 @@
+/*
+ * $LynxId: HTGopher.c,v 1.77 2022/04/01 00:18:09 tom Exp $
+ *
+ * GOPHER ACCESS HTGopher.c
+ * =============
+ *
+ * History:
+ * 26 Sep 90 Adapted from other accesses (News, HTTP) TBL
+ * 29 Nov 91 Downgraded to C, for portable implementation.
+ * 10 Mar 96 Foteos Macrides (macrides@sci.wfbr.edu). Added a
+ * form-based CSO/PH gateway. Can be invoked via a
+ * "cso://host[:port]/" or "gopher://host:105/2"
+ * URL. If a gopher URL is used with a query token
+ * ('?'), the old ISINDEX procedure will be used
+ * instead of the form-based gateway.
+ * 15 Mar 96 Foteos Macrides (macrides@sci.wfbr.edu). Pass
+ * port 79, gtype 0 gopher URLs to the finger
+ * gateway.
+ */
+
+#define HTSTREAM_INTERNAL 1
+
+#include <HTUtils.h> /* Coding convention macros */
+#include <HTFile.h> /* For HTFileFormat() */
+
+#ifndef DISABLE_GOPHER
+#include <HTAlert.h>
+#include <HTParse.h>
+#include <HTTCP.h>
+#include <HTFinger.h>
+#include <LYGlobalDefs.h>
+
+/*
+ * Implements.
+ */
+#include <HTGopher.h>
+
+#define GOPHER_PORT 70 /* See protocol spec */
+#define CSO_PORT 105 /* See protocol spec */
+#define BIG 1024 /* Bug */
+#define LINE_LENGTH 256 /* Bug */
+
+/*
+ * Gopher entity types.
+ */
+#define GOPHER_TEXT '0'
+#define GOPHER_MENU '1'
+#define GOPHER_CSO '2'
+#define GOPHER_ERROR '3'
+#define GOPHER_MACBINHEX '4'
+#define GOPHER_PCBINARY '5'
+#define GOPHER_UUENCODED '6'
+#define GOPHER_INDEX '7'
+#define GOPHER_TELNET '8'
+#define GOPHER_BINARY '9'
+#define GOPHER_GIF 'g'
+#define GOPHER_HTML 'h' /* HTML */
+#define GOPHER_CHTML 'H' /* HTML */
+#define GOPHER_SOUND 's'
+#define GOPHER_WWW 'w' /* W3 address */
+#define GOPHER_IMAGE 'I'
+#define GOPHER_TN3270 'T'
+#define GOPHER_INFO 'i'
+#define GOPHER_DUPLICATE '+'
+#define GOPHER_PLUS_IMAGE ':' /* Addition from Gopher Plus */
+#define GOPHER_PLUS_MOVIE ';'
+#define GOPHER_PLUS_SOUND '<'
+#define GOPHER_PLUS_PDF 'P'
+
+#include <HTFormat.h>
+
+/*
+ * Hypertext object building machinery.
+ */
+#include <HTML.h>
+
+#include <LYStrings.h>
+#include <LYUtils.h>
+#include <LYLeaks.h>
+
+#define PUTC(c) (*targetClass.put_character)(target, c)
+#define PUTS(s) (*targetClass.put_string)(target, s)
+#define START(e) (*targetClass.start_element)(target, e, 0, 0, -1, 0)
+#define END(e) (*targetClass.end_element)(target, e, 0)
+#define FREE_TARGET (*targetClass._free)(target)
+
+#define NEXT_CHAR HTGetCharacter()
+
+/*
+ * Module-wide variables.
+ */
+static int s; /* Socket for gopher or CSO host */
+
+struct _HTStructured {
+ const HTStructuredClass *isa; /* For gopher streams */
+ /* ... */
+};
+
+static HTStructured *target; /* the new gopher hypertext */
+static HTStructuredClass targetClass; /* Its action routines */
+
+struct _HTStream {
+ HTStreamClass *isa; /* For form-based CSO gateway - FM */
+};
+
+typedef struct _CSOfield_info { /* For form-based CSO gateway - FM */
+ struct _CSOfield_info *next;
+ char *name;
+ char *attributes;
+ char *description;
+ int id;
+ int lookup;
+ int indexed;
+ int url;
+ int max_size;
+ int defreturn;
+ int explicit_return;
+ int reserved;
+ int gpublic;
+ char name_buf[16]; /* Avoid malloc if we can */
+ char desc_buf[32]; /* Avoid malloc if we can */
+ char attr_buf[80]; /* Avoid malloc if we can */
+} CSOfield_info;
+
+static CSOfield_info *CSOfields = NULL; /* For form-based CSO gateway - FM */
+
+typedef struct _CSOformgen_context { /* For form-based CSO gateway - FM */
+ const char *host;
+ const char *seek;
+ CSOfield_info *fld;
+ int port;
+ int cur_line;
+ int cur_off;
+ int rep_line;
+ int rep_off;
+ int public_override;
+ int field_select;
+} CSOformgen_context;
+
+/* Matrix of allowed characters in filenames
+ * =========================================
+ */
+static BOOL acceptable_html[256];
+static BOOL acceptable_file[256];
+static BOOL acceptable_inited = NO;
+
+static void init_acceptable(void)
+{
+ unsigned int i;
+ const char *good =
+ "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./-_$";
+
+ for (i = 0; i < 256; i++) {
+ acceptable_html[i] = NO;
+ acceptable_file[i] = NO;
+ }
+ for (; *good; good++) {
+ acceptable_html[(unsigned int) *good] = YES;
+ acceptable_file[(unsigned int) *good] = YES;
+ }
+ for (good = ";?=#"; *good; ++good) {
+ acceptable_html[(unsigned int) *good] = YES;
+ }
+ acceptable_inited = YES;
+}
+
+/* Decode one hex character
+ * ========================
+ */
+static const char hex[17] = "0123456789abcdef";
+
+static char from_hex(int c)
+{
+ return (char) ((c >= '0') && (c <= '9') ? c - '0'
+ : (c >= 'A') && (c <= 'F') ? c - 'A' + 10
+ : (c >= 'a') && (c <= 'f') ? c - 'a' + 10
+ : 0);
+}
+
+/* Paste in an Anchor
+ * ==================
+ *
+ * The title of the destination is set, as there is no way
+ * of knowing what the title is when we arrive.
+ *
+ * On entry,
+ * HT is in append mode.
+ * text points to the text to be put into the file, 0 terminated.
+ * addr points to the hypertext reference address 0 terminated.
+ */
+BOOLEAN HT_Is_Gopher_URL = FALSE;
+
+static void write_anchor(const char *text, const char *addr)
+{
+ BOOL present[HTML_A_ATTRIBUTES];
+ const char *value[HTML_A_ATTRIBUTES];
+
+ int i;
+
+ for (i = 0; i < HTML_A_ATTRIBUTES; i++)
+ present[i] = 0;
+ present[HTML_A_HREF] = YES;
+ ((const char **) value)[HTML_A_HREF] = addr;
+ present[HTML_A_TITLE] = YES;
+ ((const char **) value)[HTML_A_TITLE] = text;
+
+ CTRACE((tfp, "HTGopher: adding URL: %s\n", addr));
+
+ HT_Is_Gopher_URL = TRUE; /* tell HTML.c that this is a Gopher URL */
+ (*targetClass.start_element) (target, HTML_A, present,
+ (const char **) value, -1, 0);
+
+ PUTS(text);
+ END(HTML_A);
+}
+
+/* Parse a Gopher Menu document
+ * ============================
+ */
+static void parse_menu(const char *arg GCC_UNUSED,
+ HTParentAnchor *anAnchor)
+{
+ char gtype;
+ char this_type;
+ int ich;
+ char line[BIG];
+ char *name = NULL, *selector = NULL; /* Gopher menu fields */
+ char *host = NULL;
+ char *port;
+ char *p = line;
+ const char *title;
+ int bytes = 0;
+ int BytesReported = 0;
+ char buffer[128];
+ BOOL *valid_chars;
+
+#define TAB '\t'
+#define HEX_ESCAPE '%'
+
+ START(HTML_HTML);
+ PUTC('\n');
+ START(HTML_HEAD);
+ PUTC('\n');
+ START(HTML_TITLE);
+ if ((title = HTAnchor_title(anAnchor)))
+ PUTS(title);
+ else
+ PUTS(GOPHER_MENU_TITLE);
+ END(HTML_TITLE);
+ PUTC('\n');
+ END(HTML_HEAD);
+ PUTC('\n');
+
+ START(HTML_BODY);
+ PUTC('\n');
+ START(HTML_H1);
+ if ((title = HTAnchor_title(anAnchor)))
+ PUTS(title);
+ else
+ PUTS(GOPHER_MENU_TITLE);
+ END(HTML_H1);
+ PUTC('\n');
+ START(HTML_PRE);
+ PUTC('\n'); /* newline after HTML_PRE forces split-line */
+ this_type = GOPHER_ERROR;
+ while ((ich = NEXT_CHAR) != EOF) {
+
+ if (interrupted_in_htgetcharacter) {
+ CTRACE((tfp,
+ "HTGopher: Interrupted in HTGetCharacter, apparently.\n"));
+ goto end_html;
+ }
+
+ if ((char) ich != LF) {
+ const char *ss = NULL;
+
+ /*
+ * Help the -source output to look like the HTML equivalent of the
+ * Gopher menu.
+ */
+ if (dump_output_immediately
+ && HTOutputFormat == WWW_DUMP) {
+ if (ich == '<') {
+ ss = "&lt;";
+ } else if (ich == '>') {
+ ss = "&gt;";
+ } else if (ich == '&') {
+ ss = "&amp;";
+ }
+ }
+ if (ss != NULL) {
+ if ((p + 5) < &line[BIG - 1]) {
+ while (*ss != '\0') {
+ *p++ = *ss++;
+ }
+ }
+ } else {
+ *p = (char) ich; /* Put character in line */
+ if (p < &line[BIG - 1])
+ p++;
+ }
+
+ } else {
+ *p++ = '\0'; /* Terminate line */
+ bytes += (int) (p - line); /* add size */
+ p = line; /* Scan it to parse it */
+ port = 0; /* Flag "not parsed" */
+ CTRACE((tfp, "HTGopher: Menu item: %s\n", line));
+ gtype = *p++;
+
+ if (bytes > BytesReported + 1024) {
+ sprintf(buffer, TRANSFERRED_X_BYTES, bytes);
+ HTProgress(buffer);
+ BytesReported = bytes;
+ }
+
+ /* Break on line with a dot by itself */
+ if ((gtype == '.') && ((*p == '\r') || (*p == 0)))
+ break;
+
+ if (gtype && *p) {
+ name = p;
+ selector = StrChr(name, TAB);
+ if (selector) {
+ *selector++ = '\0'; /* Terminate name */
+ /*
+ * Gopher+ Type=0+ objects can be binary, and will
+ * have 9 or 5 beginning their selector. Make sure
+ * we don't trash the terminal by treating them as
+ * text. - FM
+ */
+ if (gtype == GOPHER_TEXT && (*selector == GOPHER_BINARY ||
+ *selector == GOPHER_PCBINARY))
+ gtype = *selector;
+ host = StrChr(selector, TAB);
+ if (host) {
+ *host++ = '\0'; /* Terminate selector */
+ port = StrChr(host, TAB);
+ if (port) {
+ char *junk;
+
+ port[0] = ':'; /* delimit host a la W3 */
+ junk = StrChr(port, TAB);
+ if (junk)
+ *junk = '\0'; /* Chop port */
+ if ((port[1] == '0') && (!port[2]))
+ port[0] = '\0'; /* 0 means none */
+ } /* no port */
+ } /* host ok */
+ } /* selector ok */
+ }
+ /* gtype and name ok */
+ /* Nameless files are a separator line */
+ if (name != NULL && gtype == GOPHER_TEXT) {
+ int i = (int) strlen(name) - 1;
+
+ while (name[i] == ' ' && i >= 0)
+ name[i--] = '\0';
+ if (i < 0)
+ gtype = GOPHER_INFO;
+ }
+
+ if (gtype == GOPHER_WWW) { /* Gopher pointer to W3 */
+ PUTS("(HTML) ");
+ write_anchor(name, selector);
+
+ } else if (gtype == GOPHER_INFO) {
+ /* Information or separator line */
+ PUTS(" ");
+ PUTS(name);
+
+ } else if (port && /* Other types need port */
+ (gtype != GOPHER_DUPLICATE ||
+ this_type != GOPHER_ERROR)) {
+ char *address = 0;
+ const char *format = *selector ? "%s//%s@%s/" : "%s//%s/";
+
+ if (gtype == GOPHER_TELNET) {
+ PUTS(" (TEL) ");
+ if (*selector == '/')
+ ++selector;
+ HTSprintf0(&address, format, STR_TELNET_URL, selector, host);
+ } else if (gtype == GOPHER_TN3270) {
+ PUTS("(3270) ");
+ if (*selector == '/')
+ ++selector;
+ HTSprintf0(&address, format, STR_TN3270_URL, selector, host);
+ } else { /* If parsed ok */
+ char *r;
+
+ switch (gtype) {
+ case GOPHER_TEXT:
+ PUTS("(FILE) ");
+ break;
+ case GOPHER_MENU:
+ PUTS(" (DIR) ");
+ break;
+ case GOPHER_DUPLICATE:
+ PUTS(" (+++) ");
+ break;
+ case GOPHER_CSO:
+ PUTS(" (CSO) ");
+ break;
+ case GOPHER_PCBINARY:
+ PUTS(" (BIN) ");
+ break;
+ case GOPHER_UUENCODED:
+ PUTS(" (UUE) ");
+ break;
+ case GOPHER_INDEX:
+ PUTS(" (?) ");
+ break;
+ case GOPHER_BINARY:
+ PUTS(" (BIN) ");
+ break;
+ case GOPHER_GIF:
+ case GOPHER_IMAGE:
+ case GOPHER_PLUS_IMAGE:
+ PUTS(" (IMG) ");
+ break;
+ case GOPHER_SOUND:
+ case GOPHER_PLUS_SOUND:
+ PUTS(" (SND) ");
+ break;
+ case GOPHER_MACBINHEX:
+ PUTS(" (HQX) ");
+ break;
+ case GOPHER_HTML:
+ case GOPHER_CHTML:
+ PUTS("(HTML) ");
+ break;
+ case 'm':
+ PUTS("(MIME) ");
+ break;
+ case GOPHER_PLUS_MOVIE:
+ PUTS(" (MOV) ");
+ break;
+ case GOPHER_PLUS_PDF:
+ PUTS(" (PDF) ");
+ break;
+ default:
+ PUTS("(UNKN) ");
+ break;
+ }
+
+ if (gtype != GOPHER_DUPLICATE)
+ this_type = gtype;
+
+ HTSprintf0(&address, "//%s/%c", host, this_type);
+ if (gtype == GOPHER_HTML) {
+ valid_chars = acceptable_html;
+ if (*selector == '/')
+ ++selector;
+ } else {
+ valid_chars = acceptable_file;
+ }
+
+ for (r = selector; *r; r++) { /* Encode selector string */
+ if (valid_chars[UCH(*r)]) {
+ HTSprintf(&address, "%c", *r);
+ } else {
+ HTSprintf(&address, "%c%c%c",
+ HEX_ESCAPE, /* Means hex coming */
+ hex[(TOASCII(*r)) >> 4],
+ hex[(TOASCII(*r)) & 15]);
+ }
+ }
+ }
+ /* Error response from Gopher doesn't deserve to
+ be a hyperlink. */
+ if (strcmp(address, "gopher://error.host:1/0"))
+ write_anchor(name, address);
+ else
+ PUTS(name);
+ FREE(address);
+ } else { /* parse error */
+ CTRACE((tfp, "HTGopher: Bad menu item (type %d, port %s).\n",
+ gtype, NonNull(port)));
+ PUTS(line);
+
+ } /* parse error */
+
+ PUTC('\n');
+ p = line; /* Start again at beginning of line */
+
+ } /* if end of line */
+
+ } /* Loop over characters */
+
+ end_html:
+ END(HTML_PRE);
+ PUTC('\n');
+ END(HTML_BODY);
+ PUTC('\n');
+ END(HTML_HTML);
+ PUTC('\n');
+ FREE_TARGET;
+
+ return;
+}
+
+/* Parse a Gopher CSO document from an ISINDEX query.
+ * ==================================================
+ *
+ * Accepts an open socket to a CSO server waiting to send us
+ * data and puts it on the screen in a reasonable manner.
+ *
+ * Perhaps this data can be automatically linked to some
+ * other source as well???
+ *
+ * Taken from hacking by Lou Montulli@ukanaix.cc.ukans.edu
+ * on XMosaic-1.1, and put on libwww 2.11 by Arthur Secret,
+ * secret@dxcern.cern.ch .
+ */
+static void parse_cso(const char *arg,
+ HTParentAnchor *anAnchor)
+{
+ int ich;
+ char line[BIG];
+ char *p = line;
+ char *first_colon, *second_colon, last_char = '\0';
+ const char *title;
+
+ START(HTML_HEAD);
+ PUTC('\n');
+ START(HTML_TITLE);
+ if ((title = HTAnchor_title(anAnchor)))
+ PUTS(title);
+ else
+ PUTS(GOPHER_CSO_SEARCH_RESULTS);
+ END(HTML_TITLE);
+ PUTC('\n');
+ END(HTML_HEAD);
+ PUTC('\n');
+ START(HTML_H1);
+ if ((title = HTAnchor_title(anAnchor)))
+ PUTS(title);
+ else {
+ PUTS(arg);
+ PUTS(GOPHER_SEARCH_RESULTS);
+ }
+ END(HTML_H1);
+ PUTC('\n');
+ START(HTML_PRE);
+
+ /*
+ * Start grabbing chars from the network.
+ */
+ while ((ich = NEXT_CHAR) != EOF) {
+ if ((char) ich != LF) {
+ *p = (char) ich; /* Put character in line */
+ if (p < &line[BIG - 1])
+ p++;
+ } else {
+ *p = '\0'; /* Terminate line */
+ p = line; /* Scan it to parse it */
+ /*
+ * OK we now have a line in 'p'. Lets parse it and print it.
+ */
+
+ /*
+ * Break on line that begins with a 2. It's the end of data.
+ */
+ if (*p == '2')
+ break;
+
+ /*
+ * Lines beginning with 5 are errors. Print them and quit.
+ */
+ if (*p == '5') {
+ START(HTML_H2);
+ PUTS(p + 4);
+ END(HTML_H2);
+ break;
+ }
+
+ if (*p == '-') {
+ /*
+ * Data lines look like -200:#:
+ * where # is the search result number and can be multiple
+ * digits (infinite?).
+ * Find the second colon and check the digit to the left of it
+ * to see if they are different. If they are then a different
+ * person is starting. Make this line an <h2>.
+ */
+
+ /*
+ * Find the second_colon.
+ */
+ second_colon = NULL;
+ first_colon = StrChr(p, ':');
+ if (first_colon != NULL) {
+ second_colon = StrChr(first_colon + 1, ':');
+ }
+
+ if (second_colon != NULL) { /* error check */
+
+ if (*(second_colon - 1) != last_char)
+ /* print separator */
+ {
+ END(HTML_PRE);
+ START(HTML_H2);
+ }
+
+ /*
+ * Right now the record appears with the alias (first line)
+ * as the header and the rest as <pre> text.
+ *
+ * It might look better with the name as the header and the
+ * rest as a <ul> with <li> tags. I'm not sure whether the
+ * name field comes in any special order or if its even
+ * required in a record, so for now the first line is the
+ * header no matter what it is (it's almost always the
+ * alias).
+ *
+ * A <dl> with the first line as the <DT> and the rest as
+ * some form of <DD> might good also?
+ */
+
+ /*
+ * Print data.
+ */
+ PUTS(second_colon + 1);
+ PUTC('\n');
+
+ if (*(second_colon - 1) != last_char)
+ /* end separator */
+ {
+ END(HTML_H2);
+ START(HTML_PRE);
+ }
+
+ /*
+ * Save the char before the second colon for comparison on
+ * the next pass.
+ */
+ last_char = *(second_colon - 1);
+
+ } /* end if second_colon */
+ } /* end if *p == '-' */
+ } /* if end of line */
+
+ } /* Loop over characters */
+
+ /* end the text block */
+ PUTC('\n');
+ END(HTML_PRE);
+ PUTC('\n');
+ FREE_TARGET;
+
+ return; /* all done */
+} /* end of procedure */
+
+/* Display a Gopher CSO ISINDEX cover page.
+ * ========================================
+ */
+static void display_cso(const char *arg,
+ HTParentAnchor *anAnchor)
+{
+ const char *title;
+
+ START(HTML_HEAD);
+ PUTC('\n');
+ START(HTML_TITLE);
+ if ((title = HTAnchor_title(anAnchor)))
+ PUTS(title);
+ else
+ PUTS(GOPHER_CSO_INDEX);
+ END(HTML_TITLE);
+ PUTC('\n');
+ START(HTML_ISINDEX);
+ PUTC('\n');
+ END(HTML_HEAD);
+ PUTC('\n');
+ START(HTML_H1);
+ if ((title = HTAnchor_title(anAnchor)))
+ PUTS(title);
+ else {
+ PUTS(arg);
+ PUTS(INDEX_SEGMENT);
+ }
+ END(HTML_H1);
+ PUTS(GOPHER_CSO_INDEX_SUBTITLE);
+ START(HTML_P);
+ PUTS(GOPHER_CSO_SOLICIT_KEYWORDS);
+ START(HTML_P);
+ PUTS(SEGMENT_KEYWORDS_WILL);
+ PUTS(SEGMENT_PERSONS_DB_NAME);
+
+ if (!HTAnchor_title(anAnchor))
+ HTAnchor_setTitle(anAnchor, arg);
+
+ FREE_TARGET;
+ return;
+}
+
+/* Display a Gopher Index document.
+ * ================================
+ */
+static void display_index(const char *arg,
+ HTParentAnchor *anAnchor)
+{
+ const char *title;
+
+ START(HTML_HEAD);
+ PUTC('\n');
+ PUTC('\n');
+ START(HTML_TITLE);
+ if ((title = HTAnchor_title(anAnchor)))
+ PUTS(title);
+ else
+ PUTS(GOPHER_INDEX_TITLE);
+ END(HTML_TITLE);
+ PUTC('\n');
+ START(HTML_ISINDEX);
+ PUTC('\n');
+ END(HTML_HEAD);
+ PUTC('\n');
+ START(HTML_H1);
+ if ((title = HTAnchor_title(anAnchor)))
+ PUTS(title);
+ else {
+ PUTS(arg);
+ PUTS(INDEX_SEGMENT);
+ }
+ END(HTML_H1);
+ PUTS(GOPHER_INDEX_SUBTITLE);
+ START(HTML_P);
+ PUTS(GOPHER_SOLICIT_KEYWORDS);
+
+ if (!HTAnchor_title(anAnchor))
+ HTAnchor_setTitle(anAnchor, arg);
+
+ FREE_TARGET;
+ return;
+}
+
+/* De-escape a selector into a command.
+ * ====================================
+ *
+ * The % hex escapes are converted. Otherwise, the string is copied.
+ */
+static void de_escape(char *command, const char *selector)
+{
+ const char *p = selector;
+ char *q;
+
+ if (command == NULL)
+ outofmem(__FILE__, "HTLoadGopher");
+
+ q = command;
+ while (*p) { /* Decode hex */
+ if (*p == HEX_ESCAPE) {
+ char c;
+ unsigned int b;
+
+ p++;
+ c = *p++;
+ b = UCH(from_hex(c));
+ c = *p++;
+ if (!c)
+ break; /* Odd number of chars! */
+ *q++ = (char) FROMASCII((b << 4) + UCH(from_hex(c)));
+ } else {
+ *q++ = *p++; /* Record */
+ }
+ }
+ *q = '\0'; /* Terminate command */
+}
+
+/* Free the CSOfields structures. - FM
+ * ===================================
+ */
+static void free_CSOfields(void)
+{
+ CSOfield_info *cur = CSOfields;
+ CSOfield_info *prev;
+
+ while (cur) {
+ if (cur->name != cur->name_buf)
+ FREE(cur->name);
+ if (cur->attributes != cur->attr_buf)
+ FREE(cur->attributes);
+ if (cur->description != cur->desc_buf)
+ FREE(cur->description);
+ prev = cur;
+ cur = cur->next;
+ FREE(prev);
+ }
+
+ return;
+}
+
+/* Interpret CSO/PH form template keys. - FM
+ * =========================================
+ */
+static void interpret_cso_key(const char *key,
+ char *buf,
+ size_t bufsize,
+ int *length,
+ CSOformgen_context * ctx,
+ HTStream *Target)
+{
+ CSOfield_info *fld;
+
+ if ((fld = ctx->fld) != 0) {
+ /*
+ * Most substitutions only recognized inside of loops.
+ */
+ int error = 0;
+
+ if (0 == StrNCmp(key, "$(FID)", 6)) {
+ sprintf(buf, "%d", fld->id);
+ } else if (0 == StrNCmp(key, "$(FDESC)", 8)) {
+ sprintf(buf, "%.2046s", fld->description);
+ } else if (0 == StrNCmp(key, "$(FDEF)", 7)) {
+ strcpy(buf, fld->defreturn ? " checked" : "");
+ } else if (0 == StrNCmp(key, "$(FNDX)", 7)) {
+ strcpy(buf, fld->indexed ? "*" : "");
+ } else if (0 == StrNCmp(key, "$(FSIZE)", 8)) {
+ sprintf(buf, " size=%d maxlength=%d",
+ fld->max_size > 55 ? 55 : fld->max_size,
+ fld->max_size);
+ } else if (0 == StrNCmp(key, "$(FSIZE2)", 9)) {
+ sprintf(buf, " maxlength=%d", fld->max_size);
+ } else {
+ error = 1;
+ }
+ if (!error) {
+ *length = (int) strlen(buf);
+ return;
+ }
+ }
+ buf[0] = '\0';
+ if (0 == StrNCmp(key, "$(NEXTFLD)", 10)) {
+ if (!ctx->fld)
+ fld = CSOfields;
+ else
+ fld = ctx->fld->next;
+ switch (ctx->field_select) {
+ case 0:
+ /*
+ * 'Query' fields, public and lookup attributes.
+ */
+ for (; fld; fld = fld->next)
+ if (fld->gpublic && (fld->lookup == 1))
+ break;
+ break;
+ case 1:
+ /*
+ * 'Query' fields, accept lookup attribute.
+ */
+ for (; fld; fld = fld->next)
+ if (fld->lookup == 1)
+ break;
+ break;
+ case 2:
+ /*
+ * 'Return' fields, public only.
+ */
+ for (; fld; fld = fld->next)
+ if (fld->gpublic)
+ break;
+ break;
+ case 3:
+ /*
+ * All fields.
+ */
+ break;
+ }
+ if (fld) {
+ ctx->cur_line = ctx->rep_line;
+ ctx->cur_off = ctx->rep_off;
+ }
+ ctx->fld = fld;
+
+ } else if ((0 == StrNCmp(key, "$(QFIELDS)", 10)) ||
+ (0 == StrNCmp(key, "$(RFIELDS)", 10))) {
+ /*
+ * Begin iteration sequence.
+ */
+ ctx->rep_line = ctx->cur_line;
+ ctx->rep_off = ctx->cur_off;
+ ctx->fld = (CSOfield_info *) 0;
+ ctx->seek = "$(NEXTFLD)";
+ ctx->field_select = (key[2] == 'Q') ? 0 : 2;
+ if (ctx->public_override)
+ ctx->field_select++;
+
+ } else if (0 == StrNCmp(key, "$(NAMEFLD)", 10)) {
+ /*
+ * Special, locate name field. Flag lookup so QFIELDS will skip it.
+ */
+ for (fld = CSOfields; fld; fld = fld->next)
+ if (strcmp(fld->name, "name") == 0 ||
+ strcmp(fld->name, "Name") == 0) {
+ if (fld->lookup)
+ fld->lookup = 2;
+ break;
+ }
+ ctx->fld = fld;
+ } else if (0 == StrNCmp(key, "$(HOST)", 7)) {
+ strcpy(buf, ctx->host);
+ } else if (0 == StrNCmp(key, "$(PORT)", 7)) {
+ sprintf(buf, "%d", ctx->port);
+ } else {
+ /*
+ * No match, dump key to buffer so client sees it for debugging.
+ */
+ size_t out = 0;
+
+ while (*key && (*key != ')')) {
+ buf[out++] = (*key++);
+ if (out > bufsize - 2) {
+ buf[out] = '\0';
+ (*Target->isa->put_block) (Target, buf, (int) strlen(buf));
+ out = 0;
+ }
+ }
+ buf[out++] = ')';
+ buf[out] = '\0';
+ }
+ *length = (int) strlen(buf);
+ return;
+}
+
+/* Parse the elements in a CSO/PH fields structure. - FM
+ * =====================================================
+ */
+static int parse_cso_field_info(CSOfield_info *blk)
+{
+ char *info, *max_spec;
+
+ /*
+ * Initialize all fields to default values.
+ */
+ blk->indexed = blk->lookup = blk->reserved = blk->max_size = blk->url = 0;
+ blk->defreturn = blk->explicit_return = blk->gpublic = 0;
+
+ /*
+ * Search for keywords in info string and set values. Attributes are
+ * converted to all lower-case for comparison.
+ */
+ info = blk->attributes;
+ LYLowerCase(info);
+ if (strstr(info, "indexed "))
+ blk->indexed = 1;
+ if (strstr(info, "default "))
+ blk->defreturn = 1;
+ if (strstr(info, "public "))
+ blk->gpublic = 1;
+ if (strstr(info, "lookup "))
+ blk->lookup = 1;
+ if (strstr(info, "url ")) {
+ blk->url = 1;
+ blk->defreturn = 1;
+ }
+ max_spec = strstr(info, "max ");
+ if (max_spec) {
+ sscanf(&max_spec[4], "%d", &blk->max_size);
+ } else {
+ blk->max_size = 32;
+ }
+
+ return 0;
+}
+
+/* Parse a reply from a CSO/PH fields request. - FM
+ * ================================================
+ */
+static int parse_cso_fields(char *buf,
+ size_t size)
+{
+ int ich;
+ char *p = buf;
+ int i, code = 0, prev_code;
+ size_t alen;
+ char *indx, *name;
+ CSOfield_info *last, *newf;
+
+ last = CSOfields = (CSOfield_info *) 0;
+ prev_code = -2555;
+ buf[0] = '\0';
+
+ /*
+ * Start grabbing chars from the network.
+ */
+ while ((ich = NEXT_CHAR) != EOF) {
+ if (interrupted_in_htgetcharacter) {
+ CTRACE((tfp,
+ "HTLoadCSO: Interrupted in HTGetCharacter, apparently.\n"));
+ free_CSOfields();
+ buf[0] = '\0';
+ return HT_INTERRUPTED;
+ }
+
+ if ((char) ich != LF) {
+ *p = (char) ich; /* Put character in buffer */
+ if (p < &buf[size - 1]) {
+ p++;
+ }
+ } else {
+ *p = '\0'; /* Terminate line */
+ p = buf; /* Scan it to parse it */
+
+ /* OK we now have a line in 'p' lets parse it.
+ */
+
+ /*
+ * Break on line that begins with a 2. It's the end of data.
+ */
+ if (*p == '2')
+ break;
+
+ /*
+ * Lines beginning with 5 are errors. Print them and quit.
+ */
+ if (*p == '5') {
+ return 5;
+ }
+
+ if (*p == '-') {
+ /*
+ * Data lines look like -200:#:
+ * where # is the search result number and can be multiple
+ * digits (infinite?).
+ */
+
+ /*
+ * Check status, ignore any non-success.
+ */
+ if (p[1] != '2')
+ continue;
+
+ /*
+ * Parse fields within returned line into status, ndx, name,
+ * data.
+ */
+ indx = NULL;
+ name = NULL;
+ for (i = 0; p[i]; i++) {
+ if (p[i] == ':') {
+ p[i] = '\0';
+ if (!indx) {
+ indx = (char *) &p[i + 1];
+ code = atoi(indx);
+ } else if (!name) {
+ name = (char *) &p[i + 1];
+ } else {
+ i++;
+ break;
+ }
+ }
+ }
+ /*
+ * Add data to field structure.
+ */
+ if (name) {
+ if (code == prev_code) {
+ /*
+ * Remaining data are description. Save in current
+ * info block.
+ */
+ if (last != NULL) {
+ alen = strlen((char *) &p[i]) + 1;
+ if (alen > sizeof(last->desc_buf)) {
+ if (last->description != last->desc_buf)
+ FREE(last->description);
+ if (!(last->description = (char *) malloc(alen))) {
+ outofmem(__FILE__, "HTLoadCSO");
+ }
+ }
+ strcpy(last->description, (char *) &p[i]);
+ }
+ } else {
+ /*
+ * Initialize new block, append to end of list to
+ * preserve order.
+ */
+ newf = typecalloc(CSOfield_info);
+
+ if (!newf) {
+ outofmem(__FILE__, "HTLoadCSO");
+ }
+
+ if (last)
+ last->next = newf;
+ else
+ CSOfields = newf;
+ last = newf;
+
+ newf->next = (CSOfield_info *) 0;
+ newf->name = newf->name_buf;
+ alen = strlen(name) + 1;
+ if (alen > sizeof(newf->name_buf)) {
+ if (!(newf->name = (char *) malloc(alen))) {
+ outofmem(__FILE__, "HTLoadCSO");
+ }
+ }
+ strcpy(newf->name, name);
+
+ newf->attributes = newf->attr_buf;
+ alen = strlen((char *) &p[i]) + 2;
+ if (alen > sizeof(newf->attr_buf)) {
+ if (!(newf->attributes = (char *) malloc(alen))) {
+ outofmem(__FILE__, "HTLoadCSO");
+ }
+ }
+ strcpy(newf->attributes, (char *) &p[i]);
+ strcpy((char *) &newf->attributes[alen - 2], " ");
+ newf->description = newf->desc_buf;
+ newf->desc_buf[0] = '\0';
+ newf->id = atoi(indx);
+ /*
+ * Scan for keywords.
+ */
+ parse_cso_field_info(newf);
+ }
+ prev_code = code;
+ } else
+ break;
+ } /* end if *p == '-' */
+ } /* if end of line */
+
+ } /* Loop over characters */
+
+ /* end the text block */
+
+ if (buf[0] == '\0') {
+ return -1; /* no response */
+ }
+ buf[0] = '\0';
+ return 0; /* all done */
+} /* end of procedure */
+
+/* Generate a form for submitting CSO/PH searches. - FM
+ * ====================================================
+ */
+static int generate_cso_form(char *host,
+ int port,
+ char *buf,
+ size_t bufsize,
+ HTStream *Target)
+{
+ int i, j, length;
+ size_t out;
+ int full_flag = 1;
+ const char *key;
+ const char *line;
+ CSOformgen_context ctx;
+ static const char *ctemplate[] =
+ {
+ "<HTML>\n<HEAD>\n<TITLE>CSO/PH Query Form for $(HOST)</TITLE>\n</HEAD>\n<BODY>",
+ "<H2><I>CSO/PH Query Form</I> for <EM>$(HOST)</EM></H2>",
+ "To search the database for a name, fill in one or more of the fields",
+ "in the form below and activate the 'Submit query' button. At least",
+ "one of the entered fields must be flagged as indexed.",
+ "<HR><FORM method=\"POST\" action=\"cso://$(HOST)/\">",
+ "[ <input type=\"submit\" value=\"Submit query\"> | ",
+ "<input type=\"reset\" value=\"Clear fields\"> ]",
+ "<P><DL>",
+ " <DT>Search parameters (* indicates indexed field):",
+ " <DD>",
+ "$(NAMEFLD) <DL COMPACT>\n <DT><I>$(FDESC)</I>$(FNDX)",
+ " <DD>Last: <input name=\"q_$(FID)\" type=\"text\" size=49$(FSIZE2)>",
+ " <DD>First: <input name=\"q_$(FID)\" type=\"text\" size=48$(FSIZE2)>",
+ "$(QFIELDS) <DT><I>$(FDESC)</I>$(FNDX)",
+ " <DD><input name=\"q_$(FID)\" type=\"text\" $(FSIZE)>\n$(NEXTFLD)",
+ " </DL>",
+ " </DL>\n<P><DL>",
+ " <DT>Output format:",
+ " <DD>Returned data option: <select name=\"return\">",
+ " <option>default<option selected>all<option>selected</select><BR>",
+ "$(RFIELDS) <input type=\"checkbox\" name=\"r_$(FID)\"$(FDEF)> $(FDESC)<BR>",
+ "$(NEXTFLD) ",
+ " </DL></FORM><HR>\n</BODY>\n</HTML>",
+ (char *) 0
+ };
+
+ memset(&ctx, 0, sizeof(ctx));
+ ctx.host = host;
+ ctx.seek = (char *) 0;
+ ctx.port = port;
+ ctx.fld = (CSOfield_info *) 0;
+ ctx.public_override = full_flag;
+ /*
+ * Parse the strings in the template array to produce HTML document to send
+ * to client. First line is skipped for 'full' lists.
+ */
+ out = 0;
+ buf[out] = '\0';
+ for (i = 0; ctemplate[i]; i++) {
+ /*
+ * Search the current string for substitution, flagged by $(
+ */
+ for (line = ctemplate[i], j = 0; line[j]; j++) {
+ if ((line[j] == '$') && (line[j + 1] == '(')) {
+ /*
+ * Command detected, flush output buffer and find closing ')'
+ * that delimits the command.
+ */
+ buf[out] = '\0';
+ if (out > 0)
+ (*Target->isa->put_block) (Target, buf, (int) strlen(buf));
+ for (key = &line[j]; line[j + 1] && (line[j] != ')'); j++) {
+ ;
+ }
+ /*
+ * Save context, interpret command and restore updated context.
+ */
+ ctx.cur_line = i;
+ ctx.cur_off = j;
+ interpret_cso_key(key, buf, bufsize, &length, &ctx, Target);
+ i = ctx.cur_line;
+ j = ctx.cur_off;
+ line = ctemplate[i];
+ out = (size_t) length;
+
+ if (ctx.seek) {
+ /*
+ * Command wants us to skip (forward) to indicated token.
+ * Start at current position.
+ */
+ size_t slen = strlen(ctx.seek);
+
+ for (; ctemplate[i]; i++) {
+ for (line = ctemplate[i]; line[j]; j++) {
+ if (line[j] == '$')
+ if (0 == StrNCmp(ctx.seek, &line[j], slen)) {
+ if (j == 0)
+ j = (int) strlen(ctemplate[--i]) - 1;
+ else
+ --j;
+ line = ctemplate[i];
+ ctx.seek = (char *) 0;
+ break;
+ }
+ }
+ if (!ctx.seek)
+ break;
+ j = 0;
+ }
+ if (ctx.seek) {
+ char *temp = 0;
+
+ HTSprintf0(&temp, GOPHER_CSO_SEEK_FAILED, ctx.seek);
+ (*Target->isa->put_block) (Target, temp, (int) strlen(temp));
+ FREE(temp);
+ }
+ }
+ } else {
+ /*
+ * Non-command text, add to output buffer.
+ */
+ buf[out++] = line[j];
+ if (out > (bufsize - 3)) {
+ buf[out] = '\0';
+ (*Target->isa->put_block) (Target, buf, (int) strlen(buf));
+ out = 0;
+ }
+ }
+ }
+ buf[out++] = '\n';
+ buf[out] = '\0';
+ }
+ if (out > 0)
+ (*Target->isa->put_block) (Target, buf, (int) strlen(buf));
+
+ return 0;
+}
+
+/* Generate a results report for CSO/PH form-based searches. - FM
+ * ==============================================================
+ */
+static int generate_cso_report(HTStream *Target)
+{
+ int ich;
+ char line[BIG];
+ char *buf = 0;
+ char *p = line, *href = NULL;
+ int i, prev_ndx, ndx;
+ char *rcode, *ndx_str, *fname, *fvalue, *l;
+ CSOfield_info *fld;
+ BOOL stop = FALSE;
+
+ /*
+ * Read lines until non-negative status.
+ */
+ prev_ndx = -100;
+ /*
+ * Start grabbing chars from the network.
+ */
+ while (!stop && (ich = NEXT_CHAR) != EOF) {
+ if (interrupted_in_htgetcharacter) {
+ CTRACE((tfp,
+ "HTLoadCSO: Interrupted in HTGetCharacter, apparently.\n"));
+ _HTProgress(CONNECTION_INTERRUPTED);
+ goto end_CSOreport;
+ }
+
+ if ((char) ich != LF) {
+ *p = (char) ich; /* Put character in line */
+ if (p < &line[BIG - 1]) {
+ p++;
+ }
+ } else {
+ *p = '\0'; /* Terminate line */
+ /*
+ * OK we now have a line. Load it as 'p' and parse it.
+ */
+ p = line;
+ if (p[0] != '-' && p[0] != '1') {
+ stop = TRUE;
+ }
+ rcode = (p[0] == '-') ? &p[1] : p;
+ ndx_str = fname = NULL;
+ for (i = 0; p[i] != '\0'; i++) {
+ if (p[i] == ':') {
+ p[i] = '\0';
+ fname = &p[i + 1];
+ if (ndx_str) {
+ break;
+ }
+ ndx_str = fname;
+ }
+ }
+ if (ndx_str) {
+ ndx = atoi(ndx_str);
+ if (prev_ndx != ndx) {
+ if (prev_ndx != -100) {
+ HTSprintf0(&buf, "</DL></DL>\n");
+ (*Target->isa->put_block) (Target, buf, (int) strlen(buf));
+ }
+ if (ndx == 0) {
+ HTSprintf0(&buf,
+ "<HR><DL><DT>Information/status<DD><DL><DT>\n");
+ (*Target->isa->put_block) (Target, buf, (int) strlen(buf));
+ } else {
+ HTSprintf0(&buf,
+ "<HR><DL><DT>Entry %d:<DD><DL COMPACT><DT>\n", ndx);
+ (*Target->isa->put_block) (Target, buf, (int) strlen(buf));
+ }
+ prev_ndx = ndx;
+ }
+ } else {
+ HTSprintf0(&buf, "<DD>%s\n", rcode);
+ (*Target->isa->put_block) (Target, buf, (int) strlen(buf));
+ continue;
+ }
+ if ((*rcode >= '2') && (*rcode <= '5') && (fname != ndx_str)) {
+ while (*fname == ' ') {
+ fname++; /* trim leading spaces */
+ }
+ for (fvalue = fname; *fvalue; fvalue++) {
+ if (*fvalue == ':') {
+ *fvalue++ = '\0';
+ i = (int) strlen(fname) - 1;
+ while (i >= 0 && fname[i] == ' ') {
+ fname[i--] = '\0'; /* trim trailing */
+ }
+ break;
+ }
+ }
+ if (fvalue) {
+ while (*fvalue == ' ') {
+ fvalue++; /* trim leading spaces */
+ }
+ }
+ if (*fname) {
+ for (fld = CSOfields; fld; fld = fld->next) {
+ if (!strcmp(fld->name, fname)) {
+ if (fld->description) {
+ fname = fld->description;
+ }
+ break;
+ }
+ }
+ if (fld && fld->url) {
+ HTSprintf0(&buf,
+ "<DT><I>%s</I><DD><A HREF=\"%s\">%s</A>\n",
+ fname, fvalue, fvalue);
+ (*Target->isa->put_block) (Target, buf, (int) strlen(buf));
+ } else {
+ HTSprintf0(&buf, "<DT><I>%s</I><DD>", fname);
+ (*Target->isa->put_block) (Target, buf, (int) strlen(buf));
+ buf[0] = '\0';
+ l = fvalue;
+ while (*l) {
+ if (*l == '<') {
+ StrAllocCat(buf, "&lt;");
+ l++;
+ } else if (*l == '>') {
+ StrAllocCat(buf, "&gt;");
+ l++;
+ } else if (StrNCmp(l, STR_NEWS_URL, LEN_NEWS_URL) &&
+ StrNCmp(l, "snews://", 8) &&
+ StrNCmp(l, "nntp://", 7) &&
+ StrNCmp(l, "snewspost:", 10) &&
+ StrNCmp(l, "snewsreply:", 11) &&
+ StrNCmp(l, "newspost:", 9) &&
+ StrNCmp(l, "newsreply:", 10) &&
+ StrNCmp(l, "ftp://", 6) &&
+ StrNCmp(l, "file:/", 6) &&
+ StrNCmp(l, "finger://", 9) &&
+ StrNCmp(l, "http://", 7) &&
+ StrNCmp(l, "https://", 8) &&
+ StrNCmp(l, "wais://", 7) &&
+ StrNCmp(l, STR_MAILTO_URL,
+ LEN_MAILTO_URL) &&
+ StrNCmp(l, "cso://", 6) &&
+ StrNCmp(l, "gopher://", 9)) {
+ HTSprintf(&buf, "%c", *l++);
+ } else {
+ StrAllocCat(buf, "<a href=\"");
+ StrAllocCopy(href, l);
+ StrAllocCat(buf, strtok(href, " \r\n\t,>)\""));
+ StrAllocCat(buf, "\">");
+ while (*l && !StrChr(" \r\n\t,>)\"", *l)) {
+ HTSprintf(&buf, "%c", *l++);
+ }
+ StrAllocCat(buf, "</a>");
+ FREE(href);
+ }
+ }
+ StrAllocCat(buf, "\n");
+ (*Target->isa->put_block) (Target, buf, (int) strlen(buf));
+ }
+ } else {
+ HTSprintf0(&buf, "<DD>");
+ (*Target->isa->put_block) (Target, buf, (int) strlen(buf));
+ buf[0] = '\0';
+ l = fvalue;
+ while (*l) {
+ if (*l == '<') {
+ StrAllocCat(buf, "&lt;");
+ l++;
+ } else if (*l == '>') {
+ StrAllocCat(buf, "&gt;");
+ l++;
+ } else if (StrNCmp(l, STR_NEWS_URL, LEN_NEWS_URL) &&
+ StrNCmp(l, "snews://", 8) &&
+ StrNCmp(l, "nntp://", 7) &&
+ StrNCmp(l, "snewspost:", 10) &&
+ StrNCmp(l, "snewsreply:", 11) &&
+ StrNCmp(l, "newspost:", 9) &&
+ StrNCmp(l, "newsreply:", 10) &&
+ StrNCmp(l, "ftp://", 6) &&
+ StrNCmp(l, "file:/", 6) &&
+ StrNCmp(l, "finger://", 9) &&
+ StrNCmp(l, "http://", 7) &&
+ StrNCmp(l, "https://", 8) &&
+ StrNCmp(l, "wais://", 7) &&
+ StrNCmp(l, STR_MAILTO_URL, LEN_MAILTO_URL) &&
+ StrNCmp(l, "cso://", 6) &&
+ StrNCmp(l, "gopher://", 9)) {
+ HTSprintf(&buf, "%c", *l++);
+ } else {
+ StrAllocCat(buf, "<a href=\"");
+ StrAllocCopy(href, l);
+ StrAllocCat(buf, strtok(href, " \r\n\t,>)\""));
+ StrAllocCat(buf, "\">");
+ while (*l && !StrChr(" \r\n\t,>)\"", *l)) {
+ HTSprintf(&buf, "%c", *l++);
+ }
+ StrAllocCat(buf, "</a>");
+ FREE(href);
+ }
+ }
+ StrAllocCat(buf, "\n");
+ (*Target->isa->put_block) (Target, buf, (int) strlen(buf));
+ }
+ } else {
+ HTSprintf0(&buf, "<DD>%s\n", fname);
+ (*Target->isa->put_block) (Target, buf, (int) strlen(buf));
+ }
+ }
+ }
+ end_CSOreport:
+ if (prev_ndx != -100) {
+ HTSprintf0(&buf, "</DL></DL>\n");
+ (*Target->isa->put_block) (Target, buf, (int) strlen(buf));
+ }
+ FREE(buf);
+ return 0;
+}
+
+/* CSO/PH form-based search gateway - FM HTLoadCSO
+ * =====================================
+ */
+static int HTLoadCSO(const char *arg,
+ HTParentAnchor *anAnchor,
+ HTFormat format_out,
+ HTStream *sink)
+{
+ static const char end_form[] = "</BODY>\n</HTML>\n";
+ char *host, *cp, *data;
+ int port = CSO_PORT;
+ int status; /* tcp return */
+ bstring *command = NULL;
+ bstring *content = NULL;
+ int len, i, j, start, finish, flen, ndx;
+ int return_type, has_indexed;
+ CSOfield_info *fld;
+ char buf[2048];
+ HTFormat format_in = WWW_HTML;
+ HTStream *Target = NULL;
+
+ if (!acceptable_inited)
+ init_acceptable();
+
+ if (!arg)
+ return -3; /* Bad if no name specified */
+ if (!*arg)
+ return -2; /* Bad if name had zero length */
+ CTRACE((tfp, "HTLoadCSO: Looking for %s\n", arg));
+
+ /*
+ * Set up a socket to the server for the data.
+ */
+ status = HTDoConnect(arg, "cso", CSO_PORT, &s);
+ if (status == HT_INTERRUPTED) {
+ /*
+ * Interrupt cleanly.
+ */
+ CTRACE((tfp,
+ "HTLoadCSO: Interrupted on connect; recovering cleanly.\n"));
+ _HTProgress(CONNECTION_INTERRUPTED);
+ return HT_NOT_LOADED;
+ }
+ if (status < 0) {
+ CTRACE((tfp, "HTLoadCSO: Unable to connect to remote host for `%s'.\n",
+ arg));
+ return HTInetStatus("connect");
+ }
+
+ HTInitInput(s); /* Set up input buffering */
+
+ HTBprintf(&command, "fields%c%c", CR, LF);
+ if (TRACE) {
+ CTRACE((tfp, "HTLoadCSO: Connected, writing command `"));
+ trace_bstring(command);
+ CTRACE((tfp, "' to socket %d\n", s));
+ }
+ _HTProgress(GOPHER_SENDING_CSO_REQUEST);
+ status = (int) NETWRITE(s, BStrData(command), BStrLen(command));
+ BStrFree(command);
+ if (status < 0) {
+ CTRACE((tfp, "HTLoadCSO: Unable to send command.\n"));
+ return HTInetStatus("send");
+ }
+ _HTProgress(GOPHER_SENT_CSO_REQUEST);
+
+ /*
+ * Now read the data from the socket.
+ */
+ status = parse_cso_fields(buf, sizeof(buf));
+ if (status) {
+ NETCLOSE(s);
+ if (status == HT_INTERRUPTED) {
+ _HTProgress(CONNECTION_INTERRUPTED);
+ } else if (buf[0] != '\0') {
+ HTAlert(buf);
+ } else {
+ HTAlert(FAILED_NO_RESPONSE);
+ }
+ return HT_NOT_LOADED;
+ }
+ Target = HTStreamStack(format_in,
+ format_out,
+ sink, anAnchor);
+ if (Target == NULL) {
+ char *temp = 0;
+
+ HTSprintf0(&temp, CANNOT_CONVERT_I_TO_O,
+ HTAtom_name(format_in), HTAtom_name(format_out));
+ HTAlert(temp);
+ FREE(temp);
+ NETCLOSE(s);
+ return HT_NOT_LOADED;
+ }
+ host = HTParse(arg, "", PARSE_HOST);
+ if ((cp = HTParsePort(host, &port)) != NULL) {
+ if (port == CSO_PORT) {
+ *cp = '\0';
+ }
+ }
+ anAnchor->safe = TRUE;
+ if (isBEmpty(anAnchor->post_data)) {
+ generate_cso_form(host, port, buf, sizeof(buf), Target);
+ (*Target->isa->_free) (Target);
+ FREE(host);
+ NETCLOSE(s);
+ free_CSOfields();
+ return HT_LOADED;
+ }
+
+ HTBprintf(&command,
+ "<HTML>\n<HEAD>\n<TITLE>CSO/PH Results on %s</TITLE>\n</HEAD>\n<BODY>\n",
+ host);
+ (*Target->isa->put_block) (Target, BStrData(command), BStrLen(command));
+ BStrFree(command);
+ FREE(host);
+
+ BStrCopy(content, anAnchor->post_data);
+ assert(content != NULL);
+
+ if (BStrData(content)[BStrLen(content) - 1] != '&')
+ BStrCat0(content, "&");
+
+ data = BStrData(content);
+ len = BStrLen(content);
+ for (i = 0; i < len; i++) {
+ if (data[i] == '+') {
+ data[i] = ' ';
+ }
+ }
+
+ data = BStrData(content);
+ HTUnEscape(data); /* FIXME: could it have embedded null? */
+ len = BStrLen(content);
+
+ return_type = 0;
+ has_indexed = 0;
+ start = 0;
+ for (i = 0; i < len; i++) {
+ if (!data[i] || data[i] == '&') {
+ /*
+ * Value parsed. Unescape characters and look for first '=' to
+ * delimit field name from value.
+ */
+ flen = i - start;
+ finish = start + flen;
+ data[finish] = '\0';
+ for (j = start; j < finish; j++) {
+ if (data[j] == '=') {
+ /*
+ * data[start..j-1] is field name,
+ * [j+1..finish-1] is value.
+ */
+ if ((data[start + 1] == '_') &&
+ ((data[start] == 'r') || (data[start] == 'q'))) {
+ /*
+ * Decode fields number and lookup field info.
+ */
+ sscanf(&data[start + 2], "%d=", &ndx);
+ for (fld = CSOfields; fld; fld = fld->next) {
+ if (ndx == fld->id) {
+ if ((j + 1) >= finish)
+ break; /* ignore nulls */
+ if (data[start] == 'q') {
+ /*
+ * Append field to query line.
+ */
+ if (fld->lookup) {
+ if (fld->indexed)
+ has_indexed = 1;
+ if (isBEmpty(command)) {
+ BStrCopy0(command, "query ");
+ } else {
+ BStrCat0(command, " ");
+ }
+ HTBprintf(&command, "%s=\"%s\"",
+ fld->name, &data[j + 1]);
+ } else {
+ strcpy(buf,
+ "Warning: non-lookup field ignored<BR>\n");
+ (*Target->isa->put_block) (Target,
+ buf,
+ (int)
+ strlen(buf));
+ }
+ } else if (data[start] == 'r') {
+ fld->explicit_return = 1;
+ }
+ break;
+ }
+ }
+ } else if (!StrNCmp(&data[start], "return=", 7)) {
+ if (!strcmp(&data[start + 7], "all")) {
+ return_type = 1;
+ } else if (!strcmp(&data[start + 7], "selected")) {
+ return_type = 2;
+ }
+ }
+ }
+ }
+ start = i + 1;
+ }
+ }
+ BStrFree(content);
+ if (isBEmpty(command) || !has_indexed) {
+ NETCLOSE(s);
+ strcpy(buf,
+ "<EM>Error:</EM> At least one indexed field value must be specified!\n");
+ (*Target->isa->put_block) (Target, buf, (int) strlen(buf));
+ strcpy(buf, "</BODY>\n</HTML>\n");
+ (*Target->isa->put_block) (Target, buf, (int) strlen(buf));
+ (*Target->isa->_free) (Target);
+ free_CSOfields();
+ BStrFree(command);
+ return HT_LOADED;
+ }
+ /*
+ * Append return fields.
+ */
+ if (return_type == 1) {
+ BStrCat0(command, " return all");
+ } else if (return_type == 2) {
+ BStrCat0(command, " return");
+ for (fld = CSOfields; fld; fld = fld->next) {
+ if (fld->explicit_return) {
+ HTBprintf(&command, " %s", fld->name);
+ }
+ }
+ }
+ HTBprintf(&command, "%c%c", CR, LF);
+ strcpy(buf, "<H2>\n<EM>CSO/PH command:</EM> ");
+ (*Target->isa->put_block) (Target, buf, (int) strlen(buf));
+ (*Target->isa->put_block) (Target, BStrData(command), BStrLen(command));
+ strcpy(buf, "</H2>\n");
+ (*Target->isa->put_block) (Target, buf, (int) strlen(buf));
+ if (TRACE) {
+ CTRACE((tfp, "HTLoadCSO: Writing command `"));
+ trace_bstring(command);
+ CTRACE((tfp, "' to socket %d\n", s));
+ }
+ status = (int) NETWRITE(s, BStrData(command), BStrLen(command));
+ BStrFree(command);
+ if (status < 0) {
+ CTRACE((tfp, "HTLoadCSO: Unable to send command.\n"));
+ free_CSOfields();
+ return HTInetStatus("send");
+ }
+ generate_cso_report(Target);
+ NETCLOSE(s);
+ (*Target->isa->put_block) (Target, end_form, (int) sizeof(end_form) - 1);
+ (*Target->isa->_free) (Target);
+ FREE(host);
+ free_CSOfields();
+ return HT_LOADED;
+}
+
+static char *link_to_URL(const char *arg)
+{
+ char *result;
+ char *next;
+ char *temp = 0;
+
+ StrAllocCopy(temp, arg);
+ HTUnEscape(temp);
+ result = temp;
+
+ /* skip past method://host */
+ if ((next = strstr(result, "://")) != 0) {
+ result = next + 3;
+ }
+ if ((next = strchr(result, '/')) != 0) {
+ result = next + 1;
+ }
+ /* check if the selector is the special html one */
+ if (!strncmp(result, "hURL:", (size_t) 5)) {
+ result += 5;
+ next = result;
+ result = temp;
+ while ((*temp++ = *next++) != 0) ;
+ } else {
+ FREE(temp);
+ result = 0;
+ }
+ return result;
+}
+
+/* Load by name. HTLoadGopher
+ * =============
+ *
+ */
+static int HTLoadGopher(const char *arg,
+ HTParentAnchor *anAnchor,
+ HTFormat format_out,
+ HTStream *sink)
+{
+ char *hURL;
+ char *command; /* The whole command */
+ int status; /* tcp return */
+ char gtype; /* Gopher Node type */
+ char *selector; /* Selector string */
+
+ if (!acceptable_inited)
+ init_acceptable();
+
+ if (!arg)
+ return -3; /* Bad if no name specified */
+ if (!*arg)
+ return -2; /* Bad if name had zero length */
+ CTRACE((tfp, "HTGopher: Looking for %s\n", arg));
+
+ /*
+ * If it's a port 105 GOPHER_CSO gtype with no ISINDEX token ('?'), use the
+ * form-based CSO gateway (otherwise, return an ISINDEX cover page or do
+ * the ISINDEX search). - FM
+ */
+ {
+ size_t len;
+
+ if ((len = strlen(arg)) > 5) {
+ if (0 == strcmp((const char *) &arg[len - 6], ":105/2")) {
+ /* Use CSO gateway. */
+ CTRACE((tfp, "HTGopher: Passing to CSO/PH gateway.\n"));
+ return HTLoadCSO(arg, anAnchor, format_out, sink);
+ }
+ }
+ }
+
+ /*
+ * If it's a port 79/0[/...] URL, use the finger gateway. - FM
+ */
+ if (strstr(arg, ":79/0") != NULL) {
+#ifndef DISABLE_FINGER
+ CTRACE((tfp, "HTGopher: Passing to finger gateway.\n"));
+ return HTLoadFinger(arg, anAnchor, format_out, sink);
+#else /* finger is disabled */
+ HTAlert(COULD_NOT_ACCESS_DOCUMENT);
+ return HT_NOT_LOADED;
+#endif /* DISABLE_FINGER */
+ }
+
+ /*
+ * Get entity type, and selector string.
+ */
+ {
+ char *p1 = HTParse(arg, "", PARSE_PATH | PARSE_PUNCTUATION);
+
+ gtype = '1'; /* Default = menu */
+ selector = p1;
+ if ((*selector++ == '/') && (*selector)) { /* Skip first slash */
+ gtype = *selector++; /* Pick up gtype */
+ }
+ if (gtype == GOPHER_INDEX) {
+ char *query;
+
+ /*
+ * Search is allowed.
+ */
+ HTAnchor_setIndex(anAnchor, anAnchor->address);
+ query = StrChr(selector, '?'); /* Look for search string */
+ if (!query || !query[1]) { /* No search required */
+ target = HTML_new(anAnchor, format_out, sink);
+ targetClass = *target->isa;
+ display_index(arg, anAnchor); /* Display "cover page" */
+ return HT_LOADED; /* Local function only */
+ }
+ *query++ = '\0'; /* Skip '?' */
+ command =
+ (char *) malloc(strlen(selector) + 1 + strlen(query) + 2 + 1);
+ if (command == NULL)
+ outofmem(__FILE__, "HTLoadGopher");
+
+ de_escape(command, selector); /* Bug fix TBL 921208 */
+
+ strcat(command, "\t");
+
+ { /* Remove plus signs 921006 */
+ char *p;
+
+ for (p = query; *p; p++) {
+ if (*p == '+')
+ *p = ' ';
+ }
+ }
+
+ de_escape(&command[strlen(command)], query); /* bug fix LJM 940415 */
+ } else if (gtype == GOPHER_CSO) {
+ char *query;
+
+ /*
+ * Search is allowed.
+ */
+ query = StrChr(selector, '?'); /* Look for search string */
+ if (!query || !query[1]) { /* No search required */
+ target = HTML_new(anAnchor, format_out, sink);
+ targetClass = *target->isa;
+ display_cso(arg, anAnchor); /* Display "cover page" */
+ return HT_LOADED; /* Local function only */
+ }
+ HTAnchor_setIndex(anAnchor, anAnchor->address);
+ *query++ = '\0'; /* Skip '?' */
+ command = (char *) malloc(strlen("query") + 1 +
+ strlen(query) + 2 + 1);
+ if (command == NULL)
+ outofmem(__FILE__, "HTLoadGopher");
+
+ de_escape(command, selector); /* Bug fix TBL 921208 */
+
+ strcpy(command, "query ");
+
+ { /* Remove plus signs 921006 */
+ char *p;
+
+ for (p = query; *p; p++) {
+ if (*p == '+')
+ *p = ' ';
+ }
+ }
+ de_escape(&command[strlen(command)], query); /* bug fix LJM 940415 */
+
+ } else { /* Not index */
+ command = (char *) malloc(strlen(selector) + 2 + 1);
+ if (command == NULL)
+ outofmem(__FILE__, "HTLoadGopher");
+
+ de_escape(command, selector);
+ }
+ FREE(p1);
+ }
+
+ {
+ char *p = command + strlen(command);
+
+ *p++ = CR; /* Macros to be correct on Mac */
+ *p++ = LF;
+ *p = '\0';
+ }
+ /*
+ * Check for link to URL
+ */
+ if ((hURL = link_to_URL(arg)) != 0) {
+ CTRACE((tfp, "gopher found link to URL '%s'\n", hURL));
+ free(hURL);
+ }
+
+ /*
+ * Set up a socket to the server for the data.
+ */
+ status = HTDoConnect(arg, "gopher", GOPHER_PORT, &s);
+ if (status == HT_INTERRUPTED) {
+ /*
+ * Interrupt cleanly.
+ */
+ CTRACE((tfp, "HTGopher: Interrupted on connect; recovering cleanly.\n"));
+ _HTProgress(CONNECTION_INTERRUPTED);
+ FREE(command);
+ return HT_NOT_LOADED;
+ }
+ if (status < 0) {
+ CTRACE((tfp, "HTGopher: Unable to connect to remote host for `%s'.\n",
+ arg));
+ FREE(command);
+ return HTInetStatus("connect");
+ }
+
+ HTInitInput(s); /* Set up input buffering */
+
+ CTRACE((tfp, "HTGopher: Connected, writing command `%s' to socket %d\n",
+ command, s));
+
+#ifdef NOT_ASCII
+ {
+ char *p;
+
+ for (p = command; *p; p++) {
+ *p = TOASCII(*p);
+ }
+ }
+#endif
+
+ _HTProgress(GOPHER_SENDING_REQUEST);
+
+ status = (int) NETWRITE(s, command, (int) strlen(command));
+ FREE(command);
+ if (status < 0) {
+ CTRACE((tfp, "HTGopher: Unable to send command.\n"));
+ return HTInetStatus("send");
+ }
+
+ _HTProgress(GOPHER_SENT_REQUEST);
+
+ /*
+ * Now read the data from the socket.
+ */
+ switch (gtype) {
+
+ case GOPHER_TEXT:
+ HTParseSocket(WWW_PLAINTEXT, format_out, anAnchor, s, sink);
+ break;
+
+ case GOPHER_HTML:
+ case GOPHER_CHTML:
+ HTParseSocket(WWW_HTML, format_out, anAnchor, s, sink);
+ break;
+
+ case GOPHER_GIF:
+ case GOPHER_IMAGE:
+ case GOPHER_PLUS_IMAGE:
+ HTParseSocket(HTAtom_for("image/gif"),
+ format_out, anAnchor, s, sink);
+ break;
+
+ case GOPHER_MENU:
+ case GOPHER_INDEX:
+ target = HTML_new(anAnchor, format_out, sink);
+ targetClass = *target->isa;
+ parse_menu(arg, anAnchor);
+ break;
+
+ case GOPHER_CSO:
+ target = HTML_new(anAnchor, format_out, sink);
+ targetClass = *target->isa;
+ parse_cso(arg, anAnchor);
+ break;
+
+ case GOPHER_SOUND:
+ case GOPHER_PLUS_SOUND:
+ HTParseSocket(WWW_AUDIO, format_out, anAnchor, s, sink);
+ break;
+
+ case GOPHER_PLUS_MOVIE:
+ HTParseSocket(HTAtom_for("video/mpeg"), format_out, anAnchor, s, sink);
+ break;
+
+ case GOPHER_PLUS_PDF:
+ HTParseSocket(HTAtom_for("application/pdf"), format_out, anAnchor,
+ s, sink);
+ break;
+
+ default:
+ {
+ HTAtom *encoding = 0;
+ const char *desc = 0;
+ HTFormat format = HTFileFormat(arg, &encoding, &desc);
+
+ /*
+ * Ignore WWW_BINARY (since that is returned by HTFileFormat when
+ * it does not have a representation), but otherwise use the
+ * result.
+ */
+ if (format != WWW_BINARY) {
+ HTParseSocket(format, format_out, anAnchor, s, sink);
+ break;
+ }
+ }
+ /* FALL-THRU */
+
+ case GOPHER_MACBINHEX:
+ case GOPHER_PCBINARY:
+ case GOPHER_UUENCODED:
+ case GOPHER_BINARY:
+ /*
+ * Specifying WWW_UNKNOWN forces dump to local disk.
+ */
+ HTParseSocket(WWW_UNKNOWN, format_out, anAnchor, s, sink);
+ break;
+
+ } /* switch(gtype) */
+
+ NETCLOSE(s);
+ return HT_LOADED;
+}
+
+#ifdef GLOBALDEF_IS_MACRO
+#define _HTGOPHER_C_1_INIT { "gopher", HTLoadGopher, NULL }
+GLOBALDEF(HTProtocol, HTGopher, _HTGOPHER_C_1_INIT);
+#define _HTCSO_C_1_INIT { "cso", HTLoadCSO, NULL }
+GLOBALDEF(HTProtocol, HTCSO, _HTCSO_C_1_INIT);
+#else
+GLOBALDEF HTProtocol HTGopher =
+{"gopher", HTLoadGopher, NULL};
+GLOBALDEF HTProtocol HTCSO =
+{"cso", HTLoadCSO, NULL};
+#endif /* GLOBALDEF_IS_MACRO */
+
+#endif /* not DISABLE_GOPHER */
diff --git a/WWW/Library/Implementation/HTGopher.h b/WWW/Library/Implementation/HTGopher.h
new file mode 100644
index 0000000..667fc24
--- /dev/null
+++ b/WWW/Library/Implementation/HTGopher.h
@@ -0,0 +1,29 @@
+/* Gopher protocol module for libwww
+ GOPHER ACCESS
+
+ HISTORY:
+
+ 8 Jan 92 Adapted from HTTP TBL
+
+ */
+
+#ifndef HTGOPHER_H
+#define HTGOPHER_H
+
+#include <HTAccess.h>
+#include <HTAnchor.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+#ifdef GLOBALREF_IS_MACRO
+ extern GLOBALREF (HTProtocol, HTGopher);
+
+#else
+ GLOBALREF HTProtocol HTGopher;
+#endif /* GLOBALREF_IS_MACRO */
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* HTGOPHER_H */
diff --git a/WWW/Library/Implementation/HTGroup.c b/WWW/Library/Implementation/HTGroup.c
new file mode 100644
index 0000000..42d55dc
--- /dev/null
+++ b/WWW/Library/Implementation/HTGroup.c
@@ -0,0 +1,758 @@
+/* MODULE HTGroup.c
+ * GROUP FILE ROUTINES
+ *
+ * Contains group file parser and routines to match IP
+ * address templates and to find out group membership.
+ *
+ *
+ * AUTHORS:
+ * AL Ari Luotonen luotonen@dxcern.cern.ch
+ *
+ * HISTORY:
+ *
+ *
+ * BUGS:
+ *
+ *
+ *
+ * GROUP DEFINITION GRAMMAR:
+ *
+ * string = "sequence of alphanumeric characters"
+ * user_name ::= string
+ * group_name ::= string
+ * group_ref ::= group_name
+ * user_def ::= user_name | group_ref
+ * user_def_list ::= user_def { ',' user_def }
+ * user_part = user_def | '(' user_def_list ')'
+ *
+ * templ = "sequence of alphanumeric characters and '*'s"
+ * ip_number_mask ::= templ '.' templ '.' templ '.' templ
+ * domain_name_mask ::= templ { '.' templ }
+ * address ::= ip_number_mask | domain_name_mask
+ * address_def ::= address
+ * address_def_list ::= address_def { ',' address_def }
+ * address_part = address_def | '(' address_def_list ')'
+ *
+ * item ::= [user_part] ['@' address_part]
+ * item_list ::= item { ',' item }
+ * group_def ::= item_list
+ * group_decl ::= group_name ':' group_def
+ *
+ */
+
+#include <HTUtils.h>
+
+#include <HTAAUtil.h>
+#include <HTLex.h> /* Lexical analysor */
+#include <HTGroup.h> /* Implemented here */
+
+#include <LYUtils.h>
+#include <LYLeaks.h>
+
+/*
+ * Group file parser
+ */
+
+typedef HTList UserDefList;
+typedef HTList AddressDefList;
+
+typedef struct {
+ UserDefList *user_def_list;
+ AddressDefList *address_def_list;
+} Item;
+
+typedef struct {
+ char *name;
+ GroupDef *translation;
+} Ref;
+
+static void syntax_error(FILE *fp, const char *msg,
+ LexItem lex_item)
+{
+ char buffer[41];
+ int cnt = 0;
+ int ch;
+
+ while ((ch = getc(fp)) != EOF && ch != '\n')
+ if (cnt < 40)
+ buffer[cnt++] = (char) ch;
+ buffer[cnt] = (char) 0;
+
+ CTRACE((tfp, "%s %d before: '%s'\nHTGroup.c: %s (got %s)\n",
+ "HTGroup.c: Syntax error in rule file at line",
+ HTlex_line, buffer, msg, lex_verbose(lex_item)));
+ HTlex_line++;
+}
+
+static AddressDefList *parse_address_part(FILE *fp)
+{
+ AddressDefList *address_def_list = NULL;
+ LexItem lex_item;
+ BOOL only_one = NO;
+
+ lex_item = lex(fp);
+ if (lex_item == LEX_ALPH_STR || lex_item == LEX_TMPL_STR)
+ only_one = YES;
+ else if (lex_item != LEX_OPEN_PAREN ||
+ ((lex_item = lex(fp)) != LEX_ALPH_STR &&
+ lex_item != LEX_TMPL_STR)) {
+ syntax_error(fp, "Expecting a single address or '(' beginning list",
+ lex_item);
+ return NULL;
+ }
+ address_def_list = HTList_new();
+
+ for (;;) {
+ Ref *ref = typecalloc(Ref);
+
+ if (ref == NULL)
+ outofmem(__FILE__, "parse_address_part");
+
+ ref->name = NULL;
+ ref->translation = NULL;
+ StrAllocCopy(ref->name, HTlex_buffer);
+
+ HTList_addObject(address_def_list, (void *) ref);
+
+ if (only_one || (lex_item = lex(fp)) != LEX_ITEM_SEP)
+ break;
+ /*
+ * Here lex_item == LEX_ITEM_SEP; after item separator it
+ * is ok to have one or more newlines (LEX_REC_SEP) and
+ * they are ignored (continuation line).
+ */
+ do {
+ lex_item = lex(fp);
+ } while (lex_item == LEX_REC_SEP);
+
+ if (lex_item != LEX_ALPH_STR && lex_item != LEX_TMPL_STR) {
+ syntax_error(fp, "Expecting an address template", lex_item);
+ HTList_delete(address_def_list);
+ address_def_list = NULL;
+ return NULL;
+ }
+ }
+
+ if (!only_one && lex_item != LEX_CLOSE_PAREN) {
+ HTList_delete(address_def_list);
+ address_def_list = NULL;
+ syntax_error(fp, "Expecting ')' closing address list", lex_item);
+ return NULL;
+ }
+ return address_def_list;
+}
+
+static UserDefList *parse_user_part(FILE *fp)
+{
+ UserDefList *user_def_list = NULL;
+ LexItem lex_item;
+ BOOL only_one = NO;
+
+ lex_item = lex(fp);
+ if (lex_item == LEX_ALPH_STR)
+ only_one = YES;
+ else if (lex_item != LEX_OPEN_PAREN ||
+ (lex_item = lex(fp)) != LEX_ALPH_STR) {
+ syntax_error(fp, "Expecting a single name or '(' beginning list",
+ lex_item);
+ return NULL;
+ }
+ user_def_list = HTList_new();
+
+ for (;;) {
+ Ref *ref = typecalloc(Ref);
+
+ if (ref == NULL)
+ outofmem(__FILE__, "parse_user_part");
+
+ ref->name = NULL;
+ ref->translation = NULL;
+ StrAllocCopy(ref->name, HTlex_buffer);
+
+ HTList_addObject(user_def_list, (void *) ref);
+
+ if (only_one || (lex_item = lex(fp)) != LEX_ITEM_SEP)
+ break;
+ /*
+ * Here lex_item == LEX_ITEM_SEP; after item separator it
+ * is ok to have one or more newlines (LEX_REC_SEP) and
+ * they are ignored (continuation line).
+ */
+ do {
+ lex_item = lex(fp);
+ } while (lex_item == LEX_REC_SEP);
+
+ if (lex_item != LEX_ALPH_STR) {
+ syntax_error(fp, "Expecting user or group name", lex_item);
+ HTList_delete(user_def_list);
+ user_def_list = NULL;
+ return NULL;
+ }
+ }
+
+ if (!only_one && lex_item != LEX_CLOSE_PAREN) {
+ HTList_delete(user_def_list);
+ user_def_list = NULL;
+ syntax_error(fp, "Expecting ')' closing user/group list", lex_item);
+ return NULL;
+ }
+ return user_def_list;
+}
+
+static Item *parse_item(FILE *fp)
+{
+ Item *item = NULL;
+ UserDefList *user_def_list = NULL;
+ AddressDefList *address_def_list = NULL;
+ LexItem lex_item;
+
+ lex_item = lex(fp);
+ if (lex_item == LEX_ALPH_STR || lex_item == LEX_OPEN_PAREN) {
+ unlex(lex_item);
+ user_def_list = parse_user_part(fp);
+ lex_item = lex(fp);
+ }
+
+ if (lex_item == LEX_AT_SIGN) {
+ lex_item = lex(fp);
+ if (lex_item == LEX_ALPH_STR || lex_item == LEX_TMPL_STR ||
+ lex_item == LEX_OPEN_PAREN) {
+ unlex(lex_item);
+ address_def_list = parse_address_part(fp);
+ } else {
+ if (user_def_list) {
+ HTList_delete(user_def_list); /* @@@@ */
+ user_def_list = NULL;
+ }
+ syntax_error(fp, "Expected address part (single address or list)",
+ lex_item);
+ return NULL;
+ }
+ } else
+ unlex(lex_item);
+
+ if (!user_def_list && !address_def_list) {
+ syntax_error(fp, "Empty item not allowed", lex_item);
+ return NULL;
+ }
+ item = typecalloc(Item);
+ if (item == NULL)
+ outofmem(__FILE__, "parse_item");
+
+ item->user_def_list = user_def_list;
+ item->address_def_list = address_def_list;
+ return item;
+}
+
+static ItemList *parse_item_list(FILE *fp)
+{
+ ItemList *item_list = HTList_new();
+ Item *item;
+ LexItem lex_item;
+
+ for (;;) {
+ if (!(item = parse_item(fp))) {
+ HTList_delete(item_list); /* @@@@ */
+ item_list = NULL;
+ return NULL;
+ }
+ HTList_addObject(item_list, (void *) item);
+ lex_item = lex(fp);
+ if (lex_item != LEX_ITEM_SEP) {
+ unlex(lex_item);
+ return item_list;
+ }
+ /*
+ * Here lex_item == LEX_ITEM_SEP; after item separator it
+ * is ok to have one or more newlines (LEX_REC_SEP) and
+ * they are ignored (continuation line).
+ */
+ do {
+ lex_item = lex(fp);
+ } while (lex_item == LEX_REC_SEP);
+ unlex(lex_item);
+ }
+}
+
+GroupDef *HTAA_parseGroupDef(FILE *fp)
+{
+ ItemList *item_list = NULL;
+ GroupDef *group_def = NULL;
+ LexItem lex_item;
+
+ if (!(item_list = parse_item_list(fp))) {
+ return NULL;
+ }
+ group_def = typecalloc(GroupDef);
+ if (group_def == NULL)
+ outofmem(__FILE__, "HTAA_parseGroupDef");
+
+ group_def->group_name = NULL;
+ group_def->item_list = item_list;
+
+ if ((lex_item = lex(fp)) != LEX_REC_SEP) {
+ syntax_error(fp, "Garbage after group definition", lex_item);
+ }
+
+ return group_def;
+}
+
+#if 0
+static GroupDef *parse_group_decl(FILE *fp)
+{
+ char *group_name = NULL;
+ GroupDef *group_def = NULL;
+ LexItem lex_item;
+
+ do {
+ lex_item = lex(fp);
+ } while (lex_item == LEX_REC_SEP); /* Ignore empty lines */
+
+ if (lex_item != LEX_ALPH_STR) {
+ if (lex_item != LEX_EOF)
+ syntax_error(fp, "Expecting group name", lex_item);
+ return NULL;
+ }
+ StrAllocCopy(group_name, HTlex_buffer);
+
+ if (LEX_FIELD_SEP != (lex_item = lex(fp))) {
+ syntax_error(fp, "Expecting field separator", lex_item);
+ FREE(group_name);
+ return NULL;
+ }
+
+ if (!(group_def = HTAA_parseGroupDef(fp))) {
+ FREE(group_name);
+ return NULL;
+ }
+ group_def->group_name = group_name;
+
+ return group_def;
+}
+
+/*
+ * Group manipulation routines
+ */
+
+static GroupDef *find_group_def(GroupDefList *group_list,
+ const char *group_name)
+{
+ if (group_list && group_name) {
+ GroupDefList *cur = group_list;
+ GroupDef *group_def;
+
+ while (NULL != (group_def = (GroupDef *) HTList_nextObject(cur))) {
+ if (!strcmp(group_name, group_def->group_name)) {
+ return group_def;
+ }
+ }
+ }
+ return NULL;
+}
+
+void HTAA_resolveGroupReferences(GroupDef *group_def,
+ GroupDefList *group_def_list)
+{
+ if (group_def && group_def->item_list && group_def_list) {
+ ItemList *cur1 = group_def->item_list;
+ Item *item;
+
+ while (NULL != (item = (Item *) HTList_nextObject(cur1))) {
+ UserDefList *cur2 = item->user_def_list;
+ Ref *ref;
+
+ while (NULL != (ref = (Ref *) HTList_nextObject(cur2)))
+ ref->translation = find_group_def(group_def_list, ref->name);
+
+ /* Does NOT translate address_def_list */
+ }
+ }
+}
+
+static void add_group_def(GroupDefList *group_def_list,
+ GroupDef *group_def)
+{
+ HTAA_resolveGroupReferences(group_def, group_def_list);
+ HTList_addObject(group_def_list, (void *) group_def);
+}
+
+static GroupDefList *parse_group_file(FILE *fp)
+{
+ GroupDefList *group_def_list = HTList_new();
+ GroupDef *group_def;
+
+ while (NULL != (group_def = parse_group_decl(fp)))
+ add_group_def(group_def_list, group_def);
+
+ return group_def_list;
+}
+#endif
+
+/*
+ * Trace functions
+ */
+
+static void print_item(Item *item)
+{
+ if (!item)
+ fprintf(tfp, "\tNULL-ITEM\n");
+ else {
+ UserDefList *cur1 = item->user_def_list;
+ AddressDefList *cur2 = item->address_def_list;
+ Ref *user_ref = (Ref *) HTList_nextObject(cur1);
+ Ref *addr_ref = (Ref *) HTList_nextObject(cur2);
+
+ if (user_ref) {
+ fprintf(tfp, "\t[%s%s", user_ref->name,
+ (user_ref->translation ? "*REF*" : ""));
+ while (NULL != (user_ref = (Ref *) HTList_nextObject(cur1)))
+ fprintf(tfp, "; %s%s", user_ref->name,
+ (user_ref->translation ? "*REF*" : ""));
+ fprintf(tfp, "] ");
+ } else
+ fprintf(tfp, "\tANYBODY ");
+
+ if (addr_ref) {
+ fprintf(tfp, "@ [%s", addr_ref->name);
+ while (NULL != (addr_ref = (Ref *) HTList_nextObject(cur2)))
+ fprintf(tfp, "; %s", addr_ref->name);
+ fprintf(tfp, "]\n");
+ } else
+ fprintf(tfp, "@ ANYADDRESS\n");
+ }
+}
+
+static void print_item_list(ItemList *item_list)
+{
+ ItemList *cur = item_list;
+ Item *item;
+
+ if (!item_list)
+ fprintf(tfp, "EMPTY");
+ else
+ while (NULL != (item = (Item *) HTList_nextObject(cur)))
+ print_item(item);
+}
+
+void HTAA_printGroupDef(GroupDef *group_def)
+{
+ if (!group_def) {
+ fprintf(tfp, "\nNULL RECORD\n");
+ return;
+ }
+
+ fprintf(tfp, "\nGroup %s:\n",
+ (group_def->group_name ? group_def->group_name : "NULL"));
+
+ print_item_list(group_def->item_list);
+ fprintf(tfp, "\n");
+}
+
+#if 0
+static void print_group_def_list(GroupDefList *group_list)
+{
+ GroupDefList *cur = group_list;
+ GroupDef *group_def;
+
+ while (NULL != (group_def = (GroupDef *) HTList_nextObject(cur)))
+ HTAA_printGroupDef(group_def);
+}
+
+/*
+ * IP address template matching
+ */
+
+/* static part_match()
+ * MATCH ONE PART OF INET ADDRESS AGAINST
+ * A PART OF MASK (inet address has 4 parts)
+ * ON ENTRY:
+ * tcur pointer to the beginning of template part.
+ * icur pointer to the beginning of actual inet
+ * number part.
+ *
+ * ON EXIT:
+ * returns YES, if match.
+ */
+static BOOL part_match(const char *tcur,
+ const char *icur)
+{
+ char required[4];
+ char actual[4];
+ const char *cur;
+ int cnt;
+ BOOL status;
+
+ if (!tcur || !icur)
+ return NO;
+
+ cur = tcur;
+ cnt = 0;
+ while (cnt < 3 && *cur && *cur != '.')
+ required[cnt++] = *(cur++);
+ required[cnt] = (char) 0;
+
+ cur = icur;
+ cnt = 0;
+ while (cnt < 3 && *cur && *cur != '.')
+ actual[cnt++] = *(cur++);
+ actual[cnt] = (char) 0;
+
+ status = HTAA_templateMatch(required, actual);
+ CTRACE((tfp, "part_match: req: '%s' act: '%s' match: %s\n",
+ required, actual, (status ? "yes" : "no")));
+
+ return status;
+}
+
+/* static ip_number_match()
+ * MATCH INET NUMBER AGAINST AN INET NUMBER MASK
+ * ON ENTRY:
+ * template mask to match against, e.g., 128.141.*.*
+ * the_inet_addr actual inet address, e.g., 128.141.201.74
+ *
+ * ON EXIT:
+ * returns YES, if match; NO, if not.
+ */
+static BOOL ip_number_match(const char *ctemplate,
+ const char *the_inet_addr)
+{
+ const char *tcur = ctemplate;
+ const char *icur = the_inet_addr;
+ int cnt;
+
+ for (cnt = 0; cnt < 4; cnt++) {
+ if (!tcur || !icur || !part_match(tcur, icur))
+ return NO;
+ if (NULL != (tcur = StrChr(tcur, '.')))
+ tcur++;
+ if (NULL != (icur = StrChr(icur, '.')))
+ icur++;
+ }
+ return YES;
+}
+
+/* static is_domain_mask()
+ * DETERMINE IF A GIVEN MASK IS A
+ * DOMAIN NAME MASK OR AN INET NUMBER MASK
+ * ON ENTRY:
+ * mask either a domain name mask,
+ * e.g.
+ * *.cern.ch
+ *
+ * or an inet number mask,
+ * e.g.
+ * 128.141.*.*
+ *
+ * ON EXIT:
+ * returns YES, if mask is a domain name mask.
+ * NO, if it is an inet number mask.
+ */
+static BOOL is_domain_mask(const char *mask)
+{
+ const char *cur = mask;
+
+ if (!mask)
+ return NO;
+
+ while (*cur) {
+ if (*cur != '.' && *cur != '*' && (*cur < '0' || *cur > '9'))
+ return YES; /* Even one non-digit makes it a domain name mask */
+ cur++;
+ }
+ return NO; /* All digits and dots, so it is an inet number mask */
+}
+
+/* static ip_mask_match()
+ * MATCH AN IP NUMBER MASK OR IP NAME MASK
+ * AGAINST ACTUAL IP NUMBER OR IP NAME
+ *
+ * ON ENTRY:
+ * mask mask. Mask may be either an inet number
+ * mask or a domain name mask,
+ * e.g.
+ * 128.141.*.*
+ * or
+ * *.cern.ch
+ *
+ * ip_number IP number of connecting host.
+ * ip_name IP name of the connecting host.
+ *
+ * ON EXIT:
+ * returns YES, if hostname/internet number
+ * matches the mask.
+ * NO, if no match (no fire).
+ */
+static BOOL ip_mask_match(const char *mask,
+ const char *ip_number,
+ const char *ip_name)
+{
+ if (mask && (ip_number || ip_name)) {
+ if (is_domain_mask(mask)) {
+ if (HTAA_templateMatch(mask, ip_name))
+ return YES;
+ } else {
+ if (ip_number_match(mask, ip_number))
+ return YES;
+ }
+ }
+ return NO;
+}
+
+static BOOL ip_in_def_list(AddressDefList *address_def_list,
+ char *ip_number,
+ char *ip_name)
+{
+ if (address_def_list && (ip_number || ip_name)) {
+ AddressDefList *cur = address_def_list;
+ Ref *ref;
+
+ while (NULL != (ref = (Ref *) HTList_nextObject(cur))) {
+ /* Value of ref->translation is ignored, i.e., */
+ /* no recursion for ip address tamplates. */
+ if (ip_mask_match(ref->name, ip_number, ip_name))
+ return YES;
+ }
+ }
+ return NO;
+}
+
+/*
+ * Group file cached reading
+ */
+
+typedef struct {
+ char *group_filename;
+ GroupDefList *group_list;
+} GroupCache;
+
+typedef HTList GroupCacheList;
+
+static GroupCacheList *group_cache_list = NULL;
+
+GroupDefList *HTAA_readGroupFile(const char *filename)
+{
+ FILE *fp;
+ GroupCache *group_cache;
+
+ if (isEmpty(filename))
+ return NULL;
+
+ if (!group_cache_list)
+ group_cache_list = HTList_new();
+ else {
+ GroupCacheList *cur = group_cache_list;
+
+ while (NULL != (group_cache = (GroupCache *) HTList_nextObject(cur))) {
+ if (!strcmp(filename, group_cache->group_filename)) {
+ CTRACE((tfp, "%s '%s' %s\n",
+ "HTAA_readGroupFile: group file",
+ filename, "already found in cache"));
+ return group_cache->group_list;
+ } /* if cache match */
+ } /* while cached files remain */
+ } /* cache exists */
+
+ CTRACE((tfp, "HTAA_readGroupFile: reading group file `%s'\n",
+ filename));
+
+ if (!(fp = fopen(filename, TXT_R))) {
+ CTRACE((tfp, "%s '%s'\n",
+ "HTAA_readGroupFile: unable to open group file",
+ filename));
+ return NULL;
+ }
+
+ if ((group_cache = typecalloc(GroupCache)) == 0)
+ outofmem(__FILE__, "HTAA_readGroupFile");
+
+ group_cache->group_filename = NULL;
+ StrAllocCopy(group_cache->group_filename, filename);
+ group_cache->group_list = parse_group_file(fp);
+ HTList_addObject(group_cache_list, (void *) group_cache);
+ fclose(fp);
+
+ CTRACE((tfp, "Read group file '%s', results follow:\n", filename));
+ if (TRACE)
+ print_group_def_list(group_cache->group_list);
+
+ return group_cache->group_list;
+}
+
+/* PUBLIC HTAA_userAndInetInGroup()
+ * CHECK IF USER BELONGS TO TO A GIVEN GROUP
+ * AND THAT THE CONNECTION COMES FROM AN
+ * ADDRESS THAT IS ALLOWED BY THAT GROUP
+ * ON ENTRY:
+ * group the group definition structure.
+ * username connecting user.
+ * ip_number browser host IP number, optional.
+ * ip_name browser host IP name, optional.
+ * However, one of ip_number or ip_name
+ * must be given.
+ * ON EXIT:
+ * returns HTAA_IP_MASK, if IP address mask was
+ * reason for failing.
+ * HTAA_NOT_MEMBER, if user does not belong
+ * to the group.
+ * HTAA_OK if both IP address and user are ok.
+ */
+HTAAFailReasonType HTAA_userAndInetInGroup(GroupDef *group,
+ char *username,
+ char *ip_number,
+ char *ip_name)
+{
+ HTAAFailReasonType reason = HTAA_NOT_MEMBER;
+
+ if (group && username) {
+ ItemList *cur1 = group->item_list;
+ Item *item;
+
+ while (NULL != (item = (Item *) HTList_nextObject(cur1))) {
+ if (!item->address_def_list || /* Any address allowed */
+ ip_in_def_list(item->address_def_list, ip_number, ip_name)) {
+
+ if (!item->user_def_list) /* Any user allowed */
+ return HTAA_OK;
+ else {
+ UserDefList *cur2 = item->user_def_list;
+ Ref *ref;
+
+ while (NULL != (ref = (Ref *) HTList_nextObject(cur2))) {
+
+ if (ref->translation) { /* Group, check recursively */
+ reason = HTAA_userAndInetInGroup(ref->translation,
+ username,
+ ip_number, ip_name);
+ if (reason == HTAA_OK)
+ return HTAA_OK;
+ } else { /* Username, check directly */
+ if (username && *username &&
+ 0 == strcmp(ref->name, username))
+ return HTAA_OK;
+ }
+ /* Every user/group name in this group */
+ }
+ /* search for username */
+ }
+ /* IP address ok */
+ } else {
+ reason = HTAA_IP_MASK;
+ }
+ } /* while items in group */
+ }
+ /* valid parameters */
+ return reason; /* No match, or invalid parameters */
+}
+
+void GroupDef_delete(GroupDef *group_def)
+{
+ if (group_def) {
+ FREE(group_def->group_name);
+ if (group_def->item_list) {
+ HTList_delete(group_def->item_list); /* @@@@ */
+ group_def->item_list = NULL;
+ }
+ FREE(group_def);
+ }
+}
+#endif
diff --git a/WWW/Library/Implementation/HTGroup.h b/WWW/Library/Implementation/HTGroup.h
new file mode 100644
index 0000000..7874565
--- /dev/null
+++ b/WWW/Library/Implementation/HTGroup.h
@@ -0,0 +1,182 @@
+/* GROUP FILE ROUTINES
+
+ */
+
+#ifndef HTGROUP_H
+#define HTGROUP_H
+
+#include <HTList.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+ typedef HTList GroupDefList;
+ typedef HTList ItemList;
+
+ typedef struct {
+ char *group_name;
+ ItemList *item_list;
+ } GroupDef;
+
+/*
+ * Access Authorization failure reasons
+ */
+ typedef enum {
+ HTAA_OK, /* 200 OK */
+ HTAA_OK_GATEWAY, /* 200 OK, acting as a gateway */
+ HTAA_NO_AUTH, /* 401 Unauthorized, not authenticated */
+ HTAA_NOT_MEMBER, /* 401 Unauthorized, not authorized */
+ HTAA_IP_MASK, /* 403 Forbidden by IP mask */
+ HTAA_BY_RULE, /* 403 Forbidden by rule */
+ HTAA_NO_ACL, /* 403 Forbidden, ACL non-existent */
+ HTAA_NO_ENTRY, /* 403 Forbidden, no ACL entry */
+ HTAA_SETUP_ERROR, /* 403 Forbidden, server setup error */
+ HTAA_DOTDOT, /* 403 Forbidden, URL with /../ illegal */
+ HTAA_HTBIN, /* 403 Forbidden, /htbin not enabled */
+ HTAA_NOT_FOUND /* 404 Not found, or read protected */
+ } HTAAFailReasonType;
+
+/*
+
+Group definition grammar
+
+ string
+ "sequence of alphanumeric characters"
+
+ user_name
+ string
+
+ group_name
+ string
+
+ group_ref
+ group_name
+
+ user_def
+ user_name | group_ref
+
+ user_def_list
+ user_def { ',' user_def }
+
+ user_part
+ user_def | '(' user_def_list ')'
+
+ templ
+
+ "sequence of alphanumeric characters and '*'s"
+
+ ip_number_mask
+ templ '.' templ '.' templ '.' templ
+
+ domain_name_mask
+ templ { '.' templ }
+
+ address
+
+ ip_number_mask | domain_name_mask
+
+ address_def
+
+ address
+
+ address_def_list
+ address_def { ',' address_def }
+
+ address_part
+ address_def | '(' address_def_list ')'
+
+ item
+ [user_part] ['@' address_part]
+
+ item_list
+ item { ',' item }
+
+ group_def
+ item_list
+
+ group_decl
+ group_name ':' group_def
+
+ PARSE GROUP DEFINITION
+
+ */
+
+ extern GroupDef *HTAA_parseGroupDef(FILE *fp);
+
+/*
+
+Fill in Pointers to referenced Group Definitions in a Group Definition
+
+ References to groups (by their name) are resolved from group_def_list and pointers to
+ those structures are added to group_def.
+
+ */
+
+ extern void HTAA_resolveGroupReferences(GroupDef *group_def,
+ GroupDefList *group_def_list);
+
+/*
+
+Read Group File (and do caching)
+
+ If group file is already in cache returns a pointer to previously read group definition
+ list.
+
+ */
+
+ extern GroupDefList *HTAA_readGroupFile(const char *filename);
+
+/*
+
+Delete Group Definition
+
+ Groups in cache should never be freed by this function. This should only be used to
+ free group definitions read by HTAA_parseGroupDef.
+
+ */
+
+ extern void GroupDef_delete(GroupDef *group_def);
+
+/*
+
+Print Out Group Definition (for trace purposes)
+
+ */
+
+ extern void HTAA_printGroupDef(GroupDef *group_def);
+
+/*
+
+Does a User Belong to a Given Set of Groups
+
+ This function checks both the username and the internet address.
+
+ */
+
+/* PUBLIC HTAA_userAndInetInGroup()
+ * CHECK IF USER BELONGS TO TO A GIVEN GROUP
+ * AND THAT THE CONNECTION COMES FROM AN
+ * ADDRESS THAT IS ALLOWED BY THAT GROUP
+ * ON ENTRY:
+ * group the group definition structure.
+ * username connecting user.
+ * ip_number browser host IP number, optional.
+ * ip_name browser host IP name, optional.
+ * However, one of ip_number or ip_name
+ * must be given.
+ * ON EXIT:
+ * returns HTAA_IP_MASK, if IP address mask was
+ * reason for failing.
+ * HTAA_NOT_MEMBER, if user does not belong
+ * to the group.
+ * HTAA_OK if both IP address and user are ok.
+ */
+ extern HTAAFailReasonType HTAA_userAndInetInGroup(GroupDef *group,
+ char *username,
+ char *ip_number,
+ char *ip_name);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* not HTGROUP_H */
diff --git a/WWW/Library/Implementation/HTInit.h b/WWW/Library/Implementation/HTInit.h
new file mode 100644
index 0000000..4fc7000
--- /dev/null
+++ b/WWW/Library/Implementation/HTInit.h
@@ -0,0 +1,34 @@
+/* /Net/dxcern/userd/timbl/hypertext/WWW/Library/Implementation/HTInit.html
+ INITIALISATION MODULE
+
+ This module registers all the plug & play software modules which will be
+ used in the program. This is for a browser.
+
+ To override this, just copy it and link in your version before you link with
+ the library.
+
+ Implemented by HTInit.c by default.
+
+ */
+
+#ifndef HTINIT_H
+#define HTINIT_H 1
+
+#ifndef HTUTILS_H
+#include <HTUtils.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+ extern void HTFormatInit(void);
+ extern void HTPreparsedFormatInit(void);
+ extern void HTFileInit(void);
+ extern int LYTestMailcapCommand(const char *testcommand, const char *params);
+ extern BOOL LYMailcapUsesPctS(const char *controlstring);
+ extern char *LYMakeMailcapCommand(const char *command, const char *params, const char *filename);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* HTINIT_H */
diff --git a/WWW/Library/Implementation/HTLex.c b/WWW/Library/Implementation/HTLex.c
new file mode 100644
index 0000000..5a0df91
--- /dev/null
+++ b/WWW/Library/Implementation/HTLex.c
@@ -0,0 +1,142 @@
+
+/* MODULE HTLex.c
+ * LEXICAL ANALYSOR
+ *
+ * AUTHORS:
+ * AL Ari Luotonen luotonen@dxcern.cern.ch
+ *
+ * HISTORY:
+ *
+ *
+ * BUGS:
+ *
+ *
+ */
+
+#include <HTUtils.h>
+
+#include <HTLex.h> /* Implemented here */
+
+#include <LYLeaks.h>
+
+/*
+ * Global variables
+ */
+char HTlex_buffer[40]; /* Read lexical string */
+int HTlex_line = 1; /* Line number in source file */
+
+/*
+ * Module-wide variables
+ */
+static int lex_cnt;
+static BOOL lex_template;
+static LexItem lex_pushed_back = LEX_NONE;
+static FILE *cache = NULL;
+
+void unlex(LexItem lex_item)
+{
+ lex_pushed_back = lex_item;
+}
+
+LexItem lex(FILE *fp)
+{
+ int ch = 0;
+
+ if (fp != cache) { /* This cache doesn't work ok because the system */
+ cache = fp; /* often assign same FILE structure the next open */
+ HTlex_line = 1; /* file. So, if there are syntax errors in setup *
+ files it may confuse things later on. */
+ }
+ if (lex_pushed_back != LEX_NONE) {
+ LexItem ret = lex_pushed_back;
+
+ lex_pushed_back = LEX_NONE;
+ return ret;
+ }
+
+ lex_cnt = 0;
+ lex_template = NO;
+
+ for (;;) {
+ switch (ch = getc(fp)) {
+ case EOF:
+ case ' ':
+ case '\t':
+ case '\r':
+ case '\n':
+ case ':':
+ case ',':
+ case '(':
+ case ')':
+ case '@':
+ if (lex_cnt > 0) {
+ if (ch != EOF)
+ ungetc(ch, fp);
+ if (lex_template)
+ return LEX_TMPL_STR;
+ else
+ return LEX_ALPH_STR;
+ } else
+ switch (ch) {
+ case EOF:
+ return LEX_EOF;
+ case '\n':
+ HTlex_line++;
+ return LEX_REC_SEP;
+ case ':':
+ return LEX_FIELD_SEP;
+ case ',':
+ return LEX_ITEM_SEP;
+ case '(':
+ return LEX_OPEN_PAREN;
+ case ')':
+ return LEX_CLOSE_PAREN;
+ case '@':
+ return LEX_AT_SIGN;
+ default: /* Leading white space ignored (SP,TAB,CR) */
+ break;
+ }
+ break;
+ default:
+ if (lex_cnt < (int) (sizeof(HTlex_buffer) - 1))
+ HTlex_buffer[lex_cnt++] = (char) ch;
+ HTlex_buffer[lex_cnt] = '\0';
+ if ('*' == ch)
+ lex_template = YES;
+ } /* switch ch */
+ } /* forever */
+}
+
+const char *lex_verbose(LexItem lex_item)
+{
+ static char msg[sizeof(HTlex_buffer) + 30]; /* @@@@@@@@ */
+
+ switch (lex_item) {
+ case LEX_NONE: /* Internally used */
+ return "NO-LEX-ITEM";
+ case LEX_EOF: /* End of file */
+ return "end-of-file";
+ case LEX_REC_SEP: /* Record separator */
+ return "record separator (newline)";
+ case LEX_FIELD_SEP: /* Field separator */
+ return "field separator ':'";
+ case LEX_ITEM_SEP: /* List item separator */
+ return "item separator ','";
+ case LEX_OPEN_PAREN: /* Group start tag */
+ return "'('";
+ case LEX_CLOSE_PAREN: /* Group end tag */
+ return "')'";
+ case LEX_AT_SIGN: /* Address qualifier */
+ return "address qualifier '@'";
+ case LEX_ALPH_STR: /* Alphanumeric string */
+ sprintf(msg, "alphanumeric string '%.*s'",
+ (int) sizeof(HTlex_buffer), HTlex_buffer);
+ return msg;
+ case LEX_TMPL_STR: /* Template string */
+ sprintf(msg, "template string '%.*s'",
+ (int) sizeof(HTlex_buffer), HTlex_buffer);
+ return msg;
+ default:
+ return "UNKNOWN-LEX-ITEM";
+ }
+}
diff --git a/WWW/Library/Implementation/HTLex.h b/WWW/Library/Implementation/HTLex.h
new file mode 100644
index 0000000..fde9034
--- /dev/null
+++ b/WWW/Library/Implementation/HTLex.h
@@ -0,0 +1,64 @@
+/* LEXICAL ANALYSOR (MAINLY FOR CONFIG FILES)
+
+ */
+
+#ifndef HTLEX_H
+#define HTLEX_H
+
+#ifndef HTUTILS_H
+#include <HTUtils.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+ typedef enum {
+ LEX_NONE, /* Internally used */
+ LEX_EOF, /* End of file */
+ LEX_REC_SEP, /* Record separator */
+ LEX_FIELD_SEP, /* Field separator */
+ LEX_ITEM_SEP, /* List item separator */
+ LEX_OPEN_PAREN, /* Group start tag */
+ LEX_CLOSE_PAREN, /* Group end tag */
+ LEX_AT_SIGN, /* Address qualifier */
+ LEX_ALPH_STR, /* Alphanumeric string */
+ LEX_TMPL_STR /* Template string */
+ } LexItem;
+
+ extern char HTlex_buffer[]; /* Read lexical string */
+ extern int HTlex_line; /* Line number in source file */
+
+/*
+
+Get Next Lexical Item
+
+ If returns LEX_ALPH_STR or LEX_TMPL_STR the string is in global buffer lex_buffer.
+
+ */
+
+ extern LexItem lex(FILE *fp);
+
+/*
+
+Push Back Latest Item
+
+ */
+
+ extern void unlex(LexItem lex_item);
+
+/*
+
+Get the Name for Lexical Item
+
+ */
+
+ extern const char *lex_verbose(LexItem lex_item);
+
+/*
+
+ */
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* not HTLEX_H */
diff --git a/WWW/Library/Implementation/HTList.c b/WWW/Library/Implementation/HTList.c
new file mode 100644
index 0000000..d12cd38
--- /dev/null
+++ b/WWW/Library/Implementation/HTList.c
@@ -0,0 +1,402 @@
+/*
+ * $LynxId: HTList.c,v 1.20 2016/11/24 15:29:50 tom Exp $
+ *
+ * A small List class HTList.c
+ * ==================
+ *
+ * A list is represented as a sequence of linked nodes of type HTList.
+ * The first node is a header which contains no object.
+ * New nodes are inserted between the header and the rest of the list.
+ */
+
+#include <HTUtils.h>
+#include <HTList.h>
+
+#include <LYLeaks.h>
+
+/* Create list.
+*/
+HTList *HTList_new(void)
+{
+ HTList *newList;
+
+ if ((newList = typeMalloc(HTList)) == NULL)
+ outofmem(__FILE__, "HTList_new");
+
+ newList->object = NULL;
+ newList->next = NULL;
+
+ return newList;
+}
+
+/* Delete list.
+*/
+void HTList_delete(HTList *me)
+{
+ HTList *current;
+
+ while ((current = me)) {
+ me = me->next;
+ FREE(current);
+ }
+
+ return;
+}
+
+/* Reverse order of elements in list.
+ */
+HTList *HTList_reverse(HTList *start)
+{
+ HTList *cur, *succ;
+
+ if (!(start && start->next && (cur = start->next->next)))
+ return start;
+ start->next->next = NULL;
+ while (cur) {
+ succ = cur->next;
+ cur->next = start->next;
+ start->next = cur;
+ cur = succ;
+ }
+ return start;
+}
+
+/* Append a list to another.
+ *
+ * If successful, the second list will become empty but not freed.
+ */
+HTList *HTList_appendList(HTList *start,
+ HTList *tail)
+{
+ HTList *temp = start;
+
+ if (!start) {
+ CTRACE((tfp,
+ "HTList: Trying to append list %p to a nonexisting list\n",
+ (void *) tail));
+ return NULL;
+ }
+ if (!(tail && tail->next))
+ return start;
+
+ while (temp->next)
+ temp = temp->next;
+
+ temp->next = tail->next;
+ tail->next = NULL; /* tail is now an empty list */
+ return start;
+}
+
+/* Link object to START of list (so it is pointed to by the head).
+ *
+ * Unlike HTList_addObject(), it does not malloc memory for HTList entry,
+ * it use already allocated memory which should not be free'd by any
+ * list operations (optimization).
+ */
+void HTList_linkObject(HTList *me, void *newObject,
+ HTList *newNode)
+{
+ if (me) {
+ if (newNode->object == NULL && newNode->next == NULL) {
+ /* It is safe: */
+ newNode->object = newObject;
+ newNode->next = me->next;
+ me->next = newNode;
+
+ } else {
+ /*
+ * This node is already linked to some list (probably this one), so
+ * refuse changing node pointers to keep the list valid!!!
+ */
+ CTRACE((tfp, "*** HTList: Refuse linking already linked obj "));
+ CTRACE((tfp, "%p, node %p, list %p\n",
+ (void *) newObject, (void *) newNode, (void *) me));
+ }
+
+ } else {
+ CTRACE((tfp,
+ "HTList: Trying to link object %p to a nonexisting list\n",
+ newObject));
+ }
+
+ return;
+}
+
+/* Add object to START of list (so it is pointed to by the head).
+*/
+void HTList_addObject(HTList *me, void *newObject)
+{
+ HTList *newNode;
+
+ if (me) {
+ if ((newNode = typeMalloc(HTList)) == NULL)
+ outofmem(__FILE__, "HTList_addObject");
+
+ newNode->object = newObject;
+ newNode->next = me->next;
+ me->next = newNode;
+
+ } else {
+ CTRACE((tfp, "HTList: Trying to add object %p to a nonexisting list\n",
+ newObject));
+ }
+
+ return;
+}
+
+/* Append object to END of list (furthest from the head).
+*/
+void HTList_appendObject(HTList *me, void *newObject)
+{
+ HTList *temp = me;
+
+ if (temp && newObject) {
+ while (temp->next)
+ temp = temp->next;
+ HTList_addObject(temp, newObject);
+ }
+
+ return;
+}
+
+/* Insert an object into the list at a specified position.
+ * If position is 0, this places the object at the head of the list
+ * and is equivalent to HTList_addObject().
+ */
+void HTList_insertObjectAt(HTList *me, void *newObject,
+ int pos)
+{
+ HTList *newNode;
+ HTList *temp = me;
+ HTList *prevNode;
+ int Pos = pos;
+
+ if (!temp) {
+ CTRACE((tfp, "HTList: Trying to add object %p to a nonexisting list\n",
+ newObject));
+ return;
+ }
+ if (Pos < 0) {
+ Pos = 0;
+ CTRACE((tfp, "HTList: Treating negative object position %d as %d.\n",
+ pos, Pos));
+ }
+
+ prevNode = temp;
+ while ((temp = temp->next)) {
+ if (Pos == 0) {
+ if ((newNode = typeMalloc(HTList)) == NULL)
+ outofmem(__FILE__, "HTList_addObjectAt");
+
+ newNode->object = newObject;
+ newNode->next = temp;
+ if (prevNode)
+ prevNode->next = newNode;
+ return;
+ }
+ prevNode = temp;
+ Pos--;
+ }
+ if (Pos >= 0)
+ HTList_addObject(prevNode, newObject);
+
+ return;
+}
+
+/* Unlink specified object from list.
+ * It does not free memory.
+ */
+BOOL HTList_unlinkObject(HTList *me, void *oldObject)
+{
+ HTList *temp = me;
+ HTList *prevNode;
+
+ if (temp && oldObject) {
+ while (temp->next) {
+ prevNode = temp;
+ temp = temp->next;
+ if (temp->object == oldObject) {
+ prevNode->next = temp->next;
+ temp->next = NULL;
+ temp->object = NULL;
+ return YES; /* Success */
+ }
+ }
+ }
+ return NO; /* object not found or NULL list */
+}
+
+/* Remove specified object from list.
+*/
+BOOL HTList_removeObject(HTList *me, void *oldObject)
+{
+ HTList *temp = me;
+ HTList *prevNode;
+
+ if (temp && oldObject) {
+ while (temp->next) {
+ prevNode = temp;
+ temp = temp->next;
+ if (temp->object == oldObject) {
+ prevNode->next = temp->next;
+ FREE(temp);
+ return YES; /* Success */
+ }
+ }
+ }
+ return NO; /* object not found or NULL list */
+}
+
+/* Remove object at a given position in the list, where 0 is the
+ * object pointed to by the head (returns a pointer to the element
+ * (->object) for the object, and NULL if the list is empty, or
+ * if it doesn't exist - Yuk!).
+ */
+void *HTList_removeObjectAt(HTList *me, int position)
+{
+ HTList *temp = me;
+ HTList *prevNode;
+ int pos = position;
+ void *result = NULL;
+
+ if (temp != NULL && pos >= 0) {
+ prevNode = temp;
+ while ((temp = temp->next) != NULL) {
+ if (pos == 0) {
+ prevNode->next = temp->next;
+ result = temp->object;
+ FREE(temp);
+ break;
+ }
+ prevNode = temp;
+ pos--;
+ }
+ }
+
+ return result;
+}
+
+/* Unlink object from START of list (the Last one inserted
+ * via HTList_linkObject(), and pointed to by the head).
+ * It does not free memory.
+ */
+void *HTList_unlinkLastObject(HTList *me)
+{
+ HTList *lastNode;
+ void *lastObject;
+
+ if (me && me->next) {
+ lastNode = me->next;
+ lastObject = lastNode->object;
+ me->next = lastNode->next;
+ lastNode->next = NULL;
+ lastNode->object = NULL;
+ return lastObject;
+
+ } else { /* Empty list */
+ return NULL;
+ }
+}
+
+/* Remove object from START of list (the Last one inserted
+ * via HTList_addObject(), and pointed to by the head).
+ */
+void *HTList_removeLastObject(HTList *me)
+{
+ HTList *lastNode;
+ void *lastObject;
+
+ if (me && me->next) {
+ lastNode = me->next;
+ lastObject = lastNode->object;
+ me->next = lastNode->next;
+ FREE(lastNode);
+ return lastObject;
+
+ } else { /* Empty list */
+ return NULL;
+ }
+}
+
+/* Remove object from END of list (the First one inserted
+ * via HTList_addObject(), and furthest from the head).
+ */
+void *HTList_removeFirstObject(HTList *me)
+{
+ HTList *temp = me;
+ HTList *prevNode;
+ void *firstObject;
+
+ if (!temp)
+ return NULL;
+
+ prevNode = temp;
+ if (temp->next) {
+ while (temp->next) {
+ prevNode = temp;
+ temp = temp->next;
+ }
+ firstObject = temp->object;
+ prevNode->next = NULL;
+ FREE(temp);
+ return firstObject;
+
+ } else { /* Empty list */
+ return NULL;
+ }
+}
+
+/* Determine total number of objects in the list,
+ * not counting the head.
+ */
+int HTList_count(HTList *me)
+{
+ HTList *temp = me;
+ int count = 0;
+
+ if (temp)
+ while ((temp = temp->next))
+ count++;
+
+ return count;
+}
+
+/* Determine position of an object in the list (a value of 0
+ * means it is pointed to by the head; returns -1 if not found).
+ */
+int HTList_indexOf(HTList *me, void *object)
+{
+ HTList *temp = me;
+ int position = 0;
+
+ if (temp) {
+ while ((temp = temp->next)) {
+ if (temp->object == object)
+ return position;
+ position++;
+ }
+ }
+
+ return -1; /* Object not in the list */
+}
+
+/* Return pointer to the object at a specified position in the list,
+ * where 0 is the object pointed to by the head (returns NULL if
+ * the list is empty, or if it doesn't exist - Yuk!).
+ */
+void *HTList_objectAt(HTList *me, int position)
+{
+ HTList *temp = me;
+ int pos = position;
+
+ if (!temp || pos < 0)
+ return NULL;
+
+ while ((temp = temp->next)) {
+ if (pos == 0)
+ return temp->object;
+ pos--;
+ }
+
+ return NULL; /* Reached the end of the list */
+}
diff --git a/WWW/Library/Implementation/HTList.h b/WWW/Library/Implementation/HTList.h
new file mode 100644
index 0000000..93f9147
--- /dev/null
+++ b/WWW/Library/Implementation/HTList.h
@@ -0,0 +1,142 @@
+
+/* List object
+ *
+ * The list object is a generic container for storing collections
+ * of things in order.
+ */
+#ifndef HTLIST_H
+#define HTLIST_H
+
+#ifndef HTUTILS_H
+#include <HTUtils.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+ typedef struct _HTList HTList;
+
+ struct _HTList {
+ void *object;
+ HTList *next;
+ };
+
+/* Fast macro to traverse a list. Call it first with copy of the list
+ * header. It returns the first object and increments the passed list
+ * pointer. Call it with the same variable until it returns NULL.
+ */
+#define HTList_nextObject(me) \
+ ((me) && ((me) = (me)->next) ? (me)->object : NULL)
+
+/* Macro to find object pointed to by the head (returns NULL
+ * if list is empty, OR if it doesn't exist - Yuk!)
+ */
+#define HTList_lastObject(me) \
+ ((me) && (me)->next ? (me)->next->object : NULL)
+
+/* Macro to check if a list is empty (or doesn't exist - Yuk!)
+*/
+#define HTList_isEmpty(me) ((me) ? ((me)->next == NULL) : YES)
+
+/* Create list.
+*/
+ extern HTList *HTList_new(void);
+
+/* Delete list.
+*/
+ extern void HTList_delete(HTList *me);
+
+/* Reverse a list.
+*/
+ extern HTList *HTList_reverse(HTList *start);
+
+/* Append two lists, making second list empty.
+*/
+ extern HTList *HTList_appendList(HTList *start,
+ HTList *tail);
+
+/* Add object to START of list (so it is pointed to by the head).
+*/
+ extern void HTList_addObject(HTList *me,
+ void *newObject);
+
+/* Append object to END of list (furthest from the head).
+*/
+ extern void HTList_appendObject(HTList *me,
+ void *newObject);
+
+/* Insert an object into the list at a specified position.
+ * If position is 0, this places the object at the head of the list
+ * and is equivalent to HTList_addObject().
+ */
+ extern void HTList_insertObjectAt(HTList *me,
+ void *newObject,
+ int pos);
+
+/* Remove specified object from list.
+*/
+ extern BOOL HTList_removeObject(HTList *me,
+ void *oldObject);
+
+/* Remove object at a given position in the list, where 0 is the
+ * object pointed to by the head (returns a pointer to the element
+ * (->object) for the object, and NULL if the list is empty, or
+ * if it doesn't exist - Yuk!).
+ */
+ extern void *HTList_removeObjectAt(HTList *me,
+ int position);
+
+/* Remove object from START of list (the Last one inserted
+ * via HTList_addObject(), and pointed to by the head).
+ */
+ extern void *HTList_removeLastObject(HTList *me);
+
+/* Remove object from END of list (the First one inserted
+ * via HTList_addObject(), and furthest from the head).
+ */
+ extern void *HTList_removeFirstObject(HTList *me);
+
+/* Determine total number of objects in the list,
+ * not counting the head.
+ */
+ extern int HTList_count(HTList *me);
+
+/* Determine position of an object in the list (a value of 0
+ * means it is pointed to by the head; returns -1 if not found).
+ */
+ extern int HTList_indexOf(HTList *me,
+ void *object);
+
+/* Return pointer to the object at a specified position in the list,
+ * where 0 is the object pointed to by the head (returns NULL if
+ * the list is empty, or if it doesn't exist - Yuk!).
+ */
+ extern void *HTList_objectAt(HTList *me,
+ int position);
+
+/* Link object to START of list (so it is pointed to by the head).
+ *
+ * Unlike HTList_addObject(), it does not malloc memory for HTList entry,
+ * it use already allocated memory which should not be free'd by any
+ * list operations (optimization).
+ */
+ extern void HTList_linkObject(HTList *me,
+ void *newObject,
+ HTList *newNode);
+
+/* Unlink object from START of list (the Last one inserted
+ * via HTList_linkObject(), and pointed to by the head).
+ * It does not free memory.
+ */
+ extern void *HTList_unlinkLastObject(HTList *me);
+
+/* Unlink specified object from list.
+ * It does not free memory.
+ */
+ extern BOOL HTList_unlinkObject(HTList *me,
+ void *oldObject);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* HTLIST_H */
diff --git a/WWW/Library/Implementation/HTMIME.c b/WWW/Library/Implementation/HTMIME.c
new file mode 100644
index 0000000..fde89a6
--- /dev/null
+++ b/WWW/Library/Implementation/HTMIME.c
@@ -0,0 +1,2594 @@
+/*
+ * $LynxId: HTMIME.c,v 1.102 2022/03/12 14:47:02 tom Exp $
+ *
+ * MIME Message Parse HTMIME.c
+ * ==================
+ *
+ * This is RFC 1341-specific code.
+ * The input stream pushed into this parser is assumed to be
+ * stripped on CRs, ie lines end with LF, not CR LF.
+ * (It is easy to change this except for the body part where
+ * conversion can be slow.)
+ *
+ * History:
+ * Feb 92 Written Tim Berners-Lee, CERN
+ *
+ */
+
+#define HTSTREAM_INTERNAL 1
+
+#include <HTUtils.h>
+#include <HTMIME.h> /* Implemented here */
+#include <HTTP.h> /* for redirecting_url */
+#include <HTAlert.h>
+#include <HTFile.h>
+#include <HTCJK.h>
+#include <UCMap.h>
+#include <UCDefs.h>
+#include <UCAux.h>
+
+#include <LYCookie.h>
+#include <LYCharSets.h>
+#include <LYCharUtils.h>
+#include <LYStrings.h>
+#include <LYUtils.h>
+#include <LYGlobalDefs.h>
+#include <LYLeaks.h>
+
+/* MIME Object
+ * -----------
+ */
+
+typedef enum {
+ MIME_TRANSPARENT, /* put straight through to target ASAP! */
+ /* states for "Transfer-Encoding: chunked" */
+ MIME_CHUNKED,
+ mcCHUNKED_COUNT_DIGIT,
+ mcCHUNKED_COUNT_CR,
+ mcCHUNKED_COUNT_LF,
+ mcCHUNKED_EXTENSION,
+ mcCHUNKED_DATA,
+ mcCHUNKED_DATA_CR,
+ mcCHUNKED_DATA_LF,
+ /* character state-machine */
+ miBEGINNING_OF_LINE, /* first character and not a continuation */
+ miA,
+ miACCEPT_RANGES,
+ miAGE,
+ miAL,
+ miALLOW,
+ miALTERNATES,
+ miC,
+ miCACHE_CONTROL,
+ miCO,
+ miCOOKIE,
+ miCON,
+ miCONNECTION,
+ miCONTENT_,
+ miCONTENT_BASE,
+ miCONTENT_DISPOSITION,
+ miCONTENT_ENCODING,
+ miCONTENT_FEATURES,
+ miCONTENT_L,
+ miCONTENT_LANGUAGE,
+ miCONTENT_LENGTH,
+ miCONTENT_LOCATION,
+ miCONTENT_MD5,
+ miCONTENT_RANGE,
+ miCONTENT_T,
+ miCONTENT_TRANSFER_ENCODING,
+ miCONTENT_TYPE,
+ miDATE,
+ miE,
+ miETAG,
+ miEXPIRES,
+ miKEEP_ALIVE,
+ miL,
+ miLAST_MODIFIED,
+ miLINK,
+ miLOCATION,
+ miP,
+ miPR,
+ miPRAGMA,
+ miPROXY_AUTHENTICATE,
+ miPUBLIC,
+ miR,
+ miRE,
+ miREFRESH,
+ miRETRY_AFTER,
+ miS,
+ miSAFE,
+ miSE,
+ miSERVER,
+ miSET_COOKIE,
+ miSET_COOKIE1,
+ miSET_COOKIE2,
+ miT,
+ miTITLE,
+ miTRANSFER_ENCODING,
+ miU,
+ miUPGRADE,
+ miURI,
+ miV,
+ miVARY,
+ miVIA,
+ miW,
+ miWARNING,
+ miWWW_AUTHENTICATE,
+ miSKIP_GET_VALUE, /* Skip space then get value */
+ miGET_VALUE, /* Get value till white space */
+ miJUNK_LINE, /* Ignore the rest of this folded line */
+ miNEWLINE, /* Just found a LF .. maybe continuation */
+ miCHECK, /* check against check_pointer */
+ MIME_NET_ASCII, /* Translate from net ascii */
+ MIME_IGNORE /* Ignore entire file */
+ /* TRANSPARENT and IGNORE are defined as stg else in _WINDOWS */
+} MIME_state;
+
+#define VALUE_SIZE 5120 /* @@@@@@@ Arbitrary? */
+struct _HTStream {
+ const HTStreamClass *isa;
+
+ BOOL net_ascii; /* Is input net ascii? */
+ MIME_state state; /* current state */
+ MIME_state if_ok; /* got this state if match */
+ MIME_state field; /* remember which field */
+ MIME_state fold_state; /* state on a fold */
+ BOOL head_only; /* only parsing header */
+ BOOL pickup_redirection; /* parsing for location */
+ BOOL no_streamstack; /* use sink directly */
+ const char *check_pointer; /* checking input */
+
+ char *value_pointer; /* storing values */
+ char value[VALUE_SIZE];
+
+ HTParentAnchor *anchor; /* Given on creation */
+ HTStream *sink; /* Given on creation */
+
+ char *boundary; /* For multipart */
+ char *set_cookie; /* Set-Cookie */
+ char *set_cookie2; /* Set-Cookie2 */
+ char *location; /* Location */
+
+ char *refresh_url; /* "Refresh:" URL */
+
+ HTFormat c_t_encoding; /* Content-Transfer-Encoding */
+ char *compression_encoding;
+
+ BOOL chunked_encoding; /* Transfer-Encoding: chunked */
+ long chunked_size; /* ...counter for "chunked" */
+
+ HTFormat format; /* Content-Type */
+ HTStream *target; /* While writing out */
+ HTStreamClass targetClass;
+
+ HTAtom *targetRep; /* Converting into? */
+};
+
+/*
+ * This function is for trimming off any paired
+ * open- and close-double quotes from header values.
+ * It does not parse the string for embedded quotes,
+ * and will not modify the string unless both the
+ * first and last characters are double-quotes. - FM
+ */
+void HTMIME_TrimDoubleQuotes(char *value)
+{
+ size_t i;
+ char *cp = value;
+
+ if (isEmpty(cp) || *cp != '"')
+ return;
+
+ i = strlen(cp);
+ if (cp[(i - 1)] != '"')
+ return;
+ else
+ cp[(i - 1)] = '\0';
+
+ for (i = 0; value[i]; i++)
+ value[i] = cp[(i + 1)];
+}
+
+/*
+ * Check if the token from "Content-Encoding" corresponds to a compression
+ * type.
+ */
+static BOOL content_is_compressed(HTStream *me)
+{
+ char *encoding = me->anchor->content_encoding;
+ BOOL result = (BOOL) (HTEncodingToCompressType(encoding) != cftNone);
+
+ CTRACE((tfp, "content is%s compressed\n", result ? "" : " NOT"));
+ return result;
+}
+
+/*
+ * Strip quotes from a refresh-URL.
+ */
+static void dequote(char *url)
+{
+ size_t len;
+
+ len = strlen(url);
+ if (*url == '\'' && len > 1 && url[len - 1] == url[0]) {
+ url[len - 1] = '\0';
+ while ((url[0] = url[1]) != '\0') {
+ ++url;
+ }
+ }
+}
+
+/*
+ * Strip off any compression-suffix from the address and check if the result
+ * looks like one of the presentable suffixes. If so, return the corresponding
+ * MIME type.
+ */
+static const char *UncompressedContentType(HTStream *me, CompressFileType method)
+{
+ const char *result = 0;
+ char *address = me->anchor->address;
+ const char *expected = HTCompressTypeToSuffix(method);
+ const char *actual = strrchr(address, '.');
+
+ /*
+ * We have to ensure the suffix is consistent, to use HTFileFormat().
+ */
+ if (actual != 0 && !strcasecomp(actual, expected)) {
+ HTFormat format;
+ HTAtom *pencoding = 0;
+ const char *description = 0;
+
+ format = HTFileFormat(address, &pencoding, &description);
+ result = HTAtom_name(format);
+ }
+
+ return result;
+}
+
+static int pumpData(HTStream *me)
+{
+ CompressFileType method;
+ const char *new_encoding;
+ const char *new_content;
+
+ CTRACE((tfp, "Begin pumpData\n"));
+ /*
+ * If the content-type says it is compressed, and there is no
+ * content-encoding, check further and see if the address (omitting the
+ * suffix for a compressed type) looks like a type we can present. If so,
+ * rearrange things so we'll present the StreamStack code with the
+ * presentable type, already marked as compressed.
+ */
+ CTRACE((tfp, "...address{%s}\n", me->anchor->address));
+ method = HTContentTypeToCompressType(me->anchor->content_type_params);
+ if (isEmpty(me->anchor->content_encoding))
+ me->anchor->no_content_encoding = TRUE;
+ if ((method != cftNone)
+ && isEmpty(me->anchor->content_encoding)
+ && (new_content = UncompressedContentType(me, method)) != 0) {
+
+ new_encoding = HTCompressTypeToEncoding(method);
+ CTRACE((tfp, "reinterpreting as content-type:%s, encoding:%s\n",
+ new_content, new_encoding));
+
+ StrAllocCopy(me->anchor->content_encoding, new_encoding);
+ FREE(me->compression_encoding);
+ StrAllocCopy(me->compression_encoding, new_encoding);
+
+ LYStrNCpy(me->value, new_content, VALUE_SIZE - 1);
+ StrAllocCopy(me->anchor->content_type_params, me->value);
+ me->format = HTAtom_for(me->value);
+ }
+
+ if (StrChr(HTAtom_name(me->format), ';') != NULL) {
+ char *cp = NULL, *cp1, *cp2, *cp3 = NULL, *cp4;
+
+ CTRACE((tfp, "HTMIME: Extended MIME Content-Type is %s\n",
+ HTAtom_name(me->format)));
+ StrAllocCopy(cp, HTAtom_name(me->format));
+ /*
+ * Note that the Content-Type value was converted
+ * to lower case when we loaded into me->format,
+ * but there may have been a mixed or upper-case
+ * atom, so we'll force lower-casing again. We
+ * also stripped spaces and double-quotes, but
+ * we'll make sure they're still gone from any
+ * charset parameter we check. - FM
+ */
+ LYLowerCase(cp);
+ if ((cp1 = StrChr(cp, ';')) != NULL) {
+ BOOL chartrans_ok = NO;
+
+ if ((cp2 = strstr(cp1, "charset")) != NULL) {
+ int chndl;
+
+ cp2 += 7;
+ while (*cp2 == ' ' || *cp2 == '=' || *cp2 == '"')
+ cp2++;
+ StrAllocCopy(cp3, cp2); /* copy to mutilate more */
+ for (cp4 = cp3; (*cp4 != '\0' && *cp4 != '"' &&
+ *cp4 != ';' && *cp4 != ':' &&
+ !WHITE(*cp4)); cp4++) ; /* do nothing */
+ *cp4 = '\0';
+ cp4 = cp3;
+ chndl = UCGetLYhndl_byMIME(cp3);
+ if (UCCanTranslateFromTo(chndl,
+ current_char_set)) {
+ chartrans_ok = YES;
+ *cp1 = '\0';
+ me->format = HTAtom_for(cp);
+ StrAllocCopy(me->anchor->charset, cp4);
+ HTAnchor_setUCInfoStage(me->anchor, chndl,
+ UCT_STAGE_MIME,
+ UCT_SETBY_MIME);
+ } else if (chndl < 0) { /* got something but we don't
+ recognize it */
+ chndl = UCLYhndl_for_unrec;
+ if (chndl < 0)
+ /*
+ * UCLYhndl_for_unrec not defined :-( fallback to
+ * UCLYhndl_for_unspec which always valid.
+ */
+ chndl = UCLYhndl_for_unspec; /* always >= 0 */
+ if (UCCanTranslateFromTo(chndl,
+ current_char_set)) {
+ chartrans_ok = YES;
+ *cp1 = '\0';
+ me->format = HTAtom_for(cp);
+ HTAnchor_setUCInfoStage(me->anchor, chndl,
+ UCT_STAGE_MIME,
+ UCT_SETBY_DEFAULT);
+ }
+ } else {
+ /*
+ * Something like 'big5' - we cannot translate it, but
+ * the user may still be able to navigate the links.
+ */
+ *cp1 = '\0';
+ me->format = HTAtom_for(cp);
+ StrAllocCopy(me->anchor->charset, cp4);
+ HTAnchor_setUCInfoStage(me->anchor, chndl,
+ UCT_STAGE_MIME,
+ UCT_SETBY_MIME);
+ }
+ if (chartrans_ok) {
+ LYUCcharset *p_in =
+ HTAnchor_getUCInfoStage(me->anchor,
+ UCT_STAGE_MIME);
+ LYUCcharset *p_out =
+ HTAnchor_setUCInfoStage(me->anchor,
+ current_char_set,
+ UCT_STAGE_HTEXT,
+ UCT_SETBY_DEFAULT);
+
+ if (!p_out)
+ /*
+ * Try again.
+ */
+ p_out =
+ HTAnchor_getUCInfoStage(me->anchor,
+ UCT_STAGE_HTEXT);
+
+ if (!strcmp(p_in->MIMEname,
+ "x-transparent")) {
+ HTPassEightBitRaw = TRUE;
+ HTAnchor_setUCInfoStage(me->anchor,
+ HTAnchor_getUCLYhndl(me->anchor,
+ UCT_STAGE_HTEXT),
+ UCT_STAGE_MIME,
+ UCT_SETBY_DEFAULT);
+ }
+ if (!strcmp(p_out->MIMEname,
+ "x-transparent")) {
+ HTPassEightBitRaw = TRUE;
+ HTAnchor_setUCInfoStage(me->anchor,
+ HTAnchor_getUCLYhndl(me->anchor,
+ UCT_STAGE_MIME),
+ UCT_STAGE_HTEXT,
+ UCT_SETBY_DEFAULT);
+ }
+ if ((p_in->enc != UCT_ENC_CJK)
+#ifdef USE_JAPANESEUTF8_SUPPORT
+ && ((p_in->enc != UCT_ENC_UTF8)
+ || (p_out->enc != UCT_ENC_CJK))
+#endif
+ ) {
+ HTCJK = NOCJK;
+ if (!(p_in->codepoints &
+ UCT_CP_SUBSETOF_LAT1) &&
+ chndl == current_char_set) {
+ HTPassEightBitRaw = TRUE;
+ }
+ } else if (p_out->enc == UCT_ENC_CJK) {
+ Set_HTCJK(p_in->MIMEname, p_out->MIMEname);
+ }
+ } else {
+ /*
+ * Cannot translate. If according to some heuristic the
+ * given charset and the current display character both are
+ * likely to be like ISO-8859 in structure, pretend we have
+ * some kind of match.
+ */
+ BOOL given_is_8859 =
+ (BOOL) (!StrNCmp(cp4, "iso-8859-", 9) &&
+ isdigit(UCH(cp4[9])));
+ BOOL given_is_8859like =
+ (BOOL) (given_is_8859 ||
+ !StrNCmp(cp4, "windows-", 8) ||
+ !StrNCmp(cp4, "cp12", 4) ||
+ !StrNCmp(cp4, "cp-12", 5));
+ BOOL given_and_display_8859like =
+ (BOOL) (given_is_8859like &&
+ (strstr(LYchar_set_names[current_char_set],
+ "ISO-8859") ||
+ strstr(LYchar_set_names[current_char_set],
+ "windows-")));
+
+ if (given_and_display_8859like) {
+ *cp1 = '\0';
+ me->format = HTAtom_for(cp);
+ }
+ if (given_is_8859) {
+ cp1 = &cp4[10];
+ while (*cp1 &&
+ isdigit(UCH(*cp1)))
+ cp1++;
+ *cp1 = '\0';
+ }
+ if (given_and_display_8859like) {
+ StrAllocCopy(me->anchor->charset, cp4);
+ HTPassEightBitRaw = TRUE;
+ }
+ HTAlert(*cp4 ? cp4 : me->anchor->charset);
+ }
+ FREE(cp3);
+ } else {
+ /*
+ * No charset parameter is present. Ignore all other
+ * parameters, as we do when charset is present. - FM
+ */
+ *cp1 = '\0';
+ me->format = HTAtom_for(cp);
+ }
+ }
+ FREE(cp);
+ }
+ /*
+ * If we have an Expires header and haven't already set the no_cache
+ * element for the anchor, check if we should set it based on that header.
+ * - FM
+ */
+ if (me->anchor->no_cache == FALSE &&
+ me->anchor->expires != NULL) {
+ if (!strcmp(me->anchor->expires, "0")) {
+ /*
+ * The value is zero, which we treat as an absolute no-cache
+ * directive. - FM
+ */
+ me->anchor->no_cache = TRUE;
+ } else if (me->anchor->date != NULL) {
+ /*
+ * We have a Date header, so check if the value is less than or
+ * equal to that. - FM
+ */
+ if (LYmktime(me->anchor->expires, TRUE) <=
+ LYmktime(me->anchor->date, TRUE)) {
+ me->anchor->no_cache = TRUE;
+ }
+ } else if (LYmktime(me->anchor->expires, FALSE) == 0) {
+ /*
+ * We don't have a Date header, and the value is in past for us. -
+ * FM
+ */
+ me->anchor->no_cache = TRUE;
+ }
+ }
+ StrAllocCopy(me->anchor->content_type,
+ HTAtom_name(me->format));
+
+ if (me->set_cookie != NULL || me->set_cookie2 != NULL) {
+ LYSetCookie(me->set_cookie,
+ me->set_cookie2,
+ me->anchor->address);
+ FREE(me->set_cookie);
+ FREE(me->set_cookie2);
+ }
+ if (me->pickup_redirection) {
+ if (me->location && *me->location) {
+ redirecting_url = me->location;
+ me->location = NULL;
+ if (me->targetRep != WWW_DEBUG || me->sink)
+ me->head_only = YES;
+
+ } else {
+ permanent_redirection = FALSE;
+ if (me->location) {
+ CTRACE((tfp, "HTTP: 'Location:' is zero-length!\n"));
+ HTAlert(REDIRECTION_WITH_BAD_LOCATION);
+ }
+ CTRACE((tfp, "HTTP: Failed to pick up location.\n"));
+ if (me->location) {
+ FREE(me->location);
+ } else {
+ HTAlert(REDIRECTION_WITH_NO_LOCATION);
+ }
+ }
+ }
+ CTRACE((tfp, "...pumpData finished reading header\n"));
+ if (me->head_only) {
+ /* We are done! - kw */
+ me->state = MIME_IGNORE;
+ } else {
+
+ if (me->no_streamstack) {
+ me->target = me->sink;
+ } else {
+ if (!me->compression_encoding) {
+ CTRACE((tfp,
+ "HTMIME: MIME Content-Type is '%s', converting to '%s'\n",
+ HTAtom_name(me->format), HTAtom_name(me->targetRep)));
+ } else {
+ /*
+ * Change the format to that for "www/compressed" and set up a
+ * stream to deal with it. - FM
+ */
+ CTRACE((tfp, "HTMIME: MIME Content-Type is '%s',\n",
+ HTAtom_name(me->format)));
+ me->format = HTAtom_for("www/compressed");
+ CTRACE((tfp, " Treating as '%s'. Converting to '%s'\n",
+ HTAtom_name(me->format), HTAtom_name(me->targetRep)));
+ FREE(me->compression_encoding);
+ }
+ me->target = HTStreamStack(me->format, me->targetRep,
+ me->sink, me->anchor);
+ if (!me->target) {
+ CTRACE((tfp, "HTMIME: Can't translate! ** \n"));
+ me->target = me->sink; /* Cheat */
+ }
+ }
+ if (me->target) {
+ me->targetClass = *me->target->isa;
+ /*
+ * Pump rest of data right through, according to the transfer encoding.
+ */
+ me->state = (me->chunked_encoding
+ ? MIME_CHUNKED
+ : MIME_TRANSPARENT);
+ } else {
+ me->state = MIME_IGNORE; /* What else to do? */
+ }
+ if (me->refresh_url != NULL && !content_is_compressed(me)) {
+ char *url = NULL;
+ char *num = NULL;
+ char *txt = NULL;
+ const char *base = ""; /* FIXME: refresh_url may be relative to doc */
+
+ LYParseRefreshURL(me->refresh_url, &num, &url);
+ if (url != NULL && me->format == WWW_HTML) {
+ CTRACE((tfp,
+ "Formatting refresh-url as first line of result\n"));
+ HTSprintf0(&txt, gettext("Refresh: "));
+ HTSprintf(&txt, gettext("%s seconds "), num);
+ dequote(url);
+ HTSprintf(&txt, "<a href=\"%s%s\">%s</a><br>", base, url, url);
+ CTRACE((tfp, "URL %s%s\n", base, url));
+ (me->isa->put_string) (me, txt);
+ free(txt);
+ }
+ FREE(num);
+ FREE(url);
+ }
+ }
+ CTRACE((tfp, "...end of pumpData, copied %"
+ PRI_off_t " vs %"
+ PRI_off_t "\n",
+ CAST_off_t (me->anchor->actual_length),
+ CAST_off_t (me->anchor->content_length)));
+ me->anchor->actual_length = 0;
+ return HT_OK;
+}
+
+static int dispatchField(HTStream *me)
+{
+ int i, j;
+ char *cp;
+
+ *me->value_pointer = '\0';
+
+ cp = me->value_pointer;
+ while ((cp > me->value) && *(--cp) == ' ') /* S/390 -- gil -- 0146 */
+ /*
+ * Trim trailing spaces.
+ */
+ *cp = '\0';
+
+ switch (me->field) {
+ case miACCEPT_RANGES:
+ HTMIME_TrimDoubleQuotes(me->value);
+ CTRACE((tfp, "HTMIME: PICKED UP Accept-Ranges: '%s'\n",
+ me->value));
+ break;
+ case miAGE:
+ HTMIME_TrimDoubleQuotes(me->value);
+ CTRACE((tfp, "HTMIME: PICKED UP Age: '%s'\n",
+ me->value));
+ break;
+ case miALLOW:
+ HTMIME_TrimDoubleQuotes(me->value);
+ CTRACE((tfp, "HTMIME: PICKED UP Allow: '%s'\n",
+ me->value));
+ break;
+ case miALTERNATES:
+ HTMIME_TrimDoubleQuotes(me->value);
+ CTRACE((tfp, "HTMIME: PICKED UP Alternates: '%s'\n",
+ me->value));
+ break;
+ case miCACHE_CONTROL:
+ HTMIME_TrimDoubleQuotes(me->value);
+ CTRACE((tfp, "HTMIME: PICKED UP Cache-Control: '%s'\n",
+ me->value));
+ if (me->value[0] == '\0')
+ break;
+ /*
+ * Convert to lowercase and indicate in anchor. - FM
+ */
+ LYLowerCase(me->value);
+ StrAllocCopy(me->anchor->cache_control, me->value);
+ /*
+ * Check whether to set no_cache for the anchor. - FM
+ */
+ {
+ char *cp1, *cp0 = me->value;
+
+ while ((cp1 = strstr(cp0, "no-cache")) != NULL) {
+ cp1 += 8;
+ while (*cp1 != '\0' && WHITE(*cp1))
+ cp1++;
+ if (*cp1 == '\0' || *cp1 == ';') {
+ me->anchor->no_cache = TRUE;
+ break;
+ }
+ cp0 = cp1;
+ }
+ if (me->anchor->no_cache == TRUE)
+ break;
+ cp0 = me->value;
+ while ((cp1 = strstr(cp0, "max-age")) != NULL) {
+ cp1 += 7;
+ while (*cp1 != '\0' && WHITE(*cp1))
+ cp1++;
+ if (*cp1 == '=') {
+ cp1++;
+ while (*cp1 != '\0' && WHITE(*cp1))
+ cp1++;
+ if (isdigit(UCH(*cp1))) {
+ cp0 = cp1;
+ while (isdigit(UCH(*cp1)))
+ cp1++;
+ if (*cp0 == '0' && cp1 == (cp0 + 1)) {
+ me->anchor->no_cache = TRUE;
+ break;
+ }
+ }
+ }
+ cp0 = cp1;
+ }
+ }
+ break;
+ case miCOOKIE:
+ HTMIME_TrimDoubleQuotes(me->value);
+ CTRACE((tfp, "HTMIME: PICKED UP Cookie: '%s'\n",
+ me->value));
+ break;
+ case miCONNECTION:
+ HTMIME_TrimDoubleQuotes(me->value);
+ CTRACE((tfp, "HTMIME: PICKED UP Connection: '%s'\n",
+ me->value));
+ break;
+ case miCONTENT_BASE:
+ HTMIME_TrimDoubleQuotes(me->value);
+ CTRACE((tfp, "HTMIME: PICKED UP Content-Base: '%s'\n",
+ me->value));
+ if (me->value[0] == '\0')
+ break;
+ /*
+ * Indicate in anchor. - FM
+ */
+ StrAllocCopy(me->anchor->content_base, me->value);
+ break;
+ case miCONTENT_DISPOSITION:
+ HTMIME_TrimDoubleQuotes(me->value);
+ CTRACE((tfp, "HTMIME: PICKED UP Content-Disposition: '%s'\n",
+ me->value));
+ if (me->value[0] == '\0')
+ break;
+ /*
+ * Indicate in anchor. - FM
+ */
+ StrAllocCopy(me->anchor->content_disposition, me->value);
+ /*
+ * It's not clear yet from existing RFCs and IDs whether we should be
+ * looking for file;, attachment;, and/or inline; before the
+ * filename=value, so we'll just search for "filename" followed by '='
+ * and just hope we get the intended value. It is purely a suggested
+ * name, anyway. - FM
+ */
+ cp = me->anchor->content_disposition;
+ while (*cp != '\0' && strncasecomp(cp, "filename", 8))
+ cp++;
+ if (*cp == '\0')
+ break;
+ cp += 8;
+ while ((*cp != '\0') && (WHITE(*cp) || *cp == '='))
+ cp++;
+ if (*cp == '\0')
+ break;
+ while (*cp != '\0' && WHITE(*cp))
+ cp++;
+ if (*cp == '\0')
+ break;
+ StrAllocCopy(me->anchor->SugFname, cp);
+ if (*me->anchor->SugFname == '"') {
+ if ((cp = StrChr((me->anchor->SugFname + 1),
+ '"')) != NULL) {
+ *(cp + 1) = '\0';
+ HTMIME_TrimDoubleQuotes(me->anchor->SugFname);
+ } else {
+ FREE(me->anchor->SugFname);
+ break;
+ }
+ }
+ cp = me->anchor->SugFname;
+ while (*cp != '\0' && !WHITE(*cp))
+ cp++;
+ *cp = '\0';
+ if (*me->anchor->SugFname == '\0')
+ FREE(me->anchor->SugFname);
+ break;
+ case miCONTENT_ENCODING:
+ HTMIME_TrimDoubleQuotes(me->value);
+ CTRACE((tfp, "HTMIME: PICKED UP Content-Encoding: '%s'\n",
+ me->value));
+ if (me->value[0] == '\0' ||
+ !strcasecomp(me->value, "identity"))
+ break;
+ /*
+ * Convert to lowercase and indicate in anchor. - FM
+ */
+ LYLowerCase(me->value);
+ StrAllocCopy(me->anchor->content_encoding, me->value);
+ FREE(me->compression_encoding);
+ if (content_is_compressed(me)) {
+ /*
+ * Save it to use as a flag for setting up a "www/compressed"
+ * target. - FM
+ */
+ StrAllocCopy(me->compression_encoding, me->value);
+ } else {
+ /*
+ * Some server indicated "8bit", "7bit" or "binary"
+ * inappropriately. We'll ignore it. - FM
+ */
+ CTRACE((tfp, " Ignoring it!\n"));
+ }
+ break;
+ case miCONTENT_FEATURES:
+ HTMIME_TrimDoubleQuotes(me->value);
+ CTRACE((tfp, "HTMIME: PICKED UP Content-Features: '%s'\n",
+ me->value));
+ break;
+ case miCONTENT_LANGUAGE:
+ HTMIME_TrimDoubleQuotes(me->value);
+ CTRACE((tfp, "HTMIME: PICKED UP Content-Language: '%s'\n",
+ me->value));
+ if (me->value[0] == '\0')
+ break;
+ /*
+ * Convert to lowercase and indicate in anchor. - FM
+ */
+ LYLowerCase(me->value);
+ StrAllocCopy(me->anchor->content_language, me->value);
+ break;
+ case miCONTENT_LENGTH:
+ HTMIME_TrimDoubleQuotes(me->value);
+ CTRACE((tfp, "HTMIME: PICKED UP Content-Length: '%s'\n",
+ me->value));
+ if (me->value[0] == '\0')
+ break;
+ /*
+ * Convert to integer and indicate in anchor. - FM
+ */
+ me->anchor->content_length = LYatoll(me->value);
+ if (me->anchor->content_length < 0)
+ me->anchor->content_length = 0;
+ CTRACE((tfp, " Converted to integer: '%" PRI_off_t "'\n",
+ CAST_off_t (me->anchor->content_length)));
+ break;
+ case miCONTENT_LOCATION:
+ HTMIME_TrimDoubleQuotes(me->value);
+ CTRACE((tfp, "HTMIME: PICKED UP Content-Location: '%s'\n",
+ me->value));
+ if (me->value[0] == '\0')
+ break;
+ /*
+ * Indicate in anchor. - FM
+ */
+ StrAllocCopy(me->anchor->content_location, me->value);
+ break;
+ case miCONTENT_MD5:
+ HTMIME_TrimDoubleQuotes(me->value);
+ CTRACE((tfp, "HTMIME: PICKED UP Content-MD5: '%s'\n",
+ me->value));
+ if (me->value[0] == '\0')
+ break;
+ /*
+ * Indicate in anchor. - FM
+ */
+ StrAllocCopy(me->anchor->content_md5, me->value);
+ break;
+ case miCONTENT_RANGE:
+ HTMIME_TrimDoubleQuotes(me->value);
+ CTRACE((tfp, "HTMIME: PICKED UP Content-Range: '%s'\n",
+ me->value));
+ break;
+ case miCONTENT_TRANSFER_ENCODING:
+ HTMIME_TrimDoubleQuotes(me->value);
+ CTRACE((tfp, "HTMIME: PICKED UP Content-Transfer-Encoding: '%s'\n",
+ me->value));
+ if (me->value[0] == '\0')
+ break;
+ /*
+ * Force the Content-Transfer-Encoding value to all lower case. - FM
+ */
+ LYLowerCase(me->value);
+ me->c_t_encoding = HTAtom_for(me->value);
+ break;
+ case miCONTENT_TYPE:
+ HTMIME_TrimDoubleQuotes(me->value);
+ CTRACE((tfp, "HTMIME: PICKED UP Content-Type: '%s'\n",
+ me->value));
+ if (me->value[0] == '\0')
+ break;
+ /*
+ * Force the Content-Type value to all lower case and strip spaces and
+ * double-quotes. - FM
+ */
+ for (i = 0, j = 0; me->value[i]; i++) {
+ if (me->value[i] != ' ' && me->value[i] != '"') {
+ me->value[j++] = (char) TOLOWER(me->value[i]);
+ }
+ }
+ me->value[j] = '\0';
+ me->format = HTAtom_for(me->value);
+ StrAllocCopy(me->anchor->content_type_params, me->value);
+ break;
+ case miDATE:
+ HTMIME_TrimDoubleQuotes(me->value);
+ CTRACE((tfp, "HTMIME: PICKED UP Date: '%s'\n",
+ me->value));
+ if (me->value[0] == '\0')
+ break;
+ /*
+ * Indicate in anchor. - FM
+ */
+ StrAllocCopy(me->anchor->date, me->value);
+ break;
+ case miETAG:
+ /* Do not trim double quotes: an entity tag consists of an opaque
+ * quoted string, possibly prefixed by a weakness indicator.
+ */
+ CTRACE((tfp, "HTMIME: PICKED UP ETag: %s\n",
+ me->value));
+ if (me->value[0] == '\0')
+ break;
+ /*
+ * Indicate in anchor. - FM
+ */
+ StrAllocCopy(me->anchor->ETag, me->value);
+ break;
+ case miEXPIRES:
+ HTMIME_TrimDoubleQuotes(me->value);
+ CTRACE((tfp, "HTMIME: PICKED UP Expires: '%s'\n",
+ me->value));
+ if (me->value[0] == '\0')
+ break;
+ /*
+ * Indicate in anchor. - FM
+ */
+ StrAllocCopy(me->anchor->expires, me->value);
+ break;
+ case miKEEP_ALIVE:
+ HTMIME_TrimDoubleQuotes(me->value);
+ CTRACE((tfp, "HTMIME: PICKED UP Keep-Alive: '%s'\n",
+ me->value));
+ break;
+ case miLAST_MODIFIED:
+ HTMIME_TrimDoubleQuotes(me->value);
+ CTRACE((tfp, "HTMIME: PICKED UP Last-Modified: '%s'\n",
+ me->value));
+ if (me->value[0] == '\0')
+ break;
+ /*
+ * Indicate in anchor. - FM
+ */
+ StrAllocCopy(me->anchor->last_modified, me->value);
+ break;
+ case miLINK:
+ HTMIME_TrimDoubleQuotes(me->value);
+ CTRACE((tfp, "HTMIME: PICKED UP Link: '%s'\n",
+ me->value));
+ break;
+ case miLOCATION:
+ HTMIME_TrimDoubleQuotes(me->value);
+ CTRACE((tfp, "HTMIME: PICKED UP Location: '%s'\n",
+ me->value));
+ if (me->pickup_redirection && !me->location) {
+ StrAllocCopy(me->location, me->value);
+ } else {
+ CTRACE((tfp, "HTMIME: *** Ignoring Location!\n"));
+ }
+ break;
+ case miPRAGMA:
+ HTMIME_TrimDoubleQuotes(me->value);
+ CTRACE((tfp, "HTMIME: PICKED UP Pragma: '%s'\n",
+ me->value));
+ if (me->value[0] == '\0')
+ break;
+ /*
+ * Check whether to set no_cache for the anchor. - FM
+ */
+ if (!strcmp(me->value, "no-cache"))
+ me->anchor->no_cache = TRUE;
+ break;
+ case miPROXY_AUTHENTICATE:
+ HTMIME_TrimDoubleQuotes(me->value);
+ CTRACE((tfp, "HTMIME: PICKED UP Proxy-Authenticate: '%s'\n",
+ me->value));
+ break;
+ case miPUBLIC:
+ HTMIME_TrimDoubleQuotes(me->value);
+ CTRACE((tfp, "HTMIME: PICKED UP Public: '%s'\n",
+ me->value));
+ break;
+ case miREFRESH: /* nonstandard: Netscape */
+ HTMIME_TrimDoubleQuotes(me->value);
+ CTRACE((tfp, "HTMIME: PICKED UP Refresh: '%s'\n",
+ me->value));
+ StrAllocCopy(me->refresh_url, me->value);
+ break;
+ case miRETRY_AFTER:
+ HTMIME_TrimDoubleQuotes(me->value);
+ CTRACE((tfp, "HTMIME: PICKED UP Retry-After: '%s'\n",
+ me->value));
+ break;
+ case miSAFE:
+ HTMIME_TrimDoubleQuotes(me->value);
+ CTRACE((tfp, "HTMIME: PICKED UP Safe: '%s'\n",
+ me->value));
+ if (me->value[0] == '\0')
+ break;
+ /*
+ * Indicate in anchor if "YES" or "TRUE". - FM
+ */
+ if (!strcasecomp(me->value, "YES") ||
+ !strcasecomp(me->value, "TRUE")) {
+ me->anchor->safe = TRUE;
+ } else if (!strcasecomp(me->value, "NO") ||
+ !strcasecomp(me->value, "FALSE")) {
+ /*
+ * If server explicitly tells us that it has changed its mind,
+ * reset flag in anchor. - kw
+ */
+ me->anchor->safe = FALSE;
+ }
+ break;
+ case miSERVER:
+ HTMIME_TrimDoubleQuotes(me->value);
+ CTRACE((tfp, "HTMIME: PICKED UP Server: '%s'\n",
+ me->value));
+ if (me->value[0] == '\0')
+ break;
+ /*
+ * Indicate in anchor. - FM
+ */
+ StrAllocCopy(me->anchor->server, me->value);
+ break;
+ case miSET_COOKIE1:
+ HTMIME_TrimDoubleQuotes(me->value);
+ CTRACE((tfp, "HTMIME: PICKED UP Set-Cookie: '%s'\n",
+ me->value));
+ if (me->set_cookie == NULL) {
+ StrAllocCopy(me->set_cookie, me->value);
+ } else {
+ StrAllocCat(me->set_cookie, ", ");
+ StrAllocCat(me->set_cookie, me->value);
+ }
+ break;
+ case miSET_COOKIE2:
+ HTMIME_TrimDoubleQuotes(me->value);
+ CTRACE((tfp, "HTMIME: PICKED UP Set-Cookie2: '%s'\n",
+ me->value));
+ if (me->set_cookie2 == NULL) {
+ StrAllocCopy(me->set_cookie2, me->value);
+ } else {
+ StrAllocCat(me->set_cookie2, ", ");
+ StrAllocCat(me->set_cookie2, me->value);
+ }
+ break;
+ case miTITLE:
+ HTMIME_TrimDoubleQuotes(me->value);
+ CTRACE((tfp, "HTMIME: PICKED UP Title: '%s'\n",
+ me->value));
+ break;
+ case miTRANSFER_ENCODING:
+ HTMIME_TrimDoubleQuotes(me->value);
+ CTRACE((tfp, "HTMIME: PICKED UP Transfer-Encoding: '%s'\n",
+ me->value));
+ if (!strcmp(me->value, "chunked"))
+ me->chunked_encoding = YES;
+ break;
+ case miUPGRADE:
+ HTMIME_TrimDoubleQuotes(me->value);
+ CTRACE((tfp, "HTMIME: PICKED UP Upgrade: '%s'\n",
+ me->value));
+ break;
+ case miURI:
+ HTMIME_TrimDoubleQuotes(me->value);
+ CTRACE((tfp, "HTMIME: PICKED UP URI: '%s'\n",
+ me->value));
+ break;
+ case miVARY:
+ HTMIME_TrimDoubleQuotes(me->value);
+ CTRACE((tfp, "HTMIME: PICKED UP Vary: '%s'\n",
+ me->value));
+ break;
+ case miVIA:
+ HTMIME_TrimDoubleQuotes(me->value);
+ CTRACE((tfp, "HTMIME: PICKED UP Via: '%s'\n",
+ me->value));
+ break;
+ case miWARNING:
+ HTMIME_TrimDoubleQuotes(me->value);
+ CTRACE((tfp, "HTMIME: PICKED UP Warning: '%s'\n",
+ me->value));
+ break;
+ case miWWW_AUTHENTICATE:
+ HTMIME_TrimDoubleQuotes(me->value);
+ CTRACE((tfp, "HTMIME: PICKED UP WWW-Authenticate: '%s'\n",
+ me->value));
+ break;
+ default: /* Should never get here */
+ return HT_ERROR;
+ }
+ return HT_OK;
+}
+
+/*_________________________________________________________________________
+ *
+ * A C T I O N R O U T I N E S
+ */
+
+/* Character handling
+ * ------------------
+ *
+ * This is a FSM parser. It ignores field names it does not understand.
+ * Folded header fields are recognized. Lines without a fieldname at
+ * the beginning (that are not folded continuation lines) are ignored
+ * as unknown field names. Fields with empty values are not picked up.
+ */
+static void HTMIME_put_character(HTStream *me, int c)
+{
+ if (me->anchor->inHEAD) {
+ me->anchor->header_length++;
+ }
+ switch (me->state) {
+ begin_transparent:
+ case MIME_TRANSPARENT:
+ me->anchor->actual_length += 1;
+ (me->targetClass.put_character) (me->target, c);
+ return;
+
+ /* RFC-2616 describes chunked transfer coding */
+ case mcCHUNKED_DATA:
+ (*me->targetClass.put_character) (me->target, c);
+ me->chunked_size--;
+ if (me->chunked_size <= 0)
+ me->state = mcCHUNKED_DATA_CR;
+ return;
+
+ case mcCHUNKED_DATA_CR:
+ me->state = mcCHUNKED_DATA_LF;
+ if (c == CR) {
+ return;
+ }
+ /* FALLTHRU */
+
+ case mcCHUNKED_DATA_LF:
+ me->state = MIME_CHUNKED;
+ if (c == LF) {
+ return;
+ }
+
+ CTRACE((tfp, "HTIME_put_character expected LF in chunked data\n"));
+ me->state = MIME_TRANSPARENT;
+ goto begin_transparent;
+
+ /* FALLTHRU */
+ begin_chunked:
+ case MIME_CHUNKED:
+ me->chunked_size = 0;
+ me->state = mcCHUNKED_COUNT_DIGIT;
+
+ /* FALLTHRU */
+ case mcCHUNKED_COUNT_DIGIT:
+ if (isxdigit(UCH(c))) {
+ me->chunked_size <<= 4;
+ if (isdigit(UCH(c)))
+ me->chunked_size += UCH(c) - '0';
+ else
+ me->chunked_size += TOUPPER(UCH(c)) - 'A' + 10;
+ return;
+ }
+ if (c == ';')
+ me->state = mcCHUNKED_EXTENSION;
+
+ /* FALLTHRU */
+ case mcCHUNKED_EXTENSION:
+ if (c != CR && c != LF) {
+ return;
+ }
+ me->state = mcCHUNKED_COUNT_CR;
+
+ /* FALLTHRU */
+ case mcCHUNKED_COUNT_CR:
+ me->state = mcCHUNKED_COUNT_LF;
+ if (c == CR) {
+ return;
+ }
+
+ /* FALLTHRU */
+ case mcCHUNKED_COUNT_LF:
+ me->state = ((me->chunked_size != 0)
+ ? mcCHUNKED_DATA
+ : MIME_CHUNKED);
+ if (c == LF) {
+ return;
+ }
+ goto begin_chunked;
+
+ default:
+ break;
+ }
+
+ /*
+ * This slightly simple conversion just strips CR and turns LF to newline.
+ * On unix LF is \n but on Mac \n is CR for example. See NetToText for an
+ * implementation which preserves single CR or LF.
+ */
+ if (me->net_ascii) {
+ /*
+ * <sigh> This is evidence that at one time, this code supported
+ * local character sets other than ASCII. But there is so much
+ * code in HTTP.c that depends on line_buffer's having been
+ * translated to local character set that I needed to put the
+ * FROMASCII translation there, leaving this translation purely
+ * destructive. -- gil
+ */
+ /* S/390 -- gil -- 0118 */
+#ifndef NOT_ASCII
+ c = FROMASCII(c);
+#endif /* NOT_ASCII */
+ if (c == CR)
+ return;
+ else if (c == LF)
+ c = '\n';
+ }
+
+ switch (me->state) {
+
+ case MIME_IGNORE:
+ return;
+
+ case MIME_TRANSPARENT: /* Not reached see above */
+ case MIME_CHUNKED:
+ case mcCHUNKED_COUNT_DIGIT:
+ case mcCHUNKED_COUNT_CR:
+ case mcCHUNKED_COUNT_LF:
+ case mcCHUNKED_EXTENSION:
+ case mcCHUNKED_DATA:
+ case mcCHUNKED_DATA_CR:
+ case mcCHUNKED_DATA_LF:
+ return;
+
+ case MIME_NET_ASCII:
+ (*me->targetClass.put_character) (me->target, c); /* MUST BE FAST */
+ return;
+
+ case miNEWLINE:
+ if (c != '\n' && WHITE(c)) { /* Folded line */
+ me->state = me->fold_state; /* pop state before newline */
+ if (me->state == miGET_VALUE &&
+ me->value_pointer && me->value_pointer != me->value &&
+ !WHITE(*(me->value_pointer - 1))) {
+ c = ' ';
+ goto GET_VALUE; /* will add space to value if it fits - kw */
+ }
+ break;
+ } else if (me->fold_state == miGET_VALUE) {
+ /* Got a field, and now we know it's complete - so
+ * act on it. - kw */
+ dispatchField(me);
+ }
+ /* FALLTHRU */
+
+ case miBEGINNING_OF_LINE:
+ me->net_ascii = YES;
+ switch (c) {
+ case 'a':
+ case 'A':
+ me->state = miA;
+ CTRACE((tfp, "HTMIME: Got 'A' at beginning of line, state now A\n"));
+ break;
+
+ case 'c':
+ case 'C':
+ me->state = miC;
+ CTRACE((tfp, "HTMIME: Got 'C' at beginning of line, state now C\n"));
+ break;
+
+ case 'd':
+ case 'D':
+ me->check_pointer = "ate:";
+ me->if_ok = miDATE;
+ me->state = miCHECK;
+ CTRACE((tfp,
+ "HTMIME: Got 'D' at beginning of line, checking for 'ate:'\n"));
+ break;
+
+ case 'e':
+ case 'E':
+ me->state = miE;
+ CTRACE((tfp, "HTMIME: Got 'E' at beginning of line, state now E\n"));
+ break;
+
+ case 'k':
+ case 'K':
+ me->check_pointer = "eep-alive:";
+ me->if_ok = miKEEP_ALIVE;
+ me->state = miCHECK;
+ CTRACE((tfp,
+ "HTMIME: Got 'K' at beginning of line, checking for 'eep-alive:'\n"));
+ break;
+
+ case 'l':
+ case 'L':
+ me->state = miL;
+ CTRACE((tfp, "HTMIME: Got 'L' at beginning of line, state now L\n"));
+ break;
+
+ case 'p':
+ case 'P':
+ me->state = miP;
+ CTRACE((tfp, "HTMIME: Got 'P' at beginning of line, state now P\n"));
+ break;
+
+ case 'r':
+ case 'R':
+ me->state = miR;
+ CTRACE((tfp, "HTMIME: Got 'R' at beginning of line, state now R\n"));
+ break;
+
+ case 's':
+ case 'S':
+ me->state = miS;
+ CTRACE((tfp, "HTMIME: Got 'S' at beginning of line, state now S\n"));
+ break;
+
+ case 't':
+ case 'T':
+ me->state = miT;
+ CTRACE((tfp, "HTMIME: Got 'T' at beginning of line, state now T\n"));
+ break;
+
+ case 'u':
+ case 'U':
+ me->state = miU;
+ CTRACE((tfp, "HTMIME: Got 'U' at beginning of line, state now U\n"));
+ break;
+
+ case 'v':
+ case 'V':
+ me->state = miV;
+ CTRACE((tfp, "HTMIME: Got 'V' at beginning of line, state now V\n"));
+ break;
+
+ case 'w':
+ case 'W':
+ me->state = miW;
+ CTRACE((tfp, "HTMIME: Got 'W' at beginning of line, state now W\n"));
+ break;
+
+ case '\n': /* Blank line: End of Header! */
+ {
+ me->anchor->inHEAD = FALSE;
+ CTRACE((tfp, "HTMIME length %" PRI_off_t "\n",
+ CAST_off_t (me->anchor->header_length)));
+ me->net_ascii = NO;
+ pumpData(me);
+ }
+ break;
+
+ default:
+ goto bad_field_name;
+
+ } /* switch on character */
+ break;
+
+ case miA: /* Check for 'c','g' or 'l' */
+ switch (c) {
+ case 'c':
+ case 'C':
+ me->check_pointer = "cept-ranges:";
+ me->if_ok = miACCEPT_RANGES;
+ me->state = miCHECK;
+ CTRACE((tfp,
+ "HTMIME: Was A, found C, checking for 'cept-ranges:'\n"));
+ break;
+
+ case 'g':
+ case 'G':
+ me->check_pointer = "e:";
+ me->if_ok = miAGE;
+ me->state = miCHECK;
+ CTRACE((tfp, "HTMIME: Was A, found G, checking for 'e:'\n"));
+ break;
+
+ case 'l':
+ case 'L':
+ me->state = miAL;
+ CTRACE((tfp, "HTMIME: Was A, found L, state now AL'\n"));
+ break;
+
+ default:
+ CTRACE((tfp,
+ "HTMIME: Bad character `%c' found where `%s' expected\n",
+ c, "'g' or 'l'"));
+ goto bad_field_name;
+
+ } /* switch on character */
+ break;
+
+ case miAL: /* Check for 'l' or 't' */
+ switch (c) {
+ case 'l':
+ case 'L':
+ me->check_pointer = "ow:";
+ me->if_ok = miALLOW;
+ me->state = miCHECK;
+ CTRACE((tfp, "HTMIME: Was AL, found L, checking for 'ow:'\n"));
+ break;
+
+ case 't':
+ case 'T':
+ me->check_pointer = "ernates:";
+ me->if_ok = miALTERNATES;
+ me->state = miCHECK;
+ CTRACE((tfp, "HTMIME: Was AL, found T, checking for 'ernates:'\n"));
+ break;
+
+ default:
+ CTRACE((tfp,
+ "HTMIME: Bad character `%c' found where `%s' expected\n",
+ c, "'l' or 't'"));
+ goto bad_field_name;
+
+ } /* switch on character */
+ break;
+
+ case miC: /* Check for 'a' or 'o' */
+ switch (c) {
+ case 'a':
+ case 'A':
+ me->check_pointer = "che-control:";
+ me->if_ok = miCACHE_CONTROL;
+ me->state = miCHECK;
+ CTRACE((tfp,
+ "HTMIME: Was C, found A, checking for 'che-control:'\n"));
+ break;
+
+ case 'o':
+ case 'O':
+ me->state = miCO;
+ CTRACE((tfp, "HTMIME: Was C, found O, state now CO'\n"));
+ break;
+
+ default:
+ CTRACE((tfp,
+ "HTMIME: Bad character `%c' found where `%s' expected\n",
+ c, "'a' or 'o'"));
+ goto bad_field_name;
+
+ } /* switch on character */
+ break;
+
+ case miCO: /* Check for 'n' or 'o' */
+ switch (c) {
+ case 'n':
+ case 'N':
+ me->state = miCON;
+ CTRACE((tfp, "HTMIME: Was CO, found N, state now CON\n"));
+ break;
+
+ case 'o':
+ case 'O':
+ me->check_pointer = "kie:";
+ me->if_ok = miCOOKIE;
+ me->state = miCHECK;
+ CTRACE((tfp, "HTMIME: Was CO, found O, checking for 'kie:'\n"));
+ break;
+
+ default:
+ CTRACE((tfp,
+ "HTMIME: Bad character `%c' found where `%s' expected\n",
+ c, "'n' or 'o'"));
+ goto bad_field_name;
+
+ } /* switch on character */
+ break;
+
+ case miCON: /* Check for 'n' or 't' */
+ switch (c) {
+ case 'n':
+ case 'N':
+ me->check_pointer = "ection:";
+ me->if_ok = miCONNECTION;
+ me->state = miCHECK;
+ CTRACE((tfp, "HTMIME: Was CON, found N, checking for 'ection:'\n"));
+ break;
+
+ case 't':
+ case 'T':
+ me->check_pointer = "ent-";
+ me->if_ok = miCONTENT_;
+ me->state = miCHECK;
+ CTRACE((tfp, "HTMIME: Was CON, found T, checking for 'ent-'\n"));
+ break;
+
+ default:
+ CTRACE((tfp,
+ "HTMIME: Bad character `%c' found where `%s' expected\n",
+ c, "'n' or 't'"));
+ goto bad_field_name;
+
+ } /* switch on character */
+ break;
+
+ case miE: /* Check for 't' or 'x' */
+ switch (c) {
+ case 't':
+ case 'T':
+ me->check_pointer = "ag:";
+ me->if_ok = miETAG;
+ me->state = miCHECK;
+ CTRACE((tfp, "HTMIME: Was E, found T, checking for 'ag:'\n"));
+ break;
+
+ case 'x':
+ case 'X':
+ me->check_pointer = "pires:";
+ me->if_ok = miEXPIRES;
+ me->state = miCHECK;
+ CTRACE((tfp, "HTMIME: Was E, found X, checking for 'pires:'\n"));
+ break;
+
+ default:
+ CTRACE((tfp,
+ "HTMIME: Bad character `%c' found where `%s' expected\n",
+ c, "'t' or 'x'"));
+ goto bad_field_name;
+
+ } /* switch on character */
+ break;
+
+ case miL: /* Check for 'a', 'i' or 'o' */
+ switch (c) {
+ case 'a':
+ case 'A':
+ me->check_pointer = "st-modified:";
+ me->if_ok = miLAST_MODIFIED;
+ me->state = miCHECK;
+ CTRACE((tfp,
+ "HTMIME: Was L, found A, checking for 'st-modified:'\n"));
+ break;
+
+ case 'i':
+ case 'I':
+ me->check_pointer = "nk:";
+ me->if_ok = miLINK;
+ me->state = miCHECK;
+ CTRACE((tfp, "HTMIME: Was L, found I, checking for 'nk:'\n"));
+ break;
+
+ case 'o':
+ case 'O':
+ me->check_pointer = "cation:";
+ me->if_ok = miLOCATION;
+ me->state = miCHECK;
+ CTRACE((tfp, "HTMIME: Was L, found O, checking for 'cation:'\n"));
+ break;
+
+ default:
+ CTRACE((tfp,
+ "HTMIME: Bad character `%c' found where `%s' expected\n",
+ c, "'a', 'i' or 'o'"));
+ goto bad_field_name;
+
+ } /* switch on character */
+ break;
+
+ case miP: /* Check for 'r' or 'u' */
+ switch (c) {
+ case 'r':
+ case 'R':
+ me->state = miPR;
+ CTRACE((tfp, "HTMIME: Was P, found R, state now PR'\n"));
+ break;
+
+ case 'u':
+ case 'U':
+ me->check_pointer = "blic:";
+ me->if_ok = miPUBLIC;
+ me->state = miCHECK;
+ CTRACE((tfp, "HTMIME: Was P, found U, checking for 'blic:'\n"));
+ break;
+
+ default:
+ CTRACE((tfp,
+ "HTMIME: Bad character `%c' found where `%s' expected\n",
+ c, "'r' or 'u'"));
+ goto bad_field_name;
+
+ } /* switch on character */
+ break;
+
+ case miPR: /* Check for 'a' or 'o' */
+ switch (c) {
+ case 'a':
+ case 'A':
+ me->check_pointer = "gma:";
+ me->if_ok = miPRAGMA;
+ me->state = miCHECK;
+ CTRACE((tfp, "HTMIME: Was PR, found A, checking for 'gma'\n"));
+ break;
+
+ case 'o':
+ case 'O':
+ me->check_pointer = "xy-authenticate:";
+ me->if_ok = miPROXY_AUTHENTICATE;
+ me->state = miCHECK;
+ CTRACE((tfp,
+ "HTMIME: Was PR, found O, checking for 'xy-authenticate'\n"));
+ break;
+
+ default:
+ CTRACE((tfp,
+ "HTMIME: Bad character `%c' found where `%s' expected\n",
+ c, "'a' or 'o'"));
+ goto bad_field_name;
+
+ } /* switch on character */
+ break;
+
+ case miR: /* Check for 'e' */
+ switch (c) {
+ case 'e':
+ case 'E':
+ me->state = miRE;
+ CTRACE((tfp, "HTMIME: Was R, found E\n"));
+ break;
+ default:
+ CTRACE((tfp,
+ "HTMIME: Bad character `%c' found where `%s' expected\n",
+ c, "'e'"));
+ goto bad_field_name;
+
+ } /* switch on character */
+ break;
+
+ case miRE: /* Check for 'a' or 'o' */
+ switch (c) {
+ case 'f':
+ case 'F': /* nonstandard: Netscape */
+ me->check_pointer = "resh:";
+ me->if_ok = miREFRESH;
+ me->state = miCHECK;
+ CTRACE((tfp, "HTMIME: Was RE, found F, checking for '%s'\n", me->check_pointer));
+ break;
+
+ case 't':
+ case 'T':
+ me->check_pointer = "ry-after:";
+ me->if_ok = miRETRY_AFTER;
+ me->state = miCHECK;
+ CTRACE((tfp, "HTMIME: Was RE, found T, checking for '%s'\n", me->check_pointer));
+ break;
+
+ default:
+ CTRACE((tfp,
+ "HTMIME: Bad character `%c' found where `%s' expected\n",
+ c, "'f' or 't'"));
+ goto bad_field_name;
+
+ } /* switch on character */
+ break;
+
+ case miS: /* Check for 'a' or 'e' */
+ switch (c) {
+ case 'a':
+ case 'A':
+ me->check_pointer = "fe:";
+ me->if_ok = miSAFE;
+ me->state = miCHECK;
+ CTRACE((tfp, "HTMIME: Was S, found A, checking for 'fe:'\n"));
+ break;
+
+ case 'e':
+ case 'E':
+ me->state = miSE;
+ CTRACE((tfp, "HTMIME: Was S, found E, state now SE'\n"));
+ break;
+
+ default:
+ CTRACE((tfp,
+ "HTMIME: Bad character `%c' found where `%s' expected\n",
+ c, "'a' or 'e'"));
+ goto bad_field_name;
+
+ } /* switch on character */
+ break;
+
+ case miSE: /* Check for 'r' or 't' */
+ switch (c) {
+ case 'r':
+ case 'R':
+ me->check_pointer = "ver:";
+ me->if_ok = miSERVER;
+ me->state = miCHECK;
+ CTRACE((tfp, "HTMIME: Was SE, found R, checking for 'ver'\n"));
+ break;
+
+ case 't':
+ case 'T':
+ me->check_pointer = "-cookie";
+ me->if_ok = miSET_COOKIE;
+ me->state = miCHECK;
+ CTRACE((tfp, "HTMIME: Was SE, found T, checking for '-cookie'\n"));
+ break;
+
+ default:
+ CTRACE((tfp,
+ "HTMIME: Bad character `%c' found where `%s' expected\n",
+ c, "'r' or 't'"));
+ goto bad_field_name;
+
+ } /* switch on character */
+ break;
+
+ case miSET_COOKIE: /* Check for ':' or '2' */
+ switch (c) {
+ case ':':
+ me->field = miSET_COOKIE1; /* remember it */
+ me->state = miSKIP_GET_VALUE;
+ CTRACE((tfp, "HTMIME: Was SET_COOKIE, found :, processing\n"));
+ break;
+
+ case '2':
+ me->check_pointer = ":";
+ me->if_ok = miSET_COOKIE2;
+ me->state = miCHECK;
+ CTRACE((tfp, "HTMIME: Was SET_COOKIE, found 2, checking for ':'\n"));
+ break;
+
+ default:
+ CTRACE((tfp,
+ "HTMIME: Bad character `%c' found where `%s' expected\n",
+ c, "':' or '2'"));
+ goto bad_field_name;
+
+ } /* switch on character */
+ break;
+
+ case miT: /* Check for 'i' or 'r' */
+ switch (c) {
+ case 'i':
+ case 'I':
+ me->check_pointer = "tle:";
+ me->if_ok = miTITLE;
+ me->state = miCHECK;
+ CTRACE((tfp, "HTMIME: Was T, found I, checking for 'tle:'\n"));
+ break;
+
+ case 'r':
+ case 'R':
+ me->check_pointer = "ansfer-encoding:";
+ me->if_ok = miTRANSFER_ENCODING;
+ me->state = miCHECK;
+ CTRACE((tfp,
+ "HTMIME: Was T, found R, checking for 'ansfer-encoding'\n"));
+ break;
+
+ default:
+ CTRACE((tfp,
+ "HTMIME: Bad character `%c' found where `%s' expected\n",
+ c, "'i' or 'r'"));
+ goto bad_field_name;
+
+ } /* switch on character */
+ break;
+
+ case miU: /* Check for 'p' or 'r' */
+ switch (c) {
+ case 'p':
+ case 'P':
+ me->check_pointer = "grade:";
+ me->if_ok = miUPGRADE;
+ me->state = miCHECK;
+ CTRACE((tfp, "HTMIME: Was U, found P, checking for 'grade:'\n"));
+ break;
+
+ case 'r':
+ case 'R':
+ me->check_pointer = "i:";
+ me->if_ok = miURI;
+ me->state = miCHECK;
+ CTRACE((tfp, "HTMIME: Was U, found R, checking for 'i:'\n"));
+ break;
+
+ default:
+ CTRACE((tfp,
+ "HTMIME: Bad character `%c' found where `%s' expected\n",
+ c, "'p' or 'r'"));
+ goto bad_field_name;
+
+ } /* switch on character */
+ break;
+
+ case miV: /* Check for 'a' or 'i' */
+ switch (c) {
+ case 'a':
+ case 'A':
+ me->check_pointer = "ry:";
+ me->if_ok = miVARY;
+ me->state = miCHECK;
+ CTRACE((tfp, "HTMIME: Was V, found A, checking for 'ry:'\n"));
+ break;
+
+ case 'i':
+ case 'I':
+ me->check_pointer = "a:";
+ me->if_ok = miVIA;
+ me->state = miCHECK;
+ CTRACE((tfp, "HTMIME: Was V, found I, checking for 'a:'\n"));
+ break;
+
+ default:
+ CTRACE((tfp,
+ "HTMIME: Bad character `%c' found where `%s' expected\n",
+ c, "'a' or 'i'"));
+ goto bad_field_name;
+
+ } /* switch on character */
+ break;
+
+ case miW: /* Check for 'a' or 'w' */
+ switch (c) {
+ case 'a':
+ case 'A':
+ me->check_pointer = "rning:";
+ me->if_ok = miWARNING;
+ me->state = miCHECK;
+ CTRACE((tfp, "HTMIME: Was W, found A, checking for 'rning:'\n"));
+ break;
+
+ case 'w':
+ case 'W':
+ me->check_pointer = "w-authenticate:";
+ me->if_ok = miWWW_AUTHENTICATE;
+ me->state = miCHECK;
+ CTRACE((tfp,
+ "HTMIME: Was W, found W, checking for 'w-authenticate:'\n"));
+ break;
+
+ default:
+ CTRACE((tfp,
+ "HTMIME: Bad character `%c' found where `%s' expected\n",
+ c, "'a' or 'w'"));
+ goto bad_field_name;
+
+ } /* switch on character */
+ break;
+
+ case miCHECK: /* Check against string */
+ if (TOLOWER(c) == *(me->check_pointer)++) {
+ if (!*me->check_pointer)
+ me->state = me->if_ok;
+ } else { /* Error */
+ CTRACE((tfp,
+ "HTMIME: Bad character `%c' found where `%s' expected\n",
+ c, me->check_pointer - 1));
+ goto bad_field_name;
+ }
+ break;
+
+ case miCONTENT_:
+ CTRACE((tfp, "HTMIME: in case CONTENT_\n"));
+
+ switch (c) {
+ case 'b':
+ case 'B':
+ me->check_pointer = "ase:";
+ me->if_ok = miCONTENT_BASE;
+ me->state = miCHECK;
+ CTRACE((tfp,
+ "HTMIME: Was CONTENT_, found B, checking for 'ase:'\n"));
+ break;
+
+ case 'd':
+ case 'D':
+ me->check_pointer = "isposition:";
+ me->if_ok = miCONTENT_DISPOSITION;
+ me->state = miCHECK;
+ CTRACE((tfp,
+ "HTMIME: Was CONTENT_, found D, checking for 'isposition:'\n"));
+ break;
+
+ case 'e':
+ case 'E':
+ me->check_pointer = "ncoding:";
+ me->if_ok = miCONTENT_ENCODING;
+ me->state = miCHECK;
+ CTRACE((tfp,
+ "HTMIME: Was CONTENT_, found E, checking for 'ncoding:'\n"));
+ break;
+
+ case 'f':
+ case 'F':
+ me->check_pointer = "eatures:";
+ me->if_ok = miCONTENT_FEATURES;
+ me->state = miCHECK;
+ CTRACE((tfp,
+ "HTMIME: Was CONTENT_, found F, checking for 'eatures:'\n"));
+ break;
+
+ case 'l':
+ case 'L':
+ me->state = miCONTENT_L;
+ CTRACE((tfp,
+ "HTMIME: Was CONTENT_, found L, state now CONTENT_L\n"));
+ break;
+
+ case 'm':
+ case 'M':
+ me->check_pointer = "d5:";
+ me->if_ok = miCONTENT_MD5;
+ me->state = miCHECK;
+ CTRACE((tfp, "HTMIME: Was CONTENT_, found M, checking for 'd5:'\n"));
+ break;
+
+ case 'r':
+ case 'R':
+ me->check_pointer = "ange:";
+ me->if_ok = miCONTENT_RANGE;
+ me->state = miCHECK;
+ CTRACE((tfp,
+ "HTMIME: Was CONTENT_, found R, checking for 'ange:'\n"));
+ break;
+
+ case 't':
+ case 'T':
+ me->state = miCONTENT_T;
+ CTRACE((tfp,
+ "HTMIME: Was CONTENT_, found T, state now CONTENT_T\n"));
+ break;
+
+ default:
+ CTRACE((tfp, "HTMIME: Was CONTENT_, found nothing; bleah\n"));
+ goto bad_field_name;
+
+ } /* switch on character */
+ break;
+
+ case miCONTENT_L:
+ CTRACE((tfp, "HTMIME: in case CONTENT_L\n"));
+
+ switch (c) {
+ case 'a':
+ case 'A':
+ me->check_pointer = "nguage:";
+ me->if_ok = miCONTENT_LANGUAGE;
+ me->state = miCHECK;
+ CTRACE((tfp,
+ "HTMIME: Was CONTENT_L, found A, checking for 'nguage:'\n"));
+ break;
+
+ case 'e':
+ case 'E':
+ me->check_pointer = "ngth:";
+ me->if_ok = miCONTENT_LENGTH;
+ me->state = miCHECK;
+ CTRACE((tfp,
+ "HTMIME: Was CONTENT_L, found E, checking for 'ngth:'\n"));
+ break;
+
+ case 'o':
+ case 'O':
+ me->check_pointer = "cation:";
+ me->if_ok = miCONTENT_LOCATION;
+ me->state = miCHECK;
+ CTRACE((tfp,
+ "HTMIME: Was CONTENT_L, found O, checking for 'cation:'\n"));
+ break;
+
+ default:
+ CTRACE((tfp, "HTMIME: Was CONTENT_L, found nothing; bleah\n"));
+ goto bad_field_name;
+
+ } /* switch on character */
+ break;
+
+ case miCONTENT_T:
+ CTRACE((tfp, "HTMIME: in case CONTENT_T\n"));
+
+ switch (c) {
+ case 'r':
+ case 'R':
+ me->check_pointer = "ansfer-encoding:";
+ me->if_ok = miCONTENT_TRANSFER_ENCODING;
+ me->state = miCHECK;
+ CTRACE((tfp,
+ "HTMIME: Was CONTENT_T, found R, checking for 'ansfer-encoding:'\n"));
+ break;
+
+ case 'y':
+ case 'Y':
+ me->check_pointer = "pe:";
+ me->if_ok = miCONTENT_TYPE;
+ me->state = miCHECK;
+ CTRACE((tfp,
+ "HTMIME: Was CONTENT_T, found Y, checking for 'pe:'\n"));
+ break;
+
+ default:
+ CTRACE((tfp, "HTMIME: Was CONTENT_T, found nothing; bleah\n"));
+ goto bad_field_name;
+
+ } /* switch on character */
+ break;
+
+ case miACCEPT_RANGES:
+ case miAGE:
+ case miALLOW:
+ case miALTERNATES:
+ case miCACHE_CONTROL:
+ case miCOOKIE:
+ case miCONNECTION:
+ case miCONTENT_BASE:
+ case miCONTENT_DISPOSITION:
+ case miCONTENT_ENCODING:
+ case miCONTENT_FEATURES:
+ case miCONTENT_LANGUAGE:
+ case miCONTENT_LENGTH:
+ case miCONTENT_LOCATION:
+ case miCONTENT_MD5:
+ case miCONTENT_RANGE:
+ case miCONTENT_TRANSFER_ENCODING:
+ case miCONTENT_TYPE:
+ case miDATE:
+ case miETAG:
+ case miEXPIRES:
+ case miKEEP_ALIVE:
+ case miLAST_MODIFIED:
+ case miLINK:
+ case miLOCATION:
+ case miPRAGMA:
+ case miPROXY_AUTHENTICATE:
+ case miPUBLIC:
+ case miREFRESH:
+ case miRETRY_AFTER:
+ case miSAFE:
+ case miSERVER:
+ case miSET_COOKIE1:
+ case miSET_COOKIE2:
+ case miTITLE:
+ case miTRANSFER_ENCODING:
+ case miUPGRADE:
+ case miURI:
+ case miVARY:
+ case miVIA:
+ case miWARNING:
+ case miWWW_AUTHENTICATE:
+ me->field = me->state; /* remember it */
+ me->state = miSKIP_GET_VALUE;
+ /* FALLTHRU */
+
+ case miSKIP_GET_VALUE:
+ if (c == '\n') {
+ me->fold_state = me->state;
+ me->state = miNEWLINE;
+ break;
+ }
+ if (WHITE(c))
+ /*
+ * Skip white space.
+ */
+ break;
+
+ me->value_pointer = me->value;
+ me->state = miGET_VALUE;
+ /* Fall through to store first character */
+ /* FALLTHRU */
+
+ case miGET_VALUE:
+ GET_VALUE:
+ if (c != '\n') { /* Not end of line */
+ if (me->value_pointer < me->value + VALUE_SIZE - 1) {
+ *me->value_pointer++ = (char) c;
+ break;
+ } else {
+ goto value_too_long;
+ }
+ }
+ /* Fall through (if end of line) */
+ /* FALLTHRU */
+
+ case miJUNK_LINE:
+ if (c == '\n') {
+ me->fold_state = me->state;
+ me->state = miNEWLINE;
+ }
+ break;
+
+ } /* switch on state */
+
+ HTChunkPutc(&me->anchor->http_headers, UCH(c));
+ if (me->state == MIME_TRANSPARENT) {
+ HTChunkTerminate(&me->anchor->http_headers);
+ CTRACE((tfp, "Server Headers (%d bytes):\n%.*s\n",
+ me->anchor->http_headers.size,
+ me->anchor->http_headers.size,
+ me->anchor->http_headers.data));
+ CTRACE((tfp, "Server Content-Type:%s\n",
+ me->anchor->content_type_params));
+ }
+ return;
+
+ value_too_long:
+ CTRACE((tfp, "HTMIME: *** Syntax error. (string too long)\n"));
+
+ bad_field_name: /* Ignore it */
+ me->state = miJUNK_LINE;
+
+ HTChunkPutc(&me->anchor->http_headers, UCH(c));
+
+ return;
+}
+
+/* String handling
+ * ---------------
+ *
+ * Strings must be smaller than this buffer size.
+ */
+static void HTMIME_put_string(HTStream *me,
+ const char *s)
+{
+ const char *p;
+
+ if (me->state == MIME_TRANSPARENT) { /* Optimisation */
+ (*me->targetClass.put_string) (me->target, s);
+
+ } else if (me->state != MIME_IGNORE) {
+ CTRACE((tfp, "HTMIME: %s\n", s));
+
+ for (p = s; *p; p++)
+ HTMIME_put_character(me, *p);
+ }
+}
+
+/* Buffer write. Buffers can (and should!) be big.
+ * ------------
+ */
+static void HTMIME_write(HTStream *me,
+ const char *s,
+ int l)
+{
+ const char *p;
+
+ if (me->state == MIME_TRANSPARENT) { /* Optimisation */
+ (*me->targetClass.put_block) (me->target, s, l);
+
+ } else {
+ CTRACE((tfp, "HTMIME: %.*s\n", l, s));
+
+ for (p = s; p < s + l; p++)
+ HTMIME_put_character(me, *p);
+ }
+}
+
+/* Free an HTML object
+ * -------------------
+ *
+ */
+static void HTMIME_free(HTStream *me)
+{
+ if (me) {
+ FREE(me->location);
+ FREE(me->compression_encoding);
+ if (me->target)
+ (*me->targetClass._free) (me->target);
+ FREE(me);
+ }
+}
+
+/* End writing
+*/
+static void HTMIME_abort(HTStream *me,
+ HTError e)
+{
+ if (me) {
+ FREE(me->location);
+ FREE(me->compression_encoding);
+ if (me->target)
+ (*me->targetClass._abort) (me->target, e);
+ FREE(me);
+ }
+}
+
+/* Structured Object Class
+ * -----------------------
+ */
+static const HTStreamClass HTMIME =
+{
+ "MIMEParser",
+ HTMIME_free,
+ HTMIME_abort,
+ HTMIME_put_character,
+ HTMIME_put_string,
+ HTMIME_write
+};
+
+/* Subclass-specific Methods
+ * -------------------------
+ */
+HTStream *HTMIMEConvert(HTPresentation *pres,
+ HTParentAnchor *anchor,
+ HTStream *sink)
+{
+ HTStream *me;
+
+ CTRACE((tfp, "HTMIMEConvert\n"));
+ me = typecalloc(HTStream);
+
+ if (me == NULL)
+ outofmem(__FILE__, "HTMIMEConvert");
+
+ me->isa = &HTMIME;
+ me->sink = sink;
+ me->anchor = anchor;
+ me->anchor->safe = FALSE;
+ me->anchor->no_cache = FALSE;
+
+ FREE(me->anchor->cache_control);
+ FREE(me->anchor->SugFname);
+ FREE(me->anchor->charset);
+
+ HTChunkClear(&me->anchor->http_headers);
+ HTChunkInit(&me->anchor->http_headers, 128);
+
+ FREE(me->anchor->content_type_params);
+ FREE(me->anchor->content_language);
+ FREE(me->anchor->content_encoding);
+ FREE(me->anchor->content_base);
+ FREE(me->anchor->content_disposition);
+ FREE(me->anchor->content_location);
+ FREE(me->anchor->content_md5);
+
+ me->anchor->inHEAD = TRUE;
+ me->anchor->header_length = 0;
+ me->anchor->content_length = 0;
+
+ FREE(me->anchor->date);
+ FREE(me->anchor->expires);
+ FREE(me->anchor->last_modified);
+ FREE(me->anchor->ETag);
+ FREE(me->anchor->server);
+
+ me->target = NULL;
+ me->state = miBEGINNING_OF_LINE;
+ me->format = HTAtom_for(ContentTypes[LYContentType]);
+
+ CTRACE((tfp, "default Content-Type is %s\n", HTAtom_name(me->format)));
+ me->targetRep = pres->rep_out;
+ me->boundary = NULL; /* Not set yet */
+ me->set_cookie = NULL; /* Not set yet */
+ me->set_cookie2 = NULL; /* Not set yet */
+ me->refresh_url = NULL; /* Not set yet */
+ me->c_t_encoding = 0; /* Not set yet */
+ me->compression_encoding = NULL; /* Not set yet */
+ me->net_ascii = NO; /* Local character set */
+
+ HTAnchor_setUCInfoStage(me->anchor, current_char_set,
+ UCT_STAGE_STRUCTURED,
+ UCT_SETBY_DEFAULT);
+ HTAnchor_setUCInfoStage(me->anchor, current_char_set,
+ UCT_STAGE_HTEXT,
+ UCT_SETBY_DEFAULT);
+ return me;
+}
+
+HTStream *HTNetMIME(HTPresentation *pres,
+ HTParentAnchor *anchor,
+ HTStream *sink)
+{
+ HTStream *me = HTMIMEConvert(pres, anchor, sink);
+
+ if (!me)
+ return NULL;
+
+ me->net_ascii = YES;
+ return me;
+}
+
+HTStream *HTMIMERedirect(HTPresentation *pres,
+ HTParentAnchor *anchor,
+ HTStream *sink)
+{
+ HTStream *me = HTMIMEConvert(pres, anchor, sink);
+
+ if (!me)
+ return NULL;
+
+ me->pickup_redirection = YES;
+ if (me->targetRep == WWW_DEBUG && sink)
+ me->no_streamstack = YES;
+ return me;
+}
+
+/* Japanese header handling functions
+ * ==================================
+ *
+ * K&Rized and added 07-Jun-96 by FM, based on:
+ *
+////////////////////////////////////////////////////////////////////////
+ *
+ * ISO-2022-JP handling routines
+ * &
+ * MIME decode routines (quick hack just for ISO-2022-JP)
+ *
+ * Thu Jan 25 10:11:42 JST 1996
+ *
+ * Copyright (C) 1994, 1995, 1996
+ * Shuichi Ichikawa (ichikawa@nuee.nagoya-u.ac.jp)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either versions 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with SKK, see the file COPYING. If not, write to the Free
+ * Software Foundation Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * MIME decoding routines
+ *
+ * Written by S. Ichikawa,
+ * partially inspired by encdec.c of <jh@efd.lth.se>.
+ * Caller's buffers decode to no longer than the input strings.
+ */
+#include <LYCharVals.h> /* S/390 -- gil -- 0163 */
+
+static char HTmm64[] =
+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
+static char HTmmquote[] = "0123456789ABCDEF";
+static int HTmmcont = 0;
+
+static void HTmmdec_base64(char **t,
+ char *s)
+{
+ int d, count, j, val;
+ char *buf, *bp, nw[4], *p;
+
+ if ((buf = typeMallocn(char, strlen(s) * 3 + 1)) == 0)
+ outofmem(__FILE__, "HTmmdec_base64");
+
+ for (bp = buf; *s; s += 4) {
+ val = 0;
+ if (s[2] == '=')
+ count = 1;
+ else if (s[3] == '=')
+ count = 2;
+ else
+ count = 3;
+
+ for (j = 0; j <= count; j++) {
+ if (!(p = StrChr(HTmm64, s[j]))) {
+ FREE(buf);
+ return;
+ }
+ d = (int) (p - HTmm64);
+ d <<= (3 - j) * 6;
+ val += d;
+ }
+ for (j = 2; j >= 0; j--) {
+ nw[j] = (char) (val & 255);
+ val >>= 8;
+ }
+ if (count--)
+ *bp++ = nw[0];
+ if (count--)
+ *bp++ = nw[1];
+ if (count)
+ *bp++ = nw[2];
+ }
+ *bp = '\0';
+ StrAllocCopy(*t, buf);
+ FREE(buf);
+}
+
+static void HTmmdec_quote(char **t,
+ char *s)
+{
+ char *buf, cval, *bp, *p;
+
+ if ((buf = typeMallocn(char, strlen(s) + 1)) == 0)
+ outofmem(__FILE__, "HTmmdec_quote");
+
+ for (bp = buf; *s;) {
+ if (*s == '=') {
+ cval = 0;
+ if (s[1] && (p = StrChr(HTmmquote, s[1]))) {
+ cval = (char) (cval + (char) (p - HTmmquote));
+ } else {
+ *bp++ = *s++;
+ continue;
+ }
+ if (s[2] && (p = StrChr(HTmmquote, s[2]))) {
+ cval = (char) (cval << 4);
+ cval = (char) (cval + (p - HTmmquote));
+ *bp++ = cval;
+ s += 3;
+ } else {
+ *bp++ = *s++;
+ }
+ } else if (*s == '_') {
+ *bp++ = 0x20;
+ s++;
+ } else {
+ *bp++ = *s++;
+ }
+ }
+ *bp = '\0';
+ StrAllocCopy(*t, buf);
+ FREE(buf);
+}
+
+/*
+ * HTmmdecode for ISO-2022-JP - FM
+ */
+void HTmmdecode(char **target,
+ char *source)
+{
+ char *buf;
+ char *mmbuf = NULL;
+ char *m2buf = NULL;
+ char *s, *t, *u;
+ int base64, quote;
+
+ if ((buf = typeMallocn(char, strlen(source) + 1)) == 0)
+ outofmem(__FILE__, "HTmmdecode");
+
+ for (s = source, *(u = buf) = '\0'; *s;) {
+ if (!strncasecomp(s, "=?ISO-2022-JP?B?", 16)) {
+ base64 = 1;
+ } else {
+ base64 = 0;
+ }
+ if (!strncasecomp(s, "=?ISO-2022-JP?Q?", 16)) {
+ quote = 1;
+ } else {
+ quote = 0;
+ }
+ if (base64 || quote) {
+ if (HTmmcont) {
+ for (t = s - 1;
+ t >= source && (*t == ' ' || *t == '\t'); t--) {
+ u--;
+ }
+ }
+ if (mmbuf == 0) /* allocate buffer big enough for source */
+ StrAllocCopy(mmbuf, source);
+ for (s += 16, t = mmbuf; *s;) {
+ if (s[0] == '?' && s[1] == '=') {
+ break;
+ } else {
+ *t++ = *s++;
+ *t = '\0';
+ }
+ }
+ if (s[0] != '?' || s[1] != '=') {
+ goto end;
+ } else {
+ s += 2;
+ *t = '\0';
+ }
+ if (base64)
+ HTmmdec_base64(&m2buf, mmbuf);
+ else
+ HTmmdec_quote(&m2buf, mmbuf);
+ for (t = m2buf; non_empty(t);)
+ *u++ = *t++;
+ HTmmcont = 1;
+ } else {
+ if (*s != ' ' && *s != '\t')
+ HTmmcont = 0;
+ *u++ = *s++;
+ }
+ }
+ *u = '\0';
+ end:
+ StrAllocCopy(*target, buf);
+ FREE(m2buf);
+ FREE(mmbuf);
+ FREE(buf);
+}
+
+/*
+ * Insert ESC where it seems lost.
+ * (The author of this function "rjis" is S. Ichikawa.)
+ */
+int HTrjis(char **t,
+ char *s)
+{
+ char *p;
+ char *buf = NULL;
+ int kanji = 0;
+
+ if (StrChr(s, CH_ESC) || !StrChr(s, '$')) {
+ if (s != *t)
+ StrAllocCopy(*t, s);
+ return 1;
+ }
+
+ if ((buf = typeMallocn(char, strlen(s) * 2 + 1)) == 0)
+ outofmem(__FILE__, "HTrjis");
+
+ for (p = buf; *s;) {
+ if (!kanji && s[0] == '$' && (s[1] == '@' || s[1] == 'B')) {
+ if (HTmaybekanji((int) s[2], (int) s[3])) {
+ kanji = 1;
+ *p++ = CH_ESC;
+ *p++ = *s++;
+ *p++ = *s++;
+ *p++ = *s++;
+ *p++ = *s++;
+ continue;
+ }
+ *p++ = *s++;
+ continue;
+ }
+ if (kanji && s[0] == '(' && (s[1] == 'J' || s[1] == 'B')) {
+ kanji = 0;
+ *p++ = CH_ESC;
+ *p++ = *s++;
+ *p++ = *s++;
+ continue;
+ }
+ *p++ = *s++;
+ }
+ *p = *s; /* terminate string */
+
+ StrAllocCopy(*t, buf);
+ FREE(buf);
+ return 0;
+}
+
+/*
+ * The following function "maybekanji" is derived from
+ * RJIS-1.0 by Mr. Hironobu Takahashi.
+ * Maybekanji() is included here under the courtesy of the author.
+ * The original comment of rjis.c is also included here.
+ */
+/*
+ * RJIS ( Recover JIS code from broken file )
+ * Copyright (C) 1992 1994
+ * Hironobu Takahashi (takahasi@tiny.or.jp)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either versions 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with SKK, see the file COPYING. If not, write to the Free
+ * Software Foundation Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+int HTmaybekanji(int c1,
+ int c2)
+{
+
+ if ((c2 < 33) || (c2 > 126))
+ return 0;
+ if ((c1 < 33) || ((40 < c1) && (c1 < 48)) || (116 < c1))
+ return 0;
+ c2 -= 32;
+ switch (c1 - 32) {
+ case 2:
+ if ((14 < c2) && (c2 < 26))
+ return 0;
+ if ((33 < c2) && (c2 < 42))
+ return 0;
+ if ((48 < c2) && (c2 < 60))
+ return 0;
+ if ((74 < c2) && (c2 < 82))
+ return 0;
+ if ((89 < c2) && (c2 < 94))
+ return 0;
+ break;
+ case 3:
+ if (c2 < 16)
+ return 0;
+ if ((25 < c2) && (c2 < 33))
+ return 0;
+ if ((58 < c2) && (c2 < 65))
+ return 0;
+ if (90 < c2)
+ return 0;
+ break;
+ case 4:
+ if (83 < c2)
+ return 0;
+ break;
+ case 5:
+ if (86 < c2)
+ return 0;
+ break;
+ case 6:
+ if ((24 < c2) && (c2 < 33))
+ return 0;
+ if (56 < c2)
+ return 0;
+ break;
+ case 7:
+ if ((33 < c2) && (c2 < 49))
+ return 0;
+ if (81 < c2)
+ return 0;
+ break;
+ case 8:
+ if (32 < c2)
+ return 0;
+ break;
+ case 47:
+ if (51 < c2)
+ return 0;
+ break;
+ case 84:
+ if (6 < c2)
+ return 0;
+ break;
+ }
+ return 1;
+}
diff --git a/WWW/Library/Implementation/HTMIME.h b/WWW/Library/Implementation/HTMIME.h
new file mode 100644
index 0000000..6410c15
--- /dev/null
+++ b/WWW/Library/Implementation/HTMIME.h
@@ -0,0 +1,84 @@
+/* /Net/dxcern/userd/timbl/hypertext/WWW/Library/Implementation/HTMIME.html
+ MIME PARSER
+
+ The MIME parser stream presents a MIME document. It recursively invokes the
+ format manager to handle embedded formats.
+
+ As well as stripping off and parsing the headers, the MIME parser has to
+ parse any weirld MIME encodings it may meet within the body parts of
+ messages, and must deal with multipart messages.
+
+ This module is implemented to the level necessary for operation with WWW,
+ but is not currently complete for any arbitrary MIME message.
+
+ Check the source for latest additions to functionality.
+
+ The MIME parser is complicated by the fact that WWW allows real binary to be
+ sent, not ASCII encoded. Therefore the netascii decoding is included in
+ this module. One cannot layer it by converting first from Net to local
+ text, then decoding it. Of course, for local files, the net ascii decoding
+ is not needed. There are therefore two creation routines.
+
+ */
+#ifndef HTMIME_H
+#define HTMIME_H
+
+#include <HTStream.h>
+#include <HTAnchor.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+ * This function is for trimming off any paired
+ * open- and close-double quotes from header values.
+ * It does not parse the string for embedded quotes,
+ * and will not modify the string unless both the
+ * first and last characters are double-quotes. - FM
+ */ extern void HTMIME_TrimDoubleQuotes(char *value);
+
+/*
+
+ INPUT: LOCAL TEXT
+
+ */
+ extern HTStream *HTMIMEConvert(HTPresentation *pres,
+ HTParentAnchor *anchor,
+ HTStream *sink);
+
+/*
+
+ INPUT: NET ASCII
+
+ */
+ extern HTStream *HTNetMIME(HTPresentation *pres,
+ HTParentAnchor *anchor,
+ HTStream *sink);
+
+/*
+
+ INPUT: Redirection message, parse headers only for Location if present
+
+ */
+ extern HTStream *HTMIMERedirect(HTPresentation *pres,
+ HTParentAnchor *anchor,
+ HTStream *sink);
+
+/*
+
+ For handling Japanese headers.
+
+*/
+ extern void HTmmdecode(char **target,
+ char *source);
+
+ extern int HTrjis(char **target,
+ char *source);
+
+ extern int HTmaybekanji(int c1,
+ int c2);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* !HTMIME_H */
diff --git a/WWW/Library/Implementation/HTMLDTD.c b/WWW/Library/Implementation/HTMLDTD.c
new file mode 100644
index 0000000..382c141
--- /dev/null
+++ b/WWW/Library/Implementation/HTMLDTD.c
@@ -0,0 +1,334 @@
+/*
+ * $LynxId: HTMLDTD.c,v 1.58 2021/07/23 00:00:03 tom Exp $
+ *
+ * Our Static DTD for HTML
+ * -----------------------
+ */
+
+/* Implements:
+*/
+
+#include <HTUtils.h>
+#include <HTMLDTD.h>
+#include <LYLeaks.h>
+#include <LYJustify.h>
+
+/*
+ * Character entities like &nbsp now excluded from our DTD tables, they are
+ * mapped to Unicode and handled by chartrans code directly the similar way the
+ * numeric entities like &#123 does. See src/chrtrans/entities.h for real
+ * mapping.
+ */
+
+/* Entity Names
+ * ------------
+ *
+ * This table must be matched exactly with ALL the translation tables
+ * (this is an obsolete translation mechanism, probably unused,
+ * currently replaced with Unicode chartrans in most cases...)
+ */
+static const char *entities[] =
+{
+ "AElig", /* capital AE diphthong (ligature) */
+ "Aacute", /* capital A, acute accent */
+ "Acirc", /* capital A, circumflex accent */
+ "Agrave", /* capital A, grave accent */
+ "Aring", /* capital A, ring */
+ "Atilde", /* capital A, tilde */
+ "Auml", /* capital A, dieresis or umlaut mark */
+ "Ccedil", /* capital C, cedilla */
+ "Dstrok", /* capital Eth, Icelandic */
+ "ETH", /* capital Eth, Icelandic */
+ "Eacute", /* capital E, acute accent */
+ "Ecirc", /* capital E, circumflex accent */
+ "Egrave", /* capital E, grave accent */
+ "Euml", /* capital E, dieresis or umlaut mark */
+ "Iacute", /* capital I, acute accent */
+ "Icirc", /* capital I, circumflex accent */
+ "Igrave", /* capital I, grave accent */
+ "Iuml", /* capital I, dieresis or umlaut mark */
+ "Ntilde", /* capital N, tilde */
+ "Oacute", /* capital O, acute accent */
+ "Ocirc", /* capital O, circumflex accent */
+ "Ograve", /* capital O, grave accent */
+ "Oslash", /* capital O, slash */
+ "Otilde", /* capital O, tilde */
+ "Ouml", /* capital O, dieresis or umlaut mark */
+ "THORN", /* capital THORN, Icelandic */
+ "Uacute", /* capital U, acute accent */
+ "Ucirc", /* capital U, circumflex accent */
+ "Ugrave", /* capital U, grave accent */
+ "Uuml", /* capital U, dieresis or umlaut mark */
+ "Yacute", /* capital Y, acute accent */
+ "aacute", /* small a, acute accent */
+ "acirc", /* small a, circumflex accent */
+ "acute", /* spacing acute */
+ "aelig", /* small ae diphthong (ligature) */
+ "agrave", /* small a, grave accent */
+ "amp", /* ampersand */
+ "aring", /* small a, ring */
+ "atilde", /* small a, tilde */
+ "auml", /* small a, dieresis or umlaut mark */
+ "brkbar", /* broken vertical bar */
+ "brvbar", /* broken vertical bar */
+ "ccedil", /* small c, cedilla */
+ "cedil", /* spacing cedilla */
+ "cent", /* cent sign */
+ "copy", /* copyright sign */
+ "curren", /* currency sign */
+ "deg", /* degree sign */
+ "die", /* spacing dieresis */
+ "divide", /* division sign */
+ "eacute", /* small e, acute accent */
+ "ecirc", /* small e, circumflex accent */
+ "egrave", /* small e, grave accent */
+ "emdash", /* dash the width of emsp */
+ "emsp", /* em space - not collapsed */
+ "endash", /* dash the width of ensp */
+ "ensp", /* en space - not collapsed */
+ "eth", /* small eth, Icelandic */
+ "euml", /* small e, dieresis or umlaut mark */
+ "frac12", /* fraction 1/2 */
+ "frac14", /* fraction 1/4 */
+ "frac34", /* fraction 3/4 */
+ "gt", /* greater than */
+ "hibar", /* spacing macron */
+ "iacute", /* small i, acute accent */
+ "icirc", /* small i, circumflex accent */
+ "iexcl", /* inverted exclamation mark */
+ "igrave", /* small i, grave accent */
+ "iquest", /* inverted question mark */
+ "iuml", /* small i, dieresis or umlaut mark */
+ "laquo", /* angle quotation mark, left */
+ "lt", /* less than */
+ "macr", /* spacing macron */
+ "mdash", /* dash the width of emsp */
+ "micro", /* micro sign */
+ "middot", /* middle dot */
+ "nbsp", /* non breaking space */
+ "ndash", /* dash the width of ensp */
+ "not", /* negation sign */
+ "ntilde", /* small n, tilde */
+ "oacute", /* small o, acute accent */
+ "ocirc", /* small o, circumflex accent */
+ "ograve", /* small o, grave accent */
+ "ordf", /* feminine ordinal indicator */
+ "ordm", /* masculine ordinal indicator */
+ "oslash", /* small o, slash */
+ "otilde", /* small o, tilde */
+ "ouml", /* small o, dieresis or umlaut mark */
+ "para", /* paragraph sign */
+ "plusmn", /* plus-or-minus sign */
+ "pound", /* pound sign */
+ "quot", /* quote '"' */
+ "raquo", /* angle quotation mark, right */
+ "reg", /* circled R registered sign */
+ "sect", /* section sign */
+ "shy", /* soft hyphen */
+ "sup1", /* superscript 1 */
+ "sup2", /* superscript 2 */
+ "sup3", /* superscript 3 */
+ "szlig", /* small sharp s, German (sz ligature) */
+ "thinsp", /* thin space (not collapsed) */
+ "thorn", /* small thorn, Icelandic */
+ "times", /* multiplication sign */
+ "trade", /* trade mark sign (U+2122) */
+ "uacute", /* small u, acute accent */
+ "ucirc", /* small u, circumflex accent */
+ "ugrave", /* small u, grave accent */
+ "uml", /* spacing dieresis */
+ "uuml", /* small u, dieresis or umlaut mark */
+ "yacute", /* small y, acute accent */
+ "yen", /* yen sign */
+ "yuml", /* small y, dieresis or umlaut mark */
+};
+
+/* Attribute Lists
+ * ---------------
+ *
+ * Lists must be in alphabetical order by attribute name
+ * The tag elements contain the number of attributes
+ */
+
+/* From Peter Flynn's intro to the HTML Pro DTD:
+
+ %structure;
+
+ DIV, CENTER, H1 to H6, P, UL, OL, DL, DIR, MENU, PRE, XMP, LISTING, BLOCKQUOTE, BQ,
+ 2 1 2 2 1 8 8 8 8 8 8 8 8 4 4
+ MULTICOL,?NOBR, FORM, TABLE, ADDRESS, FIG, BDO, NOTE, and FN; plus?WBR, LI, and LH
+ 8 n ?1 n 8 8 2 2 2 2 2 ?1 nE 4 4
+
+ %insertions;
+
+ Elements which usually contain special-purpose material, or no text material at all.
+
+ BASEFONT, APPLET, OBJECT, EMBED, SCRIPT, MAP, MARQUEE, HR, ISINDEX, BGSOUND, TAB,?IMG,
+ 1 e? 2 2 l 1 e 2 l 8 4 4 E 1? E 1 E ! E ?1 E
+ IMAGE, BR, plus NOEMBED, SERVER, SPACER, AUDIOSCOPE, and SIDEBAR; ?area
+ 1 n 1 E n n n n n 8 E
+
+ %text;
+
+ Elements within the %structure; which directly contain running text.
+
+ Descriptive or analytic markup: EM, STRONG, DFN, CODE, SAMP, KBD, VAR, CITE, Q, LANG, AU,
+ 2 2 2 2 2 2 2 2 2 2 n 2
+ AUTHOR, PERSON, ACRONYM, ABBR, INS, DEL, and SPAN
+ 2 2 n 2 2 2 2 2
+ Visual markup:S, STRIKE, I, B, TT, U,?NOBR,?WBR, BR, BIG, SMALL, FONT, STYLE, BLINK, TAB,
+ 1 1 1 1 1 1 ?1 n ?1nE? 1 E 1 1 1 1 l 1 1 E?
+ BLACKFACE, LIMITTEXT, NOSMARTQUOTES, and SHADOW
+ 1 n 1 n 1 n 1 n
+ Hypertext and graphics: A and?IMG
+ 8 ?8 E
+ Mathematical: SUB, SUP, and MATH
+ 4 4 4 l
+ Documentary: COMMENT, ENTITY, ELEMENT, and ATTRIB
+ 4 4 n 4 n 4 n
+ %formula;
+ */
+
+/* Elements
+ * --------
+ *
+ * Must match definitions in HTMLDTD.html!
+ * Must be in alphabetical order.
+ *
+ * The T_* extra info is listed here, even though most fields are not used
+ * in SGML.c if Old_DTD is set (with the exception of some Tgf_* flags).
+ * This simplifies comparison of the tags_table0[] table (otherwise unchanged
+ * from original Lynx treatment) with the tags_table1[] table below. - kw
+ *
+ * Name*, Attributes, No. of attributes, content, extra info...
+ */
+
+#include <src0_HTMLDTD.h>
+#include <src1_HTMLDTD.h>
+
+/* Dummy space, will be filled with the contents of either tags_table1
+ or tags_table0 on calling HTSwitchDTD - kw */
+
+static HTTag tags[HTML_ALL_ELEMENTS];
+
+const SGML_dtd HTML_dtd =
+{
+ tags,
+ HTML_ELEMENTS,
+ entities, /* probably unused */
+ TABLESIZE(entities),
+};
+
+/* This function fills the "tags" part of the HTML_dtd structure with
+ what we want to use, either tags_table0 or tags_table1. Note that it
+ has to be called at least once before HTML_dtd is used, otherwise
+ the HTML_dtd contents will be invalid! This could be coded in a way
+ that would make an initialisation call unnecessary, but my C knowledge
+ is limited and I didn't want to list the whole tags_table1 table
+ twice... - kw */
+void HTSwitchDTD(int new_flag)
+{
+ if (TRACE)
+ CTRACE((tfp,
+ "HTMLDTD: Copying %s DTD element info of size %d, %d * %d\n",
+ new_flag ? "strict" : "tagsoup",
+ (int) (new_flag ? sizeof(tags_table1) : sizeof(tags_table0)),
+ HTML_ALL_ELEMENTS,
+ (int) sizeof(HTTag)));
+ if (new_flag)
+ MemCpy(tags, tags_table1, HTML_ALL_ELEMENTS * sizeof(HTTag));
+ else
+ MemCpy(tags, tags_table0, HTML_ALL_ELEMENTS * sizeof(HTTag));
+}
+
+HTTag HTTag_unrecognized =
+
+{NULL_HTTag, NULL, 0, 0, SGML_EMPTY, T__UNREC_, 0, 0};
+
+/*
+ * Utility Routine: Useful for people building HTML objects.
+ */
+
+/* Start anchor element
+ * --------------------
+ *
+ * It is kinda convenient to have a particulr routine for
+ * starting an anchor element, as everything else for HTML is
+ * simple anyway.
+ */
+struct _HTStructured {
+ HTStructuredClass *isa;
+ /* ... */
+};
+
+void HTStartAnchor(HTStructured * obj, const char *name,
+ const char *href)
+{
+ BOOL present[HTML_A_ATTRIBUTES];
+ const char *value[HTML_A_ATTRIBUTES];
+ int i;
+
+ for (i = 0; i < HTML_A_ATTRIBUTES; i++)
+ present[i] = NO;
+
+ if (name && *name) {
+ present[HTML_A_NAME] = YES;
+ value[HTML_A_NAME] = (const char *) name;
+ }
+ if (href) {
+ present[HTML_A_HREF] = YES;
+ value[HTML_A_HREF] = (const char *) href;
+ }
+
+ (*obj->isa->start_element) (obj, HTML_A, present, value, -1, 0);
+}
+
+void HTStartAnchor5(HTStructured * obj, const char *name,
+ const char *href,
+ const char *linktype,
+ int tag_charset)
+{
+ BOOL present[HTML_A_ATTRIBUTES];
+ const char *value[HTML_A_ATTRIBUTES];
+ int i;
+
+ for (i = 0; i < HTML_A_ATTRIBUTES; i++)
+ present[i] = NO;
+
+ if (name && *name) {
+ present[HTML_A_NAME] = YES;
+ value[HTML_A_NAME] = name;
+ }
+ if (href && *href) {
+ present[HTML_A_HREF] = YES;
+ value[HTML_A_HREF] = href;
+ }
+ if (linktype && *linktype) {
+ present[HTML_A_TYPE] = YES;
+ value[HTML_A_TYPE] = linktype;
+ }
+
+ (*obj->isa->start_element) (obj, HTML_A, present, value, tag_charset, 0);
+}
+
+void HTStartIsIndex(HTStructured * obj, const char *prompt,
+ const char *href)
+{
+ BOOL present[HTML_ISINDEX_ATTRIBUTES];
+ const char *value[HTML_ISINDEX_ATTRIBUTES];
+ int i;
+
+ for (i = 0; i < HTML_ISINDEX_ATTRIBUTES; i++)
+ present[i] = NO;
+
+ if (prompt && *prompt) {
+ present[HTML_ISINDEX_PROMPT] = YES;
+ value[HTML_ISINDEX_PROMPT] = (const char *) prompt;
+ }
+ if (href) {
+ present[HTML_ISINDEX_HREF] = YES;
+ value[HTML_ISINDEX_HREF] = (const char *) href;
+ }
+
+ (*obj->isa->start_element) (obj, HTML_ISINDEX, present, value, -1, 0);
+}
diff --git a/WWW/Library/Implementation/HTMLDTD.h b/WWW/Library/Implementation/HTMLDTD.h
new file mode 100644
index 0000000..10b0308
--- /dev/null
+++ b/WWW/Library/Implementation/HTMLDTD.h
@@ -0,0 +1,97 @@
+/*
+ * $LynxId: HTMLDTD.h,v 1.32 2008/07/06 17:38:13 tom Exp $
+ *
+ The HTML DTD -- software interface in libwww
+ HTML DTD - SOFTWARE INTERFACE
+
+ SGML purists should excuse the use of the term "DTD" in this file to
+ represent DTD-related information which is not exactly a DTD itself.
+
+ The C modular structure doesn't work very well here, as the dtd is
+ partly in the .h and partly in the .c which are not very independent.
+ Tant pis.
+
+ */
+#ifndef HTMLDTD_H
+#define HTMLDTD_H
+
+#include <SGML.h>
+#include <HTFont.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+ * Valid name chars for tag parsing.
+ */
+#define IsNmStart(c) (isalpha(UCH(c)))
+#define IsNmChar(c) (isalnum(UCH(c)) || \
+ c == '_' || c=='-' || c == '.' || c==':')
+#define ReallyEmptyTagNum(e) ((HTML_dtd.tags[e].contents == SGML_EMPTY) && \
+ !(HTML_dtd.tags[e].flags & Tgf_nreie))
+#define ReallyEmptyTag(t) ((t->contents == SGML_EMPTY) && \
+ !(t->flags & Tgf_nreie))
+
+#include <hdr_HTMLDTD.h>
+
+#ifdef USE_PRETTYSRC
+/* values of HTML attributes' types */
+#define HTMLA_NORMAL 0 /* nothing specific */
+#define HTMLA_ANAME 1 /* anchor name - 'id' or a's 'name' */
+#define HTMLA_HREF 2 /* href */
+#define HTMLA_CLASS 4 /* class name. */
+#define HTMLA_AUXCLASS 8 /* attribute, the value of which also designates
+ a class name */
+#endif
+ extern const SGML_dtd HTML_dtd;
+
+ extern void HTSwitchDTD(int new_flag);
+
+ extern HTTag HTTag_unrecognized;
+ extern HTTag HTTag_mixedObject;
+
+/*
+
+Start anchor element
+
+ It is kinda convenient to have a particular routine for starting an anchor
+ element, as everything else for HTML is simple anyway.
+
+ ON ENTRY
+
+ targetstream points to a structured stream object.
+
+ name and href point to attribute strings or are NULL if the attribute is
+ to be omitted.
+
+ */
+ extern void HTStartAnchor(HTStructured * targetstream, const char *name,
+ const char *href);
+
+ extern void HTStartAnchor5(HTStructured * targetstream, const char *name,
+ const char *href,
+ const char *linktype,
+ int tag_charset);
+
+/*
+
+Start IsIndex element - FM
+
+ It is kinda convenient to have a particular routine for starting an IsIndex
+ element with the prompt and/or href (action) attributes specified.
+
+ ON ENTRY
+
+ targetstream points to a structured stream object.
+
+ prompt and href point to attribute strings or are NULL if the attribute is
+ to be omitted.
+
+ */
+ extern void HTStartIsIndex(HTStructured * targetstream, const char *prompt,
+ const char *href);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* HTMLDTD_H */
diff --git a/WWW/Library/Implementation/HTMLGen.c b/WWW/Library/Implementation/HTMLGen.c
new file mode 100644
index 0000000..c63723c
--- /dev/null
+++ b/WWW/Library/Implementation/HTMLGen.c
@@ -0,0 +1,738 @@
+/*
+ * $LynxId: HTMLGen.c,v 1.46 2020/01/21 22:02:35 tom Exp $
+ *
+ * HTML Generator
+ * ==============
+ *
+ * This version of the HTML object sends HTML markup to the output stream.
+ *
+ * Bugs: Line wrapping is not done at all.
+ * All data handled as PCDATA.
+ * Should convert old XMP, LISTING and PLAINTEXT to PRE.
+ *
+ * It is not obvious to me right now whether the HEAD should be generated
+ * from the incoming data or the anchor. Currently it is from the former
+ * which is cleanest.
+ */
+
+#define HTSTREAM_INTERNAL 1
+
+#include <HTUtils.h>
+
+#define BUFFER_SIZE 200 /* Line buffer attempts to make neat breaks */
+#define MAX_CLEANNESS 20
+
+/* Implements:
+*/
+#include <HTMLGen.h>
+
+#include <HTMLDTD.h>
+#include <HTStream.h>
+#include <SGML.h>
+#include <HTFormat.h>
+
+#ifdef USE_COLOR_STYLE
+#include <LYCharUtils.h>
+#include <AttrList.h>
+#include <LYHash.h>
+#include <LYStyle.h>
+#endif
+
+#include <LYGlobalDefs.h>
+#include <LYCurses.h>
+#include <LYLeaks.h>
+
+#ifdef USE_COLOR_STYLE
+char class_string[TEMPSTRINGSIZE + 1];
+
+static char *Style_className = NULL;
+static int hcode;
+#endif
+
+/* HTML Object
+ * -----------
+ */
+struct _HTStream {
+ const HTStreamClass *isa;
+ HTStream *target;
+ HTStreamClass targetClass; /* COPY for speed */
+};
+
+struct _HTStructured {
+ const HTStructuredClass *isa;
+ HTStream *target;
+ HTStreamClass targetClass; /* COPY for speed */
+
+ char buffer[BUFFER_SIZE + 1]; /* 1for NL */
+ int buffer_maxchars;
+ char *write_pointer;
+ char *line_break[MAX_CLEANNESS + 1];
+ int cleanness;
+ BOOL overflowed;
+ BOOL delete_line_break_char[MAX_CLEANNESS + 1];
+ BOOL preformatted;
+ BOOL escape_specials;
+ BOOL in_attrval;
+#ifdef USE_COLOR_STYLE
+ HText *text;
+#endif
+};
+
+/* Flush Buffer
+ * ------------
+ */
+
+static void flush_breaks(HTStructured * me)
+{
+ int i;
+
+ for (i = 0; i <= MAX_CLEANNESS; i++) {
+ me->line_break[i] = NULL;
+ }
+}
+
+static void HTMLGen_flush(HTStructured * me)
+{
+ (*me->targetClass.put_block) (me->target,
+ me->buffer,
+ (int) (me->write_pointer - me->buffer));
+ me->write_pointer = me->buffer;
+ flush_breaks(me);
+ me->cleanness = 0;
+ me->delete_line_break_char[0] = NO;
+}
+
+#ifdef USE_COLOR_STYLE
+/*
+ * We need to flush our buffer each time before we effect a color style change,
+ * this also relies on the subsequent stage not doing any buffering - this is
+ * currently true, in cases where it matters the target stream should be the
+ * HTPlain converter. The flushing currently prevents reasonable line breaking
+ * in lines with tags. Since color styles help visual scanning of displayed
+ * source lines, and long lines are wrapped in GridText anyway, this is
+ * probably acceptable (or even A Good Thing - more to see in one screenful).
+ * The pointer to the HText structure is initialized here before we effect the
+ * first style change. Getting it from the global HTMainText variable isn't
+ * very clean, since it relies on the fact that HText_new() has already been
+ * called for the current stream stack's document by the time we start
+ * processing the first element; we rely on HTMLGenerator's callers
+ * (HTMLParsedPresent in particular) to guarantee this when it matters.
+ * Normally the target stream will have been setup by HTPlainPresent, which
+ * does what we need in this respect. (A check whether we have the right
+ * output stream could be done by checking that targetClass.name is
+ * "PlainPresenter" or similar.)
+ *
+ * All special color style handling is only done if LYPreparsedSource is set.
+ * We could always do it for displaying source generated by an internal
+ * gateway, but this makes the rule more simple for the user: color styles are
+ * applied to html source only with the -preparsed flag. - kw
+ */
+static void do_cstyle_flush(HTStructured * me)
+{
+ if (!me->text && LYPreparsedSource) {
+ me->text = HTMainText;
+ }
+ if (me->text) {
+ HTMLGen_flush(me);
+ }
+}
+#endif /* COLOR_STYLE */
+
+/* Weighted optional line break
+ *
+ * We keep track of all the breaks for when we chop the line
+ */
+
+static void allow_break(HTStructured * me, int new_cleanness, int dlbc)
+{
+ if (dlbc && me->write_pointer == me->buffer)
+ dlbc = NO;
+ me->line_break[new_cleanness] =
+ dlbc ? me->write_pointer - 1 /* Point to space */
+ : me->write_pointer; /* point to gap */
+ me->delete_line_break_char[new_cleanness] = (BOOLEAN) dlbc;
+ if (new_cleanness >= me->cleanness &&
+ (me->overflowed || me->line_break[new_cleanness] > me->buffer))
+ me->cleanness = new_cleanness;
+}
+
+/* Character handling
+ * ------------------
+ *
+ * The tricky bits are the line break handling. This attempts
+ * to synchrononise line breaks on sentence or phrase ends. This
+ * is important if one stores SGML files in a line-oriented code
+ * repository, so that if a small change is made, line ends don't
+ * shift in a ripple-through to apparently change a large part of the
+ * file. We give extra "cleanness" to spaces appearing directly
+ * after periods (full stops), [semi]colons and commas.
+ * This should make the source files easier to read and modify
+ * by hand, too, though this is not a primary design consideration. TBL
+ */
+static void HTMLGen_put_character(HTStructured * me, int c)
+{
+ if (me->escape_specials && UCH(c) < 32) {
+ if (c == HT_NON_BREAK_SPACE || c == HT_EN_SPACE ||
+ c == LY_SOFT_HYPHEN) { /* recursion... */
+ HTMLGen_put_character(me, '&');
+ HTMLGen_put_character(me, '#');
+ HTMLGen_put_character(me, 'x');
+ switch (c) {
+ case HT_NON_BREAK_SPACE: /* &#xA0; */
+ HTMLGen_put_character(me, 'A');
+ HTMLGen_put_character(me, '0');
+ break;
+ case HT_EN_SPACE: /* &#x2002; */
+ HTMLGen_put_character(me, '2');
+ HTMLGen_put_character(me, '0');
+ HTMLGen_put_character(me, '0');
+ HTMLGen_put_character(me, '2');
+ break;
+ case LY_SOFT_HYPHEN: /* &#xAD; */
+ HTMLGen_put_character(me, 'A');
+ HTMLGen_put_character(me, 'D');
+ break;
+ }
+ c = ';';
+ }
+ }
+
+ *me->write_pointer++ = (char) c;
+
+ if (c == '\n') {
+ HTMLGen_flush(me);
+ return;
+ }
+
+ /* Figure our whether we can break at this point
+ */
+ if ((!me->preformatted && (c == ' ' || c == '\t'))) {
+ int new_cleanness = 3;
+
+ if (me->write_pointer > (me->buffer + 1)) {
+ char delims[5];
+ char *p;
+
+ strcpy(delims, ",;:."); /* @@ english bias */
+ p = StrChr(delims, me->write_pointer[-2]);
+ if (p)
+ new_cleanness = (int) (p - delims + 6);
+ if (!me->in_attrval)
+ new_cleanness += 10;
+ }
+ allow_break(me, new_cleanness, YES);
+ }
+
+ /*
+ * Flush buffer out when full, or whenever the line is over the nominal
+ * maximum and we can break at all
+ */
+ if (me->write_pointer >= me->buffer + me->buffer_maxchars ||
+ (me->overflowed && me->cleanness)) {
+ if (me->cleanness) {
+ char line_break_char = me->line_break[me->cleanness][0];
+ char *saved = me->line_break[me->cleanness];
+
+ if (me->delete_line_break_char[me->cleanness])
+ saved++;
+ me->line_break[me->cleanness][0] = '\n';
+ (*me->targetClass.put_block) (me->target,
+ me->buffer,
+ (int) (me->line_break[me->cleanness] -
+ me->buffer + 1));
+ me->line_break[me->cleanness][0] = line_break_char;
+ { /* move next line in */
+ char *p = saved;
+ char *q;
+
+ for (q = me->buffer; p < me->write_pointer;)
+ *q++ = *p++;
+ }
+ me->cleanness = 0;
+ /* Now we have to check whether there are any perfectly good breaks
+ * which weren't good enough for the last line but may be good
+ * enough for the next
+ */
+ {
+ int i;
+
+ for (i = 0; i <= MAX_CLEANNESS; i++) {
+ if (me->line_break[i] != NULL &&
+ me->line_break[i] > saved) {
+ me->line_break[i] = me->line_break[i] -
+ (saved - me->buffer);
+ me->cleanness = i;
+ } else {
+ me->line_break[i] = NULL;
+ }
+ }
+ }
+
+ me->delete_line_break_char[0] = 0;
+ me->write_pointer = me->write_pointer - (saved - me->buffer);
+ me->overflowed = NO;
+
+ } else {
+ (*me->targetClass.put_block) (me->target,
+ me->buffer,
+ me->buffer_maxchars);
+ me->write_pointer = me->buffer;
+ flush_breaks(me);
+ me->overflowed = YES;
+ }
+ }
+}
+
+/* String handling
+ * ---------------
+ */
+static void HTMLGen_put_string(HTStructured * me, const char *s)
+{
+ const char *p;
+
+ for (p = s; *p; p++)
+ HTMLGen_put_character(me, *p);
+}
+
+static void HTMLGen_write(HTStructured * me, const char *s,
+ int l)
+{
+ const char *p;
+
+ for (p = s; p < (s + l); p++)
+ HTMLGen_put_character(me, *p);
+}
+
+/* Start Element
+ * -------------
+ *
+ * Within the opening tag, there may be spaces and the line may be broken at
+ * these spaces.
+ */
+static int HTMLGen_start_element(HTStructured * me, int element_number,
+ const BOOL *present,
+ STRING2PTR value,
+ int charset GCC_UNUSED,
+ char **insert GCC_UNUSED)
+{
+ int i;
+ BOOL was_preformatted = me->preformatted;
+ HTTag *tag = &HTML_dtd.tags[element_number];
+
+#if defined(USE_COLOR_STYLE)
+ char *title = NULL;
+ char *title_tmp = NULL;
+ const char *name;
+
+ if (LYPreparsedSource && (name = tag->name) != 0) {
+ char *myHash = NULL;
+
+ /*
+ * Same logic as in HTML_start_element, copied from there. - kw
+ */
+ HTSprintf(&Style_className, ";%s", name);
+ StrAllocCopy(myHash, name);
+ if (class_string[0]) {
+ StrAllocCat(myHash, ".");
+ StrAllocCat(myHash, class_string);
+ HTSprintf(&Style_className, ".%s", class_string);
+ }
+ class_string[0] = '\0';
+ strtolower(myHash);
+ hcode = color_style_1(myHash);
+ strtolower(Style_className);
+
+ if (TRACE_STYLE) {
+ fprintf(tfp, "CSSTRIM:%s -> %d", myHash, hcode);
+ if (!hashStyles[hcode].used) {
+ char *rp = strrchr(myHash, '.');
+
+ fprintf(tfp, " (undefined) %s\n", myHash);
+ if (rp) {
+ int hcd;
+
+ *rp = '\0'; /* trim the class */
+ hcd = color_style_1(myHash);
+ fprintf(tfp, "CSS:%s -> %d", myHash, hcd);
+ if (!hashStyles[hcd].used)
+ fprintf(tfp, " (undefined) %s\n", myHash);
+ else
+ fprintf(tfp, " ca=%d\n", hashStyles[hcd].color);
+ }
+ } else
+ fprintf(tfp, " ca=%d\n", hashStyles[hcode].color);
+ }
+
+ if (displayStyles[element_number + STARTAT].color > -2) {
+ CTRACE2(TRACE_STYLE,
+ (tfp, "CSSTRIM: start_element: top <%s>\n",
+ tag->name));
+ do_cstyle_flush(me);
+ HText_characterStyle(me->text, hcode, 1);
+ }
+ FREE(myHash);
+ }
+#endif /* USE_COLOR_STYLE */
+ me->preformatted = YES; /* free text within tags */
+ HTMLGen_put_character(me, '<');
+ HTMLGen_put_string(me, tag->name);
+ if (present) {
+ BOOL had_attr = NO;
+
+ for (i = 0; i < tag->number_of_attributes; i++) {
+ if (present[i]) {
+ had_attr = YES;
+ HTMLGen_put_character(me, ' ');
+ allow_break(me, 11, YES);
+#ifdef USE_COLOR_STYLE
+ /*
+ * Try to mimic HTML_start_element's special handling for
+ * HTML_LINK. If applicable, color the displayed attribute /
+ * value pairs differently. - kw
+ */
+ if (LYPreparsedSource &&
+ element_number == HTML_LINK && !title &&
+ present[HTML_LINK_CLASS] && *value[HTML_LINK_CLASS] &&
+ !present[HTML_LINK_REV] &&
+ (present[HTML_LINK_REL] || present[HTML_LINK_HREF])) {
+ if (present[HTML_LINK_TITLE] && *value[HTML_LINK_TITLE]) {
+ StrAllocCopy(title, value[HTML_LINK_TITLE]);
+ LYTrimHead(title);
+ LYTrimTail(title);
+ }
+ if ((!title || *title == '\0') && present[HTML_LINK_REL]) {
+ StrAllocCopy(title, value[HTML_LINK_REL]);
+ }
+ if (title && *title) {
+ HTSprintf0(&title_tmp, "link.%s.%s",
+ value[HTML_LINK_CLASS], title);
+ CTRACE2(TRACE_STYLE,
+ (tfp, "CSSTRIM:link=%s\n", title_tmp));
+
+ do_cstyle_flush(me);
+ HText_characterStyle(me->text,
+ color_style_1(title_tmp), 1);
+ }
+ }
+#endif
+ HTMLGen_put_string(me, tag->attributes[i].name);
+ if (value[i]) {
+ me->preformatted = was_preformatted;
+ me->in_attrval = YES;
+ if (StrChr(value[i], '"') == NULL) {
+ HTMLGen_put_string(me, "=\"");
+ HTMLGen_put_string(me, value[i]);
+ HTMLGen_put_character(me, '"');
+ } else if (StrChr(value[i], '\'') == NULL) {
+ HTMLGen_put_string(me, "='");
+ HTMLGen_put_string(me, value[i]);
+ HTMLGen_put_character(me, '\'');
+ } else { /* attribute value has both kinds of quotes */
+ const char *p;
+
+ HTMLGen_put_string(me, "=\"");
+ for (p = value[i]; *p; p++) {
+ if (*p != '"') {
+ HTMLGen_put_character(me, *p);
+ } else {
+ HTMLGen_put_string(me, "&#34;");
+ }
+ }
+ HTMLGen_put_character(me, '"');
+ }
+ me->preformatted = YES;
+ me->in_attrval = NO;
+ }
+ }
+ }
+#ifdef USE_COLOR_STYLE
+ if (had_attr && LYPreparsedSource && element_number == HTML_LINK) {
+ /*
+ * Clean up after special HTML_LINK handling - kw
+ */
+ if (title && *title) {
+ do_cstyle_flush(me);
+ HText_characterStyle(me->text, color_style_1(title_tmp), 0);
+ FREE(title_tmp);
+ }
+ FREE(title);
+ }
+#endif
+ if (had_attr)
+ allow_break(me, 12, NO);
+ }
+ HTMLGen_put_string(me, ">"); /* got rid of \n LJM */
+
+ /*
+ * Make very specific HTML assumption that PRE can't be nested!
+ */
+ me->preformatted = (BOOL) ((element_number == HTML_PRE)
+ ? YES
+ : was_preformatted);
+
+ /*
+ * Can break after element start.
+ */
+ if (!me->preformatted && tag->contents != SGML_EMPTY) {
+ if (tag->contents == SGML_ELEMENT)
+ allow_break(me, 15, NO);
+ else
+ allow_break(me, 2, NO);
+ }
+#if defined(USE_COLOR_STYLE)
+ /*
+ * Same logic as in HTML_start_element, copied from there. - kw
+ */
+
+ /* end really empty tags straight away */
+ if (LYPreparsedSource && ReallyEmptyTagNum(element_number)) {
+ CTRACE2(TRACE_STYLE,
+ (tfp, "STYLE:begin_element:ending EMPTY element style\n"));
+ do_cstyle_flush(me);
+ HText_characterStyle(me->text, hcode, STACK_OFF);
+ TrimColorClass(tag->name, Style_className, &hcode);
+ }
+#endif /* USE_COLOR_STYLE */
+ if (element_number == HTML_OBJECT && tag->contents == SGML_LITTERAL) {
+ /*
+ * These conditions only approximate the ones used in HTML.c. Let our
+ * SGML parser know that further content is to be parsed normally not
+ * literally. - kw
+ */
+ if (!present) {
+ return HT_PARSER_OTHER_CONTENT;
+ } else if (!present[HTML_OBJECT_DECLARE] &&
+ !(present[HTML_OBJECT_NAME] &&
+ value[HTML_OBJECT_NAME] && *value[HTML_OBJECT_NAME])) {
+ if (present[HTML_OBJECT_SHAPES] ||
+ !(present[HTML_OBJECT_USEMAP] &&
+ value[HTML_OBJECT_USEMAP] && *value[HTML_OBJECT_USEMAP]))
+ return HT_PARSER_OTHER_CONTENT;
+ }
+ }
+ return HT_OK;
+}
+
+/* End Element
+ * -----------
+ *
+ * When we end an element, the style must be returned to that in effect before
+ * that element. Note that anchors (etc?) don't have an associated style, so
+ * that we must scan down the stack for an element with a defined style. (In
+ * fact, the styles should be linked to the whole stack not just the top one.)
+ * TBL 921119
+ */
+static int HTMLGen_end_element(HTStructured * me, int element_number,
+ char **insert GCC_UNUSED)
+{
+ if (!me->preformatted &&
+ HTML_dtd.tags[element_number].contents != SGML_EMPTY) {
+ /*
+ * Can break before element end.
+ */
+ if (HTML_dtd.tags[element_number].contents == SGML_ELEMENT)
+ allow_break(me, 14, NO);
+ else
+ allow_break(me, 1, NO);
+ }
+ HTMLGen_put_string(me, "</");
+ HTMLGen_put_string(me, HTML_dtd.tags[element_number].name);
+ HTMLGen_put_character(me, '>');
+ if (element_number == HTML_PRE) {
+ me->preformatted = NO;
+ }
+#ifdef USE_COLOR_STYLE
+ /*
+ * Same logic as in HTML_end_element, copied from there. - kw
+ */
+ TrimColorClass(HTML_dtd.tags[element_number].name,
+ Style_className, &hcode);
+
+ if (LYPreparsedSource && !ReallyEmptyTagNum(element_number)) {
+ CTRACE2(TRACE_STYLE,
+ (tfp, "STYLE:end_element: ending non-EMPTY style\n"));
+ do_cstyle_flush(me);
+ HText_characterStyle(me->text, hcode, STACK_OFF);
+ }
+#endif /* USE_COLOR_STYLE */
+ return HT_OK;
+}
+
+/* Expanding entities
+ * ------------------
+ *
+ */
+static int HTMLGen_put_entity(HTStructured * me, int entity_number)
+{
+ int nent = (int) HTML_dtd.number_of_entities;
+
+ HTMLGen_put_character(me, '&');
+ if (entity_number < nent) {
+ HTMLGen_put_string(me, HTML_dtd.entity_names[entity_number]);
+ }
+ HTMLGen_put_character(me, ';');
+ return HT_OK;
+}
+
+/* Free an HTML object
+ * -------------------
+ *
+ */
+static void HTMLGen_free(HTStructured * me)
+{
+ (*me->targetClass.put_character) (me->target, '\n');
+ HTMLGen_flush(me);
+ (*me->targetClass._free) (me->target); /* ripple through */
+#ifdef USE_COLOR_STYLE
+ FREE(Style_className);
+#endif
+ FREE(me);
+}
+
+static void PlainToHTML_free(HTStructured * me)
+{
+ HTMLGen_end_element(me, HTML_PRE, 0);
+ HTMLGen_free(me);
+}
+
+static void HTMLGen_abort(HTStructured * me, HTError e GCC_UNUSED)
+{
+ HTMLGen_free(me);
+#ifdef USE_COLOR_STYLE
+ FREE(Style_className);
+#endif
+}
+
+static void PlainToHTML_abort(HTStructured * me, HTError e GCC_UNUSED)
+{
+ PlainToHTML_free(me);
+}
+
+/* Structured Object Class
+ * -----------------------
+ */
+static const HTStructuredClass HTMLGeneration = /* As opposed to print etc */
+{
+ "HTMLGen",
+ HTMLGen_free,
+ HTMLGen_abort,
+ HTMLGen_put_character, HTMLGen_put_string, HTMLGen_write,
+ HTMLGen_start_element, HTMLGen_end_element,
+ HTMLGen_put_entity
+};
+
+/* Subclass-specific Methods
+ * -------------------------
+ */
+HTStructured *HTMLGenerator(HTStream *output)
+{
+ HTStructured *me = (HTStructured *) malloc(sizeof(*me));
+
+ if (me == NULL)
+ outofmem(__FILE__, "HTMLGenerator");
+
+ me->isa = &HTMLGeneration;
+
+ me->target = output;
+ me->targetClass = *me->target->isa; /* Copy pointers to routines for speed */
+
+ me->write_pointer = me->buffer;
+ flush_breaks(me);
+ me->line_break[0] = me->buffer;
+ me->cleanness = 0;
+ me->overflowed = NO;
+ me->delete_line_break_char[0] = NO;
+ me->preformatted = NO;
+ me->in_attrval = NO;
+
+ /*
+ * For what line length should we attempt to wrap ? - kw
+ */
+ if (!LYPreparsedSource) {
+ me->buffer_maxchars = 80; /* work as before - kw */
+ } else if (dump_output_width > 1) {
+ me->buffer_maxchars = dump_output_width; /* try to honor -width - kw */
+ } else if (dump_output_immediately) {
+ me->buffer_maxchars = 80; /* try to honor -width - kw */
+ } else {
+ me->buffer_maxchars = (LYcolLimit - 1);
+ if (me->buffer_maxchars < 38) /* too narrow, let GridText deal */
+ me->buffer_maxchars = 40;
+ }
+ if (me->buffer_maxchars > 900) /* likely not true - kw */
+ me->buffer_maxchars = 78;
+ if (me->buffer_maxchars > BUFFER_SIZE) /* must not be larger! */
+ me->buffer_maxchars = BUFFER_SIZE - 2;
+
+ /*
+ * If dump_output_immediately is set, there likely isn't anything after
+ * this stream to interpret the Lynx special chars. Also if they get
+ * displayed via HTPlain, that will probably make non-breaking space chars
+ * etc. invisible. So let's translate them to numerical character
+ * references. For debugging purposes we'll use the new hex format.
+ */
+ me->escape_specials = LYPreparsedSource;
+
+#ifdef USE_COLOR_STYLE
+ me->text = NULL; /* Will be initialized when first needed. - kw */
+ FREE(Style_className);
+ class_string[0] = '\0';
+#endif /* COLOR_STYLE */
+
+ return me;
+}
+
+/* Stream Object Class
+ * -------------------
+ *
+ * This object just converts a plain text stream into HTML
+ * It is officially a structured stream but only the stream bits exist.
+ * This is just the easiest way of typecasting all the routines.
+ */
+static const HTStructuredClass PlainToHTMLConversion =
+{
+ "plaintexttoHTML",
+ HTMLGen_free,
+ PlainToHTML_abort,
+ HTMLGen_put_character,
+ HTMLGen_put_string,
+ HTMLGen_write,
+ NULL, /* Structured stuff */
+ NULL,
+ NULL
+};
+
+/* HTConverter from plain text to HTML Stream
+ * ------------------------------------------
+ */
+HTStream *HTPlainToHTML(HTPresentation *pres GCC_UNUSED,
+ HTParentAnchor *anchor GCC_UNUSED,
+ HTStream *sink)
+{
+ HTStructured *me = (HTStructured *) malloc(sizeof(*me));
+
+ if (me == NULL)
+ outofmem(__FILE__, "PlainToHTML");
+
+ me->isa = (const HTStructuredClass *) &PlainToHTMLConversion;
+
+ /*
+ * Copy pointers to routines for speed.
+ */
+ me->target = sink;
+ me->targetClass = *me->target->isa;
+ me->write_pointer = me->buffer;
+ flush_breaks(me);
+ me->cleanness = 0;
+ me->overflowed = NO;
+ me->delete_line_break_char[0] = NO;
+ /* try to honor -width - kw */
+ me->buffer_maxchars = (dump_output_width > 1 ?
+ dump_output_width : 80);
+
+ HTMLGen_put_string(me, "<HTML>\n<BODY>\n<PRE>\n");
+ me->preformatted = YES;
+ me->escape_specials = NO;
+ me->in_attrval = NO;
+ return (HTStream *) me;
+}
diff --git a/WWW/Library/Implementation/HTMLGen.h b/WWW/Library/Implementation/HTMLGen.h
new file mode 100644
index 0000000..f2db3d4
--- /dev/null
+++ b/WWW/Library/Implementation/HTMLGen.h
@@ -0,0 +1,26 @@
+/* /Net/dxcern/userd/timbl/hypertext/WWW/Library/Implementation/HTMLGen.html
+ HTML GENERATOR
+
+ This module converts structed stream into stream. That is, given a stream
+ to write to, it will give you a structured stream to
+
+ */
+#ifndef HTMLGEN_H
+#define HTMLGEN_H
+
+#include <HTML.h>
+#include <HTStream.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+ extern HTStructured *HTMLGenerator(HTStream *output);
+
+ extern HTStream *HTPlainToHTML(HTPresentation *pres,
+ HTParentAnchor *anchor,
+ HTStream *sink);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* HTMLGEN_H */
diff --git a/WWW/Library/Implementation/HTNews.c b/WWW/Library/Implementation/HTNews.c
new file mode 100644
index 0000000..a1b94dc
--- /dev/null
+++ b/WWW/Library/Implementation/HTNews.c
@@ -0,0 +1,3147 @@
+/*
+ * $LynxId: HTNews.c,v 1.81 2022/04/01 00:18:22 tom Exp $
+ *
+ * NEWS ACCESS HTNews.c
+ * ===========
+ *
+ * History:
+ * 26 Sep 90 Written TBL
+ * 29 Nov 91 Downgraded to C, for portable implementation.
+ */
+
+#include <HTUtils.h> /* Coding convention macros */
+
+#ifndef DISABLE_NEWS
+
+/* Implements:
+*/
+#include <HTNews.h>
+
+#include <HTCJK.h>
+#include <HTMIME.h>
+#include <HTFont.h>
+#include <HTFormat.h>
+#include <HTTCP.h>
+#include <LYUtils.h>
+#include <LYStrings.h>
+
+#define NEWS_PORT 119 /* See rfc977 */
+#define SNEWS_PORT 563 /* See Lou Montulli */
+#define APPEND /* Use append methods */
+int HTNewsChunkSize = 30; /* Number of articles for quick display */
+int HTNewsMaxChunk = 40; /* Largest number of articles in one window */
+
+#ifndef DEFAULT_NEWS_HOST
+#define DEFAULT_NEWS_HOST "news"
+#endif /* DEFAULT_NEWS_HOST */
+
+#ifndef NEWS_SERVER_FILE
+#define NEWS_SERVER_FILE "/usr/local/lib/rn/server"
+#endif /* NEWS_SERVER_FILE */
+
+#ifndef NEWS_AUTH_FILE
+#define NEWS_AUTH_FILE ".newsauth"
+#endif /* NEWS_AUTH_FILE */
+
+#ifdef USE_SSL
+
+#if defined(LIBRESSL_VERSION_NUMBER)
+/* OpenSSL and LibreSSL version numbers do not correspond */
+#elif (OPENSSL_VERSION_NUMBER >= 0x10100000L)
+#undef SSL_load_error_strings
+#define SSL_load_error_strings() /* nothing */
+#endif
+
+static SSL *Handle = NULL;
+static int channel_s = 1;
+
+#define NEWS_NETWRITE(sock, buff, size) \
+ ((Handle != NULL) \
+ ? SSL_write(Handle, buff, size) \
+ : NETWRITE(sock, buff, size))
+#define NEWS_NETCLOSE(sock) \
+ { \
+ if ((int)(sock) >= 0) { \
+ (void)NETCLOSE(sock); \
+ } \
+ if (Handle != NULL) { \
+ SSL_free(Handle); \
+ Handle = NULL; \
+ } \
+ }
+static int HTNewsGetCharacter(void);
+
+#define NEXT_CHAR HTNewsGetCharacter()
+#else
+#define NEWS_NETWRITE NETWRITE
+#define NEWS_NETCLOSE NETCLOSE
+#define NEXT_CHAR HTGetCharacter()
+#endif /* USE_SSL */
+
+#include <HTML.h>
+#include <HTAccess.h>
+#include <HTParse.h>
+#include <HTFormat.h>
+#include <HTAlert.h>
+
+#include <LYNews.h>
+#include <LYGlobalDefs.h>
+#include <LYLeaks.h>
+
+#define SnipIn(d,fmt,len,s) sprintf(d, fmt, (int)sizeof(d)-len, s)
+#define SnipIn2(d,fmt,tag,len,s) sprintf(d, fmt, tag, (int)sizeof(d)-len, s)
+
+struct _HTStructured {
+ const HTStructuredClass *isa;
+ /* ... */
+};
+
+#define LINE_LENGTH 512 /* Maximum length of line of ARTICLE etc */
+#define GROUP_NAME_LENGTH 256 /* Maximum length of group name */
+
+/*
+ * Module-wide variables.
+ */
+char *HTNewsHost = NULL; /* Default host */
+static char *NewsHost = NULL; /* Current host */
+static char *NewsHREF = NULL; /* Current HREF prefix */
+static int s; /* Socket for NewsHost */
+static int HTCanPost = FALSE; /* Current POST permission */
+static char response_text[LINE_LENGTH + 1]; /* Last response */
+
+static HTStructured *target; /* The output sink */
+static HTStructuredClass targetClass; /* Copy of fn addresses */
+static HTStream *rawtarget = NULL; /* The output sink for rawtext */
+static HTStreamClass rawtargetClass; /* Copy of fn addresses */
+static int diagnostic; /* level: 0=none 2=source */
+static BOOL rawtext = NO; /* Flag: HEAD or -mime_headers */
+static HTList *NNTP_AuthInfo = NULL; /* AUTHINFO database */
+static char *name = NULL;
+static char *address = NULL;
+static char *dbuf = NULL; /* dynamic buffer for long messages etc. */
+
+#define PUTC(c) (*targetClass.put_character)(target, c)
+#define PUTS(s) (*targetClass.put_string)(target, s)
+#define RAW_PUTS(s) (*rawtargetClass.put_string)(rawtarget, s)
+#define START(e) (*targetClass.start_element)(target, e, 0, 0, -1, 0)
+#define END(e) (*targetClass.end_element)(target, e, 0)
+#define MAYBE_END(e) if (HTML_dtd.tags[e].contents != SGML_EMPTY) \
+ (*targetClass.end_element)(target, e, 0)
+#define FREE_TARGET if (rawtext) (*rawtargetClass._free)(rawtarget); \
+ else (*targetClass._free)(target)
+#define ABORT_TARGET if (rawtext) (*rawtargetClass._abort)(rawtarget, NULL); \
+ else (*targetClass._abort)(target, NULL)
+
+typedef struct _NNTPAuth {
+ char *host;
+ char *user;
+ char *pass;
+} NNTPAuth;
+
+#ifdef LY_FIND_LEAKS
+static void free_news_globals(void)
+{
+ if (s >= 0) {
+ NEWS_NETCLOSE(s);
+ s = -1;
+ }
+ FREE(HTNewsHost);
+ FREE(NewsHost);
+ FREE(NewsHREF);
+ FREE(name);
+ FREE(address);
+ FREE(dbuf);
+}
+#endif /* LY_FIND_LEAKS */
+
+static void free_NNTP_AuthInfo(void)
+{
+ HTList *cur = NNTP_AuthInfo;
+ NNTPAuth *auth = NULL;
+
+ if (!cur)
+ return;
+
+ while (NULL != (auth = (NNTPAuth *) HTList_nextObject(cur))) {
+ FREE(auth->host);
+ FREE(auth->user);
+ FREE(auth->pass);
+ FREE(auth);
+ }
+ HTList_delete(NNTP_AuthInfo);
+ NNTP_AuthInfo = NULL;
+ return;
+}
+
+/*
+ * Initialize the authentication list by loading the user's $HOME/.newsauth
+ * file. That file is part of tin's configuration and is used by a few other
+ * programs.
+ */
+static void load_NNTP_AuthInfo(void)
+{
+ FILE *fp;
+ char fname[LY_MAXPATH];
+ char buffer[LINE_LENGTH + 1];
+
+ LYAddPathToHome(fname, sizeof(fname), NEWS_AUTH_FILE);
+
+ if ((fp = fopen(fname, "r")) != 0) {
+ while (fgets(buffer, (int) sizeof(buffer), fp) != 0) {
+ char the_host[LINE_LENGTH + 1];
+ char the_pass[LINE_LENGTH + 1];
+ char the_user[LINE_LENGTH + 1];
+
+ if (sscanf(buffer, "%s%s%s", the_host, the_pass, the_user) == 3
+ && strlen(the_host) != 0
+ && strlen(the_pass) != 0
+ && strlen(the_user) != 0) {
+ NNTPAuth *auth = typecalloc(NNTPAuth);
+
+ if (auth == NULL)
+ break;
+ StrAllocCopy(auth->host, the_host);
+ StrAllocCopy(auth->pass, the_pass);
+ StrAllocCopy(auth->user, the_user);
+
+ HTList_appendObject(NNTP_AuthInfo, auth);
+ }
+ }
+ fclose(fp);
+ }
+}
+
+const char *HTGetNewsHost(void)
+{
+ return HTNewsHost;
+}
+
+void HTSetNewsHost(const char *value)
+{
+ StrAllocCopy(HTNewsHost, value);
+}
+
+/* Initialisation for this module
+ * ------------------------------
+ *
+ * Except on the NeXT, we pick up the NewsHost name from
+ *
+ * 1. Environment variable NNTPSERVER
+ * 2. File NEWS_SERVER_FILE
+ * 3. Compilation time macro DEFAULT_NEWS_HOST
+ * 4. Default to "news"
+ *
+ * On the NeXT, we pick up the NewsHost name from, in order:
+ *
+ * 1. WorldWideWeb default "NewsHost"
+ * 2. Global default "NewsHost"
+ * 3. News default "NewsHost"
+ * 4. Compilation time macro DEFAULT_NEWS_HOST
+ * 5. Default to "news"
+ */
+static BOOL initialized = NO;
+static BOOL initialize(void)
+{
+#ifdef NeXTStep
+ char *cp = NULL;
+#endif
+
+ /*
+ * Get name of Host.
+ */
+#ifdef NeXTStep
+ if ((cp = NXGetDefaultValue("WorldWideWeb", "NewsHost")) == 0) {
+ if ((cp = NXGetDefaultValue("News", "NewsHost")) == 0) {
+ StrAllocCopy(HTNewsHost, DEFAULT_NEWS_HOST);
+ }
+ }
+ if (cp) {
+ StrAllocCopy(HTNewsHost, cp);
+ cp = NULL;
+ }
+#else
+ if (LYGetEnv("NNTPSERVER")) {
+ StrAllocCopy(HTNewsHost, LYGetEnv("NNTPSERVER"));
+ CTRACE((tfp, "HTNews: NNTPSERVER defined as `%s'\n",
+ HTNewsHost));
+ } else {
+ FILE *fp = fopen(NEWS_SERVER_FILE, TXT_R);
+
+ if (fp) {
+ char server_name[MAXHOSTNAMELEN + 1];
+
+ if (fgets(server_name, (int) sizeof server_name, fp) != NULL) {
+ char *p = StrChr(server_name, '\n');
+
+ if (p != NULL)
+ *p = '\0';
+ StrAllocCopy(HTNewsHost, server_name);
+ CTRACE((tfp, "HTNews: File %s defines news host as `%s'\n",
+ NEWS_SERVER_FILE, HTNewsHost));
+ }
+ fclose(fp);
+ }
+ }
+ if (!HTNewsHost)
+ StrAllocCopy(HTNewsHost, DEFAULT_NEWS_HOST);
+#endif /* NeXTStep */
+
+ s = -1; /* Disconnected */
+#ifdef LY_FIND_LEAKS
+ atexit(free_news_globals);
+#endif
+ return YES;
+}
+
+/* Send NNTP Command line to remote host & Check Response
+ * ------------------------------------------------------
+ *
+ * On entry,
+ * command points to the command to be sent, including CRLF, or is null
+ * pointer if no command to be sent.
+ * On exit,
+ * Negative status indicates transmission error, socket closed.
+ * Positive status is an NNTP status.
+ */
+static int response(char *command)
+{
+ int result;
+ char *p = response_text;
+ int ich;
+
+ if (command) {
+ int status;
+ int length = (int) strlen(command);
+
+ CTRACE((tfp, "NNTP command to be sent: %s", command));
+#ifdef NOT_ASCII
+ {
+ const char *p2;
+ char *q;
+ char ascii[LINE_LENGTH + 1];
+
+ for (p2 = command, q = ascii; *p2; p2++, q++) {
+ *q = TOASCII(*p2);
+ }
+ status = NEWS_NETWRITE(s, ascii, length);
+ }
+#else
+ status = (int) NEWS_NETWRITE(s, (char *) command, length);
+#endif /* NOT_ASCII */
+ if (status < 0) {
+ CTRACE((tfp, "HTNews: Unable to send command. Disconnecting.\n"));
+ NEWS_NETCLOSE(s);
+ s = -1;
+ return status;
+ } /* if bad status */
+ }
+ /* if command to be sent */
+ for (;;) {
+ ich = NEXT_CHAR;
+ if (((*p++ = (char) ich) == LF) ||
+ (p == &response_text[LINE_LENGTH])) {
+ *--p = '\0'; /* Terminate the string */
+ CTRACE((tfp, "NNTP Response: %s\n", response_text));
+ sscanf(response_text, "%d", &result);
+ return result;
+ }
+ /* if end of line */
+ if (ich == EOF) {
+ *(p - 1) = '\0';
+ if (interrupted_in_htgetcharacter) {
+ CTRACE((tfp,
+ "HTNews: Interrupted on read, closing socket %d\n",
+ s));
+ } else {
+ CTRACE((tfp, "HTNews: EOF on read, closing socket %d\n",
+ s));
+ }
+ NEWS_NETCLOSE(s); /* End of file, close socket */
+ s = -1;
+ if (interrupted_in_htgetcharacter) {
+ interrupted_in_htgetcharacter = 0;
+ return (HT_INTERRUPTED);
+ }
+ return ((int) EOF); /* End of file on response */
+ }
+ } /* Loop over characters */
+}
+
+/* Case insensitive string comparisons
+ * -----------------------------------
+ *
+ * On entry,
+ * template must be already in upper case.
+ * unknown may be in upper or lower or mixed case to match.
+ */
+static BOOL match(const char *unknown, const char *ctemplate)
+{
+ const char *u = unknown;
+ const char *t = ctemplate;
+
+ for (; *u && *t && (TOUPPER(*u) == *t); u++, t++) ; /* Find mismatch or end */
+ return (BOOL) (*t == 0); /* OK if end of template */
+}
+
+typedef enum {
+ NNTPAUTH_ERROR = 0, /* general failure */
+ NNTPAUTH_OK = 281, /* authenticated successfully */
+ NNTPAUTH_CLOSE = 502 /* server probably closed connection */
+} NNTPAuthResult;
+
+/*
+ * This function handles nntp authentication. - FM
+ */
+static NNTPAuthResult HTHandleAuthInfo(char *host)
+{
+ HTList *cur = NULL;
+ NNTPAuth *auth = NULL;
+ char *UserName = NULL;
+ char *PassWord = NULL;
+ char *msg = NULL;
+ char buffer[512];
+ int status, tries;
+
+ /*
+ * Make sure we have a host. - FM
+ */
+ if (isEmpty(host))
+ return NNTPAUTH_ERROR;
+
+ /*
+ * Check for an existing authorization entry. - FM
+ */
+ if (NNTP_AuthInfo == NULL) {
+ NNTP_AuthInfo = HTList_new();
+ load_NNTP_AuthInfo();
+#ifdef LY_FIND_LEAKS
+ atexit(free_NNTP_AuthInfo);
+#endif
+ }
+
+ cur = NNTP_AuthInfo;
+ while (NULL != (auth = (NNTPAuth *) HTList_nextObject(cur))) {
+ if (!strcmp(auth->host, host)) {
+ UserName = auth->user;
+ PassWord = auth->pass;
+ break;
+ }
+ }
+
+ /*
+ * Handle the username. - FM
+ */
+ buffer[sizeof(buffer) - 1] = '\0';
+ tries = 3;
+
+ while (tries) {
+ if (UserName == NULL) {
+ HTSprintf0(&msg, gettext("Username for news host '%s':"), host);
+ UserName = HTPrompt(msg, NULL);
+ FREE(msg);
+ if (!(UserName && *UserName)) {
+ FREE(UserName);
+ return NNTPAUTH_ERROR;
+ }
+ }
+ sprintf(buffer, "AUTHINFO USER %.*s%c%c",
+ (int) sizeof(buffer) - 17, UserName, CR, LF);
+ if ((status = response(buffer)) < 0) {
+ if (status == HT_INTERRUPTED)
+ _HTProgress(CONNECTION_INTERRUPTED);
+ else
+ HTAlert(FAILED_CONNECTION_CLOSED);
+ if (auth) {
+ if (auth->user != UserName) {
+ FREE(auth->user);
+ auth->user = UserName;
+ }
+ } else {
+ FREE(UserName);
+ }
+ return NNTPAUTH_CLOSE;
+ }
+ if (status == 281) {
+ /*
+ * Username is accepted and no password is required. - FM
+ */
+ if (auth) {
+ if (auth->user != UserName) {
+ FREE(auth->user);
+ auth->user = UserName;
+ }
+ } else {
+ /*
+ * Store the accepted username and no password. - FM
+ */
+ if ((auth = typecalloc(NNTPAuth)) != NULL) {
+ StrAllocCopy(auth->host, host);
+ auth->user = UserName;
+ HTList_appendObject(NNTP_AuthInfo, auth);
+ }
+ }
+ return NNTPAUTH_OK;
+ }
+ if (status != 381) {
+ /*
+ * Not success, nor a request for the password, so it must be an
+ * error. - FM
+ */
+ HTAlert(response_text);
+ tries--;
+ if ((tries > 0) && HTConfirm(gettext("Change username?"))) {
+ if (!auth || auth->user != UserName) {
+ FREE(UserName);
+ }
+ if ((UserName = HTPrompt(gettext("Username:"), UserName))
+ != NULL &&
+ *UserName) {
+ continue;
+ }
+ }
+ if (auth) {
+ if (auth->user != UserName) {
+ FREE(auth->user);
+ }
+ FREE(auth->pass);
+ }
+ FREE(UserName);
+ return NNTPAUTH_ERROR;
+ }
+ break;
+ }
+
+ if (status == 381) {
+ /*
+ * Handle the password. - FM
+ */
+ tries = 3;
+ while (tries) {
+ if (PassWord == NULL) {
+ HTSprintf0(&msg, gettext("Password for news host '%s':"), host);
+ PassWord = HTPromptPassword(msg, NULL);
+ FREE(msg);
+ if (!(PassWord && *PassWord)) {
+ FREE(PassWord);
+ return NNTPAUTH_ERROR;
+ }
+ }
+ sprintf(buffer, "AUTHINFO PASS %.*s%c%c",
+ (int) sizeof(buffer) - 17, PassWord, CR, LF);
+ if ((status = response(buffer)) < 0) {
+ if (status == HT_INTERRUPTED) {
+ _HTProgress(CONNECTION_INTERRUPTED);
+ } else {
+ HTAlert(FAILED_CONNECTION_CLOSED);
+ }
+ if (auth) {
+ if (auth->user != UserName) {
+ FREE(auth->user);
+ auth->user = UserName;
+ }
+ if (auth->pass != PassWord) {
+ FREE(auth->pass);
+ auth->pass = PassWord;
+ }
+ } else {
+ FREE(UserName);
+ FREE(PassWord);
+ }
+ return NNTPAUTH_CLOSE;
+ }
+ if (status == 502) {
+ /*
+ * That's what INN's nnrpd returns. It closes the connection
+ * after this. - kw
+ */
+ HTAlert(response_text);
+ if (auth) {
+ if (auth->user == UserName)
+ UserName = NULL;
+ FREE(auth->user);
+ if (auth->pass == PassWord)
+ PassWord = NULL;
+ FREE(auth->pass);
+ }
+ FREE(UserName);
+ FREE(PassWord);
+ return NNTPAUTH_CLOSE;
+ }
+ if (status == 281) {
+ /*
+ * Password also is accepted, and everything has been stored.
+ * - FM
+ */
+ if (auth) {
+ if (auth->user != UserName) {
+ FREE(auth->user);
+ auth->user = UserName;
+ }
+ if (auth->pass != PassWord) {
+ FREE(auth->pass);
+ auth->pass = PassWord;
+ }
+ } else {
+ if ((auth = typecalloc(NNTPAuth)) != NULL) {
+ StrAllocCopy(auth->host, host);
+ auth->user = UserName;
+ auth->pass = PassWord;
+ HTList_appendObject(NNTP_AuthInfo, auth);
+ }
+ }
+ return NNTPAUTH_OK;
+ }
+ /*
+ * Not success, so it must be an error. - FM
+ */
+ HTAlert(response_text);
+ if (!auth || auth->pass != PassWord) {
+ FREE(PassWord);
+ } else {
+ PassWord = NULL;
+ }
+ tries--;
+ if ((tries > 0) && HTConfirm(gettext("Change password?"))) {
+ continue;
+ }
+ if (auth) {
+ if (auth->user == UserName)
+ UserName = NULL;
+ FREE(auth->user);
+ FREE(auth->pass);
+ }
+ FREE(UserName);
+ break;
+ }
+ }
+
+ return NNTPAUTH_ERROR;
+}
+
+/* Find Author's name in mail address
+ * ----------------------------------
+ *
+ * On exit,
+ * Returns allocated string which cannot be freed by the
+ * calling function, and is reallocated on subsequent calls
+ * to this function.
+ *
+ * For example, returns "Tim Berners-Lee" if given any of
+ * " Tim Berners-Lee <tim@online.cern.ch> "
+ * or " tim@online.cern.ch ( Tim Berners-Lee ) "
+ */
+static char *author_name(char *email)
+{
+ char *p, *e;
+
+ StrAllocCopy(name, email);
+ CTRACE((tfp, "Trying to find name in: %s\n", name));
+
+ if ((p = strrchr(name, '(')) && (e = strrchr(name, ')'))) {
+ if (e > p) {
+ *e = '\0'; /* Chop off everything after the ')' */
+ return HTStrip(p + 1); /* Remove leading and trailing spaces */
+ }
+ }
+
+ if ((p = strrchr(name, '<')) && (e = strrchr(name, '>'))) {
+ if (e++ > p) {
+ while ((*p++ = *e++) != 0) /* Remove <...> */
+ ;
+ return HTStrip(name); /* Remove leading and trailing spaces */
+ }
+ }
+
+ return HTStrip(name); /* Default to the whole thing */
+}
+
+/* Find Author's mail address
+ * --------------------------
+ *
+ * On exit,
+ * Returns allocated string which cannot be freed by the
+ * calling function, and is reallocated on subsequent calls
+ * to this function.
+ *
+ * For example, returns "montulli@spaced.out.galaxy.net" if given any of
+ * " Lou Montulli <montulli@spaced.out.galaxy.net> "
+ * or " montulli@spaced.out.galaxy.net ( Lou "The Stud" Montulli ) "
+ */
+static char *author_address(char *email)
+{
+ char *p, *at, *e;
+
+ StrAllocCopy(address, email);
+ CTRACE((tfp, "Trying to find address in: %s\n", address));
+
+ if ((p = strrchr(address, '<'))) {
+ if ((e = strrchr(p, '>')) && (at = strrchr(p, '@'))) {
+ if (at < e) {
+ *e = '\0'; /* Remove > */
+ return HTStrip(p + 1); /* Remove leading and trailing spaces */
+ }
+ }
+ }
+
+ if ((p = strrchr(address, '(')) &&
+ (e = strrchr(address, ')')) && (at = StrChr(address, '@'))) {
+ if (e > p && at < e) {
+ *p = '\0'; /* Chop off everything after the ')' */
+ return HTStrip(address); /* Remove leading and trailing spaces */
+ }
+ }
+
+ if ((at = strrchr(address, '@')) && at > address) {
+ p = (at - 1);
+ e = (at + 1);
+ while (p > address && !isspace(UCH(*p)))
+ p--;
+ while (*e && !isspace(UCH(*e)))
+ e++;
+ *e = 0;
+ return HTStrip(p);
+ }
+
+ /*
+ * Default to the first word.
+ */
+ p = address;
+ while (isspace(UCH(*p)))
+ p++; /* find first non-space */
+ e = p;
+ while (!isspace(UCH(*e)) && *e != '\0')
+ e++; /* find next space or end */
+ *e = '\0'; /* terminate space */
+
+ return (p);
+}
+
+/* Start anchor element
+ * --------------------
+ */
+static void start_anchor(const char *href)
+{
+ BOOL present[HTML_A_ATTRIBUTES];
+ const char *value[HTML_A_ATTRIBUTES];
+ int i;
+
+ for (i = 0; i < HTML_A_ATTRIBUTES; i++)
+ present[i] = (BOOL) (i == HTML_A_HREF);
+ value[HTML_A_HREF] = href;
+ (*targetClass.start_element) (target, HTML_A, present, value, -1, 0);
+}
+
+/* Start link element
+ * ------------------
+ */
+static void start_link(const char *href, const char *rev)
+{
+ BOOL present[HTML_LINK_ATTRIBUTES];
+ const char *value[HTML_LINK_ATTRIBUTES];
+ int i;
+
+ for (i = 0; i < HTML_LINK_ATTRIBUTES; i++)
+ present[i] = (BOOL) (i == HTML_LINK_HREF || i == HTML_LINK_REV);
+ value[HTML_LINK_HREF] = href;
+ value[HTML_LINK_REV] = rev;
+ (*targetClass.start_element) (target, HTML_LINK, present, value, -1, 0);
+}
+
+/* Start list element
+ * ------------------
+ */
+static void start_list(int seqnum)
+{
+ BOOL present[HTML_OL_ATTRIBUTES];
+ const char *value[HTML_OL_ATTRIBUTES];
+ char SeqNum[20];
+ int i;
+
+ for (i = 0; i < HTML_OL_ATTRIBUTES; i++)
+ present[i] = (BOOL) (i == HTML_OL_SEQNUM || i == HTML_OL_START);
+ sprintf(SeqNum, "%d", seqnum);
+ value[HTML_OL_SEQNUM] = SeqNum;
+ value[HTML_OL_START] = SeqNum;
+ (*targetClass.start_element) (target, HTML_OL, present, value, -1, 0);
+}
+
+/* Paste in an Anchor
+ * ------------------
+ *
+ *
+ * On entry,
+ * HT has a selection of zero length at the end.
+ * text points to the text to be put into the file, 0 terminated.
+ * addr points to the hypertext reference address,
+ * terminated by white space, comma, NULL or '>'
+ */
+static void write_anchor(const char *text, const char *addr)
+{
+ char href[LINE_LENGTH + 1];
+ const char *p;
+ char *q;
+
+ for (p = addr; *p && (*p != '>') && !WHITE(*p) && (*p != ','); p++) {
+ ;
+ }
+ if (strlen(NewsHREF) + (size_t) (p - addr) + 1 < sizeof(href)) {
+ q = href;
+ strcpy(q, NewsHREF);
+ /* Make complete hypertext reference */
+ StrNCat(q, addr, (size_t) (p - addr));
+ } else {
+ q = NULL;
+ HTSprintf0(&q, "%s%.*s", NewsHREF, (int) (p - addr), addr);
+ }
+
+ start_anchor(q);
+ PUTS(text);
+ END(HTML_A);
+
+ if (q != href)
+ FREE(q);
+}
+
+/* Write list of anchors
+ * ---------------------
+ *
+ * We take a pointer to a list of objects, and write out each,
+ * generating an anchor for each.
+ *
+ * On entry,
+ * HT has a selection of zero length at the end.
+ * text points to a comma or space separated list of addresses.
+ * On exit,
+ * *text is NOT any more chopped up into substrings.
+ */
+static void write_anchors(char *text)
+{
+ char *start = text;
+ char *end;
+ char c;
+
+ for (;;) {
+ for (; *start && (WHITE(*start)); start++) ; /* Find start */
+ if (!*start)
+ return; /* (Done) */
+ for (end = start;
+ *end && (*end != ' ') && (*end != ','); end++) ; /* Find end */
+ if (*end)
+ end++; /* Include comma or space but not NULL */
+ c = *end;
+ *end = '\0';
+ if (*start == '<')
+ write_anchor(start, start + 1);
+ else
+ write_anchor(start, start);
+ START(HTML_BR);
+ *end = c;
+ start = end; /* Point to next one */
+ }
+}
+
+/* Abort the connection abort_socket
+ * --------------------
+ */
+static void abort_socket(void)
+{
+ CTRACE((tfp, "HTNews: EOF on read, closing socket %d\n", s));
+ NEWS_NETCLOSE(s); /* End of file, close socket */
+ if (rawtext) {
+ RAW_PUTS("Network Error: connection lost\n");
+ } else {
+ PUTS("Network Error: connection lost");
+ PUTC('\n');
+ }
+ s = -1; /* End of file on response */
+}
+
+/*
+ * Determine if a line is a valid header line. valid_header
+ * -------------------------------------------
+ */
+static BOOLEAN valid_header(char *line)
+{
+ char *colon, *space;
+
+ /*
+ * Blank or tab in first position implies this is a continuation header.
+ */
+ if (line[0] == ' ' || line[0] == '\t')
+ return (TRUE);
+
+ /*
+ * Just check for initial letter, colon, and space to make sure we discard
+ * only invalid headers.
+ */
+ colon = StrChr(line, ':');
+ space = StrChr(line, ' ');
+ if (isalpha(UCH(line[0])) && colon && space == colon + 1)
+ return (TRUE);
+
+ /*
+ * Anything else is a bad header -- it should be ignored.
+ */
+ return (FALSE);
+}
+
+/* post in an Article post_article
+ * ------------------
+ * (added by FM, modeled on Lynx's previous mini inews)
+ *
+ * Note the termination condition of a single dot on a line by itself.
+ *
+ * On entry,
+ * s Global socket number is OK
+ * postfile file with header and article to post.
+ */
+static void post_article(char *postfile)
+{
+ char line[512];
+ char buf[512];
+ char crlf[3];
+ char *cp;
+ int status;
+ FILE *fd;
+ int in_header = 1, seen_header = 0, seen_fromline = 0;
+ int blen = 0, llen = 0;
+
+ /*
+ * Open the temporary file with the nntp headers and message body. - FM
+ */
+ if ((fd = fopen(NonNull(postfile), TXT_R)) == NULL) {
+ HTAlert(FAILED_CANNOT_OPEN_POST);
+ return;
+ }
+
+ /*
+ * Read the temporary file and post in maximum 512 byte chunks. - FM
+ */
+ buf[0] = '\0';
+ sprintf(crlf, "%c%c", CR, LF);
+ while (fgets(line, (int) sizeof(line) - 2, fd) != NULL) {
+ if ((cp = StrChr(line, '\n')) != NULL)
+ *cp = '\0';
+ if (line[0] == '.') {
+ /*
+ * A single '.' means end of transmission for nntp. Lead dots on
+ * lines normally are trimmed and the EOF is not registered if the
+ * dot was not followed by CRLF. We prepend an extra dot for any
+ * line beginning with one, to retain the one intended, as well as
+ * avoid a false EOF signal. We know we have room for it in the
+ * buffer, because we normally send when it would exceed 510. - FM
+ */
+ strcat(buf, ".");
+ blen++;
+ }
+ llen = (int) strlen(line);
+ if (in_header && !strncasecomp(line, "From:", 5)) {
+ seen_header = 1;
+ seen_fromline = 1;
+ }
+ if (in_header && line[0] == '\0') {
+ if (seen_header) {
+ in_header = 0;
+ if (!seen_fromline) {
+ if (blen >= (int) sizeof(buf) - 35) {
+ IGNORE_RC(NEWS_NETWRITE(s, buf, blen));
+ buf[blen = 0] = 0;
+ }
+ strcat(buf, "From: anonymous@nowhere.you.know");
+ strcat(buf, crlf);
+ blen += 34;
+ }
+ } else {
+ continue;
+ }
+ } else if (in_header) {
+ if (valid_header(line)) {
+ seen_header = 1;
+ } else {
+ continue;
+ }
+ }
+ strcat(line, crlf);
+ llen += 2;
+ if ((blen + llen) >= (int) sizeof(buf) - 1) {
+ IGNORE_RC(NEWS_NETWRITE(s, buf, blen));
+ buf[blen = 0] = 0;
+ }
+ strcat(buf, line);
+ blen += llen;
+ }
+ fclose(fd);
+ HTSYS_remove(postfile);
+
+ /*
+ * Send the nntp EOF and get the server's response. - FM
+ */
+ if (blen >= (int) sizeof(buf) - 4) {
+ IGNORE_RC(NEWS_NETWRITE(s, buf, blen));
+ buf[blen = 0] = 0;
+ }
+ strcat(buf, ".");
+ strcat(buf, crlf);
+ blen += 3;
+ IGNORE_RC(NEWS_NETWRITE(s, buf, blen));
+
+ status = response(NULL);
+ if (status == 240) {
+ /*
+ * Successful post. - FM
+ */
+ HTProgress(response_text);
+ } else {
+ /*
+ * Shucks, something went wrong. - FM
+ */
+ HTAlert(response_text);
+ }
+}
+
+#ifdef NEWS_DEBUG
+/* for DEBUG 1997/11/07 (Fri) 17:20:16 */
+void debug_print(unsigned char *p)
+{
+ while (*p) {
+ if (*p == '\0')
+ break;
+ if (*p == 0x1b)
+ printf("[ESC]");
+ else if (*p == '\n')
+ printf("[NL]");
+ else if (*p < ' ' || *p >= 0x80)
+ printf("(%02x)", *p);
+ else
+ putchar(*p);
+ p++;
+ }
+ printf("]\n");
+}
+#endif
+
+static char *decode_mime(char **str)
+{
+ static char empty[] = "";
+
+#ifdef SH_EX
+ if (HTCJK != JAPANESE)
+ return *str;
+#endif
+ HTmmdecode(str, *str);
+ return HTrjis(str, *str) ? *str : empty;
+}
+
+/* Read in an Article read_article
+ * ------------------
+ *
+ * Note the termination condition of a single dot on a line by itself.
+ * RFC 977 specifies that the line "folding" of RFC850 is not used, so we
+ * do not handle it here.
+ *
+ * On entry,
+ * s Global socket number is OK
+ * HT Global hypertext object is ready for appending text
+ */
+static int read_article(HTParentAnchor *thisanchor)
+{
+ char line[LINE_LENGTH + 1];
+ char *full_line = NULL;
+ char *subject = NULL; /* Subject string */
+ char *from = NULL; /* From string */
+ char *replyto = NULL; /* Reply-to string */
+ char *date = NULL; /* Date string */
+ char *organization = NULL; /* Organization string */
+ char *references = NULL; /* Hrefs for other articles */
+ char *newsgroups = NULL; /* Newsgroups list */
+ char *followupto = NULL; /* Followup list */
+ char *href = NULL;
+ char *p = line;
+ char *cp;
+ const char *ccp;
+ BOOL done = NO;
+
+ /*
+ * Read in the HEADer of the article.
+ *
+ * The header fields are either ignored, or formatted and put into the
+ * text.
+ */
+ if (!diagnostic && !rawtext) {
+ while (!done) {
+ int ich = NEXT_CHAR;
+
+ *p++ = (char) ich;
+ if (ich == EOF) {
+ if (interrupted_in_htgetcharacter) {
+ interrupted_in_htgetcharacter = 0;
+ CTRACE((tfp,
+ "HTNews: Interrupted on read, closing socket %d\n",
+ s));
+ NEWS_NETCLOSE(s);
+ s = -1;
+ return (HT_INTERRUPTED);
+ }
+ abort_socket(); /* End of file, close socket */
+ return (HT_LOADED); /* End of file on response */
+ }
+ if (((char) ich == LF) || (p == &line[LINE_LENGTH])) {
+ *--p = '\0'; /* Terminate the string */
+ CTRACE((tfp, "H %s\n", line));
+
+ if (line[0] == '\t' || line[0] == ' ') {
+ int i = 0;
+
+ while (line[i]) {
+ if (line[i] == '\t')
+ line[i] = ' ';
+ i++;
+ }
+ if (full_line == NULL) {
+ StrAllocCopy(full_line, line);
+ } else {
+ StrAllocCat(full_line, line);
+ }
+ } else {
+ StrAllocCopy(full_line, line);
+ }
+
+ if (full_line[0] == '.') {
+ /*
+ * End of article?
+ */
+ if (UCH(full_line[1]) < ' ') {
+ done = YES;
+ break;
+ }
+ } else if (UCH(full_line[0]) < ' ') {
+ break; /* End of Header? */
+
+ } else if (match(full_line, "SUBJECT:")) {
+ StrAllocCopy(subject, HTStrip(StrChr(full_line, ':') + 1));
+ decode_mime(&subject);
+ } else if (match(full_line, "DATE:")) {
+ StrAllocCopy(date, HTStrip(StrChr(full_line, ':') + 1));
+
+ } else if (match(full_line, "ORGANIZATION:")) {
+ StrAllocCopy(organization,
+ HTStrip(StrChr(full_line, ':') + 1));
+ decode_mime(&organization);
+
+ } else if (match(full_line, "FROM:")) {
+ StrAllocCopy(from, HTStrip(StrChr(full_line, ':') + 1));
+ decode_mime(&from);
+
+ } else if (match(full_line, "REPLY-TO:")) {
+ StrAllocCopy(replyto, HTStrip(StrChr(full_line, ':') + 1));
+ decode_mime(&replyto);
+
+ } else if (match(full_line, "NEWSGROUPS:")) {
+ StrAllocCopy(newsgroups, HTStrip(StrChr(full_line, ':') + 1));
+
+ } else if (match(full_line, "REFERENCES:")) {
+ StrAllocCopy(references, HTStrip(StrChr(full_line, ':') + 1));
+
+ } else if (match(full_line, "FOLLOWUP-TO:")) {
+ StrAllocCopy(followupto, HTStrip(StrChr(full_line, ':') + 1));
+
+ } else if (match(full_line, "MESSAGE-ID:")) {
+ char *msgid = HTStrip(full_line + 11);
+
+ if (msgid[0] == '<' && msgid[strlen(msgid) - 1] == '>') {
+ msgid[strlen(msgid) - 1] = '\0'; /* Chop > */
+ msgid++; /* Chop < */
+ HTAnchor_setMessageID(thisanchor, msgid);
+ }
+
+ } /* end if match */
+ p = line; /* Restart at beginning */
+ } /* if end of line */
+ } /* Loop over characters */
+ FREE(full_line);
+
+ START(HTML_HEAD);
+ PUTC('\n');
+ START(HTML_TITLE);
+ if (subject && *subject != '\0')
+ PUTS(subject);
+ else
+ PUTS("No Subject");
+ END(HTML_TITLE);
+ PUTC('\n');
+ /*
+ * Put in the owner as a link rel.
+ */
+ if (from || replyto) {
+ char *temp = NULL;
+
+ StrAllocCopy(temp, author_address(replyto ? replyto : from));
+ StrAllocCopy(href, STR_MAILTO_URL);
+ if (StrChr(temp, '%') || StrChr(temp, '?')) {
+ cp = HTEscape(temp, URL_XPALPHAS);
+ StrAllocCat(href, cp);
+ FREE(cp);
+ } else {
+ StrAllocCat(href, temp);
+ }
+ start_link(href, "made");
+ PUTC('\n');
+ FREE(temp);
+ }
+ END(HTML_HEAD);
+ PUTC('\n');
+
+ START(HTML_H1);
+ if (subject && *subject != '\0')
+ PUTS(subject);
+ else
+ PUTS("No Subject");
+ END(HTML_H1);
+ PUTC('\n');
+
+ if (subject)
+ FREE(subject);
+
+ START(HTML_DLC);
+ PUTC('\n');
+
+ if (from || replyto) {
+ START(HTML_DT);
+ START(HTML_B);
+ PUTS("From:");
+ END(HTML_B);
+ PUTC(' ');
+ if (from)
+ PUTS(from);
+ else
+ PUTS(replyto);
+ MAYBE_END(HTML_DT);
+ PUTC('\n');
+
+ if (!replyto)
+ StrAllocCopy(replyto, from);
+ START(HTML_DT);
+ START(HTML_B);
+ PUTS("Reply to:");
+ END(HTML_B);
+ PUTC(' ');
+ start_anchor(href);
+ if (*replyto != '<')
+ PUTS(author_name(replyto));
+ else
+ PUTS(author_address(replyto));
+ END(HTML_A);
+ START(HTML_BR);
+ MAYBE_END(HTML_DT);
+ PUTC('\n');
+
+ FREE(from);
+ FREE(replyto);
+ }
+
+ if (date) {
+ START(HTML_DT);
+ START(HTML_B);
+ PUTS("Date:");
+ END(HTML_B);
+ PUTC(' ');
+ PUTS(date);
+ MAYBE_END(HTML_DT);
+ PUTC('\n');
+ FREE(date);
+ }
+
+ if (organization) {
+ START(HTML_DT);
+ START(HTML_B);
+ PUTS("Organization:");
+ END(HTML_B);
+ PUTC(' ');
+ PUTS(organization);
+ MAYBE_END(HTML_DT);
+ PUTC('\n');
+ FREE(organization);
+ }
+
+ /* sanitize some headers - kw */
+ if (newsgroups &&
+ ((cp = StrChr(newsgroups, '/')) ||
+ (cp = StrChr(newsgroups, '(')))) {
+ *cp = '\0';
+ }
+ if (newsgroups && !*newsgroups) {
+ FREE(newsgroups);
+ }
+ if (followupto &&
+ ((cp = StrChr(followupto, '/')) ||
+ (cp = StrChr(followupto, '(')))) {
+ *cp = '\0';
+ }
+ if (followupto && !*followupto) {
+ FREE(followupto);
+ }
+
+ if (newsgroups && HTCanPost) {
+ START(HTML_DT);
+ START(HTML_B);
+ PUTS("Newsgroups:");
+ END(HTML_B);
+ PUTC('\n');
+ MAYBE_END(HTML_DT);
+ START(HTML_DD);
+ write_anchors(newsgroups);
+ MAYBE_END(HTML_DD);
+ PUTC('\n');
+ }
+
+ if (followupto && !strcasecomp(followupto, "poster")) {
+ /*
+ * "Followup-To: poster" has special meaning. Don't use it to
+ * construct a newsreply link. -kw
+ */
+ START(HTML_DT);
+ START(HTML_B);
+ PUTS("Followup to:");
+ END(HTML_B);
+ PUTC(' ');
+ if (href) {
+ start_anchor(href);
+ PUTS("poster");
+ END(HTML_A);
+ } else {
+ PUTS("poster");
+ }
+ MAYBE_END(HTML_DT);
+ PUTC('\n');
+ FREE(followupto);
+ }
+
+ if (newsgroups && HTCanPost) {
+ /*
+ * We have permission to POST to this host, so add a link for
+ * posting followups for this article. - FM
+ */
+ if (!strncasecomp(NewsHREF, STR_SNEWS_URL, 6))
+ StrAllocCopy(href, "snewsreply://");
+ else
+ StrAllocCopy(href, "newsreply://");
+ StrAllocCat(href, NewsHost);
+ StrAllocCat(href, "/");
+ StrAllocCat(href, (followupto ? followupto : newsgroups));
+ if (*href == 'n' &&
+ (ccp = HTAnchor_messageID(thisanchor)) && *ccp) {
+ StrAllocCat(href, ";ref=");
+ if (StrChr(ccp, '<') || StrChr(ccp, '&') ||
+ StrChr(ccp, ' ') || StrChr(ccp, ':') ||
+ StrChr(ccp, '/') || StrChr(ccp, '%') ||
+ StrChr(ccp, ';')) {
+ char *cp1 = HTEscape(ccp, URL_XPALPHAS);
+
+ StrAllocCat(href, cp1);
+ FREE(cp1);
+ } else {
+ StrAllocCat(href, ccp);
+ }
+ }
+
+ START(HTML_DT);
+ START(HTML_B);
+ PUTS("Followup to:");
+ END(HTML_B);
+ PUTC(' ');
+ start_anchor(href);
+ if (StrChr((followupto ? followupto : newsgroups), ',')) {
+ PUTS("newsgroups");
+ } else {
+ PUTS("newsgroup");
+ }
+ END(HTML_A);
+ MAYBE_END(HTML_DT);
+ PUTC('\n');
+ }
+ FREE(newsgroups);
+ FREE(followupto);
+
+ if (references) {
+ START(HTML_DT);
+ START(HTML_B);
+ PUTS("References:");
+ END(HTML_B);
+ MAYBE_END(HTML_DT);
+ PUTC('\n');
+ START(HTML_DD);
+ write_anchors(references);
+ MAYBE_END(HTML_DD);
+ PUTC('\n');
+ FREE(references);
+ }
+
+ END(HTML_DLC);
+ PUTC('\n');
+ FREE(href);
+ }
+
+ if (rawtext) {
+ /*
+ * No tags, and never do a PUTC. - kw
+ */
+ ;
+ } else if (diagnostic) {
+ /*
+ * Read in the HEAD and BODY of the Article as XMP formatted text. -
+ * FM
+ */
+ START(HTML_XMP);
+ PUTC('\n');
+ } else {
+ /*
+ * Read in the BODY of the Article as PRE formatted text. - FM
+ */
+ START(HTML_PRE);
+ PUTC('\n');
+ }
+
+ p = line;
+ while (!done) {
+ int ich = NEXT_CHAR;
+
+ *p++ = (char) ich;
+ if (ich == EOF) {
+ if (interrupted_in_htgetcharacter) {
+ interrupted_in_htgetcharacter = 0;
+ CTRACE((tfp,
+ "HTNews: Interrupted on read, closing socket %d\n",
+ s));
+ NEWS_NETCLOSE(s);
+ s = -1;
+ return (HT_INTERRUPTED);
+ }
+ abort_socket(); /* End of file, close socket */
+ return (HT_LOADED); /* End of file on response */
+ }
+ if (((char) ich == LF) || (p == &line[LINE_LENGTH])) {
+ *p = '\0'; /* Terminate the string */
+ CTRACE((tfp, "B %s", line));
+#ifdef NEWS_DEBUG /* 1997/11/09 (Sun) 15:56:11 */
+ debug_print(line); /* @@@ */
+#endif
+ if (line[0] == '.') {
+ /*
+ * End of article?
+ */
+ if (UCH(line[1]) < ' ') {
+ break;
+ } else { /* Line starts with dot */
+ if (rawtext) {
+ RAW_PUTS(&line[1]);
+ } else {
+ PUTS(&line[1]); /* Ignore first dot */
+ }
+ }
+ } else {
+ if (rawtext) {
+ RAW_PUTS(line);
+ } else if (diagnostic || !scan_for_buried_news_references) {
+ /*
+ * All lines are passed as unmodified source. - FM
+ */
+ PUTS(line);
+ } else {
+ /*
+ * Normal lines are scanned for buried references to other
+ * articles. Unfortunately, it could pick up mail
+ * addresses as well! It also can corrupt uuencoded
+ * messages! So we don't do this when fetching articles as
+ * WWW_SOURCE or when downloading (diagnostic is TRUE) or
+ * if the client has set scan_for_buried_news_references to
+ * FALSE. Otherwise, we convert all "<...@...>" strings
+ * preceded by "rticle " to "news:...@..." links, and any
+ * strings that look like URLs to links. - FM
+ */
+ char *l = line;
+ char *p2;
+
+ while ((p2 = strstr(l, "rticle <")) != NULL) {
+ char *q = strrchr(p2, '>');
+ char *at = strrchr(p2, '@');
+
+ if (q && at && at < q) {
+ char c = q[1];
+
+ q[1] = 0; /* chop up */
+ p2 += 7;
+ *p2 = 0;
+ while (*l) {
+ if (StrNCmp(l, STR_NEWS_URL, LEN_NEWS_URL) &&
+ StrNCmp(l, "snews://", 8) &&
+ StrNCmp(l, "nntp://", 7) &&
+ StrNCmp(l, "snewspost:", 10) &&
+ StrNCmp(l, "snewsreply:", 11) &&
+ StrNCmp(l, "newspost:", 9) &&
+ StrNCmp(l, "newsreply:", 10) &&
+ StrNCmp(l, "ftp://", 6) &&
+ StrNCmp(l, "file:/", 6) &&
+ StrNCmp(l, "finger://", 9) &&
+ StrNCmp(l, "http://", 7) &&
+ StrNCmp(l, "https://", 8) &&
+ StrNCmp(l, "wais://", 7) &&
+ StrNCmp(l, STR_MAILTO_URL, LEN_MAILTO_URL) &&
+ StrNCmp(l, "cso://", 6) &&
+ StrNCmp(l, "gopher://", 9)) {
+ PUTC(*l++);
+ } else {
+ StrAllocCopy(href, l);
+ start_anchor(strtok(href, " \r\n\t,>)\""));
+ while (*l && !StrChr(" \r\n\t,>)\"", *l))
+ PUTC(*l++);
+ END(HTML_A);
+ FREE(href);
+ }
+ }
+ *p2 = '<'; /* again */
+ *q = 0;
+ start_anchor(p2 + 1);
+ *q = '>'; /* again */
+ PUTS(p2);
+ END(HTML_A);
+ q[1] = c; /* again */
+ l = q + 1;
+ } else {
+ break; /* line has unmatched <> */
+ }
+ }
+ while (*l) { /* Last bit of the line */
+ if (StrNCmp(l, STR_NEWS_URL, LEN_NEWS_URL) &&
+ StrNCmp(l, "snews://", 8) &&
+ StrNCmp(l, "nntp://", 7) &&
+ StrNCmp(l, "snewspost:", 10) &&
+ StrNCmp(l, "snewsreply:", 11) &&
+ StrNCmp(l, "newspost:", 9) &&
+ StrNCmp(l, "newsreply:", 10) &&
+ StrNCmp(l, "ftp://", 6) &&
+ StrNCmp(l, "file:/", 6) &&
+ StrNCmp(l, "finger://", 9) &&
+ StrNCmp(l, "http://", 7) &&
+ StrNCmp(l, "https://", 8) &&
+ StrNCmp(l, "wais://", 7) &&
+ StrNCmp(l, STR_MAILTO_URL, LEN_MAILTO_URL) &&
+ StrNCmp(l, "cso://", 6) &&
+ StrNCmp(l, "gopher://", 9))
+ PUTC(*l++);
+ else {
+ StrAllocCopy(href, l);
+ start_anchor(strtok(href, " \r\n\t,>)\""));
+ while (*l && !StrChr(" \r\n\t,>)\"", *l))
+ PUTC(*l++);
+ END(HTML_A);
+ FREE(href);
+ }
+ }
+ } /* if diagnostic or not scan_for_buried_news_references */
+ } /* if not dot */
+ p = line; /* Restart at beginning */
+ } /* if end of line */
+ } /* Loop over characters */
+
+ if (rawtext)
+ return (HT_LOADED);
+
+ if (diagnostic)
+ END(HTML_XMP);
+ else
+ END(HTML_PRE);
+ PUTC('\n');
+ return (HT_LOADED);
+}
+
+/* Read in a List of Newsgroups
+ * ----------------------------
+ *
+ * Note the termination condition of a single dot on a line by itself.
+ * RFC 977 specifies that the line "folding" of RFC850 is not used,
+ * so we do not handle it here.
+ */
+static int read_list(char *arg)
+{
+ char line[LINE_LENGTH + 1];
+ char *p;
+ BOOL done = NO;
+ BOOL head = NO;
+ BOOL tail = NO;
+ BOOL skip_this_line = NO;
+ BOOL skip_rest_of_line = NO;
+ int listing = 0;
+ char *pattern = NULL;
+ int len = 0;
+
+ /*
+ * Support head or tail matches for groups to list. - FM
+ */
+ if (arg && strlen(arg) > 1) {
+ if (*arg == '*') {
+ tail = YES;
+ StrAllocCopy(pattern, (arg + 1));
+ } else if (arg[strlen(arg) - 1] == '*') {
+ head = YES;
+ StrAllocCopy(pattern, arg);
+ pattern[strlen(pattern) - 1] = '\0';
+ }
+ if (tail || head) {
+ len = (int) strlen(pattern);
+ }
+
+ }
+
+ /*
+ * Read the server's reply.
+ *
+ * The lines are scanned for newsgroup names and descriptions.
+ */
+ START(HTML_HEAD);
+ PUTC('\n');
+ START(HTML_TITLE);
+ PUTS("Newsgroups");
+ END(HTML_TITLE);
+ PUTC('\n');
+ END(HTML_HEAD);
+ PUTC('\n');
+ START(HTML_H1);
+ PUTS("Newsgroups");
+ END(HTML_H1);
+ PUTC('\n');
+ *(p = line) = '\0';
+ START(HTML_DLC);
+ PUTC('\n');
+ while (!done) {
+ int ich = NEXT_CHAR;
+ char ch = (char) ich;
+
+ if (ich == EOF) {
+ if (interrupted_in_htgetcharacter) {
+ interrupted_in_htgetcharacter = 0;
+ CTRACE((tfp,
+ "HTNews: Interrupted on read, closing socket %d\n",
+ s));
+ NEWS_NETCLOSE(s);
+ s = -1;
+ return (HT_INTERRUPTED);
+ }
+ abort_socket(); /* End of file, close socket */
+ FREE(pattern);
+ return (HT_LOADED); /* End of file on response */
+ } else if (skip_this_line) {
+ if (ch == LF) {
+ skip_this_line = skip_rest_of_line = NO;
+ p = line;
+ }
+ continue;
+ } else if (skip_rest_of_line) {
+ if (ch != LF) {
+ continue;
+ }
+ } else if (p == &line[LINE_LENGTH]) {
+ CTRACE((tfp, "b %.*s%c[...]\n", (LINE_LENGTH), line, ch));
+ *p = '\0';
+ if (ch == LF) {
+ ; /* Will be dealt with below */
+ } else if (WHITE(ch)) {
+ ch = LF; /* May treat as line without description */
+ skip_this_line = YES; /* ...and ignore until LF */
+ } else if (StrChr(line, ' ') == NULL &&
+ StrChr(line, '\t') == NULL) {
+ /* No separator found */
+ CTRACE((tfp, "HTNews..... group name too long, discarding.\n"));
+ skip_this_line = YES; /* ignore whole line */
+ continue;
+ } else {
+ skip_rest_of_line = YES; /* skip until ch == LF found */
+ }
+ } else {
+ *p++ = ch;
+ }
+ if (ch == LF) {
+ skip_rest_of_line = NO; /* done, reset flag */
+ *p = '\0'; /* Terminate the string */
+ CTRACE((tfp, "B %s", line));
+ if (line[0] == '.') {
+ /*
+ * End of article?
+ */
+ if (UCH(line[1]) < ' ') {
+ break;
+ } else { /* Line starts with dot */
+ START(HTML_DT);
+ PUTS(&line[1]);
+ MAYBE_END(HTML_DT);
+ }
+ } else if (line[0] == '#') { /* Comment? */
+ p = line; /* Restart at beginning */
+ continue;
+ } else {
+ /*
+ * Normal lines are scanned for references to newsgroups.
+ */
+ int i = 0;
+
+ /* find whitespace if it exits */
+ for (; line[i] != '\0' && !WHITE(line[i]); i++) ; /* null body */
+
+ if (line[i] != '\0') {
+ line[i] = '\0';
+ if ((head && strncasecomp(line, pattern, len)) ||
+ (tail && (i < len ||
+ strcasecomp((line + (i - len)), pattern)))) {
+ p = line; /* Restart at beginning */
+ continue;
+ }
+ START(HTML_DT);
+ write_anchor(line, line);
+ listing++;
+ MAYBE_END(HTML_DT);
+ PUTC('\n');
+ START(HTML_DD);
+ PUTS(&line[i + 1]); /* put description */
+ MAYBE_END(HTML_DD);
+ } else {
+ if ((head && strncasecomp(line, pattern, len)) ||
+ (tail && (i < len ||
+ strcasecomp((line + (i - len)), pattern)))) {
+ p = line; /* Restart at beginning */
+ continue;
+ }
+ START(HTML_DT);
+ write_anchor(line, line);
+ MAYBE_END(HTML_DT);
+ listing++;
+ }
+ } /* if not dot */
+ p = line; /* Restart at beginning */
+ } /* if end of line */
+ } /* Loop over characters */
+ if (!listing) {
+ char *msg = NULL;
+
+ START(HTML_DT);
+ HTSprintf0(&msg, gettext("No matches for: %s"), arg);
+ PUTS(msg);
+ MAYBE_END(HTML_DT);
+ FREE(msg);
+ }
+ END(HTML_DLC);
+ PUTC('\n');
+ FREE(pattern);
+ return (HT_LOADED);
+}
+
+/* Read in a Newsgroup
+ * -------------------
+ *
+ * Unfortunately, we have to ask for each article one by one if we
+ * want more than one field.
+ *
+ */
+static int read_group(const char *groupName,
+ int first_required,
+ int last_required)
+{
+ char line[LINE_LENGTH + 1];
+ char *author = NULL;
+ char *subject = NULL;
+ char *date = NULL;
+ int i;
+ char *p;
+ BOOL done;
+
+ char buffer[LINE_LENGTH + 1];
+ char *temp = NULL;
+ char *reference = NULL; /* Href for article */
+ int art; /* Article number WITHIN GROUP */
+ int status, count, first, last; /* Response fields */
+
+ START(HTML_HEAD);
+ PUTC('\n');
+ START(HTML_TITLE);
+ PUTS("Newsgroup ");
+ PUTS(groupName);
+ END(HTML_TITLE);
+ PUTC('\n');
+ END(HTML_HEAD);
+ PUTC('\n');
+
+ sscanf(response_text, " %d %d %d %d", &status, &count, &first, &last);
+ CTRACE((tfp, "Newsgroup status=%d, count=%d, (%d-%d) required:(%d-%d)\n",
+ status, count, first, last, first_required, last_required));
+ if (last == 0) {
+ PUTS(gettext("\nNo articles in this group.\n"));
+ goto add_post;
+ }
+#define FAST_THRESHOLD 100 /* Above this, read IDs fast */
+#define CHOP_THRESHOLD 50 /* Above this, chop off the rest */
+
+ if (first_required < first)
+ first_required = first; /* clip */
+ if ((last_required == 0) || (last_required > last))
+ last_required = last;
+
+ if (last_required < first_required) {
+ PUTS(gettext("\nNo articles in this range.\n"));
+ goto add_post;
+ }
+
+ if (last_required - first_required + 1 > HTNewsMaxChunk) { /* Trim this block */
+ first_required = last_required - HTNewsChunkSize + 1;
+ }
+ CTRACE((tfp, " Chunk will be (%d-%d)\n",
+ first_required, last_required));
+
+ /*
+ * Set window title.
+ */
+ HTSprintf0(&temp, gettext("%s, Articles %d-%d"),
+ groupName, first_required, last_required);
+ START(HTML_H1);
+ PUTS(temp);
+ FREE(temp);
+ END(HTML_H1);
+ PUTC('\n');
+
+ /*
+ * Link to earlier articles.
+ */
+ if (first_required > first) {
+ int before; /* Start of one before */
+
+ if (first_required - HTNewsMaxChunk <= first)
+ before = first;
+ else
+ before = first_required - HTNewsChunkSize;
+ HTSprintf0(&dbuf, "%s%s/%d-%d", NewsHREF, groupName,
+ before, first_required - 1);
+ CTRACE((tfp, " Block before is %s\n", dbuf));
+ PUTC('(');
+ start_anchor(dbuf);
+ PUTS(gettext("Earlier articles"));
+ END(HTML_A);
+ PUTS("...)\n");
+ START(HTML_P);
+ PUTC('\n');
+ }
+
+ done = NO;
+
+/*#define USE_XHDR*/
+#ifdef USE_XHDR
+ if (count > FAST_THRESHOLD) {
+ HTSprintf0(&temp,
+ gettext("\nThere are about %d articles currently available in %s, IDs as follows:\n\n"),
+ count, groupName);
+ PUTS(temp);
+ FREE(temp);
+ sprintf(buffer, "XHDR Message-ID %d-%d%c%c", first, last, CR, LF);
+ status = response(buffer);
+ if (status == 221) {
+ p = line;
+ while (!done) {
+ int ich = NEXT_CHAR;
+
+ *p++ = ich;
+ if (ich == EOF) {
+ if (interrupted_in_htgetcharacter) {
+ interrupted_in_htgetcharacter = 0;
+ CTRACE((tfp,
+ "HTNews: Interrupted on read, closing socket %d\n",
+ s));
+ NEWS_NETCLOSE(s);
+ s = -1;
+ return (HT_INTERRUPTED);
+ }
+ abort_socket(); /* End of file, close socket */
+ return (HT_LOADED); /* End of file on response */
+ }
+ if (((char) ich == '\n') || (p == &line[LINE_LENGTH])) {
+ *p = '\0'; /* Terminate the string */
+ CTRACE((tfp, "X %s", line));
+ if (line[0] == '.') {
+ /*
+ * End of article?
+ */
+ if (UCH(line[1]) < ' ') {
+ done = YES;
+ break;
+ } else { /* Line starts with dot */
+ /* Ignore strange line */
+ }
+ } else {
+ /*
+ * Normal lines are scanned for references to articles.
+ */
+ char *space = StrChr(line, ' ');
+
+ if (space++)
+ write_anchor(space, space);
+ } /* if not dot */
+ p = line; /* Restart at beginning */
+ } /* if end of line */
+ } /* Loop over characters */
+
+ /* leaving loop with "done" set */
+ } /* Good status */
+ }
+#endif /* USE_XHDR */
+
+ /*
+ * Read newsgroup using individual fields.
+ */
+ if (!done) {
+ START(HTML_B);
+ if (first == first_required && last == last_required)
+ PUTS(gettext("All available articles in "));
+ else
+ PUTS("Articles in ");
+ PUTS(groupName);
+ END(HTML_B);
+ PUTC('\n');
+ if (LYListNewsNumbers)
+ start_list(first_required);
+ else
+ START(HTML_UL);
+ for (art = first_required; art <= last_required; art++) {
+/*#define OVERLAP*/
+#ifdef OVERLAP
+ /*
+ * With this code we try to keep the server running flat out by
+ * queuing just one extra command ahead of time. We assume (1)
+ * that the server won't abort if it gets input during output, and
+ * (2) that TCP buffering is enough for the two commands. Both
+ * these assumptions seem very reasonable. However, we HAVE had a
+ * hangup with a loaded server.
+ */
+ if (art == first_required) {
+ if (art == last_required) { /* Only one */
+ sprintf(buffer, "HEAD %d%c%c",
+ art, CR, LF);
+ status = response(buffer);
+ } else { /* First of many */
+ sprintf(buffer, "HEAD %d%c%cHEAD %d%c%c",
+ art, CR, LF, art + 1, CR, LF);
+ status = response(buffer);
+ }
+ } else if (art == last_required) { /* Last of many */
+ status = response(NULL);
+ } else { /* Middle of many */
+ sprintf(buffer, "HEAD %d%c%c", art + 1, CR, LF);
+ status = response(buffer);
+ }
+#else /* Not OVERLAP: */
+ sprintf(buffer, "HEAD %d%c%c", art, CR, LF);
+ status = response(buffer);
+#endif /* OVERLAP */
+ /*
+ * Check for a good response (221) for the HEAD request, and if so,
+ * parse it. Otherwise, indicate the error so that the number of
+ * listings corresponds to what's claimed for the range, and if we
+ * are listing numbers via an ordered list, they stay in synchrony
+ * with the article numbers. - FM
+ */
+ if (status == 221) { /* Head follows - parse it: */
+ p = line; /* Write pointer */
+ done = NO;
+ while (!done) {
+ int ich = NEXT_CHAR;
+
+ *p++ = (char) ich;
+ if (ich == EOF) {
+ if (interrupted_in_htgetcharacter) {
+ interrupted_in_htgetcharacter = 0;
+ CTRACE((tfp,
+ "HTNews: Interrupted on read, closing socket %d\n",
+ s));
+ NEWS_NETCLOSE(s);
+ s = -1;
+ return (HT_INTERRUPTED);
+ }
+ abort_socket(); /* End of file, close socket */
+ return (HT_LOADED); /* End of file on response */
+ }
+ if (((char) ich == LF) ||
+ (p == &line[LINE_LENGTH])) {
+
+ *--p = '\0'; /* Terminate & chop LF */
+ p = line; /* Restart at beginning */
+ CTRACE((tfp, "G %s\n", line));
+ switch (line[0]) {
+
+ case '.':
+ /*
+ * End of article?
+ */
+ done = (BOOL) (UCH(line[1]) < ' ');
+ break;
+
+ case 'S':
+ case 's':
+ if (match(line, "SUBJECT:")) {
+ StrAllocCopy(subject, line + 9);
+ decode_mime(&subject);
+ }
+ break;
+
+ case 'M':
+ case 'm':
+ if (match(line, "MESSAGE-ID:")) {
+ char *addr = HTStrip(line + 11) + 1; /* Chop < */
+
+ addr[strlen(addr) - 1] = '\0'; /* Chop > */
+ StrAllocCopy(reference, addr);
+ }
+ break;
+
+ case 'f':
+ case 'F':
+ if (match(line, "FROM:")) {
+ char *p2;
+
+ StrAllocCopy(author, StrChr(line, ':') + 1);
+ decode_mime(&author);
+ p2 = author + strlen(author) - 1;
+ if (*p2 == LF)
+ *p2 = '\0'; /* Chop off newline */
+ }
+ break;
+
+ case 'd':
+ case 'D':
+ if (LYListNewsDates && match(line, "DATE:")) {
+ StrAllocCopy(date,
+ HTStrip(StrChr(line, ':') + 1));
+ }
+ break;
+
+ } /* end switch on first character */
+ } /* if end of line */
+ } /* Loop over characters */
+
+ PUTC('\n');
+ START(HTML_LI);
+ p = decode_mime(&subject);
+ HTSprintf0(&temp, "\"%s\"", NonNull(p));
+ if (reference) {
+ write_anchor(temp, reference);
+ FREE(reference);
+ } else {
+ PUTS(temp);
+ }
+ FREE(temp);
+
+ if (author != NULL) {
+ PUTS(" - ");
+ if (LYListNewsDates)
+ START(HTML_I);
+ PUTS(decode_mime(&author));
+ if (LYListNewsDates)
+ END(HTML_I);
+ FREE(author);
+ }
+ if (date) {
+ if (!diagnostic) {
+ for (i = 0; date[i]; i++) {
+ if (date[i] == ' ') {
+ date[i] = HT_NON_BREAK_SPACE;
+ }
+ }
+ }
+ sprintf(buffer, " [%.*s]", (int) (sizeof(buffer) - 4), date);
+ PUTS(buffer);
+ FREE(date);
+ }
+ MAYBE_END(HTML_LI);
+ /*
+ * Indicate progress! @@@@@@
+ */
+ } else if (status == HT_INTERRUPTED) {
+ interrupted_in_htgetcharacter = 0;
+ CTRACE((tfp,
+ "HTNews: Interrupted on read, closing socket %d\n",
+ s));
+ NEWS_NETCLOSE(s);
+ s = -1;
+ return (HT_INTERRUPTED);
+ } else {
+ /*
+ * Use the response text on error. - FM
+ */
+ PUTC('\n');
+ START(HTML_LI);
+ START(HTML_I);
+ if (LYListNewsNumbers)
+ LYStrNCpy(buffer, "Status:", sizeof(buffer) - 1);
+ else
+ sprintf(buffer, "Status (ARTICLE %d):", art);
+ PUTS(buffer);
+ END(HTML_I);
+ PUTC(' ');
+ PUTS(response_text);
+ MAYBE_END(HTML_LI);
+ } /* Handle response to HEAD request */
+ } /* Loop over article */
+ FREE(author);
+ FREE(subject);
+ } /* If read headers */
+ PUTC('\n');
+ if (LYListNewsNumbers)
+ END(HTML_OL);
+ else
+ END(HTML_UL);
+ PUTC('\n');
+
+ /*
+ * Link to later articles.
+ */
+ if (last_required < last) {
+ int after; /* End of article after */
+
+ after = last_required + HTNewsChunkSize;
+ if (after == last)
+ HTSprintf0(&dbuf, "%s%s", NewsHREF, groupName); /* original group */
+ else
+ HTSprintf0(&dbuf, "%s%s/%d-%d", NewsHREF, groupName,
+ last_required + 1, after);
+ CTRACE((tfp, " Block after is %s\n", dbuf));
+ PUTC('(');
+ start_anchor(dbuf);
+ PUTS(gettext("Later articles"));
+ END(HTML_A);
+ PUTS("...)\n");
+ }
+
+ add_post:
+ if (HTCanPost) {
+ /*
+ * We have permission to POST to this host, so add a link for posting
+ * messages to this newsgroup. - FM
+ */
+ char *href = NULL;
+
+ START(HTML_HR);
+ PUTC('\n');
+ if (!strncasecomp(NewsHREF, STR_SNEWS_URL, 6))
+ StrAllocCopy(href, "snewspost://");
+ else
+ StrAllocCopy(href, "newspost://");
+ StrAllocCat(href, NewsHost);
+ StrAllocCat(href, "/");
+ StrAllocCat(href, groupName);
+ start_anchor(href);
+ PUTS(gettext("Post to "));
+ PUTS(groupName);
+ END(HTML_A);
+ FREE(href);
+ } else {
+ START(HTML_HR);
+ }
+ PUTC('\n');
+ return (HT_LOADED);
+}
+
+/* Load by name. HTLoadNews
+ * =============
+ */
+static int HTLoadNews(const char *arg,
+ HTParentAnchor *anAnchor,
+ HTFormat format_out,
+ HTStream *stream)
+{
+ char command[262]; /* The whole command */
+ char proxycmd[260]; /* The proxy command */
+ char groupName[GROUP_NAME_LENGTH]; /* Just the group name */
+ int status; /* tcp return */
+ int retries; /* A count of how hard we have tried */
+ BOOL normal_url; /* Flag: "news:" or "nntp:" (physical) URL */
+ BOOL group_wanted; /* Flag: group was asked for, not article */
+ BOOL list_wanted; /* Flag: list was asked for, not article */
+ BOOL post_wanted; /* Flag: new post to group was asked for */
+ BOOL reply_wanted; /* Flag: followup post was asked for */
+ BOOL spost_wanted; /* Flag: new SSL post to group was asked for */
+ BOOL sreply_wanted; /* Flag: followup SSL post was asked for */
+ BOOL head_wanted = NO; /* Flag: want HEAD of single article */
+ int first, last; /* First and last articles asked for */
+ char *cp = 0;
+ char *ListArg = NULL;
+ char *ProxyHost = NULL;
+ char *ProxyHREF = NULL;
+ char *postfile = NULL;
+
+#ifdef USE_SSL
+ char SSLprogress[256];
+#endif /* USE_SSL */
+
+ diagnostic = (format_out == WWW_SOURCE || /* set global flag */
+ format_out == WWW_DOWNLOAD ||
+ format_out == WWW_DUMP);
+ rawtext = NO;
+
+ CTRACE((tfp, "HTNews: Looking for %s\n", arg));
+
+ if (!initialized)
+ initialized = initialize();
+ if (!initialized)
+ return -1; /* FAIL */
+
+ FREE(NewsHREF);
+ command[0] = '\0';
+ command[sizeof(command) - 1] = '\0';
+ proxycmd[0] = '\0';
+ proxycmd[sizeof(proxycmd) - 1] = '\0';
+
+ {
+ const char *p1;
+
+ /*
+ * We will ask for the document, omitting the host name & anchor.
+ *
+ * Syntax of address is
+ * xxx@yyy Article
+ * <xxx@yyy> Same article
+ * xxxxx News group (no "@")
+ * group/n1-n2 Articles n1 to n2 in group
+ */
+ normal_url = (BOOL) (!StrNCmp(arg, STR_NEWS_URL, LEN_NEWS_URL) ||
+ !StrNCmp(arg, "nntp:", 5));
+ spost_wanted = (BOOL) (!normal_url && strstr(arg, "snewspost:") != NULL);
+ sreply_wanted = (BOOL) (!(normal_url || spost_wanted) &&
+ strstr(arg, "snewsreply:") != NULL);
+ post_wanted = (BOOL) (!(normal_url || spost_wanted || sreply_wanted) &&
+ strstr(arg, "newspost:") != NULL);
+ reply_wanted = (BOOL) (!(normal_url || spost_wanted || sreply_wanted ||
+ post_wanted) &&
+ strstr(arg, "newsreply:") != NULL);
+ group_wanted = (BOOL) ((!(spost_wanted || sreply_wanted ||
+ post_wanted || reply_wanted) &&
+ StrChr(arg, '@') == NULL) &&
+ (StrChr(arg, '*') == NULL));
+ list_wanted = (BOOL) ((!(spost_wanted || sreply_wanted ||
+ post_wanted || reply_wanted ||
+ group_wanted) &&
+ StrChr(arg, '@') == NULL) &&
+ (StrChr(arg, '*') != NULL));
+
+#ifndef USE_SSL
+ if (!strncasecomp(arg, "snewspost:", 10) ||
+ !strncasecomp(arg, "snewsreply:", 11)) {
+ HTAlert(FAILED_CANNOT_POST_SSL);
+ return HT_NOT_LOADED;
+ }
+#endif /* !USE_SSL */
+ if (post_wanted || reply_wanted || spost_wanted || sreply_wanted) {
+ /*
+ * Make sure we have a non-zero path for the newsgroup(s). - FM
+ */
+ if ((p1 = strrchr(arg, '/')) != NULL) {
+ p1++;
+ } else if ((p1 = strrchr(arg, ':')) != NULL) {
+ p1++;
+ }
+ if (!(p1 && *p1)) {
+ HTAlert(WWW_ILLEGAL_URL_MESSAGE);
+ return (HT_NO_DATA);
+ }
+ if (!(cp = HTParse(arg, "", PARSE_HOST)) || *cp == '\0') {
+ if (s >= 0 && NewsHost && strcasecomp(NewsHost, HTNewsHost)) {
+ NEWS_NETCLOSE(s);
+ s = -1;
+ }
+ StrAllocCopy(NewsHost, HTNewsHost);
+ } else {
+ if (s >= 0 && NewsHost && strcasecomp(NewsHost, cp)) {
+ NEWS_NETCLOSE(s);
+ s = -1;
+ }
+ StrAllocCopy(NewsHost, cp);
+ }
+ FREE(cp);
+ HTSprintf0(&NewsHREF, "%s://%.*s/",
+ (post_wanted ?
+ "newspost" :
+ (reply_wanted ?
+ "newreply" :
+ (spost_wanted ?
+ "snewspost" : "snewsreply"))),
+ (int) sizeof(command) - 15, NewsHost);
+
+ /*
+ * If the SSL daemon is being used as a proxy, reset p1 to the
+ * start of the proxied URL rather than to the start of the
+ * newsgroup(s). - FM
+ */
+ if (spost_wanted && strncasecomp(arg, "snewspost:", 10))
+ p1 = strstr(arg, "snewspost:");
+ if (sreply_wanted && strncasecomp(arg, "snewsreply:", 11))
+ p1 = strstr(arg, "snewsreply:");
+
+ /* p1 = HTParse(arg, "", PARSE_PATH | PARSE_PUNCTUATION); */
+ /*
+ * Don't use HTParse because news: access doesn't follow
+ * traditional rules. For instance, if the article reference
+ * contains a '#', the rest of it is lost -- JFG 10/7/92, from a
+ * bug report
+ */
+ } else if (isNNTP_URL(arg)) {
+ if (((*(arg + 5) == '\0') ||
+ (!strcmp((arg + 5), "/") ||
+ !strcmp((arg + 5), "//") ||
+ !strcmp((arg + 5), "///"))) ||
+ ((!StrNCmp((arg + 5), "//", 2)) &&
+ (!(cp = StrChr((arg + 7), '/')) || *(cp + 1) == '\0'))) {
+ p1 = "*";
+ group_wanted = FALSE;
+ list_wanted = TRUE;
+ } else if (*(arg + 5) != '/') {
+ p1 = (arg + 5);
+ } else if (*(arg + 5) == '/' && *(arg + 6) != '/') {
+ p1 = (arg + 6);
+ } else {
+ p1 = (cp ? (cp + 1) : (arg + 6));
+ }
+ if (!(cp = HTParse(arg, "", PARSE_HOST)) || *cp == '\0') {
+ if (s >= 0 && NewsHost && strcasecomp(NewsHost, HTNewsHost)) {
+ NEWS_NETCLOSE(s);
+ s = -1;
+ }
+ StrAllocCopy(NewsHost, HTNewsHost);
+ } else {
+ if (s >= 0 && NewsHost && strcasecomp(NewsHost, cp)) {
+ NEWS_NETCLOSE(s);
+ s = -1;
+ }
+ StrAllocCopy(NewsHost, cp);
+ }
+ FREE(cp);
+ SnipIn2(command, "%s//%.*s/", STR_NNTP_URL, 9, NewsHost);
+ StrAllocCopy(NewsHREF, command);
+ } else if (!strncasecomp(arg, STR_SNEWS_URL, 6)) {
+#ifdef USE_SSL
+ if (((*(arg + 6) == '\0') ||
+ (!strcmp((arg + 6), "/") ||
+ !strcmp((arg + 6), "//") ||
+ !strcmp((arg + 6), "///"))) ||
+ ((!StrNCmp((arg + 6), "//", 2)) &&
+ (!(cp = StrChr((arg + 8), '/')) || *(cp + 1) == '\0'))) {
+ p1 = "*";
+ group_wanted = FALSE;
+ list_wanted = TRUE;
+ } else if (*(arg + 6) != '/') {
+ p1 = (arg + 6);
+ } else if (*(arg + 6) == '/' && *(arg + 7) != '/') {
+ p1 = (arg + 7);
+ } else {
+ p1 = (cp ? (cp + 1) : (arg + 7));
+ }
+ if (!(cp = HTParse(arg, "", PARSE_HOST)) || *cp == '\0') {
+ if (s >= 0 && NewsHost && strcasecomp(NewsHost, HTNewsHost)) {
+ NEWS_NETCLOSE(s);
+ s = -1;
+ }
+ StrAllocCopy(NewsHost, HTNewsHost);
+ } else {
+ if (s >= 0 && NewsHost && strcasecomp(NewsHost, cp)) {
+ NEWS_NETCLOSE(s);
+ s = -1;
+ }
+ StrAllocCopy(NewsHost, cp);
+ }
+ FREE(cp);
+ sprintf(command, "%s//%.250s/", STR_SNEWS_URL, NewsHost);
+ StrAllocCopy(NewsHREF, command);
+#else
+ HTAlert(gettext("This client does not contain support for SNEWS URLs."));
+ return HT_NOT_LOADED;
+#endif /* USE_SSL */
+ } else if (!strncasecomp(arg, "news:/", 6)) {
+ if (((*(arg + 6) == '\0') ||
+ !strcmp((arg + 6), "/") ||
+ !strcmp((arg + 6), "//")) ||
+ ((*(arg + 6) == '/') &&
+ (!(cp = StrChr((arg + 7), '/')) || *(cp + 1) == '\0'))) {
+ p1 = "*";
+ group_wanted = FALSE;
+ list_wanted = TRUE;
+ } else if (*(arg + 6) != '/') {
+ p1 = (arg + 6);
+ } else {
+ p1 = (cp ? (cp + 1) : (arg + 6));
+ }
+ if (!(cp = HTParse(arg, "", PARSE_HOST)) || *cp == '\0') {
+ if (s >= 0 && NewsHost && strcasecomp(NewsHost, HTNewsHost)) {
+ NEWS_NETCLOSE(s);
+ s = -1;
+ }
+ StrAllocCopy(NewsHost, HTNewsHost);
+ } else {
+ if (s >= 0 && NewsHost && strcasecomp(NewsHost, cp)) {
+ NEWS_NETCLOSE(s);
+ s = -1;
+ }
+ StrAllocCopy(NewsHost, cp);
+ }
+ FREE(cp);
+ SnipIn(command, "news://%.*s/", 9, NewsHost);
+ StrAllocCopy(NewsHREF, command);
+ } else {
+ p1 = (arg + 5); /* Skip "news:" prefix */
+ if (*p1 == '\0') {
+ p1 = "*";
+ group_wanted = FALSE;
+ list_wanted = TRUE;
+ }
+ if (s >= 0 && NewsHost && strcasecomp(NewsHost, HTNewsHost)) {
+ NEWS_NETCLOSE(s);
+ s = -1;
+ }
+ StrAllocCopy(NewsHost, HTNewsHost);
+ StrAllocCopy(NewsHREF, STR_NEWS_URL);
+ }
+
+ /*
+ * Set up any proxy for snews URLs that returns NNTP responses for Lynx
+ * to convert to HTML, instead of doing the conversion itself, and for
+ * handling posts or followups. - TZ & FM
+ */
+ if (!strncasecomp(p1, STR_SNEWS_URL, 6) ||
+ !strncasecomp(p1, "snewspost:", 10) ||
+ !strncasecomp(p1, "snewsreply:", 11)) {
+ StrAllocCopy(ProxyHost, NewsHost);
+ if ((cp = HTParse(p1, "", PARSE_HOST)) != NULL && *cp != '\0') {
+ SnipIn2(command, "%s//%.*s", STR_SNEWS_URL, 10, cp);
+ StrAllocCopy(NewsHost, cp);
+ } else {
+ SnipIn2(command, "%s//%.*s", STR_SNEWS_URL, 10, NewsHost);
+ }
+ command[sizeof(command) - 2] = '\0';
+ FREE(cp);
+ sprintf(proxycmd, "GET %.*s%c%c%c%c",
+ (int) sizeof(proxycmd) - 9, command,
+ CR, LF, CR, LF);
+ CTRACE((tfp, "HTNews: Proxy command is '%.*s'\n",
+ (int) (strlen(proxycmd) - 4), proxycmd));
+ strcat(command, "/");
+ StrAllocCopy(ProxyHREF, NewsHREF);
+ StrAllocCopy(NewsHREF, command);
+ if (spost_wanted || sreply_wanted) {
+ /*
+ * Reset p1 so that it points to the newsgroup(s).
+ */
+ if ((p1 = strrchr(arg, '/')) != NULL) {
+ p1++;
+ } else {
+ p1 = (strrchr(arg, ':') + 1);
+ }
+ } else {
+ char *cp2;
+
+ /*
+ * Reset p1 so that it points to the newsgroup (or a wildcard),
+ * or the article.
+ */
+ if (!(cp2 = strrchr((p1 + 6), '/')) || *(cp2 + 1) == '\0') {
+ p1 = "*";
+ group_wanted = FALSE;
+ list_wanted = TRUE;
+ } else {
+ p1 = (cp2 + 1);
+ }
+ }
+ }
+
+ /*
+ * Set up command for a post, listing, or article request. - FM
+ */
+ if (post_wanted || reply_wanted || spost_wanted || sreply_wanted) {
+ strcpy(command, "POST");
+ } else if (list_wanted) {
+ if (strlen(p1) > 249) {
+ FREE(ProxyHost);
+ FREE(ProxyHREF);
+ HTAlert(URL_TOO_LONG);
+ return -400;
+ }
+ SnipIn(command, "XGTITLE %.*s", 11, p1);
+ } else if (group_wanted) {
+ char *slash = StrChr(p1, '/');
+
+ first = 0;
+ last = 0;
+ if (slash) {
+ *slash = '\0';
+ if (strlen(p1) >= sizeof(groupName)) {
+ FREE(ProxyHost);
+ FREE(ProxyHREF);
+ HTAlert(URL_TOO_LONG);
+ return -400;
+ }
+ LYStrNCpy(groupName, p1, sizeof(groupName) - 1);
+ *slash = '/';
+ (void) sscanf(slash + 1, "%d-%d", &first, &last);
+ if ((first > 0) && (isdigit(UCH(*(slash + 1)))) &&
+ (StrChr(slash + 1, '-') == NULL || first == last)) {
+ /*
+ * We got a number greater than 0, which will be loaded as
+ * first, and either no range or the range computes to
+ * zero, so make last negative, as a flag to select the
+ * group and then fetch an article by number (first)
+ * instead of by messageID. - FM
+ */
+ last = -1;
+ }
+ } else {
+ if (strlen(p1) >= sizeof(groupName)) {
+ FREE(ProxyHost);
+ FREE(ProxyHREF);
+ HTAlert(URL_TOO_LONG);
+ return -400;
+ }
+ LYStrNCpy(groupName, p1, sizeof(groupName) - 1);
+ }
+ SnipIn(command, "GROUP %.*s", 9, groupName);
+ } else {
+ size_t add_open = (size_t) (StrChr(p1, '<') == 0);
+ size_t add_close = (size_t) (StrChr(p1, '>') == 0);
+
+ if (strlen(p1) + add_open + add_close >= 252) {
+ FREE(ProxyHost);
+ FREE(ProxyHREF);
+ HTAlert(URL_TOO_LONG);
+ return -400;
+ }
+ sprintf(command, "ARTICLE %s%.*s%s",
+ add_open ? "<" : "",
+ (int) (sizeof(command) - (11 + add_open + add_close)),
+ p1,
+ add_close ? ">" : "");
+ }
+
+ {
+ char *p = command + strlen(command);
+
+ /*
+ * Terminate command with CRLF, as in RFC 977.
+ */
+ *p++ = CR; /* Macros to be correct on Mac */
+ *p++ = LF;
+ *p = 0;
+ }
+ StrAllocCopy(ListArg, p1);
+ } /* scope of p1 */
+
+ if (!*arg) {
+ FREE(NewsHREF);
+ FREE(ProxyHost);
+ FREE(ProxyHREF);
+ FREE(ListArg);
+ return NO; /* Ignore if no name */
+ }
+
+ if (!(post_wanted || reply_wanted || spost_wanted || sreply_wanted ||
+ (group_wanted && last != -1) || list_wanted)) {
+ head_wanted = anAnchor->isHEAD;
+ if (head_wanted && !StrNCmp(command, "ARTICLE ", 8)) {
+ /* overwrite "ARTICLE" - hack... */
+ strcpy(command, "HEAD ");
+ for (cp = command + 5;; cp++)
+ if ((*cp = *(cp + 3)) == '\0')
+ break;
+ }
+ rawtext = (BOOL) (head_wanted || keep_mime_headers);
+ }
+ if (rawtext) {
+ rawtarget = HTStreamStack(WWW_PLAINTEXT,
+ format_out,
+ stream, anAnchor);
+ if (!rawtarget) {
+ FREE(NewsHost);
+ FREE(NewsHREF);
+ FREE(ProxyHost);
+ FREE(ProxyHREF);
+ FREE(ListArg);
+ HTAlert(gettext("No target for raw text!"));
+ return (HT_NOT_LOADED);
+ } /* Copy routine entry points */
+ rawtargetClass = *rawtarget->isa;
+ } else
+ /*
+ * Make a hypertext object with an anchor list.
+ */
+ if (!(post_wanted || reply_wanted || spost_wanted || sreply_wanted)) {
+ target = HTML_new(anAnchor, format_out, stream);
+ targetClass = *target->isa; /* Copy routine entry points */
+ }
+
+ /*
+ * Now, let's get a stream setup up from the NewsHost.
+ */
+ for (retries = 0; retries < 2; retries++) {
+ if (s < 0) {
+ /* CONNECTING to news host */
+ char url[260];
+
+ if (!strcmp(NewsHREF, STR_NEWS_URL)) {
+ SnipIn(url, "lose://%.*s/", 9, NewsHost);
+ } else if (ProxyHREF) {
+ SnipIn(url, "%.*s", 1, ProxyHREF);
+ } else {
+ SnipIn(url, "%.*s", 1, NewsHREF);
+ }
+ CTRACE((tfp, "News: doing HTDoConnect on '%s'\n", url));
+
+ _HTProgress(gettext("Connecting to NewsHost ..."));
+
+#ifdef USE_SSL
+ if (!using_proxy &&
+ (!StrNCmp(arg, STR_SNEWS_URL, 6) ||
+ !StrNCmp(arg, "snewspost:", 10) ||
+ !StrNCmp(arg, "snewsreply:", 11)))
+ status = HTDoConnect(url, "NNTPS", SNEWS_PORT, &s);
+ else
+ status = HTDoConnect(url, "NNTP", NEWS_PORT, &s);
+#else
+ status = HTDoConnect(url, "NNTP", NEWS_PORT, &s);
+#endif /* USE_SSL */
+
+ if (status == HT_INTERRUPTED) {
+ /*
+ * Interrupt cleanly.
+ */
+ CTRACE((tfp,
+ "HTNews: Interrupted on connect; recovering cleanly.\n"));
+ _HTProgress(CONNECTION_INTERRUPTED);
+ if (!(post_wanted || reply_wanted ||
+ spost_wanted || sreply_wanted)) {
+ ABORT_TARGET;
+ }
+ FREE(NewsHost);
+ FREE(NewsHREF);
+ FREE(ProxyHost);
+ FREE(ProxyHREF);
+ FREE(ListArg);
+#ifdef USE_SSL
+ if (Handle) {
+ SSL_free(Handle);
+ Handle = NULL;
+ }
+#endif /* USE_SSL */
+ if (postfile) {
+ HTSYS_remove(postfile);
+ FREE(postfile);
+ }
+ return HT_NOT_LOADED;
+ }
+ if (status < 0) {
+ NEWS_NETCLOSE(s);
+ s = -1;
+ CTRACE((tfp, "HTNews: Unable to connect to news host.\n"));
+ if (retries < 1)
+ continue;
+ if (!(post_wanted || reply_wanted ||
+ spost_wanted || sreply_wanted)) {
+ ABORT_TARGET;
+ }
+ HTSprintf0(&dbuf, gettext("Could not access %s."), NewsHost);
+ FREE(NewsHost);
+ FREE(NewsHREF);
+ FREE(ProxyHost);
+ FREE(ProxyHREF);
+ FREE(ListArg);
+ if (postfile) {
+ HTSYS_remove(postfile);
+ FREE(postfile);
+ }
+ return HTLoadError(stream, 500, dbuf);
+ } else {
+ CTRACE((tfp, "HTNews: Connected to news host %s.\n",
+ NewsHost));
+#ifdef USE_SSL
+ /*
+ * If this is an snews url, then do the SSL stuff here
+ */
+ if (!using_proxy &&
+ (!StrNCmp(url, "snews", 5) ||
+ !StrNCmp(url, "snewspost:", 10) ||
+ !StrNCmp(url, "snewsreply:", 11))) {
+ Handle = HTGetSSLHandle();
+ SSL_set_fd(Handle, s);
+ HTSSLInitPRNG();
+ status = SSL_connect(Handle);
+
+ if (status <= 0) {
+ unsigned long SSLerror;
+
+ CTRACE((tfp,
+ "HTNews: Unable to complete SSL handshake for '%s', SSL_connect=%d, SSL error stack dump follows\n",
+ url, status));
+ SSL_load_error_strings();
+ while ((SSLerror = ERR_get_error()) != 0) {
+ CTRACE((tfp, "HTNews: SSL: %s\n",
+ ERR_error_string(SSLerror, NULL)));
+ }
+ HTAlert("Unable to make secure connection to remote host.");
+ NEWS_NETCLOSE(s);
+ s = -1;
+ if (!(post_wanted || reply_wanted ||
+ spost_wanted || sreply_wanted))
+ (*targetClass._abort) (target, NULL);
+ FREE(NewsHost);
+ FREE(NewsHREF);
+ FREE(ProxyHost);
+ FREE(ProxyHREF);
+ FREE(ListArg);
+ if (postfile) {
+#ifdef VMS
+ while (remove(postfile) == 0) ; /* loop through all versions */
+#else
+ remove(postfile);
+#endif /* VMS */
+ FREE(postfile);
+ }
+ return HT_NOT_LOADED;
+ }
+ sprintf(SSLprogress,
+ "Secure %d-bit %s (%s) NNTP connection",
+ SSL_get_cipher_bits(Handle, NULL),
+ SSL_get_cipher_version(Handle),
+ SSL_get_cipher(Handle));
+ _HTProgress(SSLprogress);
+ }
+#endif /* USE_SSL */
+ HTInitInput(s); /* set up buffering */
+ if (proxycmd[0]) {
+ status = (int) NEWS_NETWRITE(s, proxycmd, (int) strlen(proxycmd));
+ CTRACE((tfp,
+ "HTNews: Proxy command returned status '%d'.\n",
+ status));
+ }
+ if (((status = response(NULL)) / 100) != 2) {
+ NEWS_NETCLOSE(s);
+ s = -1;
+ if (status == HT_INTERRUPTED) {
+ _HTProgress(CONNECTION_INTERRUPTED);
+ if (!(post_wanted || reply_wanted ||
+ spost_wanted || sreply_wanted)) {
+ ABORT_TARGET;
+ }
+ FREE(NewsHost);
+ FREE(NewsHREF);
+ FREE(ProxyHost);
+ FREE(ProxyHREF);
+ FREE(ListArg);
+ if (postfile) {
+ HTSYS_remove(postfile);
+ FREE(postfile);
+ }
+ return (HT_NOT_LOADED);
+ }
+ if (retries < 1)
+ continue;
+ FREE(ProxyHost);
+ FREE(ProxyHREF);
+ FREE(ListArg);
+ FREE(postfile);
+ if (!(post_wanted || reply_wanted ||
+ spost_wanted || sreply_wanted)) {
+ ABORT_TARGET;
+ }
+ if (response_text[0]) {
+ HTSprintf0(&dbuf,
+ gettext("Can't read news info. News host %.20s responded: %.200s"),
+ NewsHost, response_text);
+ } else {
+ HTSprintf0(&dbuf,
+ gettext("Can't read news info, empty response from host %s"),
+ NewsHost);
+ }
+ return HTLoadError(stream, 500, dbuf);
+ }
+ if (status == 200) {
+ HTCanPost = TRUE;
+ } else {
+ HTCanPost = FALSE;
+ if (post_wanted || reply_wanted ||
+ spost_wanted || sreply_wanted) {
+ HTAlert(CANNOT_POST);
+ FREE(NewsHREF);
+ if (ProxyHREF) {
+ StrAllocCopy(NewsHost, ProxyHost);
+ FREE(ProxyHost);
+ FREE(ProxyHREF);
+ }
+ FREE(ListArg);
+ if (postfile) {
+ HTSYS_remove(postfile);
+ FREE(postfile);
+ }
+ return (HT_NOT_LOADED);
+ }
+ }
+ }
+ }
+ /* If needed opening */
+ if (post_wanted || reply_wanted ||
+ spost_wanted || sreply_wanted) {
+ if (!HTCanPost) {
+ HTAlert(CANNOT_POST);
+ FREE(NewsHREF);
+ if (ProxyHREF) {
+ StrAllocCopy(NewsHost, ProxyHost);
+ FREE(ProxyHost);
+ FREE(ProxyHREF);
+ }
+ FREE(ListArg);
+ if (postfile) {
+ HTSYS_remove(postfile);
+ FREE(postfile);
+ }
+ return (HT_NOT_LOADED);
+ }
+ if (postfile == NULL) {
+ postfile = LYNewsPost(ListArg,
+ (reply_wanted || sreply_wanted));
+ }
+ if (postfile == NULL) {
+ HTProgress(CANCELLED);
+ FREE(NewsHREF);
+ if (ProxyHREF) {
+ StrAllocCopy(NewsHost, ProxyHost);
+ FREE(ProxyHost);
+ FREE(ProxyHREF);
+ }
+ FREE(ListArg);
+ return (HT_NOT_LOADED);
+ }
+ } else {
+ /*
+ * Ensure reader mode, but don't bother checking the status for
+ * anything but HT_INTERRUPTED or a 480 Authorization request,
+ * because if the reader mode command is not needed, the server
+ * probably returned a 500, which is irrelevant at this point. -
+ * FM
+ */
+ char buffer[20];
+
+ sprintf(buffer, "mode reader%c%c", CR, LF);
+ if ((status = response(buffer)) == HT_INTERRUPTED) {
+ _HTProgress(CONNECTION_INTERRUPTED);
+ break;
+ }
+ if (status == 480) {
+ NNTPAuthResult auth_result = HTHandleAuthInfo(NewsHost);
+
+ if (auth_result == NNTPAUTH_CLOSE) {
+ if (s != -1 && !(ProxyHost || ProxyHREF)) {
+ NEWS_NETCLOSE(s);
+ s = -1;
+ }
+ }
+ if (auth_result != NNTPAUTH_OK) {
+ break;
+ }
+ if (response(buffer) == HT_INTERRUPTED) {
+ _HTProgress(CONNECTION_INTERRUPTED);
+ break;
+ }
+ }
+ }
+
+ Send_NNTP_command:
+#ifdef NEWS_DEB
+ if (postfile)
+ printf("postfile = %s, command = %s", postfile, command);
+ else
+ printf("command = %s", command);
+#endif
+ if ((status = response(command)) == HT_INTERRUPTED) {
+ _HTProgress(CONNECTION_INTERRUPTED);
+ break;
+ }
+ if (status < 0) {
+ if (retries < 1) {
+ continue;
+ } else {
+ break;
+ }
+ }
+ /*
+ * For some well known error responses which are expected to occur in
+ * normal use, break from the loop without retrying and without closing
+ * the connection. It is unlikely that these are leftovers from a
+ * timed-out connection (but we do some checks to see whether the
+ * response corresponds to the last command), or that they will give
+ * anything else when automatically retried. - kw
+ */
+ if (status == 411 && group_wanted &&
+ !StrNCmp(command, "GROUP ", 6) &&
+ !strncasecomp(response_text + 3, " No such group ", 15) &&
+ !strcmp(response_text + 18, groupName)) {
+
+ HTAlert(response_text);
+ break;
+ } else if (status == 430 && !group_wanted && !list_wanted &&
+ !StrNCmp(command, "ARTICLE <", 9) &&
+ !strcasecomp(response_text + 3, " No such article")) {
+
+ HTAlert(response_text);
+ break;
+ }
+ if ((status / 100) != 2 &&
+ status != 340 &&
+ status != 480) {
+ if (retries) {
+ if (list_wanted && !StrNCmp(command, "XGTITLE", 7)) {
+ sprintf(command, "LIST NEWSGROUPS%c%c", CR, LF);
+ goto Send_NNTP_command;
+ }
+ HTAlert(response_text);
+ } else {
+ _HTProgress(response_text);
+ }
+ NEWS_NETCLOSE(s);
+ s = -1;
+ /*
+ * Message might be a leftover "Timeout-disconnected", so try again
+ * if the retries maximum has not been reached.
+ */
+ continue;
+ }
+
+ /*
+ * Post or load a group, article, etc
+ */
+ if (status == 480) {
+ NNTPAuthResult auth_result;
+
+ /*
+ * Some servers return 480 for a failed XGTITLE. - FM
+ */
+ if (list_wanted && !StrNCmp(command, "XGTITLE", 7) &&
+ strstr(response_text, "uthenticat") == NULL &&
+ strstr(response_text, "uthor") == NULL) {
+ sprintf(command, "LIST NEWSGROUPS%c%c", CR, LF);
+ goto Send_NNTP_command;
+ }
+ /*
+ * Handle Authorization. - FM
+ */
+ if ((auth_result = HTHandleAuthInfo(NewsHost)) == NNTPAUTH_OK) {
+ goto Send_NNTP_command;
+ } else if (auth_result == NNTPAUTH_CLOSE) {
+ if (s != -1 && !(ProxyHost || ProxyHREF)) {
+ NEWS_NETCLOSE(s);
+ s = -1;
+ }
+ if (retries < 1)
+ continue;
+ }
+ status = HT_NOT_LOADED;
+ } else if (post_wanted || reply_wanted ||
+ spost_wanted || sreply_wanted) {
+ /*
+ * Handle posting of an article. - FM
+ */
+ if (status != 340) {
+ HTAlert(CANNOT_POST);
+ if (postfile) {
+ HTSYS_remove(postfile);
+ }
+ } else {
+ post_article(postfile);
+ }
+ FREE(postfile);
+ status = HT_NOT_LOADED;
+ } else if (list_wanted) {
+ /*
+ * List available newsgroups. - FM
+ */
+ _HTProgress(gettext("Reading list of available newsgroups."));
+ status = read_list(ListArg);
+ } else if (group_wanted) {
+ /*
+ * List articles in a news group. - FM
+ */
+ if (last < 0) {
+ /*
+ * We got one article number rather than a range following the
+ * slash which followed the group name, or the range was zero,
+ * so now that we have selected that group, load ARTICLE and
+ * the the number (first) as the command and go back to send it
+ * and check the response. - FM
+ */
+ sprintf(command, "%s %d%c%c",
+ head_wanted ? "HEAD" : "ARTICLE",
+ first, CR, LF);
+ group_wanted = FALSE;
+ retries = 2;
+ goto Send_NNTP_command;
+ }
+ _HTProgress(gettext("Reading list of articles in newsgroup."));
+ status = read_group(groupName, first, last);
+ } else {
+ /*
+ * Get an article from a news group. - FM
+ */
+ _HTProgress(gettext("Reading news article."));
+ status = read_article(anAnchor);
+ }
+ if (status == HT_INTERRUPTED) {
+ _HTProgress(CONNECTION_INTERRUPTED);
+ status = HT_LOADED;
+ }
+ if (!(post_wanted || reply_wanted ||
+ spost_wanted || sreply_wanted)) {
+ if (status == HT_NOT_LOADED) {
+ ABORT_TARGET;
+ } else {
+ FREE_TARGET;
+ }
+ }
+ FREE(NewsHREF);
+ if (ProxyHREF) {
+ StrAllocCopy(NewsHost, ProxyHost);
+ FREE(ProxyHost);
+ FREE(ProxyHREF);
+ }
+ FREE(ListArg);
+ if (postfile) {
+ HTSYS_remove(postfile);
+ FREE(postfile);
+ }
+ return status;
+ } /* Retry loop */
+
+#if 0
+ HTAlert(gettext("Sorry, could not load requested news."));
+ NXRunAlertPanel(NULL, "Sorry, could not load `%s'.", NULL, NULL, NULL, arg);
+ /* No -- message earlier will have covered it */
+#endif
+
+ if (!(post_wanted || reply_wanted ||
+ spost_wanted || sreply_wanted)) {
+ ABORT_TARGET;
+ }
+ FREE(NewsHREF);
+ if (ProxyHREF) {
+ StrAllocCopy(NewsHost, ProxyHost);
+ FREE(ProxyHost);
+ FREE(ProxyHREF);
+ }
+ FREE(ListArg);
+ if (postfile) {
+ HTSYS_remove(postfile);
+ FREE(postfile);
+ }
+ return HT_NOT_LOADED;
+}
+
+/*
+ * This function clears all authorization information by
+ * invoking the free_NNTP_AuthInfo() function, which normally
+ * is invoked at exit. It allows a browser command to do
+ * this at any time, for example, if the user is leaving
+ * the terminal for a period of time, but does not want
+ * to end the current session. - FM
+ */
+void HTClearNNTPAuthInfo(void)
+{
+ /*
+ * Need code to check cached documents and do something to ensure that any
+ * protected documents no longer can be accessed without a new retrieval.
+ * - FM
+ */
+
+ /*
+ * Now free all of the authorization info. - FM
+ */
+ free_NNTP_AuthInfo();
+}
+
+#ifdef USE_SSL
+static int HTNewsGetCharacter(void)
+{
+ if (!Handle)
+ return HTGetCharacter();
+ else
+ return HTGetSSLCharacter((void *) Handle);
+}
+
+int HTNewsProxyConnect(int sock,
+ const char *url,
+ HTParentAnchor *anAnchor,
+ HTFormat format_out,
+ HTStream *sink)
+{
+ int status;
+ const char *arg = url;
+ char SSLprogress[256];
+
+ s = channel_s = sock;
+ Handle = HTGetSSLHandle();
+ SSL_set_fd(Handle, s);
+ HTSSLInitPRNG();
+ status = SSL_connect(Handle);
+
+ if (status <= 0) {
+ unsigned long SSLerror;
+
+ channel_s = -1;
+ CTRACE((tfp,
+ "HTNews: Unable to complete SSL handshake for '%s', SSL_connect=%d, SSL error stack dump follows\n",
+ url, status));
+ SSL_load_error_strings();
+ while ((SSLerror = ERR_get_error()) != 0) {
+ CTRACE((tfp, "HTNews: SSL: %s\n", ERR_error_string(SSLerror, NULL)));
+ }
+ HTAlert("Unable to make secure connection to remote host.");
+ NEWS_NETCLOSE(s);
+ s = -1;
+ return HT_NOT_LOADED;
+ }
+ sprintf(SSLprogress, "Secure %d-bit %s (%s) NNTP connection",
+ SSL_get_cipher_bits(Handle, NULL),
+ SSL_get_cipher_version(Handle),
+ SSL_get_cipher(Handle));
+ _HTProgress(SSLprogress);
+ status = HTLoadNews(arg, anAnchor, format_out, sink);
+ channel_s = -1;
+ return status;
+}
+#endif /* USE_SSL */
+
+#ifdef GLOBALDEF_IS_MACRO
+#define _HTNEWS_C_1_INIT { "news", HTLoadNews, NULL }
+GLOBALDEF(HTProtocol, HTNews, _HTNEWS_C_1_INIT);
+#define _HTNEWS_C_2_INIT { "nntp", HTLoadNews, NULL }
+GLOBALDEF(HTProtocol, HTNNTP, _HTNEWS_C_2_INIT);
+#define _HTNEWS_C_3_INIT { "newspost", HTLoadNews, NULL }
+GLOBALDEF(HTProtocol, HTNewsPost, _HTNEWS_C_3_INIT);
+#define _HTNEWS_C_4_INIT { "newsreply", HTLoadNews, NULL }
+GLOBALDEF(HTProtocol, HTNewsReply, _HTNEWS_C_4_INIT);
+#define _HTNEWS_C_5_INIT { "snews", HTLoadNews, NULL }
+GLOBALDEF(HTProtocol, HTSNews, _HTNEWS_C_5_INIT);
+#define _HTNEWS_C_6_INIT { "snewspost", HTLoadNews, NULL }
+GLOBALDEF(HTProtocol, HTSNewsPost, _HTNEWS_C_6_INIT);
+#define _HTNEWS_C_7_INIT { "snewsreply", HTLoadNews, NULL }
+GLOBALDEF(HTProtocol, HTSNewsReply, _HTNEWS_C_7_INIT);
+#else
+GLOBALDEF HTProtocol HTNews =
+{"news", HTLoadNews, NULL};
+GLOBALDEF HTProtocol HTNNTP =
+{"nntp", HTLoadNews, NULL};
+GLOBALDEF HTProtocol HTNewsPost =
+{"newspost", HTLoadNews, NULL};
+GLOBALDEF HTProtocol HTNewsReply =
+{"newsreply", HTLoadNews, NULL};
+GLOBALDEF HTProtocol HTSNews =
+{"snews", HTLoadNews, NULL};
+GLOBALDEF HTProtocol HTSNewsPost =
+{"snewspost", HTLoadNews, NULL};
+GLOBALDEF HTProtocol HTSNewsReply =
+{"snewsreply", HTLoadNews, NULL};
+#endif /* GLOBALDEF_IS_MACRO */
+
+#endif /* not DISABLE_NEWS */
diff --git a/WWW/Library/Implementation/HTNews.h b/WWW/Library/Implementation/HTNews.h
new file mode 100644
index 0000000..ef9a6e4
--- /dev/null
+++ b/WWW/Library/Implementation/HTNews.h
@@ -0,0 +1,60 @@
+/* Network News Transfer protocol module for the WWW library
+ HTNEWS
+
+ */
+/* History:
+ * 26 Sep 90 Written TBL in Objective-C
+ * 29 Nov 91 Downgraded to C, for portable implementation.
+ */
+
+#ifndef HTNEWS_H
+#define HTNEWS_H
+
+#include <HTAccess.h>
+#include <HTAnchor.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+ extern int HTNewsChunkSize;
+ extern int HTNewsMaxChunk;
+
+#ifdef GLOBALREF_IS_MACRO
+ extern GLOBALREF (HTProtocol, HTNews);
+ extern GLOBALREF (HTProtocol, HTNNTP);
+ extern GLOBALREF (HTProtocol, HTNewsPost);
+ extern GLOBALREF (HTProtocol, HTNewsReply);
+ extern GLOBALREF (HTProtocol, HTSNews);
+ extern GLOBALREF (HTProtocol, HTSNewsPost);
+ extern GLOBALREF (HTProtocol, HTSNewsReply);
+
+#else
+ GLOBALREF HTProtocol HTNews;
+ GLOBALREF HTProtocol HTNNTP;
+ GLOBALREF HTProtocol HTNewsPost;
+ GLOBALREF HTProtocol HTNewsReply;
+ GLOBALREF HTProtocol HTSNews;
+ GLOBALREF HTProtocol HTSNewsPost;
+ GLOBALREF HTProtocol HTSNewsReply;
+#endif /* GLOBALREF_IS_MACRO */
+
+ extern void HTSetNewsHost(const char *value);
+ extern const char *HTGetNewsHost(void);
+ extern char *HTNewsHost;
+
+ extern void HTClearNNTPAuthInfo(void);
+
+#ifdef USE_SSL
+ extern SSL_CTX *ssl_ctx;
+
+ extern int HTNewsProxyConnect(int sock,
+ const char *url,
+ HTParentAnchor *anAnchor,
+ HTFormat format_out,
+ HTStream *sink);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* HTNEWS_H */
diff --git a/WWW/Library/Implementation/HTParse.c b/WWW/Library/Implementation/HTParse.c
new file mode 100644
index 0000000..2e45441
--- /dev/null
+++ b/WWW/Library/Implementation/HTParse.c
@@ -0,0 +1,1383 @@
+/*
+ * $LynxId: HTParse.c,v 1.98 2021/07/27 21:29:49 tom Exp $
+ *
+ * Parse HyperText Document Address HTParse.c
+ * ================================
+ */
+
+#include <HTUtils.h>
+#include <HTParse.h>
+
+#include <LYUtils.h>
+#include <LYLeaks.h>
+#include <LYStrings.h>
+#include <LYCharUtils.h>
+#include <LYGlobalDefs.h>
+
+#ifdef HAVE_ALLOCA_H
+#include <alloca.h>
+#else
+#ifdef __MINGW32__
+#include <malloc.h>
+#endif /* __MINGW32__ */
+#endif
+
+#ifdef USE_IDN2
+#include <idn2.h>
+#define FreeIdna(out) idn2_free(out)
+#elif defined(USE_IDNA)
+#include <idna.h>
+#include <idn-free.h>
+#define FreeIdna(out) idn_free(out)
+#define IDN2_OK IDNA_SUCCESS
+#endif
+
+#define HEX_ESCAPE '%'
+
+struct struct_parts {
+ char *access;
+ char *host;
+ char *absolute;
+ char *relative;
+ char *search; /* treated normally as part of path */
+ char *anchor;
+};
+
+#if 0 /* for debugging */
+static void show_parts(const char *name, struct struct_parts *parts, int line)
+{
+ if (TRACE) {
+ CTRACE((tfp, "struct_parts(%s) %s@%d\n", name, __FILE__, line));
+ CTRACE((tfp, " access '%s'\n", NONNULL(parts->access)));
+ CTRACE((tfp, " host '%s'\n", NONNULL(parts->host)));
+ CTRACE((tfp, " absolute '%s'\n", NONNULL(parts->absolute)));
+ CTRACE((tfp, " relative '%s'\n", NONNULL(parts->relative)));
+ CTRACE((tfp, " search '%s'\n", NONNULL(parts->search)));
+ CTRACE((tfp, " anchor '%s'\n", NONNULL(parts->anchor)));
+ }
+}
+#define SHOW_PARTS(name) show_parts(#name, &name, __LINE__)
+#else
+#define SHOW_PARTS(name) /* nothing */
+#endif
+
+/* Strip white space off a string. HTStrip()
+ * -------------------------------
+ *
+ * On exit,
+ * Return value points to first non-white character, or to 0 if none.
+ * All trailing white space is OVERWRITTEN with zero.
+ */
+char *HTStrip(char *s)
+{
+#define SPACE(c) ((c == ' ') || (c == '\t') || (c == '\n'))
+ char *p;
+
+ for (p = s; *p; p++) { /* Find end of string */
+ ;
+ }
+ for (p--; p >= s; p--) {
+ if (SPACE(*p))
+ *p = '\0'; /* Zap trailing blanks */
+ else
+ break;
+ }
+ while (SPACE(*s))
+ s++; /* Strip leading blanks */
+ return s;
+}
+
+/* Scan a filename for its constituents. scan()
+ * -------------------------------------
+ *
+ * On entry,
+ * name points to a document name which may be incomplete.
+ * On exit,
+ * absolute or relative may be nonzero (but not both).
+ * host, anchor and access may be nonzero if they were specified.
+ * Any which are nonzero point to zero terminated strings.
+ */
+static void scan(char *name,
+ struct struct_parts *parts)
+{
+ char *after_access;
+ char *p;
+
+ parts->access = NULL;
+ parts->host = NULL;
+ parts->absolute = NULL;
+ parts->relative = NULL;
+ parts->search = NULL; /* normally not used - kw */
+ parts->anchor = NULL;
+
+ /*
+ * Scan left-to-right for a scheme (access).
+ */
+ after_access = name;
+ for (p = name; *p; p++) {
+ if (*p == ':') {
+ *p = '\0';
+ parts->access = name; /* Access name has been specified */
+ after_access = (p + 1);
+ break;
+ }
+ if (*p == '/' || *p == '#' || *p == ';' || *p == '?')
+ break;
+ }
+
+ /*
+ * Scan left-to-right for a fragment (anchor).
+ */
+ for (p = after_access; *p; p++) {
+ if (*p == '#') {
+ parts->anchor = (p + 1);
+ *p = '\0'; /* terminate the rest */
+ break; /* leave things after first # alone - kw */
+ }
+ }
+
+ /*
+ * Scan left-to-right for a host or absolute path.
+ */
+ p = after_access;
+ if (*p == '/') {
+ if (p[1] == '/') {
+ parts->host = (p + 2); /* host has been specified */
+ *p = '\0'; /* Terminate access */
+ p = StrChr(parts->host, '/'); /* look for end of host name if any */
+ if (p != NULL) {
+ *p = '\0'; /* Terminate host */
+ parts->absolute = (p + 1); /* Root has been found */
+ } else {
+ p = StrChr(parts->host, '?');
+ if (p != NULL) {
+ *p = '\0'; /* Terminate host */
+ parts->search = (p + 1);
+ }
+ }
+ } else {
+ parts->absolute = (p + 1); /* Root found but no host */
+ }
+ } else {
+ parts->relative = (*after_access) ?
+ after_access : NULL; /* NULL for "" */
+ }
+
+ /*
+ * Check schemes that commonly have unescaped hashes.
+ */
+ if (parts->access && parts->anchor &&
+ /* optimize */ StrChr("lnsdLNSD", *parts->access) != NULL) {
+ if ((!parts->host && strcasecomp(parts->access, "lynxcgi")) ||
+ !strcasecomp(parts->access, "nntp") ||
+ !strcasecomp(parts->access, "snews") ||
+ !strcasecomp(parts->access, "news") ||
+ !strcasecomp(parts->access, "data")) {
+ /*
+ * Access specified but no host and not a lynxcgi URL, so the
+ * anchor may not really be one, e.g., news:j462#36487@foo.bar, or
+ * it's an nntp or snews URL, or news URL with a host. Restore the
+ * '#' in the address.
+ */
+ /* but only if we have found a path component of which this will
+ * become part. - kw */
+ if (parts->relative || parts->absolute) {
+ *(parts->anchor - 1) = '#';
+ parts->anchor = NULL;
+ }
+ }
+ }
+} /*scan */
+
+#if defined(HAVE_ALLOCA) && !defined(LY_FIND_LEAKS)
+#define LYalloca(x) alloca((size_t)(x))
+#define LYalloca_free(x) {}
+#else
+#define LYalloca(x) malloc((size_t)(x))
+#define LYalloca_free(x) free((void *)(x))
+#endif
+
+static char *strchr_or_end(char *string, int ch)
+{
+ char *result = StrChr(string, ch);
+
+ if (result == 0) {
+ result = string + strlen(string);
+ }
+ return result;
+}
+
+/*
+ * Given a host specification that may end with a port number, e.g.,
+ * foobar:123
+ * point to the ':' which begins the ":port" to make it simple to handle the
+ * substring.
+ *
+ * If no port is found (or a syntax error), return null.
+ */
+char *HTParsePort(char *host, int *portp)
+{
+ int brackets = 0;
+ char *result = NULL;
+
+ *portp = 0;
+ if (host != NULL) {
+ while (*host != '\0' && result == 0) {
+ switch (*host++) {
+ case ':':
+ if (brackets == 0 && isdigit(UCH(*host))) {
+ char *next = NULL;
+
+ *portp = (int) strtol(host, &next, 10);
+ if (next != 0 && next != host && *next == '\0') {
+ result = (host - 1);
+ CTRACE((tfp, "HTParsePort %d\n", *portp));
+ }
+ }
+ break;
+ case '[': /* for ipv6 */
+ ++brackets;
+ break;
+ case ']': /* for ipv6 */
+ --brackets;
+ break;
+ }
+ }
+ }
+ return result;
+}
+
+#if defined(USE_IDNA) || defined(USE_IDN2)
+static int hex_decode(int ch)
+{
+ int result = -1;
+
+ if (ch >= '0' && ch <= '9')
+ result = (ch - '0');
+ else if (ch >= 'a' && ch <= 'f')
+ result = (ch - 'a') + 10;
+ else if (ch >= 'A' && ch <= 'F')
+ result = (ch - 'A') + 10;
+ return result;
+}
+
+/*
+ * Convert in-place the given hostname to IDNA form. That requires up to 64
+ * characters, and we've allowed for that, with MIN_PARSE.
+ */
+static void convert_to_idna(char *host)
+{
+ size_t length = strlen(host);
+ char *endhost = host + length;
+ char *buffer = malloc(length + 1);
+ char *params = malloc(length + 1);
+ char *output = NULL;
+ char *src, *dst;
+ int code;
+ int hi, lo;
+
+ if (buffer != NULL && params != NULL) {
+ code = TRUE;
+ *params = '\0';
+ for (dst = buffer, src = host; src < endhost; ++dst) {
+ int ch = *src++;
+
+ if (RFC_3986_GEN_DELIMS(ch)) {
+ strcpy(params, src - 1);
+ *dst = '\0';
+ break;
+ } else if (ch == HEX_ESCAPE) {
+ if ((src + 1) < endhost
+ && (hi = hex_decode(src[0])) >= 0
+ && (lo = hex_decode(src[1])) >= 0) {
+
+ *dst = (char) ((hi << 4) | lo);
+ src += 2;
+ } else {
+ CTRACE((tfp, "convert_to_idna: `%s' is malformed\n", host));
+ code = FALSE;
+ break;
+ }
+ } else {
+ *dst = (char) ch;
+ }
+ }
+ if (code) {
+ *dst = '\0';
+#ifdef USE_IDN2
+#if (!defined(IDN2_VERSION_NUMBER) || IDN2_VERSION_NUMBER < 0x02000003)
+ /*
+ * Older libidn2 mishandles STD3, stripping underscores.
+ */
+ if (strchr(buffer, '_') != NULL) {
+ code = -1;
+ } else
+#endif
+ switch (LYidnaMode) {
+ case LYidna2003:
+ code = idn2_to_ascii_8z(buffer, &output, IDN2_TRANSITIONAL);
+ break;
+ case LYidna2008:
+ /* IDNA2008 rules without the TR46 amendments */
+ code = idn2_to_ascii_8z(buffer, &output, 0);
+ break;
+ case LYidnaTR46:
+ code = idn2_to_ascii_8z(buffer, &output, IDN2_NONTRANSITIONAL
+ | IDN2_NFC_INPUT);
+ break;
+ case LYidnaCompat:
+ /* IDNA2008 */
+ code = idn2_to_ascii_8z(buffer, &output, IDN2_NONTRANSITIONAL
+ | IDN2_NFC_INPUT);
+ if (code == IDN2_DISALLOWED) {
+ /* IDNA2003 - compatible */
+ code = idn2_to_ascii_8z(buffer, &output, IDN2_TRANSITIONAL);
+ }
+ break;
+ }
+#else
+ code = idna_to_ascii_8z(buffer, &output, IDNA_USE_STD3_ASCII_RULES);
+#endif
+ if (code == IDN2_OK) {
+ CTRACE((tfp, "convert_to_idna: `%s' -> `%s': OK\n", buffer, output));
+ strcpy(host, output);
+ strcat(host, params);
+ } else {
+ CTRACE((tfp, "convert_to_idna: `%s': %s\n",
+ buffer,
+ idna_strerror((Idna_rc) code)));
+ }
+ if (output)
+ FreeIdna(output);
+ }
+ }
+ free(buffer);
+ free(params);
+}
+#define MIN_PARSE 80
+#else
+#define MIN_PARSE 8
+#endif
+
+/* Parse a Name relative to another name. HTParse()
+ * --------------------------------------
+ *
+ * This returns those parts of a name which are given (and requested)
+ * substituting bits from the related name where necessary.
+ *
+ * Originally based on RFC 1808, some details in RFC 3986 are used.
+ *
+ * On entry,
+ * aName A filename given
+ * relatedName A name relative to which aName is to be parsed
+ * wanted A mask for the bits which are wanted.
+ *
+ * On exit,
+ * returns A pointer to a malloc'd string which MUST BE FREED
+ */
+char *HTParse(const char *aName,
+ const char *relatedName,
+ int wanted)
+{
+ char *result = NULL;
+ char *tail = NULL; /* a pointer to the end of the 'result' string */
+ char *return_value = NULL;
+ size_t len, len1, len2;
+ size_t need;
+ char *name = NULL;
+ char *rel = NULL;
+ char *p, *q;
+ char *acc_method;
+ struct struct_parts given, related;
+
+ CTRACE((tfp, "HTParse: aName:`%s'\n", aName));
+ CTRACE((tfp, " relatedName:`%s'\n", relatedName));
+
+ if (wanted & (PARSE_STRICTPATH | PARSE_QUERY)) { /* if detail wanted... */
+ if ((wanted & (PARSE_STRICTPATH | PARSE_QUERY))
+ == (PARSE_STRICTPATH | PARSE_QUERY)) /* if strictpath AND query */
+ wanted |= PARSE_PATH; /* then treat as if PARSE_PATH wanted */
+ if (wanted & PARSE_PATH) /* if PARSE_PATH wanted */
+ wanted &= ~(PARSE_STRICTPATH | PARSE_QUERY); /* ignore details */
+ }
+/* *INDENT-OFF* */
+ CTRACE((tfp, " want:%s%s%s%s%s%s%s\n",
+ wanted & PARSE_PUNCTUATION ? " punc" : "",
+ wanted & PARSE_ANCHOR ? " anchor" : "",
+ wanted & PARSE_PATH ? " path" : "",
+ wanted & PARSE_HOST ? " host" : "",
+ wanted & PARSE_ACCESS ? " access" : "",
+ wanted & PARSE_STRICTPATH ? " PATH" : "",
+ wanted & PARSE_QUERY ? " QUERY" : ""));
+/* *INDENT-ON* */
+
+ /*
+ * Allocate the temporary string. Optimized.
+ */
+ len1 = strlen(aName) + 1;
+ len2 = strlen(relatedName) + 1;
+ len = len1 + len2 + MIN_PARSE; /* Lots of space: more than enough */
+
+ need = (len * 2 + len1 + len2);
+ if (need > (size_t) max_uri_size ||
+ (int) need < (int) len1 ||
+ (int) need < (int) len2)
+ return StrAllocCopy(return_value, "");
+
+ result = tail = (char *) LYalloca(need);
+ if (result == NULL) {
+ outofmem(__FILE__, "HTParse");
+ }
+ *result = '\0';
+ name = result + len;
+ rel = name + len1;
+
+ /*
+ * Make working copy of the input string to cut up.
+ */
+ MemCpy(name, aName, len1);
+
+ /*
+ * Cut up the string into URL fields.
+ */
+ scan(name, &given);
+ SHOW_PARTS(given);
+
+ /*
+ * Now related string.
+ */
+ if ((given.access && given.host && given.absolute) || !*relatedName) {
+ /*
+ * Inherit nothing!
+ */
+ related.access = NULL;
+ related.host = NULL;
+ related.absolute = NULL;
+ related.relative = NULL;
+ related.search = NULL;
+ related.anchor = NULL;
+ } else {
+ MemCpy(rel, relatedName, len2);
+ scan(rel, &related);
+ }
+ SHOW_PARTS(related);
+
+ /*
+ * Handle the scheme (access) field.
+ */
+ if (given.access && given.host && !given.relative && !given.absolute) {
+ if (!strcmp(given.access, "http") ||
+ !strcmp(given.access, "https") ||
+ !strcmp(given.access, "ftp")) {
+
+ /*
+ * Assume root.
+ */
+ given.absolute = empty_string;
+ }
+ }
+ acc_method = given.access ? given.access : related.access;
+ if (wanted & PARSE_ACCESS) {
+ if (acc_method) {
+ strcpy(tail, acc_method);
+ tail += strlen(tail);
+ if (wanted & PARSE_PUNCTUATION) {
+ *tail++ = ':';
+ *tail = '\0';
+ }
+ }
+ }
+
+ /*
+ * If different schemes, inherit nothing.
+ *
+ * We'll try complying with RFC 1808 and the Fielding draft, and inherit
+ * nothing if both schemes are given, rather than only when they differ,
+ * except for file URLs - FM
+ *
+ * After trying it for a while, it's still premature, IHMO, to go along
+ * with it, so this is back to inheriting for identical schemes whether or
+ * not they are "file". If you want to try it again yourself, uncomment
+ * the strcasecomp() below. - FM
+ */
+ if ((given.access && related.access) &&
+ ( /* strcasecomp(given.access, "file") || */
+ strcmp(given.access, related.access))) {
+ related.host = NULL;
+ related.absolute = NULL;
+ related.relative = NULL;
+ related.search = NULL;
+ related.anchor = NULL;
+ }
+
+ /*
+ * Handle the host field.
+ */
+ if (wanted & PARSE_HOST) {
+ if (given.host || related.host) {
+ if (wanted & PARSE_PUNCTUATION) {
+ *tail++ = '/';
+ *tail++ = '/';
+ }
+ strcpy(tail, given.host ? given.host : related.host);
+ /*
+ * Ignore default port numbers, and trailing dots on FQDNs, which
+ * will only cause identical addresses to look different. (related
+ * is already a clean url).
+ */
+ {
+ char *p2, *h;
+ int portnumber;
+ int gen_delims = 0;
+
+ if ((p2 = HTSkipToAt(result, &gen_delims)) != NULL
+ && gen_delims == 0) {
+ tail = (p2 + 1);
+ }
+ p2 = HTParsePort(result, &portnumber);
+ if (p2 != NULL && acc_method != NULL) {
+ /*
+ * Port specified.
+ */
+#define ACC_METHOD(a,b) (!strcmp(acc_method, a) && (portnumber == b))
+ if (ACC_METHOD("http", 80) ||
+ ACC_METHOD("https", 443) ||
+ ACC_METHOD("gopher", 70) ||
+ ACC_METHOD("ftp", 21) ||
+ ACC_METHOD("wais", 210) ||
+ ACC_METHOD("nntp", 119) ||
+ ACC_METHOD("news", 119) ||
+ ACC_METHOD("newspost", 119) ||
+ ACC_METHOD("newsreply", 119) ||
+ ACC_METHOD("snews", 563) ||
+ ACC_METHOD("snewspost", 563) ||
+ ACC_METHOD("snewsreply", 563) ||
+ ACC_METHOD("finger", 79) ||
+ ACC_METHOD("telnet", 23) ||
+ ACC_METHOD("tn3270", 23) ||
+ ACC_METHOD("rlogin", 513) ||
+ ACC_METHOD("cso", 105))
+ *p2 = '\0'; /* It is the default: ignore it */
+ }
+ if (p2 == NULL) {
+ int len3 = (int) strlen(tail);
+
+ if (len3 > 0) {
+ h = tail + len3 - 1; /* last char of hostname */
+ if (*h == '.')
+ *h = '\0'; /* chop final . */
+ }
+ } else if (p2 != result) {
+ h = p2;
+ h--; /* End of hostname */
+ if (*h == '.') {
+ /*
+ * Slide p2 over h.
+ */
+ while (*p2 != '\0')
+ *h++ = *p2++;
+ *h = '\0'; /* terminate */
+ }
+ }
+ }
+#if defined(USE_IDNA) || defined(USE_IDN2)
+ /*
+ * Depending on locale-support, we could have a literal UTF-8
+ * string as a host name, or a URL-encoded form of that.
+ */
+ convert_to_idna(tail);
+#endif
+ }
+ }
+
+ /*
+ * Trim any blanks from the result so far - there's no excuse for blanks
+ * in a hostname. Also update the tail here.
+ */
+ tail = LYRemoveBlanks(result);
+
+ /*
+ * If host in given or related was ended directly with a '?' (no slash),
+ * fake the search part into absolute. This is the only case search is
+ * returned from scan. A host must have been present. this restores the
+ * '?' at which the host part had been truncated in scan, we have to do
+ * this after host part handling is done. - kw
+ */
+ if (given.search && *(given.search - 1) == '\0') {
+ given.absolute = given.search - 1;
+ given.absolute[0] = '?';
+ } else if (related.search && !related.absolute &&
+ *(related.search - 1) == '\0') {
+ related.absolute = related.search - 1;
+ related.absolute[0] = '?';
+ }
+
+ /*
+ * If different hosts, inherit no path.
+ */
+ if (given.host && related.host)
+ if (strcmp(given.host, related.host) != 0) {
+ related.absolute = NULL;
+ related.relative = NULL;
+ related.anchor = NULL;
+ }
+
+ /*
+ * Handle the path.
+ */
+ if (wanted & (PARSE_PATH | PARSE_STRICTPATH | PARSE_QUERY)) {
+ int want_detail = (wanted & (PARSE_STRICTPATH | PARSE_QUERY));
+
+ if (acc_method && !given.absolute && given.relative) {
+ /*
+ * Treat all given nntp or snews paths, or given paths for news
+ * URLs with a host, as absolute.
+ */
+ switch (*acc_method) {
+ case 'N':
+ case 'n':
+ if (!strcasecomp(acc_method, "nntp") ||
+ (!strcasecomp(acc_method, "news") &&
+ !strncasecomp(result, "news://", 7))) {
+ given.absolute = given.relative;
+ given.relative = NULL;
+ }
+ break;
+ case 'S':
+ case 's':
+ if (!strcasecomp(acc_method, "snews")) {
+ given.absolute = given.relative;
+ given.relative = NULL;
+ }
+ break;
+ }
+ }
+
+ if (given.absolute) { /* All is given */
+ char *base = tail;
+
+ if (wanted & PARSE_PUNCTUATION)
+ *tail++ = '/';
+ strcpy(tail, given.absolute);
+ HTSimplify(base, TRUE);
+ CTRACE((tfp, "HTParse: (ABS)\n"));
+ } else if (related.absolute) { /* Adopt path not name */
+ char *base = tail;
+
+ *tail++ = '/';
+ strcpy(tail, related.absolute);
+ if (given.relative) {
+ /* RFC 1808 part 4 step 5 (if URL path is empty) */
+ /* a) if given has params, add/replace that */
+ if (given.relative[0] == ';') {
+ strcpy(strchr_or_end(tail, ';'), given.relative);
+ }
+ /* b) if given has query, add/replace that */
+ else if (given.relative[0] == '?') {
+ strcpy(strchr_or_end(tail, '?'), given.relative);
+ }
+ /* otherwise fall through to RFC 1808 part 4 step 6 */
+ else {
+ p = StrChr(tail, '?'); /* Search part? */
+ if (p == NULL)
+ p = (tail + strlen(tail) - 1);
+ for (; *p != '/'; p--) ; /* last / */
+ p[1] = '\0'; /* Remove filename */
+ strcat(p, given.relative); /* Add given one */
+ }
+ HTSimplify(base, FALSE);
+ if (*base == '\0')
+ strcpy(base, "/");
+ } else {
+ HTSimplify(base, TRUE);
+ }
+ if (base[0] == '/' && base[1] == '/') {
+ char *pz;
+
+ for (pz = base; (pz[0] = pz[1]) != '\0'; ++pz) ;
+ }
+ CTRACE((tfp, "HTParse: (Related-ABS)\n"));
+ } else if (given.relative) {
+ strcpy(tail, given.relative); /* what we've got */
+ HTSimplify(tail, FALSE);
+ CTRACE((tfp, "HTParse: (REL)\n"));
+ } else if (related.relative) {
+ strcpy(tail, related.relative);
+ HTSimplify(tail, FALSE);
+ CTRACE((tfp, "HTParse: (Related-REL)\n"));
+ } else { /* No inheritance */
+ if (!isLYNXCGI(aName) &&
+ !isLYNXEXEC(aName) &&
+ !isLYNXPROG(aName)) {
+ *tail++ = '/';
+ *tail = '\0';
+ } else {
+ HTSimplify(tail, FALSE);
+ }
+ if (!strcmp(result, "news:/"))
+ result[5] = '*';
+ CTRACE((tfp, "HTParse: (No inheritance)\n"));
+ }
+ if (want_detail) {
+ p = StrChr(tail, '?'); /* Search part? */
+ if (p) {
+ if (PARSE_STRICTPATH) {
+ *p = '\0';
+ } else {
+ if (!(wanted & PARSE_PUNCTUATION))
+ p++;
+ do {
+ *tail++ = *p;
+ } while (*p++);
+ }
+ } else {
+ if (wanted & PARSE_QUERY)
+ *tail = '\0';
+ }
+ }
+ }
+
+ /*
+ * Handle the fragment (anchor). Never inherit.
+ */
+ if (wanted & PARSE_ANCHOR) {
+ if (given.anchor && *given.anchor) {
+ tail += strlen(tail);
+ if (wanted & PARSE_PUNCTUATION)
+ *tail++ = '#';
+ strcpy(tail, given.anchor);
+ }
+ }
+
+ /*
+ * If there are any blanks remaining in the string, escape them as needed.
+ * See the discussion in LYLegitimizeHREF() for example.
+ */
+ if ((p = StrChr(result, ' ')) != 0) {
+ switch (is_url(result)) {
+ case UNKNOWN_URL_TYPE:
+ CTRACE((tfp, "HTParse: ignore:`%s'\n", result));
+ break;
+ case LYNXEXEC_URL_TYPE:
+ case LYNXPROG_URL_TYPE:
+ case LYNXCGI_URL_TYPE:
+ case LYNXPRINT_URL_TYPE:
+ case LYNXHIST_URL_TYPE:
+ case LYNXDOWNLOAD_URL_TYPE:
+ case LYNXKEYMAP_URL_TYPE:
+ case LYNXIMGMAP_URL_TYPE:
+ case LYNXCOOKIE_URL_TYPE:
+ case LYNXCACHE_URL_TYPE:
+ case LYNXDIRED_URL_TYPE:
+ case LYNXOPTIONS_URL_TYPE:
+ case LYNXCFG_URL_TYPE:
+ case LYNXCOMPILE_OPTS_URL_TYPE:
+ case LYNXMESSAGES_URL_TYPE:
+ CTRACE((tfp, "HTParse: spaces:`%s'\n", result));
+ break;
+ case NOT_A_URL_TYPE:
+ default:
+ CTRACE((tfp, "HTParse: encode:`%s'\n", result));
+ do {
+ q = p + strlen(p) + 2;
+
+ while (q != p + 1) {
+ q[0] = q[-2];
+ --q;
+ }
+ p[0] = HEX_ESCAPE;
+ p[1] = '2';
+ p[2] = '0';
+ } while ((p = StrChr(result, ' ')) != 0);
+ break;
+ }
+ }
+ CTRACE((tfp, "HTParse: result:`%s'\n", result));
+
+ StrAllocCopy(return_value, result);
+ LYalloca_free(result);
+
+ /* FIXME: could be optimized using HTParse() internals */
+ if (*relatedName &&
+ ((wanted & PARSE_ALL_WITHOUT_ANCHOR) == PARSE_ALL_WITHOUT_ANCHOR)) {
+ /*
+ * Check whether to fill in localhost. - FM
+ */
+ LYFillLocalFileURL(&return_value, relatedName);
+ CTRACE((tfp, "pass LYFillLocalFile:`%s'\n", return_value));
+ }
+
+ return return_value; /* exactly the right length */
+}
+
+/* HTParseAnchor(), fast HTParse() specialization
+ * ----------------------------------------------
+ *
+ * On exit,
+ * returns A pointer within input string (probably to its end '\0')
+ */
+const char *HTParseAnchor(const char *aName)
+{
+ const char *p = aName;
+
+ for (; *p && *p != '#'; p++) {
+ ;
+ }
+ if (*p == '#') {
+ /* the safe way based on HTParse() -
+ * keeping in mind scan() peculiarities on schemes:
+ */
+ struct struct_parts given;
+ size_t need = ((unsigned) ((p - aName) + (int) strlen(p) + 1));
+ char *name;
+
+ if (need > (size_t) max_uri_size) {
+ p += strlen(p);
+ } else {
+ name = (char *) LYalloca(need);
+
+ if (name == NULL) {
+ outofmem(__FILE__, "HTParseAnchor");
+ }
+ strcpy(name, aName);
+ scan(name, &given);
+ LYalloca_free(name);
+
+ p++; /*next to '#' */
+ if (given.anchor == NULL) {
+ for (; *p; p++) /*scroll to end '\0' */
+ ;
+ }
+ }
+ }
+ return p;
+}
+
+/* Simplify a filename. HTSimplify()
+ * --------------------
+ *
+ * A unix-style file is allowed to contain the sequence xxx/../ which may
+ * be replaced by "" , and the sequence "/./" which may be replaced by "/".
+ * Simplification helps us recognize duplicate filenames.
+ *
+ * RFC 3986 section 5.2.4 says to do this whether or not the path was relative.
+ */
+void HTSimplify(char *filename, BOOL absolute)
+{
+#define MY_FMT "HTParse HTSimplify\t(%s)"
+#ifdef NO_LYNX_TRACE
+#define debug_at(at) /* nothing */
+#define atln "?"
+#else
+ const char *atln;
+
+#define debug_at(at) atln = at
+#endif
+ char *mark;
+ char *p;
+ size_t limit;
+
+ CTRACE2(TRACE_HTPARSE,
+ (tfp, MY_FMT " %s\n",
+ filename,
+ absolute ? "ABS" : "REL"));
+
+ if (LYIsPathSep(*filename) && !absolute)
+ ++filename;
+ mark = filename;
+ limit = strlen(filename);
+
+ for (p = filename; *p; ++p) {
+ if (*p == '?' || *p == '#') {
+ limit = (size_t) (p - filename);
+ break;
+ }
+ }
+ while ((limit != 0) && (*filename != '\0')) {
+ size_t trim = 0;
+ size_t skip = 0;
+ size_t last = 0;
+
+ debug_at("?");
+ p = filename;
+ if (limit >= 2 && !memcmp(p, "./", 2)) { /* 2A */
+ debug_at("2A");
+ trim = 2;
+ } else if (limit >= 3 && !memcmp(p, "../", 3)) {
+ debug_at("2A2");
+ trim = 3;
+ } else if (limit >= 3 && !memcmp(p, "/./", 3)) { /* 2B */
+ debug_at("2B");
+ trim = 2;
+ skip = 1;
+ } else if (limit == 2 && !memcmp(p, "/.", 2)) {
+ debug_at("2B2");
+ trim = 1;
+ skip = 1;
+ } else if (limit >= 4 && !memcmp(p, "/../", 4)) { /* 2C */
+ debug_at("2C");
+ trim = 3;
+ skip = 1;
+ last = 1;
+ } else if (limit == 3 && !memcmp(p, "/..", 3)) {
+ debug_at("2C2");
+ trim = 2;
+ skip = 1;
+ last = 1;
+ } else if (limit == 2 && !memcmp(p, "..", 2)) { /* 2D */
+ debug_at("2D");
+ trim = 2;
+ } else if (limit == 1 && !memcmp(p, ".", 1)) {
+ debug_at("2D2");
+ trim = 1;
+ }
+ if (trim) {
+ CTRACE2(TRACE_HTPARSE,
+ (tfp, MY_FMT " trim %lu/%lu (%.*s) '%.*s' @%s\n",
+ mark, (unsigned long) trim, (unsigned long) limit,
+ (int) trim, p + skip, (int) limit, p, atln));
+ }
+ if (last) {
+ char *prior = filename;
+
+ if (prior != mark) {
+ --prior;
+ while (prior != mark && *prior != '/') {
+ --prior;
+ }
+ }
+ if (prior != filename) {
+ trim += (size_t) (filename - prior);
+ limit += (size_t) (filename - prior);
+ filename = prior;
+ CTRACE2(TRACE_HTPARSE,
+ (tfp, MY_FMT " TRIM %lu/%lu (%.*s)\n",
+ mark, (unsigned long) trim, (unsigned long) limit,
+ (int) trim, filename + skip));
+ }
+ }
+ if (trim) {
+ limit -= trim;
+ for (p = filename;; ++p) {
+ if ((p[0] = p[trim]) == '\0') {
+ break;
+ }
+ if (skip) {
+ p[0] = '/';
+ skip = 0;
+ }
+ }
+ CTRACE2(TRACE_HTPARSE,
+ (tfp, MY_FMT " loop %lu\n", mark, (unsigned long) limit));
+ } else {
+ if (*filename == '/') {
+ ++filename;
+ --limit;
+ }
+ while ((limit != 0) && (*filename != '/')) {
+ ++filename;
+ --limit;
+ }
+ }
+ }
+ CTRACE2(TRACE_HTPARSE, (tfp, MY_FMT " done\n", mark));
+#undef MY_FMT
+}
+
+/* Make Relative Name. HTRelative()
+ * -------------------
+ *
+ * This function creates and returns a string which gives an expression of
+ * one address as related to another. Where there is no relation, an absolute
+ * address is returned.
+ *
+ * On entry,
+ * Both names must be absolute, fully qualified names of nodes
+ * (no anchor bits)
+ *
+ * On exit,
+ * The return result points to a newly allocated name which, if
+ * parsed by HTParse relative to relatedName, will yield aName.
+ * The caller is responsible for freeing the resulting name later.
+ *
+ */
+char *HTRelative(const char *aName,
+ const char *relatedName)
+{
+ char *result = NULL;
+ const char *p = aName;
+ const char *q = relatedName;
+ const char *after_access = NULL;
+ const char *path = NULL;
+ const char *last_slash = NULL;
+ int slashes = 0;
+
+ for (; *p; p++, q++) { /* Find extent of match */
+ if (*p != *q)
+ break;
+ if (*p == ':')
+ after_access = p + 1;
+ if (*p == '/') {
+ last_slash = p;
+ slashes++;
+ if (slashes == 3)
+ path = p;
+ }
+ }
+
+ /* q, p point to the first non-matching character or zero */
+
+ if (!after_access) { /* Different access */
+ StrAllocCopy(result, aName);
+ } else if (slashes < 3) { /* Different nodes */
+ StrAllocCopy(result, after_access);
+ } else if (slashes == 3) { /* Same node, different path */
+ StrAllocCopy(result, path);
+ } else { /* Some path in common */
+ unsigned levels = 0;
+
+ for (; *q && (*q != '#'); q++)
+ if (*q == '/')
+ levels++;
+ result = typecallocn(char, 3 * levels + strlen(last_slash) + 1);
+
+ if (result == NULL)
+ outofmem(__FILE__, "HTRelative");
+
+ result[0] = '\0';
+ for (; levels; levels--)
+ strcat(result, "../");
+ strcat(result, last_slash + 1);
+ }
+ CTRACE((tfp,
+ "HTparse: `%s' expressed relative to\n `%s' is\n `%s'.\n",
+ aName, relatedName, result));
+ return result;
+}
+
+#define AlloCopy(next,base,extra) \
+ typecallocn(char, ((next - base) + ((int) extra)))
+
+/* Escape undesirable characters using % HTEscape()
+ * -------------------------------------
+ *
+ * This function takes a pointer to a string in which
+ * some characters may be unacceptable unescaped.
+ * It returns a string which has these characters
+ * represented by a '%' character followed by two hex digits.
+ *
+ * Unlike HTUnEscape(), this routine returns a calloc'd string.
+ */
+/* *INDENT-OFF* */
+static const unsigned char isAcceptable[96] =
+
+/* Bit 0 xalpha -- see HTFile.h
+ * Bit 1 xpalpha -- as xalpha but with plus.
+ * Bit 2 ... path -- as xpalphas but with /
+ */
+ /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */
+ { 0,0,0,0,0,0,0,0,0,0,7,6,0,7,7,4, /* 2x !"#$%&'()*+,-./ */
+ 7,7,7,7,7,7,7,7,7,7,0,0,0,0,0,0, /* 3x 0123456789:;<=>? */
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, /* 4x @ABCDEFGHIJKLMNO */
+ 7,7,7,7,7,7,7,7,7,7,7,0,0,0,0,7, /* 5X PQRSTUVWXYZ[\]^_ */
+ 0,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, /* 6x `abcdefghijklmno */
+ 7,7,7,7,7,7,7,7,7,7,7,0,0,0,0,0 }; /* 7X pqrstuvwxyz{|}~ DEL */
+/* *INDENT-ON* */
+
+static const char *hex = "0123456789ABCDEF";
+
+#define ACCEPTABLE(a) ( a>=32 && a<128 && ((isAcceptable[a-32]) & mask))
+
+char *HTEscape(const char *str,
+ unsigned mask)
+{
+ const char *p;
+ char *q;
+ char *result;
+ size_t unacceptable = 0;
+
+ for (p = str; *p; p++)
+ if (!ACCEPTABLE(UCH(TOASCII(*p))))
+ unacceptable++;
+ result = AlloCopy(p, str, (unacceptable * 2) + 1);
+
+ if (result == NULL)
+ outofmem(__FILE__, "HTEscape");
+
+ for (q = result, p = str; *p; p++) {
+ unsigned char a = UCH(TOASCII(*p));
+
+ if (!ACCEPTABLE(a)) {
+ *q++ = HEX_ESCAPE; /* Means hex coming */
+ *q++ = hex[a >> 4];
+ *q++ = hex[a & 15];
+ } else
+ *q++ = *p;
+ }
+ *q = '\0'; /* Terminate */
+ return result;
+}
+
+/* Escape unsafe characters using % HTEscapeUnsafe()
+ * --------------------------------
+ *
+ * This function takes a pointer to a string in which
+ * some characters may be that may be unsafe are unescaped.
+ * It returns a string which has these characters
+ * represented by a '%' character followed by two hex digits.
+ *
+ * Unlike HTUnEscape(), this routine returns a malloc'd string.
+ */
+#define UNSAFE(ch) (((ch) <= 32) || ((ch) >= 127))
+
+char *HTEscapeUnsafe(const char *str)
+{
+ const char *p;
+ char *q;
+ char *result;
+ size_t unacceptable = 0;
+
+ for (p = str; *p; p++)
+ if (UNSAFE(UCH(TOASCII(*p))))
+ unacceptable++;
+ result = AlloCopy(p, str, (unacceptable * 2) + 1);
+
+ if (result == NULL)
+ outofmem(__FILE__, "HTEscapeUnsafe");
+
+ for (q = result, p = str; *p; p++) {
+ unsigned char a = UCH(TOASCII(*p));
+
+ if (UNSAFE(a)) {
+ *q++ = HEX_ESCAPE; /* Means hex coming */
+ *q++ = hex[a >> 4];
+ *q++ = hex[a & 15];
+ } else
+ *q++ = *p;
+ }
+ *q = '\0'; /* Terminate */
+ return result;
+}
+
+/* Escape undesirable characters using % but space to +. HTEscapeSP()
+ * -----------------------------------------------------
+ *
+ * This function takes a pointer to a string in which
+ * some characters may be unacceptable unescaped.
+ * It returns a string which has these characters
+ * represented by a '%' character followed by two hex digits,
+ * except that spaces are converted to '+' instead of %2B.
+ *
+ * Unlike HTUnEscape(), this routine returns a calloced string.
+ */
+char *HTEscapeSP(const char *str,
+ unsigned mask)
+{
+ const char *p;
+ char *q;
+ char *result;
+ size_t unacceptable = 0;
+
+ for (p = str; *p; p++)
+ if (!(*p == ' ' || ACCEPTABLE(UCH(TOASCII(*p)))))
+ unacceptable++;
+ result = AlloCopy(p, str, (unacceptable * 2) + 1);
+
+ if (result == NULL)
+ outofmem(__FILE__, "HTEscape");
+
+ for (q = result, p = str; *p; p++) {
+ unsigned char a = UCH(TOASCII(*p));
+
+ if (a == 32) {
+ *q++ = '+';
+ } else if (!ACCEPTABLE(a)) {
+ *q++ = HEX_ESCAPE; /* Means hex coming */
+ *q++ = hex[a >> 4];
+ *q++ = hex[a & 15];
+ } else {
+ *q++ = *p;
+ }
+ }
+ *q = '\0'; /* Terminate */
+ return result;
+}
+
+/* Decode %xx escaped characters. HTUnEscape()
+ * ------------------------------
+ *
+ * This function takes a pointer to a string in which some
+ * characters may have been encoded in %xy form, where xy is
+ * the ASCII hex code for character 16x+y.
+ * The string is converted in place, as it will never grow.
+ */
+static char from_hex(int c)
+{
+ return (char) (c >= '0' && c <= '9' ? c - '0'
+ : c >= 'A' && c <= 'F' ? c - 'A' + 10
+ : c - 'a' + 10); /* accept small letters just in case */
+}
+
+char *HTUnEscape(char *str)
+{
+ char *p = str;
+ char *q = str;
+
+ if (!(p && *p))
+ return str;
+
+ while (*p != '\0') {
+ if (*p == HEX_ESCAPE &&
+ /*
+ * Tests shouldn't be needed, but better safe than sorry.
+ */
+ p[1] && p[2] &&
+ isxdigit(UCH(p[1])) &&
+ isxdigit(UCH(p[2]))) {
+ p++;
+ if (*p)
+ *q = (char) (from_hex(*p++) * 16);
+ if (*p) {
+ /*
+ * Careful! FROMASCII() may evaluate its arg more than once!
+ */
+ /* S/390 -- gil -- 0221 */
+ *q = (char) (*q + from_hex(*p++));
+ }
+ *q = FROMASCII(*q);
+ q++;
+ } else {
+ *q++ = *p++;
+ }
+ }
+
+ *q = '\0';
+ return str;
+
+} /* HTUnEscape */
+
+/* Decode some %xx escaped characters. HTUnEscapeSome()
+ * ----------------------------------- Klaus Weide
+ * (kweide@tezcat.com)
+ * This function takes a pointer to a string in which some
+ * characters may have been encoded in %xy form, where xy is
+ * the ASCII hex code for character 16x+y, and a pointer to
+ * a second string containing one or more characters which
+ * should be unescaped if escaped in the first string.
+ * The first string is converted in place, as it will never grow.
+ */
+char *HTUnEscapeSome(char *str,
+ const char *do_trans)
+{
+ char *p = str;
+ char *q = str;
+ char testcode;
+
+ if (p == NULL || *p == '\0' || do_trans == NULL || *do_trans == '\0')
+ return str;
+
+ while (*p != '\0') {
+ if (*p == HEX_ESCAPE &&
+ p[1] && p[2] && /* tests shouldn't be needed, but.. */
+ isxdigit(UCH(p[1])) &&
+ isxdigit(UCH(p[2])) &&
+ (testcode = (char) FROMASCII(from_hex(p[1]) * 16 +
+ from_hex(p[2]))) && /* %00 no good */
+ StrChr(do_trans, testcode)) { /* it's one of the ones we want */
+ *q++ = testcode;
+ p += 3;
+ } else {
+ *q++ = *p++;
+ }
+ }
+
+ *q = '\0';
+ return str;
+
+} /* HTUnEscapeSome */
+/* *INDENT-OFF* */
+static const unsigned char crfc[96] =
+
+/* Bit 0 xalpha -- need "quoting"
+ * Bit 1 xpalpha -- need \escape if quoted
+ */
+ /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */
+ { 1,0,3,0,0,0,0,0,1,1,0,0,1,0,1,0, /* 2x !"#$%&'()*+,-./ */
+ 0,0,0,0,0,0,0,0,0,0,1,1,1,0,1,0, /* 3x 0123456789:;<=>? */
+ 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 4x @ABCDEFGHIJKLMNO */
+ 0,0,0,0,0,0,0,0,0,0,0,1,2,1,0,0, /* 5X PQRSTUVWXYZ[\]^_ */
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 6x `abcdefghijklmno */
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3 }; /* 7X pqrstuvwxyz{|}~ DEL */
+/* *INDENT-ON* */
+
+#define ASCII_TAB '\011'
+#define ASCII_LF '\012'
+#define ASCII_CR '\015'
+#define ASCII_SPC '\040'
+#define ASCII_BAK '\134'
+
+/*
+ * Turn a string which is not a RFC 822 token into a quoted-string. - KW
+ * The "quoted" parameter tells whether we need the beginning/ending quote
+ * marks. If not, the caller will provide them -TD
+ */
+void HTMake822Word(char **str,
+ int quoted)
+{
+ const char *p;
+ char *q;
+ char *result;
+ unsigned char a;
+ unsigned added = 0;
+
+ if (isEmpty(*str)) {
+ StrAllocCopy(*str, quoted ? "\"\"" : "");
+ return;
+ }
+ for (p = *str; *p; p++) {
+ a = UCH(TOASCII(*p)); /* S/390 -- gil -- 0240 */
+ if (a < 32 || a >= 128 ||
+ ((crfc[a - 32]) & 1)) {
+ if (!added)
+ added = 2;
+ if (a >= 160 || a == '\t')
+ continue;
+ if (a == '\r' || a == '\n')
+ added += 2;
+ else if ((a & 127) < 32 || ((crfc[a - 32]) & 2))
+ added++;
+ }
+ }
+ if (!added)
+ return;
+ result = AlloCopy(p, *str, added + 1);
+ if (result == NULL)
+ outofmem(__FILE__, "HTMake822Word");
+
+ q = result;
+ if (quoted)
+ *q++ = '"';
+ /*
+ * Having converted the character to ASCII, we can't use symbolic
+ * escape codes, since they're in the host character set, which
+ * is not necessarily ASCII. Thus we use octal escape codes instead.
+ * -- gil (Paul Gilmartin) <pg@sweng.stortek.com>
+ */
+ /* S/390 -- gil -- 0268 */
+ for (p = *str; *p; p++) {
+ a = UCH(TOASCII(*p));
+ if ((a != ASCII_TAB) &&
+ ((a & 127) < ASCII_SPC ||
+ (a < 128 && ((crfc[a - 32]) & 2))))
+ *q++ = ASCII_BAK;
+ *q++ = *p;
+ if (a == ASCII_LF ||
+ (a == ASCII_CR && (TOASCII(*(p + 1)) != ASCII_LF)))
+ *q++ = ' ';
+ }
+ if (quoted)
+ *q++ = '"';
+ *q = '\0'; /* Terminate */
+ FREE(*str);
+ *str = result;
+}
diff --git a/WWW/Library/Implementation/HTParse.h b/WWW/Library/Implementation/HTParse.h
new file mode 100644
index 0000000..49b40b3
--- /dev/null
+++ b/WWW/Library/Implementation/HTParse.h
@@ -0,0 +1,212 @@
+/*
+ * $LynxId: HTParse.h,v 1.26 2021/07/05 20:56:50 tom Exp $
+ * HTParse: URL parsing in the WWW Library
+ * HTPARSE
+ *
+ * This module of the WWW library contains code to parse URLs and various
+ * related things.
+ * Implemented by HTParse.c .
+ */
+#ifndef HTPARSE_H
+#define HTPARSE_H
+
+#ifndef HTUTILS_H
+#include <HTUtils.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define RFC_3986_UNRESERVED(c) (isalnum(UCH(c)) || strchr("-._~", UCH(c)) != 0)
+#define RFC_3986_GEN_DELIMS(c) ((c) != 0 && strchr(":/?#[]@", UCH(c)) != 0)
+#define RFC_3986_SUB_DELIMS(c) ((c) != 0 && strchr("!$&'()*+,;=", UCH(c)) != 0)
+
+/*
+ * The following are flag bits which may be ORed together to form
+ * a number to give the 'wanted' argument to HTParse.
+ */
+#define PARSE_ACCESS 16
+#define PARSE_HOST 8
+#define PARSE_PATH 4
+#define PARSE_ANCHOR 2
+#define PARSE_PUNCTUATION 1
+#define PARSE_ALL 31
+#define PARSE_ALL_WITHOUT_ANCHOR (PARSE_ALL ^ PARSE_ANCHOR)
+/*
+ * Additional flag bits for more details on components already
+ * covered by the above. The PARSE_PATH above doesn't really
+ * strictly refer to the path component in the sense of the URI
+ * specs only, but rather to that combined with a possible query
+ * component. - kw
+ */
+#define PARSE_STRICTPATH 32
+#define PARSE_QUERY 64
+/*
+ * The following are valid mask values. The terms are the BNF names
+ * in the URL document.
+ */
+#define URL_XALPHAS UCH(1)
+#define URL_XPALPHAS UCH(2)
+#define URL_PATH UCH(4)
+
+#ifdef USE_IDN2
+ typedef enum {
+ LYidna2003 = 1,
+ LYidna2008,
+ LYidnaTR46,
+ LYidnaCompat
+ } HTIdnaModes;
+
+ extern int LYidnaMode;
+#endif
+
+/* Strip white space off a string. HTStrip()
+ * -------------------------------
+ *
+ * On exit,
+ * Return value points to first non-white character, or to 0 if none.
+ * All trailing white space is OVERWRITTEN with zero.
+ */
+ extern char *HTStrip(char *s);
+
+/*
+ * Parse a port number
+ * -------------------
+ *
+ * On entry,
+ * host A pointer to hostname possibly followed by port
+ *
+ * On exit,
+ * returns A pointer to the ":" before the port
+ * sets the port number via the pointer portp.
+ */
+ extern char *HTParsePort(char *host, int *portp);
+
+/* Parse a Name relative to another name. HTParse()
+ * --------------------------------------
+ *
+ * This returns those parts of a name which are given (and requested)
+ * substituting bits from the related name where necessary.
+ *
+ * On entry,
+ * aName A filename given
+ * relatedName A name relative to which aName is to be parsed
+ * wanted A mask for the bits which are wanted.
+ *
+ * On exit,
+ * returns A pointer to a malloc'd string which MUST BE FREED
+ */
+ extern char *HTParse(const char *aName,
+ const char *relatedName,
+ int wanted);
+
+/* HTParseAnchor(), fast HTParse() specialization
+ * ----------------------------------------------
+ *
+ * On exit,
+ * returns A pointer within input string (probably to its end '\0')
+ */
+ extern const char *HTParseAnchor(const char *aName);
+
+/* Simplify a filename. HTSimplify()
+ * --------------------
+ *
+ * A unix-style file is allowed to contain the sequence xxx/../ which may
+ * be replaced by "" , and the sequence "/./" which may be replaced by "/".
+ * Simplification helps us recognize duplicate filenames.
+ */
+ extern void HTSimplify(char *filename, BOOL absolute);
+
+/* Make Relative Name. HTRelative()
+ * -------------------
+ *
+ * This function creates and returns a string which gives an expression of
+ * one address as related to another. Where there is no relation, an absolute
+ * address is returned.
+ *
+ * On entry,
+ * Both names must be absolute, fully qualified names of nodes
+ * (no anchor bits)
+ *
+ * On exit,
+ * The return result points to a newly allocated name which, if
+ * parsed by HTParse relative to relatedName, will yield aName.
+ * The caller is responsible for freeing the resulting name later.
+ *
+ */
+ extern char *HTRelative(const char *aName,
+ const char *relatedName);
+
+/* Escape undesirable characters using % HTEscape()
+ * -------------------------------------
+ *
+ * This function takes a pointer to a string in which
+ * some characters may be unacceptable are unescaped.
+ * It returns a string which has these characters
+ * represented by a '%' character followed by two hex digits.
+ *
+ * Unlike HTUnEscape(), this routine returns a malloc'd string.
+ */
+ extern char *HTEscape(const char *str,
+ unsigned mask);
+
+/* Escape unsafe characters using % HTEscapeUnsafe()
+ * --------------------------------
+ *
+ * This function takes a pointer to a string in which
+ * some characters may be that may be unsafe are unescaped.
+ * It returns a string which has these characters
+ * represented by a '%' character followed by two hex digits.
+ *
+ * Unlike HTUnEscape(), this routine returns a malloc'd string.
+ */
+ extern char *HTEscapeUnsafe(const char *str);
+
+/* Escape undesirable characters using % but space to +. HTEscapeSP()
+ * -----------------------------------------------------
+ *
+ * This function takes a pointer to a string in which
+ * some characters may be unacceptable are unescaped.
+ * It returns a string which has these characters
+ * represented by a '%' character followed by two hex digits,
+ * except that spaces are converted to '+' instead of %2B.
+ *
+ * Unlike HTUnEscape(), this routine returns a malloc'd string.
+ */
+ extern char *HTEscapeSP(const char *str,
+ unsigned mask);
+
+/* Decode %xx escaped characters. HTUnEscape()
+ * ------------------------------
+ *
+ * This function takes a pointer to a string in which some
+ * characters may have been encoded in %xy form, where xy is
+ * the acsii hex code for character 16x+y.
+ * The string is converted in place, as it will never grow.
+ */
+ extern char *HTUnEscape(char *str);
+
+/* Decode some %xx escaped characters. HTUnEscapeSome()
+ * ----------------------------------- Klaus Weide
+ * (kweide@tezcat.com)
+ * This function takes a pointer to a string in which some
+ * characters may have been encoded in %xy form, where xy is
+ * the acsii hex code for character 16x+y, and a pointer to
+ * a second string containing one or more characters which
+ * should be unescaped if escaped in the first string.
+ * The first string is converted in place, as it will never grow.
+ */
+ extern char *HTUnEscapeSome(char *str,
+ const char *do_trans);
+
+/*
+ * Turn a string which is not a RFC 822 token into a quoted-string. - KW
+ */
+ extern void HTMake822Word(char **str,
+ int quoted);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* HTPARSE_H */
diff --git a/WWW/Library/Implementation/HTPlain.c b/WWW/Library/Implementation/HTPlain.c
new file mode 100644
index 0000000..ff52731
--- /dev/null
+++ b/WWW/Library/Implementation/HTPlain.c
@@ -0,0 +1,601 @@
+/*
+ * $LynxId: HTPlain.c,v 1.61 2020/01/21 22:05:46 tom Exp $
+ *
+ * Plain text object HTWrite.c
+ * =================
+ *
+ * This version of the stream object just writes to a socket.
+ * The socket is assumed open and left open.
+ *
+ * Bugs:
+ * strings written must be less than buffer size.
+ */
+
+#define HTSTREAM_INTERNAL 1
+
+#include <HTUtils.h>
+#include <LYCharVals.h> /* S/390 -- gil -- 0288 */
+
+#include <HTPlain.h>
+
+#include <HTChunk.h>
+#include <HText.h>
+#include <HTStyle.h>
+#define Lynx_HTML_Handler
+#include <HTML.h> /* styles[] */
+
+#define BUFFER_SIZE 4096; /* Tradeoff */
+
+#include <HTMLDTD.h>
+#include <HTCJK.h>
+#include <UCMap.h>
+#include <UCDefs.h>
+#include <UCAux.h>
+
+#include <LYCharSets.h>
+#include <LYStrings.h>
+#include <LYLeaks.h>
+
+static int HTPlain_lastraw = -1;
+static int HTPlain_bs_pending = 0; /* 1:bs 2:underline 3:underline+bs - kw */
+
+/* HTML Object
+ * -----------
+ */
+struct _HTStream {
+ const HTStreamClass *isa;
+ HText *text;
+ /*
+ * The node_anchor UCInfo and handle for the input (PARSER) stage. - FM
+ */
+ LYUCcharset *inUCI;
+ int inUCLYhndl;
+ /*
+ * The node_anchor UCInfo and handle for the output (HTEXT) stage. - FM
+ */
+ LYUCcharset *outUCI;
+ int outUCLYhndl;
+
+ UTFDecodeState U;
+ UCTransParams T;
+};
+
+static char replace_buf[64]; /* buffer for replacement strings */
+
+static void HTPlain_getChartransInfo(HTStream *me, HTParentAnchor *anchor)
+{
+ if (me->inUCLYhndl < 0) {
+ HTAnchor_copyUCInfoStage(anchor, UCT_STAGE_PARSER, UCT_STAGE_MIME,
+ UCT_SETBY_PARSER);
+ me->inUCLYhndl = HTAnchor_getUCLYhndl(anchor, UCT_STAGE_PARSER);
+ }
+ if (me->outUCLYhndl < 0) {
+ int chndl = HTAnchor_getUCLYhndl(anchor, UCT_STAGE_HTEXT);
+
+ if (chndl < 0) {
+ chndl = current_char_set;
+ HTAnchor_setUCInfoStage(anchor, chndl,
+ UCT_STAGE_HTEXT, UCT_SETBY_DEFAULT);
+ }
+ HTAnchor_setUCInfoStage(anchor, chndl,
+ UCT_STAGE_HTEXT, UCT_SETBY_DEFAULT);
+ me->outUCLYhndl = HTAnchor_getUCLYhndl(anchor, UCT_STAGE_HTEXT);
+ }
+ me->inUCI = HTAnchor_getUCInfoStage(anchor, UCT_STAGE_PARSER);
+ me->outUCI = HTAnchor_getUCInfoStage(anchor, UCT_STAGE_HTEXT);
+}
+
+/* Write the buffer out to the socket
+ * ----------------------------------
+ */
+
+/*_________________________________________________________________________
+ *
+ * A C T I O N R O U T I N E S
+ */
+
+static void HTPlain_write(HTStream *me, const char *s,
+ int l);
+
+/* Character handling
+ * ------------------
+ */
+static void HTPlain_put_character(HTStream *me, int c)
+{
+#ifdef REMOVE_CR_ONLY
+ /*
+ * Throw away \r's.
+ */
+ if (c != '\r') {
+ HText_appendCharacter(me->text, c);
+ }
+#else
+ /*
+ * See HTPlain_write() for explanations of the following code (we've been
+ * called via HTPlain_put_string() to do for each character of a terminated
+ * string what HTPlain_write() does via a while loop for each character in
+ * a stream of given length). - FM
+ */
+ if ((HTPlain_lastraw == '\r') && c == '\n') {
+ HTPlain_lastraw = -1;
+ return;
+ }
+ if (c == '\b' || c == '_' || HTPlain_bs_pending) {
+ char temp[1];
+
+ temp[0] = (char) c;
+ HTPlain_write(me, temp, 1);
+ return;
+ }
+ HTPlain_lastraw = UCH(c);
+ if (c == '\r') {
+ HText_appendCharacter(me->text, '\n');
+ } else if (TOASCII(UCH(c)) >= 127) { /* S/390 -- gil -- 0305 */
+ char temp[1];
+
+ temp[0] = (char) c;
+ /*
+ * For now, don't repeat everything here that has been done below - KW
+ */
+ HTPlain_write(me, temp, 1);
+ } else if (IS_CJK_TTY) {
+ HText_appendCharacter(me->text, c);
+ } else if (TOASCII(UCH(c)) >= 127 && TOASCII(UCH(c)) < 161 &&
+ HTPassHighCtrlRaw) {
+ HText_appendCharacter(me->text, c);
+#if CH_NBSP < 127
+ } else if (UCH(c) == CH_NBSP) { /* S/390 -- gil -- 0341 */
+ HText_appendCharacter(me->text, ' ');
+#endif
+#if CH_SHY < 127
+ } else if (UCH(c) == CH_SHY) {
+ return;
+#endif
+ } else if ((UCH(c) >= ' ' && TOASCII(UCH(c)) < 127) ||
+ c == '\n' || c == '\t') {
+ HText_appendCharacter(me->text, c);
+ }
+#endif /* REMOVE_CR_ONLY */
+}
+
+/* String handling
+ * ---------------
+ *
+ */
+static void HTPlain_put_string(HTStream *me, const char *s)
+{
+#ifdef REMOVE_CR_ONLY
+ HText_appendText(me->text, s);
+#else
+ const char *p;
+
+ if (s == NULL)
+ return;
+ for (p = s; *p; p++) {
+ HTPlain_put_character(me, *p);
+ }
+#endif /* REMOVE_CR_ONLY */
+}
+
+/*
+ * Entry function for displayed text/plain and WWW_SOURCE strings. - FM
+ * ---------------------------------------------------------------
+ */
+static void HTPlain_write(HTStream *me, const char *s, int l)
+{
+ const char *p;
+ const char *e = s + l;
+ int c;
+ unsigned c_unsign;
+ BOOL chk;
+ UCode_t code, uck = -1;
+ int saved_char_in = '\0';
+
+ for (p = s; p < e; p++) {
+#ifdef REMOVE_CR_ONLY
+ /*
+ * Append the whole string, but remove any \r's. - FM
+ */
+ if (*p != '\r') {
+ HText_appendCharacter(me->text, *p);
+ }
+#else
+ if (*p == '\b') {
+ if (HTPlain_lastraw >= UCH(' ') &&
+ HTPlain_lastraw != '\r' && HTPlain_lastraw != '\n') {
+ if (!HTPlain_bs_pending) {
+ HTPlain_bs_pending = 1;
+ continue;
+ } else if (HTPlain_bs_pending == 2) {
+ HTPlain_bs_pending = 3;
+ continue;
+ }
+ }
+ if (HTPlain_bs_pending >= 2)
+ HText_appendCharacter(me->text, '_');
+ HTPlain_bs_pending = 0;
+ } else if (*p == '_') {
+ if (!HTPlain_bs_pending) {
+ HTPlain_bs_pending = 2;
+ HTPlain_lastraw = UCH(*p);
+ continue;
+ }
+ }
+
+ /*
+ * Try to handle lone LFs, CRLFs and lone CRs as newline, and to deal
+ * with control, ASCII, and 8-bit characters based on best guesses of
+ * what's appropriate. - FM
+ */
+ if ((HTPlain_lastraw == '\r') && *p == '\n') {
+ HTPlain_lastraw = -1;
+ continue;
+ }
+
+ if (HTPlain_bs_pending &&
+ !(UCH(*p) >= ' ' && *p != '\r' && *p != '\n' &&
+ (HTPlain_lastraw == UCH(*p) ||
+ HTPlain_lastraw == UCH('_') ||
+ *p == '_'))) {
+ if (HTPlain_bs_pending >= 2)
+ HText_appendCharacter(me->text, '_');
+ HTPlain_bs_pending = 0;
+ } else if (HTPlain_bs_pending == 1) {
+ HTPlain_bs_pending = 0;
+ continue; /* ignore last two of "X\bX" or "X\b_" - kw */
+ } else if (HTPlain_bs_pending == 3) {
+ if (*p == '_') {
+ HTPlain_bs_pending = 2;
+ continue; /* ignore last two of "_\b_" - kw */
+ } else {
+ HTPlain_bs_pending = 0;
+ /* ignore first two of "_\bX" - kw */
+ }
+ } else if (HTPlain_bs_pending == 2) {
+ HText_appendCharacter(me->text, '_');
+ if (*p == '_')
+ continue; /* keep second of "__" pending - kw */
+ HTPlain_bs_pending = 0;
+ } else {
+ HTPlain_bs_pending = 0;
+ }
+ HTPlain_lastraw = UCH(*p);
+ if (*p == '\r') {
+ HText_appendCharacter(me->text, '\n');
+ continue;
+ }
+ /*
+ * Make sure the character is handled as Unicode whenever that's
+ * appropriate. - FM
+ */
+ c = *p;
+ c_unsign = UCH(c);
+ code = (UCode_t) c_unsign;
+ saved_char_in = '\0';
+ /*
+ * Combine any UTF-8 multibytes into Unicode to check for special
+ * characters. - FM, TD
+ */
+ if (me->T.decode_utf8) {
+ switch (HTDecodeUTF8(&(me->U), &c, &code)) {
+ case dUTF8_ok:
+ if (code < 256) {
+ c = FROMASCII((char) code);
+ c_unsign = UCH(c);
+ }
+ break;
+ case dUTF8_err:
+ code = UCS_REPL;
+ strcpy(me->U.utf_buf, "\357\277\275");
+ me->U.utf_buf_p = (me->U.utf_buf + 3);
+ break;
+ case dUTF8_more:
+ continue;
+ }
+ }
+ /*
+ * Convert characters from non-UTF-8 charsets to Unicode (if
+ * appropriate). - FM
+ */
+ if (!(me->T.decode_utf8 &&
+ UCH(*p) > 127)) {
+ if (me->T.trans_to_uni &&
+ (TOASCII(code) >= LYlowest_eightbit[me->inUCLYhndl] || /* S/390 -- gil -- 0389 */
+ (code < ' ' && code != 0 &&
+ me->T.trans_C0_to_uni))) {
+ /*
+ * Convert the octet to Unicode. - FM
+ */
+ code = (UCode_t) UCTransToUni(c, me->inUCLYhndl);
+ if (code > 0) {
+ saved_char_in = c;
+ if (code < 256) {
+ c = FROMASCII((char) code);
+ c_unsign = UCH(c);
+ }
+ }
+ } else if (code < 32 && code != 0 &&
+ me->T.trans_C0_to_uni) {
+ /*
+ * Quote from SGML.c:
+ * "This else if may be too ugly to keep. - KW"
+ */
+ if (me->T.trans_from_uni &&
+ (((code = UCTransToUni(c, me->inUCLYhndl)) >= 32) ||
+ (me->T.transp &&
+ (code = UCTransToUni(c, me->inUCLYhndl)) > 0))) {
+ saved_char_in = c;
+ if (code < 256) {
+ c = FROMASCII((char) code);
+ c_unsign = UCH(c);
+ }
+ } else {
+ uck = -1;
+ if (me->T.transp) {
+ uck = UCTransCharStr(replace_buf, 60, c,
+ me->inUCLYhndl,
+ me->inUCLYhndl, NO);
+ }
+ if (!me->T.transp || uck < 0) {
+ uck = UCTransCharStr(replace_buf, 60, c,
+ me->inUCLYhndl,
+ me->outUCLYhndl, YES);
+ }
+ if (uck == 0) {
+ continue;
+ } else if (uck < 0) {
+ me->U.utf_buf[0] = '\0';
+ } else {
+ c = replace_buf[0];
+ if (c && replace_buf[1]) {
+ HText_appendText(me->text, replace_buf);
+ continue;
+ }
+ }
+ me->U.utf_buf[0] = '\0';
+ code = UCH(c);
+ } /* Next line end of ugly stuff for C0. - KW */
+ } else {
+ me->U.utf_buf[0] = '\0';
+ code = UCH(c);
+ }
+ }
+ /*
+ * At this point we have either code in Unicode (and c in latin1 if
+ * code is in the latin1 range), or code and c will have to be passed
+ * raw.
+ */
+
+ /*
+ * If CJK mode is on, we'll assume the document matches the user's
+ * display character set, and if not, the user should toggle off
+ * raw/CJK mode to reload. - FM
+ */
+ if (IS_CJK_TTY) {
+ HText_appendCharacter(me->text, c);
+
+#define PASSHICTRL (me->T.transp || \
+ code >= LYlowest_eightbit[me->inUCLYhndl])
+#define PASS8859SPECL me->T.pass_160_173_raw
+#define PASSHI8BIT (HTPassEightBitRaw || \
+ (me->T.do_8bitraw && !me->T.trans_from_uni))
+ /*
+ * If HTPassHighCtrlRaw is set (e.g., for KOI8-R) assume the
+ * document matches and pass 127-160 8-bit characters. If it
+ * doesn't match, the user should toggle raw/CJK mode off. - FM
+ */
+ } else if (TOASCII(code) >= 127 && TOASCII(code) < 161 && /* S/390 -- gil -- 0427 */
+ PASSHICTRL && PASS8859SPECL) {
+ HText_appendCharacter(me->text, c);
+ } else if (code == CH_SHY && PASS8859SPECL) {
+ HText_appendCharacter(me->text, c);
+ /*
+ * If neither HTPassHighCtrlRaw nor CJK is set, play it safe and
+ * treat 160 (nbsp) as an ASCII space (32). - FM
+ */
+ } else if (code == CH_NBSP) {
+ HText_appendCharacter(me->text, ' ');
+ /*
+ * If neither HTPassHighCtrlRaw nor CJK is set, play it safe and
+ * ignore 173 (shy). - FM
+ * Now only ignore it for color style, which doesn't handle it
+ * anyway. Otherwise pass it on as LY_SOFT_HYPHEN and let HText
+ * deal with it. It should be either ignored, or displayed as a
+ * hyphen if it was indeed at the end of a line. Well it should.
+ * - kw
+ */
+ } else if (code == CH_SHY) {
+#ifndef USE_COLOR_STYLE
+ HText_appendCharacter(me->text, LY_SOFT_HYPHEN);
+#endif
+ continue;
+ /*
+ * If we get to here, pass the displayable ASCII characters. - FM
+ */
+ } else if ((code >= ' ' && code != UCS_REPL && TOASCII(code) < 127) ||
+ (PASSHI8BIT &&
+ c >= LYlowest_eightbit[me->outUCLYhndl]) ||
+ *p == '\n' || *p == '\t') {
+ HText_appendCharacter(me->text, c);
+ /*
+ * Use an ASCII space (32) for ensp, emsp or thinsp. - FM
+ */
+ } else if (code == 8194 || code == 8195 || code == 8201) {
+ HText_appendCharacter(me->text, ' ');
+ /*
+ * If we want the raw character, pass it now. - FM
+ */
+ } else if (me->T.use_raw_char_in && saved_char_in) {
+ HText_appendCharacter(me->text, saved_char_in);
+/******************************************************************
+ * I. LATIN-1 OR UCS2 TO DISPLAY CHARSET
+ ******************************************************************/
+ } else if ((chk = (BOOL) (me->T.trans_from_uni && code >= 160)) &&
+ (uck = UCTransUniChar(code,
+ me->outUCLYhndl)) >= ' ' && /* S/390 -- gil -- 0464 */
+ uck < 256) {
+ CTRACE((tfp, "UCTransUniChar returned 0x%.2" PRI_UCode_t
+ ":'%c'.\n",
+ uck, FROMASCII(UCH(uck))));
+ HText_appendCharacter(me->text, ((char) (uck & 0xff)));
+ } else if (chk &&
+ (uck == -4 ||
+ (me->T.repl_translated_C0 && uck > 0 && uck < ' ')) && /* S/390 -- gil -- 0481 */
+ /*
+ * Not found; look for replacement string.
+ */
+ (uck = UCTransUniCharStr(replace_buf, 60, code,
+ me->outUCLYhndl, 0) >= 0)) {
+ /*
+ * No further tests for valididy - assume that whoever defined
+ * replacement strings knew what she was doing.
+ */
+ HText_appendText(me->text, replace_buf);
+ /*
+ * If we get to here, and should have translated, translation has
+ * failed so far.
+ */
+ } else if (chk && TOASCII(code) > 127 && me->T.output_utf8) { /* S/390 -- gil -- 0498 */
+ /*
+ * We want UTF-8 output, so do it now. - FM
+ */
+ if (*me->U.utf_buf) {
+ HText_appendText(me->text, me->U.utf_buf);
+ me->U.utf_buf[0] = '\0';
+ me->U.utf_buf_p = me->U.utf_buf;
+ } else if (UCConvertUniToUtf8(code, replace_buf)) {
+ HText_appendText(me->text, replace_buf);
+ } else {
+ /*
+ * Out of luck, so use the UHHH notation (ugh). - gil
+ */
+ /* S/390 -- gil -- 0517 */
+ sprintf(replace_buf, "U%.2lX", (unsigned long) TOASCII(code));
+ HText_appendText(me->text, replace_buf);
+ }
+ /*
+ * If we don't actually want the character, make it safe and output
+ * that now. - FM
+ */
+ } else if ((c_unsign > 0 &&
+ (int) c_unsign < LYlowest_eightbit[me->outUCLYhndl]) ||
+ (me->T.trans_from_uni && !HTPassEightBitRaw)) {
+ /*
+ * If we do not have the "7-bit approximations" as our output
+ * character set (in which case we did it already) seek a
+ * translation for that. Otherwise, or if the translation fails,
+ * use UHHH notation. - FM
+ */
+ if ((chk = (BOOL) (me->outUCLYhndl !=
+ UCGetLYhndl_byMIME("us-ascii"))) &&
+ (uck = UCTransUniChar(code,
+ UCGetLYhndl_byMIME("us-ascii")))
+ >= ' ' && TOASCII(uck) < 127) { /* S/390 -- gil -- 0535 */
+ /*
+ * Got an ASCII character (yippey). - FM
+ */
+ c = FROMASCII((char) uck);
+ HText_appendCharacter(me->text, c);
+ } else if ((chk && uck == -4) &&
+ (uck = UCTransUniCharStr(replace_buf,
+ 60, code,
+ UCGetLYhndl_byMIME("us-ascii"),
+ 0) >= 0)) {
+ /*
+ * Got a replacement string (yippey). - FM
+ */
+ HText_appendText(me->text, replace_buf);
+ } else if (code == 8204 || code == 8205) {
+ /*
+ * Ignore 8204 (zwnj) or 8205 (zwj), if we get to here. - FM
+ */
+ CTRACE((tfp, "HTPlain_write: Ignoring '%" PRI_UCode_t "'.\n", code));
+ } else if (code == 8206 || code == 8207) {
+ /*
+ * Ignore 8206 (lrm) or 8207 (rlm), if we get to here. - FM
+ */
+ CTRACE((tfp, "HTPlain_write: Ignoring '%" PRI_UCode_t "'.\n", code));
+ } else {
+ /*
+ * Out of luck, so use the UHHH notation (ugh). - FM
+ */
+ /* do not print UHHH for now
+ sprintf(replace_buf, "U%.2lX", code);
+ HText_appendText(me->text, replace_buf);
+ */
+ }
+ /*
+ * If we get to here and have a monobyte character, pass it. - FM
+ */
+ } else if (c_unsign != 0 && c_unsign < 256) {
+ HText_appendCharacter(me->text, c);
+ }
+#endif /* REMOVE_CR_ONLY */
+ }
+}
+
+/* Free an HTML object
+ * -------------------
+ *
+ * Note that the SGML parsing context is freed, but the created object is
+ * not, as it takes on an existence of its own unless explicitly freed.
+ */
+static void HTPlain_free(HTStream *me)
+{
+ if (HTPlain_bs_pending >= 2)
+ HText_appendCharacter(me->text, '_');
+ FREE(me);
+}
+
+/* End writing
+*/
+static void HTPlain_abort(HTStream *me, HTError e GCC_UNUSED)
+{
+ HTPlain_free(me);
+}
+
+/* Structured Object Class
+ * -----------------------
+ */
+static const HTStreamClass HTPlain =
+{
+ "PlainPresenter",
+ HTPlain_free,
+ HTPlain_abort,
+ HTPlain_put_character, HTPlain_put_string, HTPlain_write,
+};
+
+/* New object
+ * ----------
+ */
+HTStream *HTPlainPresent(HTPresentation *pres GCC_UNUSED, HTParentAnchor *anchor,
+ HTStream *sink GCC_UNUSED)
+{
+
+ HTStream *me = (HTStream *) malloc(sizeof(*me));
+
+ if (me == NULL)
+ outofmem(__FILE__, "HTPlain_new");
+
+ me->isa = &HTPlain;
+
+ HTPlain_lastraw = -1;
+
+ me->U.utf_count = 0;
+ me->U.utf_char = 0;
+ me->U.utf_buf[0] = me->U.utf_buf[6] = me->U.utf_buf[7] = '\0';
+ me->U.utf_buf_p = me->U.utf_buf;
+ me->outUCLYhndl = HTAnchor_getUCLYhndl(anchor, UCT_STAGE_HTEXT);
+ me->inUCLYhndl = HTAnchor_getUCLYhndl(anchor, UCT_STAGE_PARSER);
+ HTPlain_getChartransInfo(me, anchor);
+ UCSetTransParams(&me->T,
+ me->inUCLYhndl, me->inUCI,
+ me->outUCLYhndl,
+ HTAnchor_getUCInfoStage(anchor, UCT_STAGE_HTEXT));
+
+ me->text = HText_new(anchor);
+ HText_setStyle(me->text, LYstyles(HTML_XMP));
+ HText_beginAppend(me->text);
+
+ return (HTStream *) me;
+}
diff --git a/WWW/Library/Implementation/HTPlain.h b/WWW/Library/Implementation/HTPlain.h
new file mode 100644
index 0000000..24fd669
--- /dev/null
+++ b/WWW/Library/Implementation/HTPlain.h
@@ -0,0 +1,21 @@
+/* /Net/dxcern/userd/timbl/hypertext/WWW/Library/Implementation/HTPlain.html
+ PLAIN TEXT OBJECT
+
+ */
+#ifndef HTPLAIN_H
+#define HTPLAIN_H
+
+#include <HTStream.h>
+#include <HTAnchor.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+ extern HTStream *HTPlainPresent(HTPresentation *pres,
+ HTParentAnchor *anchor,
+ HTStream *sink);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* HTPLAIN_H */
diff --git a/WWW/Library/Implementation/HTRules.c b/WWW/Library/Implementation/HTRules.c
new file mode 100644
index 0000000..91ac93d
--- /dev/null
+++ b/WWW/Library/Implementation/HTRules.c
@@ -0,0 +1,704 @@
+/*
+ * $LynxId: HTRules.c,v 1.47 2017/07/02 19:45:22 tom Exp $
+ *
+ * Configuration manager for Hypertext Daemon HTRules.c
+ * ==========================================
+ *
+ *
+ * History:
+ * 3 Jun 91 Written TBL
+ * 10 Aug 91 Authorisation added after Daniel Martin (pass, fail)
+ * Rule order in file changed
+ * Comments allowed with # on 1st char of rule line
+ * 17 Jun 92 Bug fix: pass and fail failed if didn't contain '*' TBL
+ * 1 Sep 93 Bug fix: no memory check - Nathan Torkington
+ * BYTE_ADDRESSING removed - Arthur Secret
+ * 11 Sep 93 MD Changed %i into %d in debug printf.
+ * VMS does not recognize %i.
+ * Bug Fix: in case of PASS, only one parameter to printf.
+ * 19 Sep 93 AL Added Access Authorization stuff.
+ * 1 Nov 93 AL Added htbin.
+ * 25 May 99 KW Added redirect for lynx.
+ *
+ */
+
+#include <HTUtils.h>
+
+/* (c) CERN WorldWideWeb project 1990,91. See Copyright.html for details */
+#include <HTRules.h>
+
+#include <HTFile.h>
+#include <LYLeaks.h>
+#include <HTAAProt.h>
+
+#define LINE_LENGTH 256
+
+typedef struct _rule {
+ struct _rule *next;
+ HTRuleOp op;
+ char *pattern;
+ char *equiv;
+ char *condition_op; /* as strings - may be inefficient, */
+ char *condition; /* but this is not for a server - kw */
+} rule;
+
+#ifndef NO_RULES
+
+#include <HTTP.h> /* for redirecting_url, indirectly HTPermitRedir - kw */
+#include <LYGlobalDefs.h> /* for LYUserSpecifiedURL - kw */
+#include <LYStrings.h> /* for LYscanFloat */
+#include <LYUtils.h> /* for LYFixCursesOn - kw */
+#include <HTAlert.h>
+
+/* Global variables
+ * ----------------
+ */
+char *HTBinDir = NULL; /* Physical /htbin directory path. */
+
+ /* In future this should not be global. */
+char *HTSearchScript = NULL; /* Search script name. */
+
+/* Module-wide variables
+ * ---------------------
+ */
+
+static rule *rules = 0; /* Pointer to first on list */
+
+#ifndef PUT_ON_HEAD
+static rule *rule_tail = 0; /* Pointer to last on list */
+#endif
+
+/* Add rule to the list HTAddRule()
+ * --------------------
+ *
+ * On entry,
+ * pattern points to 0-terminated string containing a single "*"
+ * equiv points to the equivalent string with * for the
+ * place where the text matched by * goes.
+ * On exit,
+ * returns 0 if success, -1 if error.
+ */
+
+int HTAddRule(HTRuleOp op, const char *pattern,
+ const char *equiv,
+ const char *cond_op,
+ const char *cond)
+{ /* BYTE_ADDRESSING removed and memory check - AS - 1 Sep 93 */
+ rule *temp;
+ char *pPattern = NULL;
+
+ temp = typecalloc(rule);
+ if (temp == NULL)
+ outofmem(__FILE__, "HTAddRule");
+
+ if (equiv) { /* Two operands */
+ char *pEquiv = NULL;
+
+ StrAllocCopy(pEquiv, equiv);
+ temp->equiv = pEquiv;
+ } else {
+ temp->equiv = 0;
+ }
+ if (cond_op) {
+ StrAllocCopy(temp->condition_op, cond_op);
+ StrAllocCopy(temp->condition, cond);
+ }
+ StrAllocCopy(pPattern, pattern);
+ temp->pattern = pPattern;
+ temp->op = op;
+
+ if (equiv) {
+ CTRACE((tfp, "Rule: For `%s' op %d `%s'", pattern, (int) op, equiv));
+ } else {
+ CTRACE((tfp, "Rule: For `%s' op %d", pattern, (int) op));
+ }
+ if (cond_op) {
+ CTRACE((tfp, "\t%s %s\n", cond_op, NONNULL(cond)));
+ } else {
+ CTRACE((tfp, "\n"));
+ }
+
+ if (!rules) {
+#ifdef LY_FIND_LEAKS
+ atexit(HTClearRules);
+#endif
+ }
+#ifdef PUT_ON_HEAD
+ temp->next = rules;
+ rules = temp;
+#else
+ temp->next = 0;
+ if (rule_tail)
+ rule_tail->next = temp;
+ else
+ rules = temp;
+ rule_tail = temp;
+#endif
+
+ return 0;
+}
+
+/* Clear all rules HTClearRules()
+ * ---------------
+ *
+ * On exit,
+ * There are no rules
+ *
+ * See also
+ * HTAddRule()
+ */
+void HTClearRules(void)
+{
+ while (rules) {
+ rule *temp = rules;
+
+ rules = temp->next;
+ FREE(temp->pattern);
+ FREE(temp->equiv);
+ FREE(temp->condition_op);
+ FREE(temp->condition);
+ FREE(temp);
+ }
+#ifndef PUT_ON_HEAD
+ rule_tail = 0;
+#endif
+}
+
+static BOOL rule_cond_ok(rule * r)
+{
+ BOOL result;
+
+ if (!r->condition_op)
+ return YES;
+ if (strcmp(r->condition_op, "if") && strcmp(r->condition_op, "unless")) {
+ CTRACE((tfp, "....... rule ignored, unrecognized `%s'!\n",
+ r->condition_op));
+ return NO;
+ }
+ if (!strcmp(r->condition, "redirected"))
+ result = (BOOL) (redirection_attempts > 0);
+ else if (!strcmp(r->condition, "userspec"))
+ result = LYUserSpecifiedURL;
+ else {
+ CTRACE((tfp, "....... rule ignored, unrecognized `%s %s'!\n",
+ r->condition_op, NONNULL(r->condition)));
+ return NO;
+ }
+ if (!strcmp(r->condition_op, "if"))
+ return result;
+ else
+ return (BOOL) (!result);
+
+}
+
+/* Translate by rules HTTranslate()
+ * ------------------
+ *
+ * The most recently defined rules are applied first.
+ *
+ * On entry,
+ * required points to a string whose equivalent value is needed
+ * On exit,
+ * returns the address of the equivalent string allocated from
+ * the heap which the CALLER MUST FREE. If no translation
+ * occurred, then it is a copy of the original.
+ * NEW FEATURES:
+ * When a "protect" or "defprot" rule is matched,
+ * a call to HTAA_setCurrentProtection() or
+ * HTAA_setDefaultProtection() is made to notify
+ * the Access Authorization module that the file is
+ * protected, and so it knows how to handle it.
+ * -- AL
+ */
+char *HTTranslate(const char *required)
+{
+ rule *r;
+ char *current = NULL;
+ char *msgtmp = NULL;
+ const char *pMsg;
+ int proxy_none_flag = 0;
+ int permitredir_flag = 0;
+
+ StrAllocCopy(current, required);
+
+ HTAA_clearProtections(); /* Reset from previous call -- AL */
+
+ for (r = rules; r; r = r->next) {
+ char *p = r->pattern;
+ int m = 0; /* Number of characters matched against wildcard */
+ const char *q = current;
+
+ for (; *p && *q; p++, q++) { /* Find first mismatch */
+ if (*p != *q)
+ break;
+ }
+
+ if (*p == '*') { /* Match up to wildcard */
+ m = (int) strlen(q) - (int) strlen(p + 1); /* Amount to match to wildcard */
+ if (m < 0)
+ continue; /* tail is too short to match */
+ if (0 != strcmp(q + m, p + 1))
+ continue; /* Tail mismatch */
+ } else
+ /* Not wildcard */ if (*p != *q)
+ continue; /* plain mismatch: go to next rule */
+
+ if (!rule_cond_ok(r)) /* check condition, next rule if false - kw */
+ continue;
+
+ switch (r->op) { /* Perform operation */
+
+ case HT_DefProt:
+ case HT_Protect:
+#ifdef ACCESS_AUTH
+ {
+ char *local_copy = NULL;
+ char *p2;
+ char *eff_ids = NULL;
+ char *prot_file = NULL;
+
+ CTRACE((tfp, "HTRule: `%s' matched %s %s: `%s'\n",
+ current,
+ (r->op == HT_Protect ? "Protect" : "DefProt"),
+ "rule, setup",
+ (r->equiv ? r->equiv :
+ (r->op == HT_Protect ? "DEFAULT" : "NULL!!"))));
+
+ if (r->equiv) {
+ StrAllocCopy(local_copy, r->equiv);
+ p2 = local_copy;
+ prot_file = HTNextField(&p2);
+ eff_ids = HTNextField(&p2);
+ }
+
+ if (r->op == HT_Protect)
+ HTAA_setCurrentProtection(current, prot_file, eff_ids);
+ else
+ HTAA_setDefaultProtection(current, prot_file, eff_ids);
+
+ FREE(local_copy);
+
+ /* continue translating rules */
+ }
+#endif /* ACCESS_AUTH */
+ break;
+
+ case HT_UserMsg: /* Produce message immediately */
+ LYFixCursesOn("show rule message:");
+ HTUserMsg2((r->equiv ? r->equiv : "Rule: %s"), current);
+ break;
+ case HT_InfoMsg: /* Produce messages immediately */
+ case HT_Progress:
+ case HT_Alert:
+ LYFixCursesOn("show rule message:"); /* and fall through */
+ /* FALLTHRU */
+ case HT_AlwaysAlert:
+ pMsg = r->equiv ? r->equiv :
+ (r->op == HT_AlwaysAlert) ? "%s" : "Rule: %s";
+ if (StrChr(pMsg, '%')) {
+ HTSprintf0(&msgtmp, pMsg, current);
+ pMsg = msgtmp;
+ }
+ switch (r->op) { /* Actually produce message */
+ case HT_InfoMsg:
+ HTInfoMsg(pMsg);
+ break;
+ case HT_Progress:
+ HTProgress(pMsg);
+ break;
+ case HT_Alert:
+ HTAlert(pMsg);
+ break;
+ case HT_AlwaysAlert:
+ HTAlwaysAlert("Rule alert:", pMsg);
+ break;
+ default:
+ break;
+ }
+ FREE(msgtmp);
+ break;
+
+ case HT_PermitRedir: /* Set special flag */
+ permitredir_flag = 1;
+ CTRACE((tfp, "HTRule: Mark for redirection permitted\n"));
+ break;
+
+ case HT_Pass: /* Authorised */
+ if (!r->equiv) {
+ if (proxy_none_flag) {
+ char *temp = NULL;
+
+ StrAllocCopy(temp, "NoProxy=");
+ StrAllocCat(temp, current);
+ FREE(current);
+ current = temp;
+ }
+ CTRACE((tfp, "HTRule: Pass `%s'\n", current));
+ return current;
+ }
+ /* FALLTHRU */
+
+ case HT_Map:
+ case HT_Redirect:
+ case HT_RedirectPerm:
+ if (*p == *q) { /* End of both strings, no wildcard */
+ CTRACE((tfp, "For `%s' using `%s'\n", current, r->equiv));
+ StrAllocCopy(current, r->equiv); /* use entire translation */
+ } else {
+ char *ins = StrChr(r->equiv, '*'); /* Insertion point */
+
+ if (ins) { /* Consistent rule!!! */
+ char *temp = NULL;
+
+ HTSprintf0(&temp, "%.*s%.*s%s",
+ (int) (ins - r->equiv),
+ r->equiv,
+ m,
+ q,
+ ins + 1);
+ CTRACE((tfp, "For `%s' using `%s'\n",
+ current, temp));
+ FREE(current);
+ current = temp; /* Use this */
+
+ } else { /* No insertion point */
+ char *temp = NULL;
+
+ StrAllocCopy(temp, r->equiv);
+ CTRACE((tfp, "For `%s' using `%s'\n",
+ current, temp));
+ FREE(current);
+ current = temp; /* Use this */
+ } /* If no insertion point exists */
+ }
+ if (r->op == HT_Pass) {
+ if (proxy_none_flag) {
+ char *temp = NULL;
+
+ StrAllocCopy(temp, "NoProxy=");
+ StrAllocCat(temp, current);
+ FREE(current);
+ current = temp;
+ }
+ CTRACE((tfp, "HTRule: ...and pass `%s'\n",
+ current));
+ return current;
+ } else if (r->op == HT_Redirect) {
+ CTRACE((tfp, "HTRule: ...and redirect to `%s'\n",
+ current));
+ redirecting_url = current;
+ HTPermitRedir = (BOOL) (permitredir_flag == 1);
+ return (char *) 0;
+ } else if (r->op == HT_RedirectPerm) {
+ CTRACE((tfp, "HTRule: ...and redirect like 301 to `%s'\n",
+ current));
+ redirecting_url = current;
+ permanent_redirection = TRUE;
+ HTPermitRedir = (BOOL) (permitredir_flag == 1);
+ return (char *) 0;
+ }
+ break;
+
+ case HT_UseProxy:
+ if (r->equiv && 0 == strcasecomp(r->equiv, "none")) {
+ CTRACE((tfp, "For `%s' will not use proxy\n", current));
+ proxy_none_flag = 1;
+ } else if (proxy_none_flag) {
+ CTRACE((tfp, "For `%s' proxy server ignored: %s\n",
+ current,
+ NONNULL(r->equiv)));
+ } else {
+ char *temp = NULL;
+
+ StrAllocCopy(temp, "Proxied=");
+ StrAllocCat(temp, r->equiv);
+ StrAllocCat(temp, current);
+ CTRACE((tfp, "HTRule: proxy server found: %s\n",
+ NONNULL(r->equiv)));
+ FREE(current);
+ return temp;
+ }
+ break;
+
+ case HT_Invalid:
+ case HT_Fail: /* Unauthorised */
+ CTRACE((tfp, "HTRule: *** FAIL `%s'\n", current));
+ FREE(current);
+ return (char *) 0;
+ } /* if tail matches ... switch operation */
+
+ } /* loop over rules */
+
+ if (proxy_none_flag) {
+ char *temp = NULL;
+
+ StrAllocCopy(temp, "NoProxy=");
+ StrAllocCat(temp, current);
+ FREE(current);
+ return temp;
+ }
+
+ return current;
+}
+
+/* Load one line of configuration
+ * ------------------------------
+ *
+ * Call this, for example, to load a X resource with config info.
+ *
+ * returns 0 OK, < 0 syntax error.
+ */
+int HTSetConfiguration(char *config)
+{
+ HTRuleOp op;
+ char *line = NULL;
+ char *pointer = NULL;
+ char *word1;
+ const char *word2;
+ const char *word3;
+ const char *cond_op = NULL;
+ const char *cond = NULL;
+ float quality, secs, secs_per_byte;
+ long maxbytes;
+ int status;
+
+ StrAllocCopy(line, config);
+ if (line != NULL) {
+ char *p = line;
+
+ /* Chop off comments */
+ while ((p = StrChr(p, '#'))) {
+ if (p == line || isspace(UCH(*(p - 1)))) {
+ *p = 0;
+ break;
+ } else {
+ p++;
+ }
+ }
+ }
+ pointer = line;
+ word1 = HTNextField(&pointer);
+ if (!word1) {
+ FREE(line);
+ return 0;
+ }; /* Comment only or blank */
+
+ word2 = HTNextField(&pointer);
+
+ if (0 == strcasecomp(word1, "defprot") ||
+ 0 == strcasecomp(word1, "protect"))
+ word3 = pointer; /* The rest of the line to be parsed by AA module */
+ else
+ word3 = HTNextField(&pointer); /* Just the next word */
+
+ if (!word2) {
+ fprintf(stderr, "HTRule: %s %s\n", RULE_NEEDS_DATA, line);
+ FREE(line);
+ return -2; /*syntax error */
+ }
+
+ if (0 == strcasecomp(word1, "suffix")) {
+ char *encoding = HTNextField(&pointer);
+
+ status = 0;
+ if (pointer)
+ status = LYscanFloat(pointer, &quality);
+
+ HTSetSuffix(word2, word3,
+ encoding ? encoding : "binary",
+ status >= 1 ? quality : (float) 1.0);
+
+ } else if (0 == strcasecomp(word1, "presentation")) {
+ status = 0;
+ if (pointer) {
+ const char *temp = pointer;
+
+ if (LYscanFloat2(&temp, &quality)) {
+ status = 1;
+ if (LYscanFloat2(&temp, &secs)) {
+ status = 2;
+ if (LYscanFloat2(&temp, &secs_per_byte)) {
+ status = 3;
+ if (sscanf(temp, "%ld", &maxbytes)) {
+ status = 4;
+ }
+ }
+ }
+ }
+ }
+
+ HTSetPresentation(word2, word3, NULL,
+ status >= 1 ? quality : 1.0,
+ status >= 2 ? secs : 0.0,
+ status >= 3 ? secs_per_byte : 0.0,
+ status >= 4 ? maxbytes : 0,
+ mediaCFG);
+
+ } else if (0 == strncasecomp(word1, "htbin", 5) ||
+ 0 == strncasecomp(word1, "bindir", 6)) {
+ StrAllocCopy(HTBinDir, word2); /* Physical /htbin location */
+
+ } else if (0 == strncasecomp(word1, "search", 6)) {
+ StrAllocCopy(HTSearchScript, word2); /* Search script name */
+
+ } else {
+ op = 0 == strcasecomp(word1, "map") ? HT_Map
+ : 0 == strcasecomp(word1, "pass") ? HT_Pass
+ : 0 == strcasecomp(word1, "fail") ? HT_Fail
+ : 0 == strcasecomp(word1, "redirect") ? HT_Redirect
+ : 0 == strncasecomp(word1, "redirectperm", 12) ? HT_RedirectPerm
+ : 0 == strcasecomp(word1, "redirecttemp") ? HT_Redirect
+ : 0 == strcasecomp(word1, "permitredirection") ? HT_PermitRedir
+ : 0 == strcasecomp(word1, "useproxy") ? HT_UseProxy
+ : 0 == strcasecomp(word1, "alert") ? HT_Alert
+ : 0 == strcasecomp(word1, "alwaysalert") ? HT_AlwaysAlert
+ : 0 == strcasecomp(word1, "progress") ? HT_Progress
+ : 0 == strcasecomp(word1, "usermsg") ? HT_UserMsg
+ : 0 == strcasecomp(word1, "infomsg") ? HT_InfoMsg
+ : 0 == strcasecomp(word1, "defprot") ? HT_DefProt
+ : 0 == strcasecomp(word1, "protect") ? HT_Protect
+ : HT_Invalid;
+ if (op == HT_Invalid) {
+ fprintf(stderr, "HTRule: %s '%s'\n", RULE_INCORRECT, config);
+ } else {
+ switch (op) {
+ case HT_Fail: /* never a or other 2nd parameter */
+ case HT_PermitRedir:
+ cond_op = word3;
+ if (cond_op && *cond_op) {
+ word3 = NULL;
+ cond = HTNextField(&pointer);
+ }
+ break;
+
+ case HT_Pass: /* possibly a URL2 */
+ if (word3 && (!strcasecomp(word3, "if") ||
+ !strcasecomp(word3, "unless"))) {
+ cond_op = word3;
+ word3 = NULL;
+ cond = HTNextField(&pointer);
+ break;
+ }
+ /* else fall through */
+ case HT_Map: /* always a URL2 (or other 2nd parameter) */
+ case HT_Redirect:
+ case HT_RedirectPerm:
+ case HT_UseProxy:
+ cond_op = HTNextField(&pointer);
+ /* check for extra status word in "Redirect" */
+ if (op == HT_Redirect && 0 == strcasecomp(word1, "redirect") &&
+ cond_op &&
+ strcasecomp(cond_op, "if") &&
+ strcasecomp(cond_op, "unless")) {
+ if (0 == strcmp(word2, "301") ||
+ 0 == strcasecomp(word2, "permanent")) {
+ op = HT_RedirectPerm;
+ } else if (!(0 == strcmp(word2, "302") ||
+ 0 == strcmp(word2, "303") ||
+ 0 == strcasecomp(word2, "temp") ||
+ 0 == strcasecomp(word2, "seeother"))) {
+ CTRACE((tfp, "Rule: Ignoring `%s' in Redirect\n", word2));
+ }
+ word2 = word3;
+ word3 = cond_op; /* cond_op isn't condition op after all */
+ cond_op = HTNextField(&pointer);
+ }
+ if (cond_op && *cond_op)
+ cond = HTNextField(&pointer);
+ break;
+
+ case HT_Progress:
+ case HT_InfoMsg:
+ case HT_UserMsg:
+ case HT_Alert:
+ case HT_AlwaysAlert:
+ cond_op = HTNextField(&pointer);
+ if (cond_op && *cond_op)
+ cond = HTNextField(&pointer);
+ if (word3) { /* Fix string with too may %s - kw */
+ const char *cp = word3;
+ char *cp1, *cp2;
+
+ while ((cp1 = StrChr(cp, '%'))) {
+ if (cp1[1] == '\0') {
+ *cp1 = '\0';
+ break;
+ } else if (cp1[1] == '%') {
+ cp = cp1 + 2;
+ continue;
+ } else
+ while ((cp2 = StrChr(cp1 + 2, '%'))) {
+ if (cp2[1] == '\0') {
+ *cp2 = '\0';
+ break;
+ } else if (cp2[1] == '%') {
+ cp1 = cp2;
+ } else {
+ *cp2 = '?'; /* replace bad % */
+ cp1 = cp2;
+ }
+ }
+ break;
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+ if (cond_op && cond && *cond && !strcasecomp(cond_op, "unless")) {
+ cond_op = "unless";
+ } else if (cond_op && cond && *cond &&
+ !strcasecomp(cond_op, "if")) {
+ cond_op = "if";
+ } else if (cond_op || cond) {
+ fprintf(stderr, "HTRule: %s '%s'\n", RULE_INCORRECT, config);
+ FREE(line); /* syntax error, condition is a mess - kw */
+ return -2; /* NB unrecognized cond passes here - kw */
+ }
+ if (cond && !strncasecomp(cond, "redirected", (int) strlen(cond))) {
+ cond = "redirected"; /* recognized, canonical case - kw */
+ } else if (cond && strlen(cond) >= 8 &&
+ !strncasecomp(cond, "userspecified", (int) strlen(cond))) {
+ cond = "userspec"; /* also allow abbreviation - kw */
+ }
+ HTAddRule(op, word2, word3, cond_op, cond);
+ }
+ }
+ FREE(line);
+ return 0;
+}
+
+/* Load the rules from a file HTLoadRules()
+ * --------------------------
+ *
+ * On entry,
+ * Rules can be in any state
+ * On exit,
+ * Any existing rules will have been kept.
+ * Any new rules will have been loaded.
+ * Returns 0 if no error, 0 if error!
+ *
+ * Bugs:
+ * The strings may not contain spaces.
+ */
+
+int HTLoadRules(const char *filename)
+{
+ FILE *fp = fopen(filename, TXT_R);
+ char line[LINE_LENGTH + 1];
+
+ if (!fp) {
+ CTRACE((tfp, "HTRules: Can't open rules file %s\n", filename));
+ return -1; /* File open error */
+ }
+ for (;;) {
+ if (!fgets(line, LINE_LENGTH + 1, fp))
+ break; /* EOF or error */
+ (void) HTSetConfiguration(line);
+ }
+ fclose(fp);
+ return 0; /* No error or syntax errors ignored */
+}
+
+#endif /* NO_RULES */
diff --git a/WWW/Library/Implementation/HTRules.h b/WWW/Library/Implementation/HTRules.h
new file mode 100644
index 0000000..9bb8593
--- /dev/null
+++ b/WWW/Library/Implementation/HTRules.h
@@ -0,0 +1,169 @@
+/* Configuration Manager for libwww
+ * CONFIGURATION MANAGER
+ *
+ * Author Tim Berners-Lee/CERN. Public domain. Please mail changes to
+ * timbl@info.cern.ch.
+ *
+ * The configuration information loaded includes tables (file suffixes,
+ * presentation methods) in other modules. The most likely routines needed by
+ * developers will be:
+ *
+ * HTSetConfiguration to load configuration information.
+ *
+ * HTLoadRules to load a whole file of configuration information
+ *
+ * HTTranslate to translate a URL using the rule table.
+ *
+ */
+#ifndef HTRULE_H
+#define HTRULE_H
+
+#ifndef HTUTILS_H
+#include <HTUtils.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+ typedef enum {
+ HT_Invalid,
+ HT_Map,
+ HT_Pass,
+ HT_Fail,
+ HT_DefProt,
+ HT_Protect,
+ HT_Progress,
+ HT_InfoMsg,
+ HT_UserMsg,
+ HT_Alert,
+ HT_AlwaysAlert,
+ HT_Redirect,
+ HT_RedirectPerm,
+ HT_PermitRedir,
+ HT_UseProxy
+ } HTRuleOp;
+
+#ifndef NO_RULES
+
+/*
+
+Server Side Script Execution
+
+ If a URL starts with /htbin/ it is understood to mean a script execution request on
+ server. This feature needs to be turned on by setting HTBinDir by the htbin rule.
+ Index searching is enabled by setting HTSearchScript into the name of script in BinDir
+ doing the actual search by search rule (BinDir must also be set in this case, of
+ course).
+
+ */
+
+ extern char *HTBinDir; /* Physical /htbin location */
+ extern char *HTSearchScript; /* Search script name */
+
+/*
+
+HTAddRule: Add rule to the list
+
+ ON ENTRY,
+
+ pattern points to 0-terminated string containing a single "*"
+
+ equiv points to the equivalent string with * for the place where the
+ text matched by * goes; or to other 2nd parameter
+ meaning depends on op).
+
+ cond_op, additional condition for applying rule; cond_op should
+ cond be either NULL (no additional condition), or one of
+ the strings "if" or "unless"; if cond_op is not NULL,
+ cond should point to a recognized condition keyword
+ (as a string) such as "userspec", "redirected".
+
+ ON EXIT,
+
+ returns 0 if success, -1 if error.
+
+ Note that if BYTE_ADDRESSING is set, the three blocks required are allocated and
+ deallocated as one. This will save time and storage, when malloc's allocation units are
+ large.
+
+ */
+ extern int HTAddRule(HTRuleOp op, const char *pattern,
+ const char *equiv,
+ const char *cond_op,
+ const char *cond);
+
+/*
+
+HTClearRules: Clear all rules
+
+ ON EXIT,
+
+ Rule file There are no rules
+
+ */
+
+ extern void HTClearRules(void);
+
+/*
+
+HTTranslate: Translate by rules
+
+ */
+
+/*
+
+ ON ENTRY,
+
+ required points to a string whose equivalent value is needed
+
+ ON EXIT,
+
+ returns the address of the equivalent string allocated from the heap
+ which the CALLER MUST FREE. If no translation occurred, then it is
+ a copy of the original.
+
+ */
+ extern char *HTTranslate(const char *required);
+
+/*
+
+HTSetConfiguration: Load one line of configuration information
+
+ ON ENTRY,
+
+ config is a string in the syntax of a rule file line.
+
+ This routine may be used for loading configuration information from sources other than
+ the rule file, for example INI files for X resources.
+
+ */
+ extern int HTSetConfiguration(char *config);
+
+/*
+
+HtLoadRules: Load the rules from a file
+
+ ON ENTRY,
+
+ Rule table Rules can be in any state
+
+ ON EXIT,
+
+ Rule table Any existing rules will have been kept. Any new rules will have
+ been loaded on top, so as to be tried first.
+
+ Returns 0 if no error.
+
+ */
+
+ extern int HTLoadRules(const char *filename);
+
+/*
+
+ */
+
+#endif /* NO_RULES */
+#ifdef __cplusplus
+}
+#endif
+#endif /* HTRULE_H */
diff --git a/WWW/Library/Implementation/HTStream.h b/WWW/Library/Implementation/HTStream.h
new file mode 100644
index 0000000..a753266
--- /dev/null
+++ b/WWW/Library/Implementation/HTStream.h
@@ -0,0 +1,69 @@
+/*
+ * $LynxId: HTStream.h,v 1.16 2011/06/11 12:08:40 tom Exp $
+ *
+ * The Stream class definition -- libwww
+ STREAM OBJECT DEFINITION
+
+ A Stream object is something which accepts a stream of text.
+
+ The creation methods will vary on the type of Stream Object. All creation
+ methods return a pointer to the stream type below.
+
+ As you can see, but the methods used to write to the stream and close it are
+ pointed to be the object itself.
+
+ */
+#ifndef HTSTREAM_H
+#define HTSTREAM_H
+
+#ifndef HTUTILS_H
+#include <HTUtils.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+ typedef struct _HTStream HTStream;
+
+/*
+
+ These are the common methods of all streams. They should be
+ self-explanatory.
+
+ */
+ typedef struct _HTStreamClass {
+
+ const char *name; /* Just for diagnostics */
+
+ void (*_free) (HTStream *me);
+
+ void (*_abort) (HTStream *me, HTError e);
+
+ void (*put_character) (HTStream *me, int ch);
+
+ void (*put_string) (HTStream *me, const char *str);
+
+ void (*put_block) (HTStream *me, const char *str, int len);
+
+ } HTStreamClass;
+
+#ifndef HTSTREAM_INTERNAL
+ struct _HTStream {
+ HTStreamClass *isa;
+ };
+#endif
+/*
+
+ Generic Error Stream
+
+ The Error stream simply signals an error on all output methods.
+ This can be used to stop a stream as soon as data arrives, for
+ example from the network.
+
+ */
+ extern HTStream *HTErrorStream(void);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* HTSTREAM_H */
diff --git a/WWW/Library/Implementation/HTString.c b/WWW/Library/Implementation/HTString.c
new file mode 100644
index 0000000..3cb8358
--- /dev/null
+++ b/WWW/Library/Implementation/HTString.c
@@ -0,0 +1,1464 @@
+/*
+ * $LynxId: HTString.c,v 1.82 2022/03/12 12:19:10 Gisle.Vanem Exp $
+ *
+ * Case-independent string comparison HTString.c
+ *
+ * Original version came with listserv implementation.
+ * Version TBL Oct 91 replaces one which modified the strings.
+ * 02-Dec-91 (JFG) Added stralloccopy and stralloccat
+ * 23 Jan 92 (TBL) Changed strallocc* to 8 char HTSAC* for VM and suchlike
+ * 6 Oct 92 (TBL) Moved WWW_TraceFlag in here to be in library
+ * 15 Nov 98 (TD) Added HTSprintf.
+ */
+
+#include <HTUtils.h>
+#include <HTFile.h>
+
+#include <LYLeaks.h>
+#include <LYUtils.h>
+#include <LYStrings.h>
+
+#ifdef USE_IGNORE_RC
+int ignore_unused;
+#endif
+
+#ifndef NO_LYNX_TRACE
+BOOLEAN WWW_TraceFlag = 0; /* Global trace flag for ALL W3 code */
+int WWW_TraceMask = 0; /* Global trace flag for ALL W3 code */
+#endif
+
+#ifdef _WINDOWS
+#undef VC
+#define VC "2.14FM"
+#endif
+
+#ifndef VC
+#define VC "2.14"
+#endif /* !VC */
+
+const char *HTLibraryVersion = VC; /* String for help screen etc */
+
+/*
+ * strcasecomp8 is a variant of strcasecomp (below)
+ * ------------ -----------
+ * but uses 8bit upper/lower case information
+ * from the current display charset.
+ * It returns 0 if exact match.
+ */
+int strcasecomp8(const char *a,
+ const char *b)
+{
+ const char *p = a;
+ const char *q = b;
+
+ for (; *p && *q; p++, q++) {
+ int diff = UPPER8(*p, *q);
+
+ if (diff)
+ return diff;
+ }
+ if (*p)
+ return 1; /* p was longer than q */
+ if (*q)
+ return -1; /* p was shorter than q */
+ return 0; /* Exact match */
+}
+
+/*
+ * strncasecomp8 is a variant of strncasecomp (below)
+ * ------------- ------------
+ * but uses 8bit upper/lower case information
+ * from the current display charset.
+ * It returns 0 if exact match.
+ */
+int strncasecomp8(const char *a,
+ const char *b,
+ int n)
+{
+ const char *p = a;
+ const char *q = b;
+
+ for (;; p++, q++) {
+ int diff;
+
+ if (p == (a + n))
+ return 0; /* Match up to n characters */
+ if (!(*p && *q))
+ return (*p - *q);
+ diff = UPPER8(*p, *q);
+ if (diff)
+ return diff;
+ }
+ /*NOTREACHED */
+}
+
+#ifndef VM /* VM has these already it seems */
+/* Strings of any length
+ * ---------------------
+ */
+int strcasecomp(const char *a,
+ const char *b)
+{
+ const char *p = a;
+ const char *q = b;
+
+ for (; *p && *q; p++, q++) {
+ int diff = TOLOWER(*p) - TOLOWER(*q);
+
+ if (diff)
+ return diff;
+ }
+ if (*p)
+ return 1; /* p was longer than q */
+ if (*q)
+ return -1; /* p was shorter than q */
+ return 0; /* Exact match */
+}
+
+/* With count limit
+ * ----------------
+ */
+int strncasecomp(const char *a,
+ const char *b,
+ int n)
+{
+ const char *p = a;
+ const char *q = b;
+
+ for (;; p++, q++) {
+ int diff;
+
+ if (p == (a + n))
+ return 0; /* Match up to n characters */
+ if (!(*p && *q))
+ return (*p - *q);
+ diff = TOLOWER(*p) - TOLOWER(*q);
+ if (diff)
+ return diff;
+ }
+ /*NOTREACHED */
+}
+#endif /* VM */
+
+#define end_component(p) (*(p) == '.' || *(p) == '\0')
+
+#ifdef DEBUG_ASTERISK
+#define SHOW_ASTERISK CTRACE
+#else
+#define SHOW_ASTERISK(p) /* nothing */
+#endif
+
+#define SHOW_ASTERISK_NUM(a,b,c) \
+ SHOW_ASTERISK((tfp, "test @%d, '%s' vs '%s' (%d)\n", __LINE__, a,b,c))
+
+#define SHOW_ASTERISK_TXT(a,b,c) \
+ SHOW_ASTERISK((tfp, "test @%d, '%s' vs '%s' %s\n", __LINE__, a,b,c))
+
+/*
+ * Compare names as described in RFC 2818: ignore case, allow wildcards.
+ * Return zero on a match, nonzero on mismatch -TD
+ *
+ * From RFC 2818:
+ * Names may contain the wildcard character * which is considered to match any
+ * single domain name component or component fragment. E.g., *.a.com matches
+ * foo.a.com but not bar.foo.a.com. f*.com matches foo.com but not bar.com.
+ */
+int strcasecomp_asterisk(const char *a, const char *b)
+{
+ const char *p;
+ int result = 0;
+ int done = FALSE;
+
+ while (!result && !done) {
+ SHOW_ASTERISK_TXT(a, b, "main");
+ if (*a == '*') {
+ p = b;
+ for (;;) {
+ SHOW_ASTERISK_TXT(a, p, "loop");
+ if (end_component(p)) {
+ if (end_component(a + 1)) {
+ b = p - 1;
+ result = 0;
+ } else {
+ result = 1;
+ }
+ break;
+ } else if (strcasecomp_asterisk(a + 1, p)) {
+ ++p;
+ } else {
+ b = p - 1;
+ result = 0; /* found a match starting at 'p' */
+ done = TRUE;
+ break;
+ }
+ }
+ SHOW_ASTERISK_NUM(a, b, result);
+ } else if (*b == '*') {
+ result = strcasecomp_asterisk(b, a);
+ SHOW_ASTERISK_NUM(a, b, result);
+ done = (result == 0);
+ } else if (*a == '\0' || *b == '\0') {
+ result = (*a != *b);
+ SHOW_ASTERISK_NUM(a, b, result);
+ break;
+ } else if (TOLOWER(UCH(*a)) != TOLOWER(UCH(*b))) {
+ result = 1;
+ SHOW_ASTERISK_NUM(a, b, result);
+ break;
+ }
+ ++a;
+ ++b;
+ }
+ return result;
+}
+
+#ifdef DEBUG_ASTERISK
+void mismatch_asterisk(void)
+{
+ /* *INDENT-OFF* */
+ static struct {
+ const char *a;
+ const char *b;
+ int code;
+ } table[] = {
+ { "foo.bar", "*.*", 0 },
+ { "foo.bar", "*.b*", 0 },
+ { "foo.bar", "*.ba*", 0 },
+ { "foo.bar", "*.bar*", 0 },
+ { "foo.bar", "*.*bar*", 0 },
+ { "foo.bar", "*.*.", 1 },
+ { "foo.bar", "fo*.b*", 0 },
+ { "*oo.bar", "fo*.b*", 0 },
+ { "*oo.bar.com", "fo*.b*", 1 },
+ { "*oo.bar.com", "fo*.b*m", 1 },
+ { "*oo.bar.com", "fo*.b*.c*", 0 },
+ };
+ /* *INDENT-ON* */
+
+ unsigned n;
+ int code;
+
+ CTRACE((tfp, "mismatch_asterisk testing\n"));
+ for (n = 0; n < TABLESIZE(table); ++n) {
+ CTRACE((tfp, "-------%d\n", n));
+ code = strcasecomp_asterisk(table[n].a, table[n].b);
+ if (code != table[n].code) {
+ CTRACE((tfp, "mismatch_asterisk '%s' '%s' got %d, want %d\n",
+ table[n].a, table[n].b, code, table[n].code));
+ }
+ }
+}
+#endif
+
+#ifdef NOT_ASCII
+
+/* Case-insensitive with ASCII collating sequence
+ * ----------------
+ */
+int AS_casecomp(const char *p,
+ const char *q)
+{
+ int diff;
+
+ for (;; p++, q++) {
+ if (!(*p && *q))
+ return (UCH(*p) - UCH(*q));
+ diff = TOASCII(TOLOWER(*p))
+ - TOASCII(TOLOWER(*q));
+ if (diff)
+ return diff;
+ }
+ /*NOTREACHED */
+}
+
+/* With count limit and ASCII collating sequence
+ * ----------------
+ * AS_cmp uses n == -1 to compare indefinite length.
+ */
+int AS_ncmp(const char *p,
+ const char *q,
+ unsigned int n)
+{
+ const char *a = p;
+ int diff;
+
+ for (; (unsigned) (p - a) < n; p++, q++) {
+ if (!(*p && *q))
+ return (UCH(*p) - UCH(*q));
+ diff = TOASCII(*p)
+ - TOASCII(*q);
+ if (diff)
+ return diff;
+ }
+ return 0; /* Match up to n characters */
+}
+#endif /* NOT_ASCII */
+
+/* Allocate a new copy of a string, and returns it
+*/
+char *HTSACopy(char **dest,
+ const char *src)
+{
+ if (src != 0) {
+ if (src != *dest) {
+ size_t size = strlen(src) + 1;
+
+ FREE(*dest);
+ *dest = (char *) malloc(size);
+ if (*dest == NULL)
+ outofmem(__FILE__, "HTSACopy");
+ MemCpy(*dest, src, size);
+ }
+ } else {
+ FREE(*dest);
+ }
+ return *dest;
+}
+
+/* String Allocate and Concatenate
+*/
+char *HTSACat(char **dest,
+ const char *src)
+{
+ if (src && *src && (src != *dest)) {
+ if (*dest) {
+ size_t length = strlen(*dest);
+
+ *dest = (char *) realloc(*dest, length + strlen(src) + 1);
+ if (*dest == NULL)
+ outofmem(__FILE__, "HTSACat");
+ strcpy(*dest + length, src);
+ } else {
+ *dest = (char *) malloc(strlen(src) + 1);
+ if (*dest == NULL)
+ outofmem(__FILE__, "HTSACat");
+ strcpy(*dest, src);
+ }
+ }
+ return *dest;
+}
+
+/* optimized for heavily realloc'd strings, store length inside */
+
+#define EXTRA_TYPE size_t /* type we use for length */
+#define EXTRA_SIZE sizeof(void *) /* alignment >= sizeof(EXTRA_TYPE) */
+
+void HTSAFree_extra(char *s)
+{
+ free(s - EXTRA_SIZE);
+}
+
+/* never shrink */
+char *HTSACopy_extra(char **dest,
+ const char *src)
+{
+ if (src != 0) {
+ size_t srcsize = strlen(src) + 1;
+ EXTRA_TYPE size = 0;
+
+ if (*dest != 0) {
+ size = *(EXTRA_TYPE *) (void *) ((*dest) - EXTRA_SIZE);
+ }
+ if ((*dest == 0) || (size < srcsize)) {
+ FREE_extra(*dest);
+ size = srcsize * 2; /* x2 step */
+ *dest = (char *) malloc(size + EXTRA_SIZE);
+ if (*dest == NULL)
+ outofmem(__FILE__, "HTSACopy_extra");
+ *(EXTRA_TYPE *) (void *) (*dest) = size;
+ *dest += EXTRA_SIZE;
+ }
+ MemCpy(*dest, src, srcsize);
+ } else {
+ Clear_extra(*dest);
+ }
+ return *dest;
+}
+
+/* Find next Field
+ * ---------------
+ *
+ * On entry,
+ * *pstr points to a string containing white space separated
+ * field, optionlly quoted.
+ *
+ * On exit,
+ * *pstr has been moved to the first delimiter past the
+ * field
+ * THE STRING HAS BEEN MUTILATED by a 0 terminator
+ *
+ * returns a pointer to the first field
+ */
+char *HTNextField(char **pstr)
+{
+ char *p = *pstr;
+ char *start = NULL; /* start of field */
+
+ if (p != NULL) {
+ while (*p && WHITE(*p))
+ p++; /* Strip white space */
+ if (!*p) {
+ *pstr = p;
+ } else {
+ if (*p == '"') { /* quoted field */
+ p++;
+ start = p;
+ for (; *p && *p != '"'; p++) {
+ if (*p == '\\' && p[1])
+ p++; /* Skip escaped chars */
+ }
+ } else {
+ start = p;
+ while (*p && !WHITE(*p))
+ p++; /* Skip first field */
+ }
+ if (*p)
+ *p++ = '\0';
+ *pstr = p;
+ }
+ }
+ return start;
+}
+
+/* Find next Token
+ * ---------------
+ * Finds the next token in a string
+ * On entry,
+ * *pstr points to a string to be parsed.
+ * delims lists characters to be recognized as delimiters.
+ * If NULL, default is white space "," ";" or "=".
+ * The word can optionally be quoted or enclosed with
+ * chars from bracks.
+ * Comments surrounded by '(' ')' are filtered out
+ * unless they are specifically requested by including
+ * ' ' or '(' in delims or bracks.
+ * bracks lists bracketing chars. Some are recognized as
+ * special, for those give the opening char.
+ * If NULL, defaults to <"> and "<" ">".
+ * found points to location to fill with the ending delimiter
+ * found, or is NULL.
+ *
+ * On exit,
+ * *pstr has been moved to the first delimiter past the
+ * field
+ * THE STRING HAS BEEN MUTILATED by a 0 terminator
+ * found points to the delimiter found unless it was NULL.
+ * Returns a pointer to the first word or NULL on error
+ */
+char *HTNextTok(char **pstr,
+ const char *delims,
+ const char *bracks,
+ char *found)
+{
+ char *p = *pstr;
+ char *start = NULL;
+ BOOL get_blanks, skip_comments;
+ BOOL get_comments;
+ BOOL get_closing_char_too = FALSE;
+ char closer;
+
+ if (isEmpty(pstr))
+ return NULL;
+ if (!delims)
+ delims = " ;,=";
+ if (!bracks)
+ bracks = "<\"";
+
+ get_blanks = (BOOL) (!StrChr(delims, ' ') && !StrChr(bracks, ' '));
+ get_comments = (BOOL) (StrChr(bracks, '(') != NULL);
+ skip_comments = (BOOL) (!get_comments && !StrChr(delims, '(') && !get_blanks);
+#define skipWHITE(c) (!get_blanks && WHITE(c))
+
+ while (*p && skipWHITE(*p))
+ p++; /* Strip white space */
+ if (!*p) {
+ *pstr = p;
+ if (found)
+ *found = '\0';
+ return NULL; /* No first field */
+ }
+ while (1) {
+ /* Strip white space and other delimiters */
+ while (*p && (skipWHITE(*p) || StrChr(delims, *p)))
+ p++;
+ if (!*p) {
+ *pstr = p;
+ if (found)
+ *found = *(p - 1);
+ return NULL; /* No field */
+ }
+
+ if (*p == '(' && (skip_comments || get_comments)) { /* Comment */
+ int comment_level = 0;
+
+ if (get_comments && !start)
+ start = p + 1;
+ for (; *p && (*p != ')' || --comment_level > 0); p++) {
+ if (*p == '(')
+ comment_level++;
+ else if (*p == '"') { /* quoted field within Comment */
+ for (p++; *p && *p != '"'; p++)
+ if (*p == '\\' && *(p + 1))
+ p++; /* Skip escaped chars */
+ if (!*p)
+ break; /* (invalid) end of string found, leave */
+ }
+ if (*p == '\\' && *(p + 1))
+ p++; /* Skip escaped chars */
+ }
+ if (get_comments)
+ break;
+ if (*p)
+ p++;
+ if (get_closing_char_too) {
+ if (!*p || (!StrChr(bracks, *p) && StrChr(delims, *p))) {
+ break;
+ } else
+ get_closing_char_too = (BOOL) (StrChr(bracks, *p) != NULL);
+ }
+ } else if (StrChr(bracks, *p)) { /* quoted or bracketed field */
+ switch (*p) {
+ case '<':
+ closer = '>';
+ break;
+ case '[':
+ closer = ']';
+ break;
+ case '{':
+ closer = '}';
+ break;
+ case ':':
+ closer = ';';
+ break;
+ default:
+ closer = *p;
+ }
+ if (!start)
+ start = ++p;
+ for (; *p && *p != closer; p++)
+ if (*p == '\\' && *(p + 1))
+ p++; /* Skip escaped chars */
+ if (get_closing_char_too) {
+ p++;
+ if (!*p || (!StrChr(bracks, *p) && StrChr(delims, *p))) {
+ break;
+ } else
+ get_closing_char_too = (BOOL) (StrChr(bracks, *p) != NULL);
+ } else
+ break; /* kr95-10-9: needs to stop here */
+ } else { /* Spool field */
+ if (!start)
+ start = p;
+ while (*p && !skipWHITE(*p) && !StrChr(bracks, *p) &&
+ !StrChr(delims, *p))
+ p++;
+ if (*p && StrChr(bracks, *p)) {
+ get_closing_char_too = TRUE;
+ } else {
+ if (*p == '(' && skip_comments) {
+ *pstr = p;
+ HTNextTok(pstr, NULL, "(", found); /* Advance pstr */
+ *p = '\0';
+ if (*pstr && **pstr)
+ (*pstr)++;
+ return start;
+ }
+ break; /* Got it */
+ }
+ }
+ }
+ if (found)
+ *found = *p;
+
+ if (*p)
+ *p++ = '\0';
+ *pstr = p;
+ return start;
+}
+
+static char *HTAlloc(char *ptr, size_t length)
+{
+ if (ptr != 0)
+ ptr = (char *) realloc(ptr, length);
+ else
+ ptr = (char *) malloc(length);
+ if (ptr == 0)
+ outofmem(__FILE__, "HTAlloc");
+ return ptr;
+}
+
+/*
+ * If SAVE_TIME_NOT_SPACE is defined, StrAllocVsprintf will hang on to
+ * its temporary string buffers instead of allocating and freeing them
+ * in each invocation. They only grow and never shrink, and won't be
+ * cleaned up on exit. - kw
+ */
+#if defined(_REENTRANT) || defined(_THREAD_SAFE) || defined(LY_FIND_LEAKS)
+#undef SAVE_TIME_NOT_SPACE
+#endif
+
+/*
+ * Replacement for sprintf, allocates buffer on the fly according to what's
+ * needed for its arguments. Unlike sprintf, this always concatenates to the
+ * destination buffer, so we do not have to provide both flavors.
+ */
+typedef enum {
+ Flags,
+ Width,
+ Prec,
+ Type,
+ Format
+} PRINTF;
+
+#define VA_INTGR(type) ival = (int) va_arg((*ap), type)
+#define VA_FLOAT(type) fval = (double) va_arg((*ap), type)
+#define VA_POINT(type) pval = (char *) va_arg((*ap), type)
+
+#define NUM_WIDTH 10 /* allow for width substituted for "*" in "%*s" */
+ /* also number of chars assumed to be needed in addition
+ to a given precision in floating point formats */
+
+#define GROW_EXPR(n) (((n) * 3) / 2)
+#define GROW_SIZE 256
+
+PUBLIC_IF_FIND_LEAKS char *StrAllocVsprintf(char **pstr,
+ size_t dst_len,
+ const char *fmt,
+ va_list *ap)
+{
+#ifdef HAVE_VASPRINTF
+ /*
+ * Use vasprintf() if we have it, since it is simplest.
+ */
+ char *result = 0;
+ char *temp = 0;
+
+ /* discard old destination if no length was given */
+ if (pstr && !dst_len) {
+ if (*pstr)
+ FREE(*pstr);
+ }
+
+ if (vasprintf(&temp, fmt, *ap) >= 0) {
+ if (dst_len != 0) {
+ size_t src_len = strlen(temp);
+ size_t new_len = dst_len + src_len + 1;
+
+ result = HTAlloc(pstr ? *pstr : 0, new_len);
+ if (result != 0) {
+ strcpy(result + dst_len, temp);
+ }
+ (free) (temp);
+ } else {
+ result = temp;
+ mark_malloced(temp, strlen(temp));
+ }
+ }
+
+ if (pstr != 0)
+ *pstr = result;
+
+ return result;
+#else /* !HAVE_VASPRINTF */
+ /*
+ * If vasprintf() is not available, this works - but does not implement
+ * the POSIX '$' formatting character which may be used in some of the
+ * ".po" files.
+ */
+#ifdef SAVE_TIME_NOT_SPACE
+ static size_t tmp_len = 0;
+ static size_t fmt_len = 0;
+ static char *tmp_ptr = NULL;
+ static char *fmt_ptr = NULL;
+
+#else
+ size_t tmp_len = GROW_SIZE;
+ char *tmp_ptr = 0;
+ char *fmt_ptr;
+#endif /* SAVE_TIME_NOT_SPACE */
+ size_t have, need;
+ char *dst_ptr = pstr ? *pstr : NULL;
+ const char *format = fmt;
+
+ if (isEmpty(fmt))
+ return 0;
+
+ need = strlen(fmt) + 1;
+#ifdef SAVE_TIME_NOT_SPACE
+ if (!fmt_ptr || fmt_len < need * NUM_WIDTH) {
+ fmt_ptr = HTAlloc(fmt_ptr, fmt_len = need * NUM_WIDTH);
+ }
+ if (!tmp_ptr || tmp_len < GROW_SIZE) {
+ tmp_ptr = HTAlloc(tmp_ptr, tmp_len = GROW_SIZE);
+ }
+#else
+ if ((fmt_ptr = malloc(need * NUM_WIDTH)) == 0
+ || (tmp_ptr = malloc(tmp_len)) == 0) {
+ outofmem(__FILE__, "StrAllocVsprintf");
+ }
+#endif /* SAVE_TIME_NOT_SPACE */
+
+ if (dst_ptr == 0) {
+ dst_ptr = HTAlloc(dst_ptr, have = GROW_SIZE + need);
+ } else {
+ have = strlen(dst_ptr) + 1;
+ need += dst_len;
+ if (have < need)
+ dst_ptr = HTAlloc(dst_ptr, have = GROW_SIZE + need);
+ }
+
+ while (*fmt != '\0') {
+ if (*fmt == '%') {
+ static char dummy[] = "";
+ PRINTF state = Flags;
+ char *pval = dummy; /* avoid const-cast */
+ double fval = 0.0;
+ int done = FALSE;
+ int ival = 0;
+ int prec = -1;
+ int type = 0;
+ int used = 0;
+ int width = -1;
+ size_t f = 0;
+
+ fmt_ptr[f++] = *fmt;
+ while (*++fmt != '\0' && !done) {
+ fmt_ptr[f++] = *fmt;
+
+ if (isdigit(UCH(*fmt))) {
+ int num = *fmt - '0';
+
+ if (state == Flags && num != 0)
+ state = Width;
+ if (state == Width) {
+ if (width < 0)
+ width = 0;
+ width = (width * 10) + num;
+ } else if (state == Prec) {
+ if (prec < 0)
+ prec = 0;
+ prec = (prec * 10) + num;
+ }
+ } else if (*fmt == '*') {
+ VA_INTGR(int);
+
+ if (state == Flags)
+ state = Width;
+ if (state == Width) {
+ width = ival;
+ } else if (state == Prec) {
+ prec = ival;
+ }
+ sprintf(&fmt_ptr[--f], "%d", ival);
+ f = strlen(fmt_ptr);
+ } else if (isalpha(UCH(*fmt))) {
+ done = TRUE;
+ switch (*fmt) {
+ case 'Z': /* FALLTHRU */
+ case 'h': /* FALLTHRU */
+ case 'l': /* FALLTHRU */
+ case 'L': /* FALLTHRU */
+ done = FALSE;
+ type = *fmt;
+ break;
+ case 'o': /* FALLTHRU */
+ case 'i': /* FALLTHRU */
+ case 'd': /* FALLTHRU */
+ case 'u': /* FALLTHRU */
+ case 'x': /* FALLTHRU */
+ case 'X': /* FALLTHRU */
+ if (type == 'l')
+ VA_INTGR(long);
+
+ else if (type == 'Z')
+ VA_INTGR(size_t);
+
+ else
+ VA_INTGR(int);
+
+ used = 'i';
+ break;
+ case 'f': /* FALLTHRU */
+ case 'e': /* FALLTHRU */
+ case 'E': /* FALLTHRU */
+ case 'g': /* FALLTHRU */
+ case 'G': /* FALLTHRU */
+ VA_FLOAT(double);
+
+ used = 'f';
+ break;
+ case 'c':
+ VA_INTGR(int);
+
+ used = 'c';
+ break;
+ case 's':
+ VA_POINT(char *);
+
+ if (prec < 0)
+ prec = (int) strlen(pval);
+ used = 's';
+ break;
+ case 'p':
+ VA_POINT(void *);
+
+ used = 'p';
+ break;
+ case 'n':
+ VA_POINT(int *);
+
+ used = 0;
+ break;
+ default:
+ CTRACE((tfp, "unknown format character '%c' in %s\n",
+ *fmt, format));
+ break;
+ }
+ } else if (*fmt == '.') {
+ state = Prec;
+ } else if (*fmt == '%') {
+ done = TRUE;
+ used = '%';
+ }
+ }
+ fmt_ptr[f] = '\0';
+
+ if (prec > 0) {
+ switch (used) {
+ case 'f':
+ if (width < prec + NUM_WIDTH)
+ width = prec + NUM_WIDTH;
+ /* FALLTHRU */
+ case 'i':
+ /* FALLTHRU */
+ case 'p':
+ if (width < prec + 2)
+ width = prec + 2; /* leading sign/space/zero, "0x" */
+ break;
+ case 'c':
+ break;
+ case '%':
+ break;
+ default:
+ if (width < prec)
+ width = prec;
+ break;
+ }
+ }
+ if (width >= (int) tmp_len) {
+ tmp_len = GROW_EXPR(tmp_len + width);
+ tmp_ptr = HTAlloc(tmp_ptr, tmp_len);
+ }
+
+ switch (used) {
+ case 'i':
+ case 'c':
+ sprintf(tmp_ptr, fmt_ptr, ival);
+ break;
+ case 'f':
+ sprintf(tmp_ptr, fmt_ptr, fval);
+ break;
+ default:
+ sprintf(tmp_ptr, fmt_ptr, pval);
+ break;
+ }
+ need = dst_len + strlen(tmp_ptr) + 1;
+ if (need >= have) {
+ dst_ptr = HTAlloc(dst_ptr, have = GROW_EXPR(need));
+ }
+ strcpy(dst_ptr + dst_len, tmp_ptr);
+ dst_len += strlen(tmp_ptr);
+ } else {
+ if ((dst_len + 2) >= have) {
+ dst_ptr = HTAlloc(dst_ptr, (have += GROW_SIZE));
+ }
+ dst_ptr[dst_len++] = *fmt++;
+ }
+ }
+
+#ifndef SAVE_TIME_NOT_SPACE
+ FREE(tmp_ptr);
+ FREE(fmt_ptr);
+#endif
+ dst_ptr[dst_len] = '\0';
+ if (pstr)
+ *pstr = dst_ptr;
+ return (dst_ptr);
+#endif /* HAVE_VASPRINTF */
+}
+#undef SAVE_TIME_NOT_SPACE
+
+/*
+ * Replacement for sprintf, allocates buffer on the fly according to what's
+ * needed for its arguments. Unlike sprintf, this always concatenates to the
+ * destination buffer.
+ */
+/* Note: if making changes, also check the memory tracking version
+ * LYLeakHTSprintf in LYLeaks.c. - kw */
+#ifdef HTSprintf /* if hidden by LYLeaks stuff */
+#undef HTSprintf
+#endif
+char *HTSprintf(char **pstr, const char *fmt, ...)
+{
+ char *result = 0;
+ size_t inuse = 0;
+ va_list ap;
+
+ LYva_start(ap, fmt);
+ {
+ if (pstr != 0 && *pstr != 0)
+ inuse = strlen(*pstr);
+ result = StrAllocVsprintf(pstr, inuse, fmt, &ap);
+ }
+ va_end(ap);
+
+ return (result);
+}
+
+/*
+ * Replacement for sprintf, allocates buffer on the fly according to what's
+ * needed for its arguments. Like sprintf, this always resets the destination
+ * buffer.
+ */
+/* Note: if making changes, also check the memory tracking version
+ * LYLeakHTSprintf0 in LYLeaks.c. - kw */
+#ifdef HTSprintf0 /* if hidden by LYLeaks stuff */
+#undef HTSprintf0
+#endif
+char *HTSprintf0(char **pstr, const char *fmt, ...)
+{
+ char *result = 0;
+ va_list ap;
+
+ LYva_start(ap, fmt);
+ {
+ result = StrAllocVsprintf(pstr, (size_t) 0, fmt, &ap);
+ }
+ va_end(ap);
+
+ return (result);
+}
+
+/*
+ * Returns a quoted or escaped form of the given parameter, suitable for use in
+ * a command string.
+ */
+#if USE_QUOTED_PARAMETER
+#define S_QUOTE '\''
+#define D_QUOTE '"'
+char *HTQuoteParameter(const char *parameter)
+{
+ size_t i;
+ size_t last;
+ size_t n = 0;
+ size_t quoted = 0;
+ char *result;
+
+ if (parameter == 0)
+ parameter = "";
+
+ last = strlen(parameter);
+ for (i = 0; i < last; ++i)
+ if (StrChr("\\&#$^*?(){}<>\"';`|", parameter[i]) != 0
+ || isspace(UCH(parameter[i])))
+ ++quoted;
+
+ result = (char *) malloc(last + 5 * quoted + 3);
+ if (result == NULL)
+ outofmem(__FILE__, "HTQuoteParameter");
+
+ n = 0;
+#if (USE_QUOTED_PARAMETER == 1)
+ /*
+ * Only double-quotes are used in Win32/DOS -TD
+ */
+ if (quoted)
+ result[n++] = D_QUOTE;
+ for (i = 0; i < last; i++) {
+ result[n++] = parameter[i];
+ }
+ if (quoted)
+ result[n++] = D_QUOTE;
+#else
+ if (quoted)
+ result[n++] = S_QUOTE;
+ for (i = 0; i < last; i++) {
+ if (parameter[i] == S_QUOTE) {
+ result[n++] = S_QUOTE;
+ result[n++] = D_QUOTE;
+ result[n++] = parameter[i];
+ result[n++] = D_QUOTE;
+ result[n++] = S_QUOTE;
+ } else {
+ /* Note: No special handling of other characters, including
+ backslash, since we are constructing a single-quoted string!
+ Backslash has no special escape meaning within those for sh
+ and compatible shells, so trying to escape a backslash by
+ doubling it is unnecessary and would be interpreted by the
+ shell as an additional data character. - kw 2000-05-02
+ */
+ result[n++] = parameter[i];
+ }
+ }
+ if (quoted)
+ result[n++] = S_QUOTE;
+#endif
+ result[n] = '\0';
+ return result;
+}
+#endif
+
+#define HTIsParam(string) ((string[0] == '%' && string[1] == 's'))
+
+/*
+ * Returns the number of "%s" tokens in a system command-template.
+ */
+int HTCountCommandArgs(const char *command)
+{
+ int number = 0;
+
+ while (command[0] != 0) {
+ if (HTIsParam(command))
+ number++;
+ command++;
+ }
+ return number;
+}
+
+/*
+ * Returns a pointer into the given string after the given parameter number
+ */
+static const char *HTAfterCommandArg(const char *command,
+ int number)
+{
+ while (number > 0) {
+ if (command[0] != 0) {
+ if (HTIsParam(command)) {
+ number--;
+ command++;
+ }
+ command++;
+ } else {
+ break;
+ }
+ }
+ return command;
+}
+
+#if USE_QUOTED_PARAMETER
+/*
+ * Recursively trim possible parameters of the source until an existing file
+ * is found. If no file is found, return -1. If a file is found, return
+ * the offset to a blank just after the filename.
+ *
+ * TODO: this could be smarter about trimming, e.g., matching quotes.
+ */
+static int skipPathname(const char *target, const char *source)
+{
+ int result = -1;
+ const char *last;
+ struct stat stat_info;
+
+ if (HTStat(target, &stat_info) == 0
+ && S_ISREG(stat_info.st_mode)) {
+ result = 0;
+ } else if (*target != ' ' && (last = strrchr(target, ' ')) != NULL) {
+ char *temp = NULL;
+ int inner;
+
+ while (last != target && last[-1] == ' ')
+ --last;
+
+ StrAllocCopy(temp, target);
+ result = (int) (last - target);
+ temp[result] = '\0';
+
+ if ((inner = skipPathname(temp, source)) < 0) {
+ result = -1;
+ } else if (inner > 0) {
+ result = inner;
+ }
+
+ FREE(temp);
+ }
+ CTRACE((tfp, "skip/recur %d '%s'\n", result, target));
+ return result;
+}
+#endif
+
+/*
+ * Like HTAddParam, but the parameter may be an environment variable, which we
+ * will expand and append. Do this only for things like the command-verb,
+ * where we obtain the parameter from the user's configuration. Any quoting
+ * required for the environment variable has to be done within its value, e.g.,
+ *
+ * setenv EDITOR 'xvile -name "No such class"'
+ *
+ * This is useful only when we quote parameters, of course.
+ */
+#if USE_QUOTED_PARAMETER
+void HTAddXpand(char **result,
+ const char *command,
+ int number,
+ const char *parameter)
+{
+ if (parameter == NULL)
+ parameter = "";
+ if (number > 0) {
+ const char *last = HTAfterCommandArg(command, number - 1);
+ const char *next = last;
+
+ if (number <= 1) {
+ FREE(*result);
+ }
+
+ while (next[0] != 0) {
+ if (HTIsParam(next)) {
+ if (next != last) {
+ size_t len = ((size_t) (next - last)
+ + ((*result != 0)
+ ? strlen(*result)
+ : 0));
+
+ HTSACat(result, last);
+ (*result)[len] = 0;
+ }
+ if (LYisAbsPath(parameter)) {
+ int skip = skipPathname(parameter, parameter);
+ char *quoted;
+
+ if (skip > 0) {
+ char *temp = NULL;
+
+ StrAllocCopy(temp, parameter);
+ temp[skip] = 0;
+
+ quoted = HTQuoteParameter(temp);
+ HTSACat(result, quoted);
+ FREE(quoted);
+
+ temp[skip] = ' ';
+ HTSACat(result, temp + skip);
+ FREE(temp);
+ } else {
+ quoted = HTQuoteParameter(parameter);
+ HTSACat(result, quoted);
+ FREE(quoted);
+ }
+ } else {
+ /* leave it unquoted, e.g., environment variable expanded */
+ HTSACat(result, parameter);
+ }
+ CTRACE((tfp, "PARAM-EXP:%s\n", *result));
+ return;
+ }
+ next++;
+ }
+ }
+}
+#endif /* USE_QUOTED_PARAMETER */
+
+/*
+ * Append string to a system command that we are constructing, without quoting.
+ * We're given the index of the newest parameter we're processing. Zero
+ * indicates none, so a value of '1' indicates that we copy from the beginning
+ * of the command string up to the first parameter, substitute the quoted
+ * parameter and return the result.
+ *
+ * Parameters are substituted at "%s" tokens, like printf. Other printf-style
+ * tokens are not substituted; they are passed through without change.
+ */
+void HTAddToCmd(char **result,
+ const char *command,
+ int number,
+ const char *string)
+{
+ if (number > 0) {
+ const char *last = HTAfterCommandArg(command, number - 1);
+ const char *next = last;
+
+ if (number <= 1) {
+ FREE(*result);
+ }
+ if (string == 0)
+ string = "";
+ while (next[0] != 0) {
+ if (HTIsParam(next)) {
+ if (next != last) {
+ size_t len = ((size_t) (next - last)
+ + ((*result != 0)
+ ? strlen(*result)
+ : 0));
+
+ HTSACat(result, last);
+ (*result)[len] = 0;
+ }
+ HTSACat(result, string);
+ CTRACE((tfp, "PARAM-ADD:%s\n", *result));
+ return;
+ }
+ next++;
+ }
+ }
+}
+
+/*
+ * Append string-parameter to a system command that we are constructing. The
+ * string is a complete parameter (which is a necessary assumption so we can
+ * quote it properly).
+ */
+void HTAddParam(char **result,
+ const char *command,
+ int number,
+ const char *parameter)
+{
+ if (number > 0) {
+#if USE_QUOTED_PARAMETER
+ char *quoted = HTQuoteParameter(parameter);
+
+ HTAddToCmd(result, command, number, quoted);
+ FREE(quoted);
+#else
+ HTAddToCmd(result, command, number, parameter);
+#endif
+ }
+}
+
+/*
+ * Append the remaining command-string to a system command (compare with
+ * HTAddParam). Any remaining "%s" tokens are copied as empty strings.
+ */
+void HTEndParam(char **result,
+ const char *command,
+ int number)
+{
+ const char *last;
+ int count;
+
+ count = HTCountCommandArgs(command);
+ if (count < number)
+ number = count;
+ last = HTAfterCommandArg(command, number);
+ if (last[0] != 0) {
+ HTSACat(result, last);
+ }
+ CTRACE((tfp, "PARAM-END:%s\n", *result));
+}
+
+/* Binary-strings (may have embedded nulls). Some modules (HTGopher) assume
+ * there is a null on the end, anyway.
+ */
+
+/* (Re)allocate a bstring, e.g., to increase its buffer size for ad hoc
+ * operations.
+ */
+void HTSABAlloc(bstring **dest, int len)
+{
+ if (*dest == 0) {
+ *dest = typecalloc(bstring);
+
+ if (*dest == 0)
+ outofmem(__FILE__, "HTSABAlloc");
+ }
+
+ if ((*dest)->len != len) {
+ (*dest)->str = typeRealloc(char, (*dest)->str, len);
+
+ if ((*dest)->str == 0)
+ outofmem(__FILE__, "HTSABAlloc");
+
+ (*dest)->len = len;
+ }
+}
+
+/* Allocate a new bstring, and return it.
+*/
+void HTSABCopy(bstring **dest, const char *src,
+ int len)
+{
+ bstring *t;
+ unsigned need = (unsigned) (len + 1);
+
+ CTRACE2(TRACE_BSTRING,
+ (tfp, "HTSABCopy(%p, %p, %d)\n",
+ (void *) dest, (const void *) src, len));
+ HTSABFree(dest);
+ if (src) {
+ if (TRACE_BSTRING) {
+ CTRACE((tfp, "=== %4d:", len));
+ trace_bstring2(src, len);
+ CTRACE((tfp, "\n"));
+ }
+ if ((t = (bstring *) malloc(sizeof(bstring))) == NULL)
+ outofmem(__FILE__, "HTSABCopy");
+
+ if ((t->str = typeMallocn(char, need)) == NULL)
+ outofmem(__FILE__, "HTSABCopy");
+
+ MemCpy(t->str, src, len);
+ t->len = len;
+ t->str[t->len] = '\0';
+ *dest = t;
+ }
+ if (TRACE_BSTRING) {
+ CTRACE((tfp, "=> %4d:", BStrLen(*dest)));
+ trace_bstring(*dest);
+ CTRACE((tfp, "\n"));
+ }
+}
+
+/*
+ * Initialize with a null-terminated string (discards the null).
+ */
+void HTSABCopy0(bstring **dest, const char *src)
+{
+ HTSABCopy(dest, src, (int) strlen(src));
+}
+
+/*
+ * Append a block of memory to a bstring.
+ */
+void HTSABCat(bstring **dest, const char *src,
+ int len)
+{
+ bstring *t = *dest;
+
+ CTRACE2(TRACE_BSTRING,
+ (tfp, "HTSABCat(%p, %p, %d)\n",
+ (void *) dest, (const void *) src, len));
+ if (src) {
+ unsigned need = (unsigned) (len + 1);
+
+ if (TRACE_BSTRING) {
+ CTRACE((tfp, "=== %4d:", len));
+ trace_bstring2(src, len);
+ CTRACE((tfp, "\n"));
+ }
+ if (t) {
+ unsigned length = (unsigned) t->len + need;
+
+ t->str = typeRealloc(char, t->str, length);
+ } else {
+ if ((t = typecalloc(bstring)) == NULL)
+ outofmem(__FILE__, "HTSACat");
+
+ t->str = typeMallocn(char, need);
+ }
+ if (t->str == NULL)
+ outofmem(__FILE__, "HTSACat");
+
+ MemCpy(t->str + t->len, src, len);
+ t->len += len;
+ t->str[t->len] = '\0';
+ *dest = t;
+ }
+ if (TRACE_BSTRING) {
+ CTRACE((tfp, "=> %4d:", BStrLen(*dest)));
+ trace_bstring(*dest);
+ CTRACE((tfp, "\n"));
+ }
+}
+
+/*
+ * Append a null-terminated string (discards the null).
+ */
+void HTSABCat0(bstring **dest, const char *src)
+{
+ HTSABCat(dest, src, (int) strlen(src));
+}
+
+/*
+ * Compare two bstring's for equality
+ */
+BOOL HTSABEql(bstring *a, bstring *b)
+{
+ unsigned len_a = (unsigned) ((a != 0) ? a->len : 0);
+ unsigned len_b = (unsigned) ((b != 0) ? b->len : 0);
+
+ if (len_a == len_b) {
+ if (len_a == 0
+ || MemCmp(a->str, b->str, a->len) == 0)
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/*
+ * Deallocate a bstring.
+ */
+void HTSABFree(bstring **ptr)
+{
+ if (*ptr != NULL) {
+ FREE((*ptr)->str);
+ FREE(*ptr);
+ *ptr = NULL;
+ }
+}
+
+/*
+ * Use this function to perform formatted sprintf's onto the end of a bstring.
+ * The bstring may contain embedded nulls; the formatted portions must not.
+ */
+bstring *HTBprintf(bstring **pstr, const char *fmt, ...)
+{
+ bstring *result = 0;
+ char *temp = 0;
+ va_list ap;
+
+ LYva_start(ap, fmt);
+ {
+ temp = StrAllocVsprintf(&temp, (size_t) 0, fmt, &ap);
+ if (non_empty(temp)) {
+ HTSABCat(pstr, temp, (int) strlen(temp));
+ }
+ FREE(temp);
+ result = *pstr;
+ }
+ va_end(ap);
+
+ return (result);
+}
+
+/*
+ * Write binary-data to the logfile, making it safe for most editors to view.
+ * That is most, since we do not restrict line-length. Nulls and other
+ * non-printing characters are addressed.
+ */
+void trace_bstring2(const char *text,
+ int size)
+{
+ int n;
+
+ if (text != 0) {
+ for (n = 0; n < size; ++n) {
+ int ch = UCH(text[n]);
+
+ switch (ch) {
+ case '\\':
+ fputs("\\\\", tfp);
+ break;
+ case '\r':
+ fputs("\\r", tfp);
+ break;
+ case '\t':
+ fputs("\\t", tfp);
+ break;
+ case '\f':
+ fputs("\\f", tfp);
+ break;
+ default:
+ if (isprint(ch) || isspace(ch)) {
+ fputc(ch, tfp);
+ } else {
+ fprintf(tfp, "\\%03o", ch);
+ }
+ break;
+ }
+ }
+ }
+}
+
+void trace_bstring(bstring *data)
+{
+ trace_bstring2(BStrData(data), BStrLen(data));
+}
diff --git a/WWW/Library/Implementation/HTString.h b/WWW/Library/Implementation/HTString.h
new file mode 100644
index 0000000..82520ad
--- /dev/null
+++ b/WWW/Library/Implementation/HTString.h
@@ -0,0 +1,167 @@
+/*
+ * $LynxId: HTString.h,v 1.41 2021/06/09 19:30:55 tom Exp $
+ * String handling for libwww
+ * STRINGS
+ *
+ * Case-independent string comparison and allocations with copies etc
+ */
+#ifndef HTSTRING_H
+#define HTSTRING_H
+
+#ifndef HTUTILS_H
+#include <HTUtils.h>
+#endif /* HTUTILS_H */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+ extern const char *HTLibraryVersion; /* String for help screen etc */
+
+/*
+ * EBCDIC string comparison using ASCII collating sequence
+ */
+#ifdef NOT_ASCII
+ extern int AS_casecomp(const char *a, const char *b);
+ extern int AS_ncmp(const char *a, const char *b, unsigned int n);
+
+#define AS_cmp( a, b ) ( AS_ncmp( ( a ), ( b ), -1 ) )
+
+#else
+#define AS_casecomp( a, b ) ( strcasecomp( ( a ), ( b ) ) )
+#define AS_ncmp( a, b, c ) ( StrNCmp( ( a ), ( b ), ( c ) ) )
+#define AS_cmp strcmp
+
+#endif /* NOT_ASCII */
+
+#define StrNCat(a,b,c) strncat((a),(b),(size_t)(c))
+#define StrNCpy(a,b,c) strncpy((a),(b),(size_t)(c))
+#define StrNCmp(a,b,c) strncmp((a),(b),(size_t)(c))
+
+#define MemCpy(a,b,c) memcpy((a),(b),(size_t)(c))
+#define MemCmp(a,b,c) memcmp((a),(b),(size_t)(c))
+
+ /*
+ * Workaround for glibc header defect combined with -Wlogical-op warnings
+ */
+#define StrChr (strchr)
+
+ /*
+ * Case-insensitive string comparison
+ *
+ * The usual routines (comp instead of cmp) had some problem.
+ */
+ extern int strcasecomp(const char *a, const char *b);
+ extern int strncasecomp(const char *a, const char *b, int n);
+
+ extern int strcasecomp8(const char *a, const char *b);
+ extern int strncasecomp8(const char *a, const char *b, int n);
+
+ extern int strcasecomp_asterisk(const char *a, const char *b);
+
+ /*
+ * strcasecomp8 and strncasecomp8 are variants of strcasecomp and
+ * strncasecomp, but use 8bit upper/lower case information from the
+ * current display charset
+ */
+
+ /*
+ * Malloced string manipulation
+ */
+#define StrAllocCopy(dest, src) HTSACopy (&(dest), src)
+#define StrAllocCat(dest, src) HTSACat (&(dest), src)
+ extern char *HTSACopy(char **dest, const char *src);
+ extern char *HTSACat(char **dest, const char *src);
+
+ /*
+ * optimized for heavily realloc'd strings in temp objects
+ */
+#define StrAllocCopy_extra(dest, src) HTSACopy_extra (&(dest), src)
+#define FREE_extra(x) {if (x != NULL) {HTSAFree_extra(x); x = NULL;}}
+#define Clear_extra(x) {if (x != NULL) {*x = '\0';}}
+ extern char *HTSACopy_extra(char **dest, const char *src);
+ extern void HTSAFree_extra(char *s);
+
+ /*
+ * Next word or quoted string
+ */
+ extern char *HTNextField(char **pstr);
+
+ /* A more general parser - kw */
+ extern char *HTNextTok(char **pstr,
+ const char *delims, const char *bracks, char *found);
+
+ extern char *HTSprintf(char **pstr, const char *fmt, ...) GCC_PRINTFLIKE(2,3);
+ extern char *HTSprintf0(char **pstr, const char *fmt, ...) GCC_PRINTFLIKE(2,3);
+
+#if defined(LY_FIND_LEAKS) /* private otherwise */
+ extern char *StrAllocVsprintf(char **pstr,
+ size_t len,
+ const char *fmt,
+ va_list *ap);
+#endif
+
+#if defined(__CYGWIN__)
+#define USE_QUOTED_PARAMETER 2 /* single and double-quoting */
+#elif defined(DOSPATH)
+#define USE_QUOTED_PARAMETER 1 /* double-quoting only */
+#elif (defined(VMS) || defined(__EMX__))
+#define USE_QUOTED_PARAMETER 0 /* no quoting */
+#else
+#define USE_QUOTED_PARAMETER 2 /* single and double-quoting */
+#endif
+
+#if USE_QUOTED_PARAMETER
+ extern char *HTQuoteParameter(const char *parameter);
+ extern void HTAddXpand(char **result, const char *command, int number, const char *parameter);
+
+#else
+#define HTQuoteParameter(parameter) parameter /* simplify ifdef'ing */
+#define HTAddXpand(result,command,number,parameter) HTAddParam(result,command,number,parameter)
+#endif
+
+ extern int HTCountCommandArgs(const char *command);
+ extern void HTAddToCmd(char **result, const char *command, int number, const char *string);
+ extern void HTAddParam(char **result, const char *command, int number, const char *parameter);
+ extern void HTEndParam(char **result, const char *command, int number);
+
+/* Force an option, with leading blanks, to be appended without quoting them */
+#define HTOptParam(result, command, number, parameter) HTSACat(result, parameter)
+
+/* Binary copy and concat */
+ typedef struct {
+ char *str;
+ int len;
+ } bstring;
+
+ extern void HTSABAlloc(bstring **dest, int len);
+ extern void HTSABCopy(bstring **dest, const char *src, int len);
+ extern void HTSABCopy0(bstring **dest, const char *src);
+ extern void HTSABCat(bstring **dest, const char *src, int len);
+ extern void HTSABCat0(bstring **dest, const char *src);
+ extern BOOL HTSABEql(bstring *a, bstring *b);
+ extern void HTSABFree(bstring **ptr);
+
+#define BStrLen(s) (((s) != 0) ? (s)->len : 0)
+#define BStrData(s) (((s) != 0) ? (s)->str : 0)
+
+#define BINEQ(a,b) (HTSABEql(a,b)) /* like STREQ() */
+
+#define isBEmpty(p) ((p) == 0 || BStrData(p) == 0 || BStrLen(p) == 0)
+
+#define BStrAlloc(d,n) HTSABAlloc( &(d), n)
+#define BStrCopy(d,s) HTSABCopy( &(d), BStrData(s), BStrLen(s))
+#define BStrCopy0(d,s) HTSABCopy0( &(d), s)
+#define BStrCopy1(d,s,n) HTSABCopy( &(d), s, n)
+#define BStrCat(d,s) HTSABCat( &(d), BStrData(s), BStrLen(s))
+#define BStrCat0(d,s) HTSABCat0( &(d), s)
+#define BStrFree(d) HTSABFree( &(d))
+
+ extern bstring *HTBprintf(bstring **pstr, const char *fmt, ...) GCC_PRINTFLIKE(2,3);
+
+ extern void trace_bstring(bstring *data);
+ extern void trace_bstring2(const char *text, int size);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* HTSTRING_H */
diff --git a/WWW/Library/Implementation/HTStyle.c b/WWW/Library/Implementation/HTStyle.c
new file mode 100644
index 0000000..54c0bc5
--- /dev/null
+++ b/WWW/Library/Implementation/HTStyle.c
@@ -0,0 +1,378 @@
+/*
+ * $LynxId: HTStyle.c,v 1.16 2009/11/27 13:01:48 tom Exp $
+ *
+ * Style Implementation for Hypertext HTStyle.c
+ * ==================================
+ *
+ * Styles allow the translation between a logical property
+ * of a piece of text and its physical representation.
+ *
+ * A StyleSheet is a collection of styles, defining the
+ * translation necessary to
+ * represent a document. It is a linked list of styles.
+ */
+
+#include <HTUtils.h>
+#include <HTStyle.h>
+
+#include <LYLeaks.h>
+
+/* Create a new style
+*/
+HTStyle *HTStyleNew(void)
+{
+ HTStyle *self = typecalloc(HTStyle);
+
+ if (self == NULL)
+ outofmem(__FILE__, "HTStyleNew");
+ return self;
+}
+
+/* Create a new style with a name
+*/
+HTStyle *HTStyleNewNamed(const char *name)
+{
+ HTStyle *self = HTStyleNew();
+
+ StrAllocCopy(self->w_name, name);
+ self->id = -1; /* <0 */
+ return self;
+}
+
+/* Free a style
+*/
+HTStyle *HTStyleFree(HTStyle *self)
+{
+ FREE(self->w_name);
+ FREE(self->w_SGMLTag);
+ FREE(self);
+ return NULL;
+}
+
+#ifdef SUPPRESS /* Only on the NeXT */
+/* Read a style from a stream (without its name)
+ * --------------------------
+ *
+ * Reads a style with paragraph information from a stream.
+ * The style name is not read or written by these routines.
+ */
+#define NONE_STRING "(None)"
+#define HTStream NXStream
+
+HTStyle *HTStyleRead(HTStyle *style, HTStream *stream)
+{
+ char myTag[STYLE_NAME_LENGTH];
+ char fontName[STYLE_NAME_LENGTH];
+ NXTextStyle *p;
+ int tab;
+ int gotpara; /* flag: have we got a paragraph definition? */
+
+ NXScanf(stream, "%s%s%f%d",
+ myTag,
+ fontName,
+ &style->fontSize,
+ &gotpara);
+ if (gotpara) {
+ if (!style->paragraph) {
+ style->paragraph = malloc(sizeof(*(style->paragraph)));
+ if (!style->paragraph)
+ outofmem(__FILE__, "HTStyleRead");
+ style->paragraph->tabs = 0;
+ }
+ p = style->paragraph;
+ NXScanf(stream, "%f%f%f%f%hd%f%f%hd",
+ &p->indent1st,
+ &p->indent2nd,
+ &p->lineHt,
+ &p->descentLine,
+ &p->alignment,
+ &style->spaceBefore,
+ &style->spaceAfter,
+ &p->numTabs);
+ FREE(p->tabs);
+ p->tabs = malloc(p->numTabs * sizeof(p->tabs[0]));
+ if (!p->tabs)
+ outofmem(__FILE__, "HTStyleRead");
+ for (tab = 0; tab < p->numTabs; tab++) {
+ NXScanf(stream, "%hd%f",
+ &p->tabs[tab].kind,
+ &p->tabs[tab].x);
+ }
+ } else { /* No paragraph */
+ FREE(style->paragraph);
+ } /* if no paragraph */
+ StrAllocCopy(style->SGMLTag, myTag);
+ if (strcmp(fontName, NONE_STRING) == 0)
+ style->font = 0;
+ else
+ style->font =[Font newFont: fontName size:style->fontSize];
+ return NULL;
+}
+
+/* Write a style to a stream in a compatible way
+*/
+HTStyle *HTStyleWrite(HTStyle *style, NXStream * stream)
+{
+ int tab;
+ NXTextStyle *p = style->paragraph;
+
+ NXPrintf(stream, "%s %s %f %d\n",
+ style->SGMLTag,
+ style->font ?[style->font name] : NONE_STRING,
+ style->fontSize,
+ p != 0);
+
+ if (p) {
+ NXPrintf(stream, "\t%f %f %f %f %d %f %f\t%d\n",
+ p->indent1st,
+ p->indent2nd,
+ p->lineHt,
+ p->descentLine,
+ p->alignment,
+ style->spaceBefore,
+ style->spaceAfter,
+ p->numTabs);
+
+ for (tab = 0; tab < p->numTabs; tab++)
+ NXPrintf(stream, "\t%d %f\n",
+ p->tabs[tab].kind,
+ p->tabs[tab].x);
+ }
+ return style;
+}
+
+/* Write a style to stdout for diagnostics
+*/
+HTStyle *HTStyleDump(HTStyle *style)
+{
+ int tab;
+ NXTextStyle *p = style->paragraph;
+
+ printf(STYLE_DUMP_FONT,
+ style,
+ style->name,
+ style->SGMLTag,
+ [style->font name],
+ style->fontSize);
+ if (p) {
+ printf(STYLE_DUMP_IDENT,
+ p->indent1st,
+ p->indent2nd,
+ p->lineHt,
+ p->descentLine);
+ printf(STYLE_DUMP_ALIGN,
+ p->alignment,
+ p->numTabs,
+ style->spaceBefore,
+ style->spaceAfter);
+
+ for (tab = 0; tab < p->numTabs; tab++) {
+ printf(STYLE_DUMP_TAB,
+ p->tabs[tab].kind,
+ p->tabs[tab].x);
+ }
+ printf("\n");
+ } /* if paragraph */
+ return style;
+}
+#endif /* SUPPRESS */
+
+/* StyleSheet Functions
+ * ====================
+ */
+
+/* Searching for styles:
+*/
+HTStyle *HTStyleNamed(HTStyleSheet *self, const char *name)
+{
+ HTStyle *scan;
+
+ for (scan = self->styles; scan; scan = scan->next)
+ if (0 == strcmp(GetHTStyleName(scan), name))
+ return scan;
+ CTRACE((tfp, "StyleSheet: No style named `%s'\n", name));
+ return NULL;
+}
+
+#ifdef NEXT_SUPRESS /* Not in general common code */
+
+HTStyle *HTStyleMatching(HTStyleSheet *self, HTStyle *style)
+{
+ HTStyle *scan;
+
+ for (scan = self->styles; scan; scan = scan->next)
+ if (scan->paragraph == para)
+ return scan;
+ return NULL;
+}
+
+/* Find the style which best fits a given run
+ * ------------------------------------------
+ *
+ * This heuristic is used for guessing the style for a run of
+ * text which has been pasted in. In order, we try:
+ *
+ * A style whose paragraph structure is actually used by the run.
+ * A style matching in font
+ * A style matching in paragraph style exactly
+ * A style matching in paragraph to a degree
+ */
+
+HTStyle *HTStyleForRun(HTStyleSheet *self, NXRun * run)
+{
+ HTStyle *scan;
+ HTStyle *best = 0;
+ int bestMatch = 0;
+ NXTextStyle *rp = run->paraStyle;
+
+ for (scan = self->styles; scan; scan = scan->next)
+ if (scan->paragraph == run->paraStyle)
+ return scan; /* Exact */
+
+ for (scan = self->styles; scan; scan = scan->next) {
+ NXTextStyle *sp = scan->paragraph;
+
+ if (sp) {
+ int match = 0;
+
+ if (sp->indent1st == rp->indent1st)
+ match = match + 1;
+ if (sp->indent2nd == rp->indent2nd)
+ match = match + 2;
+ if (sp->lineHt == rp->lineHt)
+ match = match + 1;
+ if (sp->numTabs == rp->numTabs)
+ match = match + 1;
+ if (sp->alignment == rp->alignment)
+ match = match + 3;
+ if (scan->font == run->font)
+ match = match + 10;
+ if (match > bestMatch) {
+ best = scan;
+ bestMatch = match;
+ }
+ }
+ }
+ CTRACE((tfp, "HTStyleForRun: Best match for style is %d out of 18\n",
+ bestMatch));
+ return best;
+}
+#endif /* NEXT_SUPRESS */
+
+/* Add a style to a sheet
+ * ----------------------
+ */
+HTStyleSheet *HTStyleSheetAddStyle(HTStyleSheet *self, HTStyle *style)
+{
+ style->next = 0; /* The style will go on the end */
+ if (!self->styles) {
+ self->styles = style;
+ } else {
+ HTStyle *scan;
+
+ for (scan = self->styles; scan->next; scan = scan->next) ; /* Find end */
+ scan->next = style;
+ }
+ return self;
+}
+
+/* Remove the given object from a style sheet if it exists
+*/
+HTStyleSheet *HTStyleSheetRemoveStyle(HTStyleSheet *self, HTStyle *style)
+{
+ if (self->styles == style) {
+ self->styles = style->next;
+ return self;
+ } else {
+ HTStyle *scan;
+
+ for (scan = self->styles; scan; scan = scan->next) {
+ if (scan->next == style) {
+ scan->next = style->next;
+ return self;
+ }
+ }
+ }
+ return NULL;
+}
+
+/* Create new style sheet
+*/
+
+HTStyleSheet *HTStyleSheetNew(void)
+{
+ HTStyleSheet *self = typecalloc(HTStyleSheet);
+
+ if (self == NULL)
+ outofmem(__FILE__, "HTStyleSheetNew");
+ return self;
+}
+
+/* Free off a style sheet pointer
+*/
+HTStyleSheet *HTStyleSheetFree(HTStyleSheet *self)
+{
+ HTStyle *style;
+
+ while ((style = self->styles) != 0) {
+ self->styles = style->next;
+ HTStyleFree(style);
+ }
+ FREE(self);
+ return NULL;
+}
+
+/* Read a stylesheet from a typed stream
+ * -------------------------------------
+ *
+ * Reads a style sheet from a stream. If new styles have the same names
+ * as existing styles, they replace the old ones without changing the ids.
+ */
+
+#ifdef NEXT_SUPRESS /* Only on the NeXT */
+HTStyleSheet *HTStyleSheetRead(HTStyleSheet *self, NXStream * stream)
+{
+ int numStyles;
+ int i;
+ HTStyle *style;
+ char styleName[80];
+
+ NXScanf(stream, " %d ", &numStyles);
+ CTRACE((tfp, "Stylesheet: Reading %d styles\n", numStyles));
+ for (i = 0; i < numStyles; i++) {
+ NXScanf(stream, "%s", styleName);
+ style = HTStyleNamed(self, styleName);
+ if (!style) {
+ style = HTStyleNewNamed(styleName);
+ (void) HTStyleSheetAddStyle(self, style);
+ }
+ (void) HTStyleRead(style, stream);
+ if (TRACE)
+ HTStyleDump(style);
+ }
+ return self;
+}
+
+/* Write a stylesheet to a typed stream
+ * ------------------------------------
+ *
+ * Writes a style sheet to a stream.
+ */
+
+HTStyleSheet *HTStyleSheetWrite(HTStyleSheet *self, NXStream * stream)
+{
+ int numStyles = 0;
+ HTStyle *style;
+
+ for (style = self->styles; style; style = style->next)
+ numStyles++;
+ NXPrintf(stream, "%d\n", numStyles);
+
+ CTRACE((tfp, "StyleSheet: Writing %d styles\n", numStyles));
+ for (style = self->styles; style; style = style->next) {
+ NXPrintf(stream, "%s ", style->name);
+ (void) HTStyleWrite(style, stream);
+ }
+ return self;
+}
+#endif /* NEXT_SUPRESS */
diff --git a/WWW/Library/Implementation/HTStyle.h b/WWW/Library/Implementation/HTStyle.h
new file mode 100644
index 0000000..79ba003
--- /dev/null
+++ b/WWW/Library/Implementation/HTStyle.h
@@ -0,0 +1,241 @@
+/*
+ * $LynxId: HTStyle.h,v 1.18 2020/01/21 22:02:35 tom Exp $
+ HTStyle: Style management for libwww
+ STYLE DEFINITION FOR HYPERTEXT
+
+ Styles allow the translation between a logical property of a piece of text
+ and its physical representation.
+
+ A StyleSheet is a collection of styles, defining the translation necessary
+ to represent a document. It is a linked list of styles.
+
+Overriding this module
+
+ Why is the style structure declared in the HTStyle.h module, instead of
+ having the user browser define the structure, and the HTStyle routines just
+ use sizeof() for copying?
+
+ It's not obvious whether HTStyle.c should be common code. It's useful to
+ have common code for loading style sheets, especially if the movement toward
+ standard style sheets gets going.
+
+ If it IS common code, then both the hypertext object and HTStyle.c must know
+ the structure of a style, so HTStyle.h is a suitable place to put that.
+ HTStyle.c has to be compiled with a knowledge of the
+
+ It we take it out of the library, then of course HTStyle could be declared
+ as an undefined structure. The only references to it are in the
+ structure-flattening code HTML.c and HTPlain.c, which only use
+ HTStypeNamed().
+
+ You can in any case override this function in your own code, which will
+ prevent the HTStyle from being loaded. You will be able to redefine your
+ style structure in this case without problems, as no other moule needs to
+ know it.
+
+ */
+#ifndef HTStyle_H
+#define HTStyle_H
+
+#include <HTAnchor.h>
+
+typedef long int HTFont; /* Dummy definition instead */
+
+#ifdef NeXT_suppressed
+#include <appkit/appkit.h>
+typedef NXCoord HTCoord;
+
+#define HTParagraphStyle NXTextStyle
+#define HTCoord NXCoord
+typedef struct _color {
+ float grey;
+ int RGBColor;
+} HTColor;
+
+#else
+
+typedef int HTCoord; /* changed from float to int - kw */
+
+typedef struct _HTParagraphStyle {
+ HTCoord left_indent; /* @@@@ junk! etc etc */
+} HTParagraphStyle;
+
+typedef int HTColor; /* Sorry about the US spelling! */
+
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+#define STYLE_NAME_LENGTH 80 /* @@@@@@@@@@@ */
+ typedef struct {
+ short kind; /* only NX_LEFTTAB implemented */
+ HTCoord position; /* x coordinate for stop */
+ } HTTabStop;
+
+/* The Style Structure
+ * -------------------
+ */
+
+ typedef struct _HTStyle {
+
+/* Style management information
+*/
+ struct _HTStyle *next; /* Link for putting into stylesheet */
+ char *w_name; /* Style name */
+ const char *c_name; /* Style name */
+ int id; /* equivalent of name, for speed */
+ char *w_SGMLTag; /* Tag name to start */
+ const char *c_SGMLTag; /* Tag name to start */
+
+/* Character attributes (a la NXRun)
+*/
+ HTFont font; /* Font id */
+ HTCoord fontSize; /* The size of font, not independent */
+ HTColor color; /* text gray of current run */
+ int superscript; /* superscript (-sub) in points */
+
+ HTAnchor *anchor; /* Anchor id if any, else zero */
+
+/* Paragraph Attributes (a la NXTextStyle)
+*/
+ HTCoord indent1st; /* how far first line in paragraph is
+ * indented */
+ HTCoord leftIndent; /* how far second line is indented */
+ HTCoord rightIndent; /* (Missing from NeXT version */
+ short alignment; /* quad justification */
+ HTCoord lineHt; /* line height */
+ HTCoord descentLine; /* descender bottom from baseline */
+ const HTTabStop *tabs; /* array of tab stops, 0 terminated */
+
+ BOOL wordWrap; /* Yes means wrap at space not char */
+ BOOL freeFormat; /* Yes means \n is just white space */
+ HTCoord spaceBefore; /* Omissions from NXTextStyle */
+ HTCoord spaceAfter;
+ int paraFlags; /* Paragraph flags, bits as follows: */
+
+#define PARA_KEEP 1 /* Do not break page within this paragraph */
+#define PARA_WITH_NEXT 2 /* Do not break page after this paragraph */
+
+#define HT_JUSTIFY 0 /* For alignment */
+#define HT_LEFT 1
+#define HT_RIGHT 2
+#define HT_CENTER 3
+
+ } HTStyle;
+
+#define GetHTStyleName(p) ((p)->w_name ? (p)->w_name : (p)->c_name)
+#define GetHTStyleSGML(p) ((p)->w_SGMLTag ? (p)->w_SGMLTag : (p)->c_SGMLTag)
+
+#define HTStyleInit( \
+ next, name, SGML_tag, \
+ font, fontsize, color, superscript, \
+ anchor, indent1st, leftIndent, rightIndent, \
+ alignment, lineHt, descentLine, \
+ tabs, wordWrap, freeFormat, spaceBefore, spaceAfter, paraFlags) \
+ { \
+ next, NULL, #name, ST_##name, NULL, SGML_tag, \
+ font, fontsize, color, superscript, \
+ anchor, indent1st, leftIndent, rightIndent, \
+ alignment, lineHt, descentLine, \
+ tabs, wordWrap, freeFormat, spaceBefore, spaceAfter, paraFlags }
+
+#define HT_ALIGN_NONE (-1)
+
+/* Style functions:
+*/
+ extern HTStyle *HTStyleNew(void);
+ extern HTStyle *HTStyleNewNamed(const char *name);
+ extern HTStyle *HTStyleFree(HTStyle *self);
+
+#ifdef SUPRESS
+ extern HTStyle *HTStyleRead(HTStyle *self, HTStream *stream);
+ extern HTStyle *HTStyleWrite(HTStyle *self, HTStream *stream);
+#endif
+/* Style Sheet
+ * -----------
+ */
+ typedef struct _HTStyleSheet {
+ const char *name;
+ HTStyle *styles;
+ } HTStyleSheet;
+
+/* Stylesheet functions:
+*/
+ extern HTStyleSheet *HTStyleSheetNew(void);
+ extern HTStyleSheet *HTStyleSheetFree(HTStyleSheet *self);
+ extern HTStyle *HTStyleNamed(HTStyleSheet *self, const char *name);
+ extern HTStyle *HTStyleForParagraph(HTStyleSheet *self, HTParagraphStyle * paraStyle);
+ extern HTStyle *HTStyleMatching(HTStyleSheet *self, HTStyle *style);
+
+/* extern HTStyle * HTStyleForRun (HTStyleSheet *self, NXRun * run); */
+ extern HTStyleSheet *HTStyleSheetAddStyle(HTStyleSheet *self, HTStyle *style);
+ extern HTStyleSheet *HTStyleSheetRemoveStyle(HTStyleSheet *self, HTStyle *style);
+
+#ifdef SUPPRESS
+ extern HTStyleSheet *HTStyleSheetRead(HTStyleSheet *self, HTStream *stream);
+ extern HTStyleSheet *HTStyleSheetWrite(HTStyleSheet *self, HTStream *stream);
+#endif
+#define CLEAR_POINTER ((void *)-1) /* Pointer value means "clear me" */
+
+/* DefaultStyle.c */
+ extern HTStyleSheet *DefaultStyle(HTStyle ***result_array);
+
+/* enum, use this instead of HTStyle name comparisons */
+ enum HTStyle_Enum {
+ ST_Normal = 0,
+ ST_DivCenter,
+ ST_DivLeft,
+ ST_DivRight,
+ ST_Banner,
+ ST_Blockquote,
+ ST_Bq,
+ ST_Footnote,
+ ST_List,
+ ST_List1,
+ ST_List2,
+ ST_List3,
+ ST_List4,
+ ST_List5,
+ ST_List6,
+ ST_Menu,
+ ST_Menu1,
+ ST_Menu2,
+ ST_Menu3,
+ ST_Menu4,
+ ST_Menu5,
+ ST_Menu6,
+ ST_Glossary,
+ ST_Glossary1,
+ ST_Glossary2,
+ ST_Glossary3,
+ ST_Glossary4,
+ ST_Glossary5,
+ ST_Glossary6,
+ ST_GlossaryCompact,
+ ST_GlossaryCompact1,
+ ST_GlossaryCompact2,
+ ST_GlossaryCompact3,
+ ST_GlossaryCompact4,
+ ST_GlossaryCompact5,
+ ST_GlossaryCompact6,
+ ST_Example,
+ ST_Preformatted,
+ ST_Listing,
+ ST_Address,
+ ST_Note,
+ ST_Heading1,
+ ST_Heading2,
+ ST_Heading3,
+ ST_Heading4,
+ ST_Heading5,
+ ST_Heading6,
+ ST_HeadingCenter,
+ ST_HeadingLeft,
+ ST_HeadingRight
+ };
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* HTStyle_H */
diff --git a/WWW/Library/Implementation/HTTCP.c b/WWW/Library/Implementation/HTTCP.c
new file mode 100644
index 0000000..a3bfda6
--- /dev/null
+++ b/WWW/Library/Implementation/HTTCP.c
@@ -0,0 +1,2623 @@
+/*
+ * $LynxId: HTTCP.c,v 1.163 2022/04/01 23:18:35 Rajeev.V.Pillai Exp $
+ *
+ * Generic Communication Code HTTCP.c
+ * ==========================
+ *
+ * This code is in common between client and server sides.
+ *
+ * 16 Jan 92 TBL Fix strtol() undefined on CMU Mach.
+ * 25 Jun 92 JFG Added DECNET option through TCP socket emulation.
+ * 13 Sep 93 MD Added correct return of vmserrorno for HTInetStatus.
+ * Added decoding of vms error message for MULTINET.
+ * 7-DEC-1993 Bjorn S. Nilsson, ALEPH, CERN, VMS UCX ioctl() changes
+ * (done of Mosaic)
+ * 19 Feb 94 Danny Mayer Added Bjorn Fixes to Lynx version
+ * 7 Mar 94 Danny Mayer Added Fix UCX version for full domain name
+ * 20 May 94 Andy Harper Added support for CMU TCP/IP transport
+ * 17 Nov 94 Andy Harper Added support for SOCKETSHR transport
+ * 16 Jul 95 S. Bjorndahl added kluge to deal with LIBCMU bug
+ */
+
+#define LYNX_ADDRINFO struct addrinfo
+#define LYNX_HOSTENT struct hostent
+
+#include <HTUtils.h>
+#include <HTParse.h>
+#include <HTAlert.h>
+#include <HTTCP.h>
+#include <LYGlobalDefs.h> /* added for no_suspend */
+#include <LYUtils.h>
+
+#ifdef NSL_FORK
+#include <signal.h>
+#include <www_wait.h>
+#define FREE_NSL_FORK(p) { FREE(p); }
+#elif defined(_WINDOWS_NSL)
+#define FREE_NSL_FORK(p) if ((p) == gbl_phost) { FREE(p); }
+#else
+#define FREE_NSL_FORK(p) /* nothing */
+#endif /* NSL_FORK */
+
+#ifdef HAVE_RESOLV_H
+#include <resolv.h>
+#endif
+
+#ifdef __DJGPP__
+#include <netdb.h>
+#endif /* __DJGPP__ */
+
+#define OK_HOST(p) ((p) != 0 && ((p)->h_length) != 0)
+
+#ifdef SVR4_BSDSELECT
+int BSDselect(int nfds,
+ fd_set * readfds,
+ fd_set * writefds,
+ fd_set * exceptfds,
+ struct timeval *select_timeout);
+
+#ifdef select
+#undef select
+#endif /* select */
+#define select BSDselect
+#ifdef SOCKS
+#ifdef Rselect
+#undef Rselect
+#endif /* Rselect */
+#define Rselect BSDselect
+#endif /* SOCKS */
+#endif /* SVR4_BSDSELECT */
+
+#include <LYLeaks.h>
+
+/*
+ * Module-Wide variables
+ */
+static char *hostname = NULL; /* The name of this host */
+
+/*
+ * PUBLIC VARIABLES
+ */
+#ifdef SOCKS
+unsigned long socks_bind_remoteAddr; /* for long Rbind */
+#endif /* SOCKS */
+
+/* Encode INET status (as in sys/errno.h) inet_status()
+ * ------------------
+ *
+ * On entry,
+ * where gives a description of what caused the error
+ * global errno gives the error number in the Unix way.
+ *
+ * On return,
+ * returns a negative status in the Unix way.
+ */
+
+#ifdef DECL_SYS_ERRLIST
+extern char *sys_errlist[]; /* see man perror on cernvax */
+extern int sys_nerr;
+#endif /* DECL_SYS_ERRLIST */
+
+#ifdef __DJGPP__
+static int ResolveYield(void)
+{
+ return HTCheckForInterrupt()? 0 : 1;
+}
+#endif
+
+#if defined(VMS) && defined(UCX)
+/*
+ * A routine to mimic the ioctl function for UCX.
+ * Bjorn S. Nilsson, 25-Nov-1993. Based on an example in the UCX manual.
+ */
+#include <HTioctl.h>
+
+int HTioctl(int d,
+ int request,
+ int *argp)
+{
+ int sdc, status;
+ unsigned short fun, iosb[4];
+ char *p5, *p6;
+ struct comm {
+ int command;
+ char *addr;
+ } ioctl_comm;
+ struct it2 {
+ unsigned short len;
+ unsigned short opt;
+ struct comm *addr;
+ } ioctl_desc;
+
+ if ((sdc = vaxc$get_sdc(d)) == 0) {
+ set_errno(EBADF);
+ return -1;
+ }
+ ioctl_desc.opt = UCX$C_IOCTL;
+ ioctl_desc.len = sizeof(struct comm);
+
+ ioctl_desc.addr = &ioctl_comm;
+ if (request & IOC_OUT) {
+ fun = IO$_SENSEMODE;
+ p5 = 0;
+ p6 = (char *) &ioctl_desc;
+ } else {
+ fun = IO$_SETMODE;
+ p5 = (char *) &ioctl_desc;
+ p6 = 0;
+ }
+ ioctl_comm.command = request;
+ ioctl_comm.addr = (char *) argp;
+ status = sys$qiow(0, sdc, fun, iosb, 0, 0, 0, 0, 0, 0, p5, p6);
+ if (!(status & 01)) {
+ set_errno(status);
+ return -1;
+ }
+ if (!(iosb[0] & 01)) {
+ set_errno(iosb[0]);
+ return -1;
+ }
+ return 0;
+}
+#endif /* VMS && UCX */
+
+#define MY_FORMAT "TCP: Error %d in `SOCKET_ERRNO' after call to %s() failed.\n\t%s\n"
+ /* third arg is transport/platform specific */
+
+/* Report Internet Error
+ * ---------------------
+ */
+int HTInetStatus(const char *where)
+{
+ int status;
+ int saved_errno = errno;
+
+#ifdef VMS
+#ifdef MULTINET
+ SOCKET_ERRNO = vmserrno;
+#endif /* MULTINET */
+#endif /* VMS */
+
+#ifdef VM
+ CTRACE((tfp, MY_FORMAT, SOCKET_ERRNO, where,
+ "(Error number not translated)")); /* What Is the VM equiv? */
+#define ER_NO_TRANS_DONE
+#endif /* VM */
+
+#ifdef VMS
+#ifdef MULTINET
+ CTRACE((tfp, MY_FORMAT, SOCKET_ERRNO, where,
+ vms_errno_string()));
+#else
+ CTRACE((tfp, MY_FORMAT, SOCKET_ERRNO, where,
+ ((SOCKET_ERRNO > 0 && SOCKET_ERRNO <= 65) ?
+ strerror(SOCKET_ERRNO) : "(Error number not translated)")));
+#endif /* MULTINET */
+#define ER_NO_TRANS_DONE
+#endif /* VMS */
+
+#ifdef HAVE_STRERROR
+ CTRACE((tfp, MY_FORMAT, SOCKET_ERRNO, where,
+ strerror(SOCKET_ERRNO)));
+#define ER_NO_TRANS_DONE
+#endif /* HAVE_STRERROR */
+
+#ifndef ER_NO_TRANS_DONE
+ CTRACE((tfp, MY_FORMAT, SOCKET_ERRNO, where,
+ (SOCKET_ERRNO < sys_nerr ?
+ sys_errlist[SOCKET_ERRNO] : "Unknown error")));
+#endif /* !ER_NO_TRANS_DONE */
+
+#ifdef VMS
+#ifndef MULTINET
+ CTRACE((tfp,
+ " Unix error number (SOCKET_ERRNO) = %ld dec\n",
+ SOCKET_ERRNO));
+ CTRACE((tfp,
+ " VMS error (vaxc$errno) = %lx hex\n",
+ vaxc$errno));
+#endif /* MULTINET */
+#endif /* VMS */
+
+ set_errno(saved_errno);
+
+#ifdef VMS
+ /*
+ * uerrno and errno happen to be zero if vmserrno <> 0
+ */
+#ifdef MULTINET
+ status = -vmserrno;
+#else
+ status = -vaxc$errno;
+#endif /* MULTINET */
+#else
+ status = -SOCKET_ERRNO;
+#endif /* VMS */
+ return status;
+}
+
+/* Parse a cardinal value parse_cardinal()
+ * ----------------------
+ *
+ * On entry,
+ * *pp points to first character to be interpreted, terminated by
+ * non 0:9 character.
+ * *pstatus points to status already valid
+ * maxvalue gives the largest allowable value.
+ *
+ * On exit,
+ * *pp points to first unread character
+ * *pstatus points to status updated iff bad
+ */
+unsigned int HTCardinal(int *pstatus,
+ char **pp,
+ unsigned int max_value)
+{
+ unsigned int n;
+
+ if ((**pp < '0') || (**pp > '9')) { /* Null string is error */
+ *pstatus = -3; /* No number where one expected */
+ return 0;
+ }
+
+ n = 0;
+ while ((**pp >= '0') && (**pp <= '9'))
+ n = n * 10 + (unsigned) (*((*pp)++) - '0');
+
+ if (n > max_value) {
+ *pstatus = -4; /* Cardinal outside range */
+ return 0;
+ }
+
+ return n;
+}
+
+#ifndef DECNET /* Function only used below for a trace message */
+/* Produce a string for an Internet address
+ * ----------------------------------------
+ *
+ * On exit,
+ * returns a pointer to a static string which must be copied if
+ * it is to be kept.
+ */
+const char *HTInetString(LY_SOCKADDR * soc_A)
+{
+#ifdef INET6
+ static char hostbuf[MAXHOSTNAMELEN];
+ struct sockaddr *soc_addr = &(soc_A->soc_address);
+
+ getnameinfo(soc_addr,
+ SA_LEN(soc_addr),
+ hostbuf, (socklen_t) sizeof(hostbuf),
+ NULL, 0,
+ NI_NUMERICHOST);
+ return hostbuf;
+#else
+ struct sockaddr_in *soc_in = &(soc_A->soc_in);
+ static char string[20];
+
+ sprintf(string, "%d.%d.%d.%d",
+ (int) *((unsigned char *) (&soc_in->sin_addr) + 0),
+ (int) *((unsigned char *) (&soc_in->sin_addr) + 1),
+ (int) *((unsigned char *) (&soc_in->sin_addr) + 2),
+ (int) *((unsigned char *) (&soc_in->sin_addr) + 3));
+ return string;
+#endif /* INET6 */
+}
+#endif /* !DECNET */
+
+/* Check whether string is a valid Internet hostname - kw
+ * -------------------------------------------------
+ *
+ * Checks whether
+ * - contains only valid chars for domain names (actually, the
+ * restrictions are somewhat relaxed),
+ * - no leading dots or empty segments,
+ * - no segment starts with '-' or '+' [this protects telnet command],
+ * - max. length of dot-separated segment <= 63 (RFC 1034,1035),
+ * - total length <= 254 (if it ends with dot) or 253 (otherwise)
+ * [an interpretation of RFC 1034,1035, although RFC 1123
+ * suggests 255 as limit - kw].
+ *
+ * Note: user (before '@') and port (after ':') components from
+ * host part of URL should be already stripped (if appropriate)
+ * from the input string.
+ *
+ * On exit,
+ * returns 1 if valid, otherwise 0.
+ */
+BOOL valid_hostname(char *name)
+{
+ int i = 1, iseg = 0;
+ char *cp = name;
+
+ if (!(name && *name))
+ return NO;
+ for (; (*cp && i <= 253); cp++, i++) {
+ if (*cp == '.') {
+ if (iseg == 0) {
+ return NO;
+ } else {
+ iseg = 0;
+ continue;
+ }
+ } else if (iseg == 0 && (*cp == '-' || *cp == '+')) {
+ return NO;
+ } else if (++iseg > 63) {
+ return NO;
+ }
+ if (!isalnum(UCH(*cp)) &&
+ *cp != '-' && *cp != '_' &&
+ *cp != '$' && *cp != '+') {
+ return NO;
+ }
+ }
+ return (BOOL) (*cp == '\0' || (*cp == '.' && iseg != 0 && cp[1] == '\0'));
+}
+
+/* for transfer of status from child to parent: */
+typedef struct _statuses {
+ size_t rehostentlen;
+ int h_length;
+ int child_errno; /* sometimes useful to pass this on */
+ int child_h_errno;
+ BOOL h_errno_valid;
+} STATUSES;
+
+/*
+ * Function to allow us to be killed with a normal signal (not
+ * SIGKILL), but don't go through normal libc exit() processing, which
+ * would screw up parent's stdio. -BL
+ */
+#ifdef NSL_FORK
+static void quench(int sig GCC_UNUSED)
+{
+ _exit(2);
+}
+#endif
+
+int lynx_nsl_status = HT_OK;
+
+#define DEBUG_HOSTENT /* disable in case of problems */
+#define DEBUG_HOSTENT_CHILD /* for NSL_FORK, may screw up trace file */
+
+/*
+ * dump_hostent - dumps the contents of a LYNX_HOSTENT to the
+ * trace log or stderr, including all pointer values, strings, and
+ * addresses, in a format inspired by gdb's print format. - kw
+ */
+static void dump_hostent(const char *msgprefix,
+ const void *data)
+{
+ if (TRACE) {
+ int i;
+ char **pcnt;
+ const LYNX_HOSTENT *phost = data;
+
+ CTRACE((tfp, "%s: %p ", msgprefix, (const void *) phost));
+ if (phost) {
+ CTRACE((tfp, "{ h_name = %p", (void *) phost->h_name));
+ if (phost->h_name) {
+ CTRACE((tfp, " \"%s\",", phost->h_name));
+ } else {
+ CTRACE((tfp, ","));
+ }
+ CTRACE((tfp, "\n\t h_aliases = %p", (void *) phost->h_aliases));
+ if (phost->h_aliases) {
+ CTRACE((tfp, " {"));
+ for (pcnt = phost->h_aliases; *pcnt; pcnt++) {
+ CTRACE((tfp, "%s %p \"%s\"",
+ (pcnt == phost->h_aliases ? " " : ", "),
+ (void *) *pcnt, *pcnt));
+ }
+ CTRACE((tfp, "%s0x0 },\n\t",
+ (*phost->h_aliases ? ", " : " ")));
+ } else {
+ CTRACE((tfp, ",\n\t"));
+ }
+ CTRACE((tfp, " h_addrtype = %d,", phost->h_addrtype));
+ CTRACE((tfp, " h_length = %d,\n\t", phost->h_length));
+ CTRACE((tfp, " h_addr_list = %p", (void *) phost->h_addr_list));
+ if (phost->h_addr_list) {
+ CTRACE((tfp, " {"));
+ for (pcnt = phost->h_addr_list; *pcnt; pcnt++) {
+ CTRACE((tfp, "%s %p",
+ (pcnt == phost->h_addr_list ? "" : ","),
+ (void *) *pcnt));
+ for (i = 0; i < phost->h_length; i++) {
+ CTRACE((tfp, "%s%d%s", (i == 0 ? " \"" : "."),
+ (int) *((unsigned char *) (*pcnt) + i),
+ (i + 1 == phost->h_length ? "\"" : "")));
+ }
+ }
+ if (*phost->h_addr_list) {
+ CTRACE((tfp, ", 0x0 } }"));
+ } else {
+ CTRACE((tfp, " 0x0 } }"));
+ }
+ } else {
+ CTRACE((tfp, "}"));
+ }
+ }
+ CTRACE((tfp, "\n"));
+ fflush(tfp);
+ }
+}
+
+#ifdef NSL_FORK
+
+/*
+ * Even though it is a small amount, we cannot count on reading the whole
+ * struct via a pipe in one read -TD
+ */
+static unsigned read_bytes(int fd, char *buffer, size_t length)
+{
+ unsigned result = 0;
+
+ while (length != 0) {
+ unsigned got = (unsigned) read(fd, buffer, length);
+
+ if ((int) got > 0) {
+ result += got;
+ buffer += got;
+ length -= got;
+ } else {
+ break;
+ }
+ }
+ return result;
+}
+
+static unsigned read_hostent(int fd, char *buffer, size_t length)
+{
+ unsigned have = read_bytes(fd, buffer, length);
+
+ if (have) {
+ LYNX_HOSTENT *data = (LYNX_HOSTENT *) (void *) buffer;
+ char *next_char = (char *) data + sizeof(*data);
+ char **next_ptr = (char **) (void *) next_char;
+ long offset = 0;
+ int n;
+ int num_addrs = 0;
+ int num_aliases = 0;
+
+ if (data->h_addr_list) {
+ data->h_addr_list = next_ptr;
+ while (next_ptr[num_addrs] != 0) {
+ ++num_addrs;
+ }
+ next_ptr += (num_addrs + 1);
+ next_char += (size_t) (num_addrs + 1) * sizeof(data->h_addr_list[0]);
+ }
+
+ if (data->h_aliases) {
+ data->h_aliases = next_ptr;
+ while (next_ptr[num_aliases] != 0) {
+ ++num_aliases;
+ }
+ next_char += (size_t) (num_aliases + 1) * sizeof(data->h_aliases[0]);
+ }
+
+ if (data->h_name) {
+ offset = next_char - data->h_name;
+ data->h_name = next_char;
+ } else if (data->h_addr_list) {
+ offset = next_char - (char *) data->h_addr_list[0];
+ } else if (data->h_aliases) {
+ offset = next_char - (char *) data->h_aliases[0];
+ }
+
+ if (data->h_addr_list) {
+ for (n = 0; n < num_addrs; ++n) {
+ data->h_addr_list[n] += offset;
+ }
+ }
+
+ if (data->h_aliases) {
+ for (n = 0; n < num_aliases; ++n) {
+ data->h_aliases[n] += offset;
+ }
+ }
+ }
+
+ return have;
+}
+#endif /* NSL_FORK */
+
+/*
+ * fill_rehostent - copies as much as possible relevant content from
+ * the LYNX_HOSTENT pointed to by phost to the char buffer given
+ * by rehostent, subject to maximum output length rehostentsize,
+ * following pointers and building self-contained output which can be
+ * cast to a LYNX_HOSTENT. - kw
+ * See also description of LYGetHostByName.
+ */
+#if defined(NSL_FORK) || defined(_WINDOWS_NSL)
+
+#define REHOSTENT_SIZE 128 /* not bigger than pipe buffer! */
+
+typedef struct {
+ LYNX_HOSTENT h;
+ char rest[REHOSTENT_SIZE];
+} AlignedHOSTENT;
+
+static size_t fill_rehostent(void **rehostent,
+ const LYNX_HOSTENT *phost)
+{
+ static const char *this_func = "fill_rehostent";
+
+ LYNX_HOSTENT *data = 0;
+ int num_addrs = 0;
+ int num_aliases = 0;
+ char *result = 0;
+ char *p_next_char;
+ char **p_next_charptr;
+ size_t name_len = 0;
+ size_t need = sizeof(LYNX_HOSTENT);
+ int n;
+
+ if (!phost)
+ return 0;
+
+ if (phost->h_name) {
+ name_len = strlen(phost->h_name);
+ need += name_len + 1;
+ }
+ if (phost->h_addr_list) {
+ while (phost->h_addr_list[num_addrs]) {
+ num_addrs++;
+ }
+ need += ((size_t) num_addrs + 1) * ((size_t) phost->h_length
+ + sizeof(phost->h_addr_list[0]));
+ }
+ if (phost->h_aliases) {
+ while (phost->h_aliases[num_aliases]) {
+ need += strlen(phost->h_aliases[num_aliases]) + 1;
+ num_aliases++;
+ }
+ need += ((size_t) num_aliases + 1) * sizeof(phost->h_aliases[0]);
+ }
+
+ if ((result = calloc(need, sizeof(char))) == 0)
+ outofmem(__FILE__, this_func);
+
+ *rehostent = result;
+
+ data = (LYNX_HOSTENT *) (void *) result;
+
+ data->h_addrtype = phost->h_addrtype;
+ data->h_length = phost->h_length;
+
+ p_next_char = result + sizeof(LYNX_HOSTENT);
+
+ p_next_charptr = (char **) (void *) p_next_char;
+ if (phost->h_addr_list)
+ p_next_char += (size_t) (num_addrs + 1) * sizeof(phost->h_addr_list[0]);
+ if (phost->h_aliases)
+ p_next_char += (size_t) (num_aliases + 1) * sizeof(phost->h_aliases[0]);
+
+ if (phost->h_name) {
+ data->h_name = p_next_char;
+ strcpy(p_next_char, phost->h_name);
+ p_next_char += name_len + 1;
+ }
+
+ if (phost->h_addr_list) {
+ data->h_addr_list = p_next_charptr;
+ for (n = 0; n < num_addrs; ++n) {
+ MemCpy(p_next_char, phost->h_addr_list[n], phost->h_length);
+ *p_next_charptr++ = p_next_char;
+ p_next_char += phost->h_length;
+ }
+ ++p_next_charptr;
+ }
+
+ if (phost->h_aliases) {
+ data->h_aliases = p_next_charptr;
+ for (n = 0; n < num_aliases; ++n) {
+ strcpy(p_next_char, phost->h_aliases[n]);
+ *p_next_charptr++ = p_next_char;
+ p_next_char += strlen(phost->h_aliases[n]) + 1;;
+ }
+ }
+ return need;
+}
+#endif /* NSL_FORK */
+
+/*
+ * This chunk of code is used in both win32 and cygwin.
+ */
+#if defined(_WINDOWS_NSL)
+static LYNX_HOSTENT *gbl_phost; /* Pointer to host - See netdb.h */
+
+#if !(defined(__CYGWIN__) && defined(NSL_FORK))
+static int donelookup;
+
+static unsigned long __stdcall _fork_func(void *arg)
+{
+ const char *host = (const char *) arg;
+ static AlignedHOSTENT aligned_full_rehostent;
+ char *rehostent = (char *) &aligned_full_rehostent;
+ size_t rehostentlen = 0;
+
+#ifdef SH_EX
+ unsigned long addr;
+
+ addr = (unsigned long) inet_addr(host);
+ if (addr != INADDR_NONE)
+ gbl_phost = gethostbyaddr((char *) &addr, sizeof(addr), AF_INET);
+ else
+ gbl_phost = gethostbyname(host);
+#else
+ gbl_phost = gethostbyname(host);
+#endif
+
+ if (gbl_phost) {
+ rehostentlen = fill_rehostent((void **) &rehostent, gbl_phost);
+ if (rehostentlen == 0) {
+ gbl_phost = (LYNX_HOSTENT *) NULL;
+ } else {
+ gbl_phost = (LYNX_HOSTENT *) rehostent;
+ }
+ }
+
+ donelookup = TRUE;
+ return (unsigned long) (gbl_phost);
+}
+#endif /* __CYGWIN__ */
+#endif /* _WINDOWS_NSL */
+
+#ifdef NSL_FORK
+
+#ifndef HAVE_H_ERRNO
+#undef h_errno
+#define h_errno my_errno
+static int my_errno;
+
+#else /* we do HAVE_H_ERRNO: */
+#ifndef h_errno /* there may be a macro as well as the extern data */
+extern int h_errno;
+#endif
+#endif
+
+static BOOL setup_nsl_fork(void (*really) (const char *,
+ const char *,
+ STATUSES *,
+ void **),
+ unsigned (*readit) (int, char *, size_t),
+ void (*dumpit) (const char *, const void *),
+ const char *host,
+ const char *port,
+ void **rehostent)
+{
+ static const char *this_func = "setup_nsl_fork";
+
+ STATUSES statuses;
+
+ /*
+ * fork-based gethostbyname() with checks for interrupts.
+ * - Tom Zerucha (tz@execpc.com) & FM
+ */
+ int got_rehostent = 0;
+
+#if HAVE_SIGACTION
+ sigset_t old_sigset;
+ sigset_t new_sigset;
+#endif
+ /*
+ * Pipe, child pid, status buffers, start time, select() control
+ * variables.
+ */
+ int fpid, waitret;
+ int pfd[2], selret;
+ unsigned readret;
+
+#ifdef HAVE_TYPE_UNIONWAIT
+ union wait waitstat;
+
+#else
+ int waitstat = 0;
+#endif
+ time_t start_time = time((time_t *) 0);
+ fd_set readfds;
+ struct timeval one_second;
+ long dns_patience = 30; /* how many seconds will we wait for DNS? */
+ int child_exited = 0;
+
+ memset(&statuses, 0, sizeof(STATUSES));
+ statuses.h_errno_valid = NO;
+
+ /*
+ * Reap any children that have terminated since last time through.
+ * This might include children that we killed, then waited with WNOHANG
+ * before they were actually ready to be reaped. (Should be max of 1
+ * in this state, but the loop is safe if waitpid() is implemented
+ * correctly: returns 0 when children exist but none have exited; -1
+ * with errno == ECHILD when no children.) -BL
+ */
+ do {
+ waitret = waitpid(-1, 0, WNOHANG);
+ } while (waitret > 0 || (waitret == -1 && errno == EINTR));
+ waitret = 0;
+
+ IGNORE_RC(pipe(pfd));
+
+#if HAVE_SIGACTION
+ /*
+ * Attempt to prevent a rare situation where the child could execute
+ * the Lynx signal handlers because it gets killed before it even has a
+ * chance to reset its handlers, resulting in bogus 'Exiting via
+ * interrupt' message and screen corruption or worse.
+ * Should that continue to be reported, for systems without
+ * sigprocmask(), we need to find a different solutions for those. -
+ * kw 19990430
+ */
+ sigemptyset(&new_sigset);
+ sigaddset(&new_sigset, SIGTERM);
+ sigaddset(&new_sigset, SIGINT);
+#ifndef NOSIGHUP
+ sigaddset(&new_sigset, SIGHUP);
+#endif /* NOSIGHUP */
+#ifdef SIGTSTP
+ sigaddset(&new_sigset, SIGTSTP);
+#endif /* SIGTSTP */
+#ifdef SIGWINCH
+ sigaddset(&new_sigset, SIGWINCH);
+#endif /* SIGWINCH */
+ sigprocmask(SIG_BLOCK, &new_sigset, &old_sigset);
+#endif /* HAVE_SIGACTION */
+
+ if ((fpid = fork()) == 0) {
+ /*
+ * Child - for the long call.
+ *
+ * Make sure parent can kill us at will. -BL
+ */
+ (void) signal(SIGTERM, quench);
+
+ /*
+ * Also make sure the child does not run one of the signal handlers
+ * that may have been installed by Lynx if one of those signals
+ * occurs. For example we don't want the child to remove temp
+ * files on ^C, let the parent deal with that. - kw
+ */
+ (void) signal(SIGINT, quench);
+#ifndef NOSIGHUP
+ (void) signal(SIGHUP, quench);
+#endif /* NOSIGHUP */
+#ifdef SIGTSTP
+ if (no_suspend)
+ (void) signal(SIGTSTP, SIG_IGN);
+ else
+ (void) signal(SIGTSTP, SIG_DFL);
+#endif /* SIGTSTP */
+#ifdef SIGWINCH
+ (void) signal(SIGWINCH, SIG_IGN);
+#endif /* SIGWINCH */
+#ifndef __linux__
+#ifndef DOSPATH
+ signal(SIGBUS, SIG_DFL);
+#endif /* DOSPATH */
+#endif /* !__linux__ */
+ signal(SIGSEGV, SIG_DFL);
+ signal(SIGILL, SIG_DFL);
+
+#if HAVE_SIGACTION
+ /* Restore signal mask to whatever it was before the fork. -kw */
+ sigprocmask(SIG_SETMASK, &old_sigset, NULL);
+#endif /* HAVE_SIGACTION */
+
+ /*
+ * Child won't use read side. -BL
+ */
+ close(pfd[0]);
+#ifdef HAVE_H_ERRNO
+ /* to detect cases when it doesn't get set although it should */
+ h_errno = -2;
+#endif
+ set_errno(0);
+ really(host, port, &statuses, rehostent);
+ /*
+ * Send variables indicating status of lookup to parent. That
+ * includes rehostentlen, which the parent will use as the size for
+ * the second read (if > 0).
+ */
+ if (!statuses.child_errno)
+ statuses.child_errno = errno;
+ IGNORE_RC(write(pfd[1], &statuses, sizeof(statuses)));
+
+ if (statuses.rehostentlen) {
+ /*
+ * Return our resulting rehostent through pipe...
+ */
+ IGNORE_RC(write(pfd[1], *rehostent, statuses.rehostentlen));
+ close(pfd[1]);
+ _exit(0);
+ } else {
+ /*
+ * ... or return error as exit code.
+ */
+ _exit(1);
+ }
+ }
+#if HAVE_SIGACTION
+ /*
+ * (parent) Restore signal mask to whatever it was before the fork. -
+ * kw
+ */
+ sigprocmask(SIG_SETMASK, &old_sigset, NULL);
+#endif /* HAVE_SIGACTION */
+
+ /*
+ * (parent) Wait until lookup finishes, or interrupt, or cycled too
+ * many times (just in case) -BL
+ */
+
+ close(pfd[1]); /* parent won't use write side -BL */
+
+ if (fpid < 0) { /* fork failed */
+ close(pfd[0]);
+ goto failed;
+ }
+
+ while (child_exited || (long) (time((time_t *) 0) - start_time) < dns_patience) {
+
+ FD_ZERO(&readfds);
+ /*
+ * This allows us to abort immediately, not after 1-second timeout,
+ * when user hits abort key. Can't do this when using SLANG (or at
+ * least I don't know how), so SLANG users must live with up-to-1s
+ * timeout. -BL
+ *
+ * Whoops -- we need to make sure stdin is actually selectable!
+ * /dev/null isn't, on some systems, which makes some useful Lynx
+ * invocations fail. -BL
+ */
+ {
+ int kbd_fd = LYConsoleInputFD(TRUE);
+
+ if (kbd_fd != INVSOC) {
+ FD_SET(kbd_fd, &readfds);
+ }
+ }
+
+ one_second.tv_sec = 1;
+ one_second.tv_usec = 0;
+ FD_SET(pfd[0], &readfds);
+
+ /*
+ * Return when data received, interrupted, or failed. If nothing
+ * is waiting, we sleep for 1 second in select(), to be nice to the
+ * system. -BL
+ */
+#ifdef SOCKS
+ if (socks_flag)
+ selret = Rselect(pfd[0] + 1, &readfds, NULL, NULL, &one_second);
+ else
+#endif /* SOCKS */
+ selret = select(pfd[0] + 1, &readfds, NULL, NULL, &one_second);
+
+ if ((selret > 0) && FD_ISSET(pfd[0], &readfds)) {
+ /*
+ * First get status, including length of address. -BL, kw
+ */
+ readret = read_bytes(pfd[0], (char *) &statuses, sizeof(statuses));
+ if (readret == sizeof(statuses)) {
+ h_errno = statuses.child_h_errno;
+ set_errno(statuses.child_errno);
+#ifdef HAVE_H_ERRNO
+ if (statuses.h_errno_valid) {
+ lynx_nsl_status = HT_H_ERRNO_VALID;
+ /*
+ * If something went wrong in the child process other
+ * than normal lookup errors, and it appears that we
+ * have enough info to know what went wrong, generate
+ * diagnostic output. ENOMEM observed on linux in
+ * processes constrained with ulimit. It would be too
+ * unkind to abort the session, access to local files
+ * or through a proxy may still work. - kw
+ */
+ if (
+#ifdef NETDB_INTERNAL /* linux glibc: defined in netdb.h */
+ (errno && h_errno == NETDB_INTERNAL) ||
+#endif
+ (errno == ENOMEM &&
+ statuses.rehostentlen == 0 &&
+ /* should probably be NETDB_INTERNAL if child
+ memory exhausted, but we may find that
+ h_errno remains unchanged. - kw */
+ h_errno == -2)) {
+#ifndef MULTINET
+ HTInetStatus("CHILD gethostbyname");
+#endif
+ HTAlert(LYStrerror(statuses.child_errno));
+ if (errno == ENOMEM) {
+ /*
+ * Not much point in continuing, right? Fake a
+ * 'z', should shorten pointless guessing
+ * cycle. - kw
+ */
+ LYFakeZap(YES);
+ }
+ }
+ }
+#endif /* HAVE_H_ERRNO */
+ if (statuses.rehostentlen != 0) {
+ /*
+ * Then get the full reorganized hostent. -BL, kw
+ */
+ if ((*rehostent = malloc(statuses.rehostentlen)) == 0)
+ outofmem(__FILE__, this_func);
+ readret = (*readit) (pfd[0], *rehostent, statuses.rehostentlen);
+#ifdef DEBUG_HOSTENT
+ dumpit("Read from pipe", *rehostent);
+#endif
+ if (readret == statuses.rehostentlen) {
+ got_rehostent = 1;
+ lynx_nsl_status = HT_OK;
+ } else if (!statuses.h_errno_valid) {
+ lynx_nsl_status = HT_INTERNAL;
+ }
+ }
+ } else {
+ lynx_nsl_status = HT_ERROR;
+ }
+ /*
+ * Make sure child is cleaned up. -BL
+ */
+ if (!child_exited)
+ waitret = waitpid(fpid, &waitstat, WNOHANG);
+ if (!WIFEXITED(waitstat) && !WIFSIGNALED(waitstat)) {
+ kill(fpid, SIGTERM);
+ waitret = waitpid(fpid, &waitstat, WNOHANG);
+ }
+ break;
+ }
+
+ /*
+ * Clean up if child exited before & no data received. -BL
+ */
+ if (child_exited) {
+ waitret = waitpid(fpid, &waitstat, WNOHANG);
+ break;
+ }
+ /*
+ * If child exited, loop once more looking for data. -BL
+ */
+ if ((waitret = waitpid(fpid, &waitstat, WNOHANG)) > 0) {
+ /*
+ * Data will be arriving right now, so make sure we don't
+ * short-circuit out for too many loops, and skip the interrupt
+ * check. -BL
+ */
+ child_exited = 1;
+ continue;
+ }
+
+ /*
+ * Abort if interrupt key pressed.
+ */
+ if (HTCheckForInterrupt()) {
+ CTRACE((tfp, "%s: INTERRUPTED gethostbyname.\n", this_func));
+ kill(fpid, SIGTERM);
+ waitpid(fpid, NULL, WNOHANG);
+ close(pfd[0]);
+ lynx_nsl_status = HT_INTERRUPTED;
+ return FALSE;
+ }
+ }
+ close(pfd[0]);
+ if (waitret <= 0) {
+ kill(fpid, SIGTERM);
+ waitret = waitpid(fpid, &waitstat, WNOHANG);
+ }
+ if (waitret > 0) {
+ if (WIFEXITED(waitstat)) {
+ CTRACE((tfp,
+ "%s: NSL_FORK child %d exited, status 0x%x.\n",
+ this_func, (int) waitret, WEXITSTATUS(waitstat)));
+ } else if (WIFSIGNALED(waitstat)) {
+ CTRACE((tfp,
+ "%s: NSL_FORK child %d got signal, status 0x%x!\n",
+ this_func, (int) waitret, WTERMSIG(waitstat)));
+#ifdef WCOREDUMP
+ if (WCOREDUMP(waitstat)) {
+ CTRACE((tfp,
+ "%s: NSL_FORK child %d dumped core!\n",
+ this_func, (int) waitret));
+ }
+#endif /* WCOREDUMP */
+ } else if (WIFSTOPPED(waitstat)) {
+ CTRACE((tfp,
+ "%s: NSL_FORK child %d is stopped, status 0x%x!\n",
+ this_func, (int) waitret, WSTOPSIG(waitstat)));
+ }
+ }
+ if (!got_rehostent) {
+ goto failed;
+ }
+ return TRUE;
+ failed:
+ return FALSE;
+}
+
+/*
+ * This is called via the child-side of the fork.
+ */
+static void really_gethostbyname(const char *host,
+ const char *port GCC_UNUSED,
+ STATUSES * statuses,
+ void **rehostent)
+{
+ LYNX_HOSTENT *phost; /* Pointer to host - See netdb.h */
+ LYNX_HOSTENT *result = 0;
+
+ (void) port;
+
+ phost = gethostbyname(host);
+ statuses->rehostentlen = 0;
+ statuses->child_errno = errno;
+ statuses->child_h_errno = h_errno;
+#ifdef HAVE_H_ERRNO
+ statuses->h_errno_valid = YES;
+#endif
+#ifdef MVS
+ CTRACE((tfp, "really_gethostbyname() returned %d\n", phost));
+#endif /* MVS */
+
+#ifdef DEBUG_HOSTENT_CHILD
+ dump_hostent("CHILD gethostbyname", phost);
+#endif
+ if (OK_HOST(phost)) {
+ statuses->rehostentlen = fill_rehostent(rehostent, phost);
+ result = (LYNX_HOSTENT *) (*rehostent);
+#ifdef DEBUG_HOSTENT_CHILD
+ dump_hostent("CHILD fill_rehostent", result);
+#endif
+ }
+ if (statuses->rehostentlen <= sizeof(LYNX_HOSTENT) || !OK_HOST(result)) {
+ statuses->rehostentlen = 0;
+ statuses->h_length = 0;
+ } else {
+ statuses->h_length = result->h_length;
+#ifdef HAVE_H_ERRNO
+ if (h_errno == -2) /* success, but h_errno unchanged? */
+ statuses->h_errno_valid = NO;
+#endif
+ }
+}
+#endif /* NSL_FORK */
+
+/* Resolve an internet hostname, like gethostbyname
+ * ------------------------------------------------
+ *
+ * On entry,
+ * host points to the given host name, not numeric address,
+ * without colon or port number.
+ *
+ * On exit,
+ * returns a pointer to a LYNX_HOSTENT in static storage,
+ * or NULL in case of error or user interruption.
+ *
+ * The interface is intended to be exactly the same as for (Unix)
+ * gethostbyname(), except for the following:
+ *
+ * If NSL_FORK is not used, the result of gethostbyname is returned
+ * directly. Otherwise:
+ * All lists, addresses, and strings referred to by pointers in the
+ * returned struct are located, together with the returned struct
+ * itself, in a buffer of size REHOSTENT_SIZE. If not everything fits,
+ * some info is omitted, but the function is careful to still return
+ * a valid structure, without truncating strings; it tries to return,
+ * in order of decreasing priority, the first address (h_addr_list[0]), the
+ * official name (h_name), the additional addresses, then alias names.
+ *
+ * If NULL is returned, the reason is made available in the global
+ * variable lynx_nsl_status, with one of the following values:
+ * HT_INTERRUPTED Interrupted by user
+ * HT_NOT_ACCEPTABLE Hostname detected as invalid
+ * (also sets h_errno)
+ * HT_H_ERRNO_VALID An error occurred, and h_errno holds
+ * an appropriate value
+ * HT_ERROR Resolver error, reason not known
+ * HT_INTERNAL Internal error
+ */
+static LYNX_HOSTENT *LYGetHostByName(char *host)
+{
+ static const char *this_func = "LYGetHostByName";
+
+#ifdef NSL_FORK
+ /* for transfer of result between from child to parent: */
+ LYNX_HOSTENT *rehostent = 0;
+#endif /* NSL_FORK */
+
+ LYNX_HOSTENT *result_phost = NULL;
+
+#ifdef __DJGPP__
+ _resolve_hook = ResolveYield;
+#endif
+
+ if (!host) {
+ CTRACE((tfp, "%s: Can't parse `NULL'.\n", this_func));
+ lynx_nsl_status = HT_INTERNAL;
+ return NULL;
+ }
+ CTRACE((tfp, "%s: parsing `%s'.\n", this_func, host));
+
+ /* Could disable this if all our callers already check - kw */
+ if (HTCheckForInterrupt()) {
+ CTRACE((tfp, "%s: INTERRUPTED for '%s'.\n", this_func, host));
+ lynx_nsl_status = HT_INTERRUPTED;
+ return NULL;
+ }
+
+ if (!valid_hostname(host)) {
+ lynx_nsl_status = HT_NOT_ACCEPTABLE;
+#ifdef NO_RECOVERY
+#ifdef _WINDOWS
+ WSASetLastError(NO_RECOVERY);
+#else
+ h_errno = NO_RECOVERY;
+#endif
+#endif
+ return NULL;
+ }
+#ifdef MVS /* Outstanding problem with crash in MVS gethostbyname */
+ CTRACE((tfp, "%s: Calling gethostbyname(%s)\n", this_func, host));
+#endif /* MVS */
+
+ CTRACE_FLUSH(tfp); /* so child messages will not mess up parent log */
+
+ lynx_nsl_status = HT_INTERNAL; /* should be set to something else below */
+
+#ifdef NSL_FORK
+ if (!setup_nsl_fork(really_gethostbyname,
+ read_hostent,
+ dump_hostent,
+ host, NULL, (void **) &rehostent)) {
+ goto failed;
+ }
+ result_phost = rehostent;
+#else /* Not NSL_FORK: */
+
+#ifdef _WINDOWS_NSL
+ {
+ HANDLE hThread;
+ DWORD dwThreadID;
+
+#ifndef __CYGWIN__
+ if (!system_is_NT) { /* for Windows9x */
+ unsigned long t;
+
+ t = (unsigned long) inet_addr(host);
+ if (t != INADDR_NONE)
+ gbl_phost = gethostbyaddr((char *) &t, sizeof(t), AF_INET);
+ else
+ gbl_phost = gethostbyname(host);
+ } else { /* for Windows NT */
+#endif /* !__CYGWIN__ */
+ gbl_phost = (LYNX_HOSTENT *) NULL;
+ donelookup = FALSE;
+
+#if defined(__CYGWIN__) || defined(USE_WINSOCK2_H)
+ SetLastError(WSAHOST_NOT_FOUND);
+#else
+ WSASetLastError(WSAHOST_NOT_FOUND);
+#endif
+
+ hThread = CreateThread(NULL, 4096UL, _fork_func, host, 0UL,
+ &dwThreadID);
+ if (!hThread)
+ MessageBox(NULL, "CreateThread",
+ "CreateThread Failed", 0L);
+
+ while (!donelookup) {
+ if (HTCheckForInterrupt()) {
+ /* Note that host is a character array and is not freed */
+ /* to avoid possible subthread problems: */
+ if (!CloseHandle(hThread)) {
+ MessageBox((void *) NULL,
+ "CloseHandle", "CloseHandle Failed", 0L);
+ }
+ lynx_nsl_status = HT_INTERRUPTED;
+ return NULL;
+ }
+ }
+#ifndef __CYGWIN__
+ }
+#endif /* !__CYGWIN__ */
+ if (gbl_phost) {
+ lynx_nsl_status = HT_OK;
+ result_phost = gbl_phost;
+ } else {
+ lynx_nsl_status = HT_ERROR;
+ goto failed;
+ }
+ }
+
+#else /* !NSL_FORK, !_WINDOWS_NSL: */
+ {
+ LYNX_HOSTENT *phost;
+
+ phost = gethostbyname(host); /* See netdb.h */
+#ifdef MVS
+ CTRACE((tfp, "%s: gethostbyname() returned %d\n", this_func, phost));
+#endif /* MVS */
+ if (phost) {
+ lynx_nsl_status = HT_OK;
+ result_phost = phost;
+ } else {
+ lynx_nsl_status = HT_H_ERRNO_VALID;
+ goto failed;
+ }
+ }
+#endif /* !NSL_FORK, !_WINDOWS_NSL */
+#endif /* !NSL_FORK */
+
+#ifdef DEBUG_HOSTENT
+ dump_hostent(this_func, result_phost);
+ CTRACE((tfp, "%s: Resolved name to a hostent.\n", this_func));
+#endif
+
+ return result_phost; /* OK */
+
+ failed:
+ CTRACE((tfp, "%s: Can't find internet node name `%s'.\n", this_func, host));
+ return NULL;
+}
+
+BOOLEAN LYCheckHostByName(char *host)
+{
+ LYNX_HOSTENT *data = LYGetHostByName(host);
+ BOOLEAN result = (data != NULL);
+
+ FREE_NSL_FORK(data);
+ return result;
+}
+
+/* Parse a network node address and port
+ * -------------------------------------
+ *
+ * On entry,
+ * str points to a string with a node name or number,
+ * with optional trailing colon and port number.
+ * soc_in points to the binary internet or decnet address field.
+ *
+ * On exit,
+ * *soc_in is filled in. If no port is specified in str, that
+ * field is left unchanged in *soc_in.
+ */
+#ifndef INET6
+static int HTParseInet(SockA *soc_in, const char *str)
+{
+ static const char *this_func = "HTParseInet";
+
+ char *port;
+ int dotcount_ip = 0; /* for dotted decimal IP addr */
+ char *strptr;
+ char *host = NULL;
+
+ if (!str) {
+ CTRACE((tfp, "%s: Can't parse `NULL'.\n", this_func));
+ return -1;
+ }
+ CTRACE((tfp, "%s: parsing `%s'.\n", this_func, str));
+ if (HTCheckForInterrupt()) {
+ CTRACE((tfp, "%s: INTERRUPTED for '%s'.\n", this_func, str));
+ return -1;
+ }
+ StrAllocCopy(host, str); /* Make a copy we can mutilate */
+ /*
+ * Parse port number if present.
+ */
+ if ((port = StrChr(host, ':')) != NULL) {
+ *port++ = 0; /* Chop off port */
+ strptr = port;
+ if (port[0] >= '0' && port[0] <= '9') {
+#ifdef UNIX
+ soc_in->sin_port = (PortNumber) htons(strtol(port, &strptr, 10));
+#else /* VMS: */
+#ifdef DECNET
+ soc_in->sdn_objnum = (unsigned char) (strtol(port, &strptr, 10));
+#else
+ soc_in->sin_port = htons((PortNumber) strtol(port, &strptr, 10));
+#endif /* Decnet */
+#endif /* Unix vs. VMS */
+#ifdef SUPPRESS /* 1. crashes!?!. 2. URL syntax has number not name */
+ } else {
+ struct servent *serv = getservbyname(port, (char *) 0);
+
+ if (serv) {
+ soc_in->sin_port = serv->s_port;
+ } else {
+ CTRACE((tfp, "TCP: Unknown service %s\n", port));
+ }
+#endif /* SUPPRESS */
+ }
+ if (strptr && *strptr != '\0') {
+ FREE(host);
+ HTAlwaysAlert(NULL, gettext("Address has invalid port"));
+ return -1;
+ }
+ }
+#ifdef DECNET
+ /*
+ * Read Decnet node name. @@ Should know about DECnet addresses, but it's
+ * probably worth waiting until the Phase transition from IV to V.
+ */
+ soc_in->sdn_nam.n_len = min(DN_MAXNAML, strlen(host)); /* <=6 in phase 4 */
+ StrNCpy(soc_in->sdn_nam.n_name, host, soc_in->sdn_nam.n_len + 1);
+ CTRACE((tfp,
+ "DECnet: Parsed address as object number %d on host %.6s...\n",
+ soc_in->sdn_objnum, host));
+#else /* parse Internet host: */
+
+ if (*host >= '0' && *host <= '9') { /* Test for numeric node address: */
+ strptr = host;
+ while (*strptr) {
+ if (*strptr == '.') {
+ dotcount_ip++;
+ } else if (!isdigit(UCH(*strptr))) {
+ break;
+ }
+ strptr++;
+ }
+ if (*strptr) { /* found non-numeric, assume domain name */
+ dotcount_ip = 0;
+ }
+ }
+
+ /*
+ * Parse host number if present.
+ */
+ if (dotcount_ip == 3) /* Numeric node address: */
+ {
+#ifdef GUSI
+ soc_in->sin_addr = inet_addr(host); /* See netinet/in.h */
+#else
+#ifdef HAVE_INET_ATON
+ if (!inet_aton(host, &(soc_in->sin_addr))) {
+ CTRACE((tfp, "inet_aton(%s) returns error\n", host));
+ FREE(host);
+ return -1;
+ }
+#else
+ soc_in->sin_addr.s_addr = inet_addr(host); /* See arpa/inet.h */
+#endif /* HAVE_INET_ATON */
+#endif /* GUSI */
+ FREE(host);
+ } else { /* Alphanumeric node name: */
+
+#ifdef MVS /* Outstanding problem with crash in MVS gethostbyname */
+ CTRACE((tfp, "%s: Calling LYGetHostByName(%s)\n", this_func, host));
+#endif /* MVS */
+
+#ifdef _WINDOWS_NSL
+ gbl_phost = LYGetHostByName(host); /* See above */
+ if (!gbl_phost)
+ goto failed;
+ MemCpy((void *) &soc_in->sin_addr, gbl_phost->h_addr_list[0], gbl_phost->h_length);
+ FREE(gbl_phost);
+#else /* !_WINDOWS_NSL */
+ {
+ LYNX_HOSTENT *phost;
+
+ phost = LYGetHostByName(host); /* See above */
+
+ if (!phost)
+ goto failed;
+ if (phost->h_length != sizeof soc_in->sin_addr) {
+ HTAlwaysAlert(host, gettext("Address length looks invalid"));
+ }
+ MemCpy((void *) &soc_in->sin_addr, phost->h_addr_list[0], phost->h_length);
+ FREE_NSL_FORK(phost);
+ }
+#endif /* _WINDOWS_NSL */
+
+ FREE(host);
+ } /* Alphanumeric node name */
+
+ CTRACE((tfp,
+ "%s: Parsed address as port %d, IP address %d.%d.%d.%d\n",
+ this_func,
+ (int) ntohs(soc_in->sin_port),
+ (int) *((unsigned char *) (&soc_in->sin_addr) + 0),
+ (int) *((unsigned char *) (&soc_in->sin_addr) + 1),
+ (int) *((unsigned char *) (&soc_in->sin_addr) + 2),
+ (int) *((unsigned char *) (&soc_in->sin_addr) + 3)));
+#endif /* Internet vs. Decnet */
+
+ return 0; /* OK */
+
+ failed:
+ CTRACE((tfp, "%s: Can't find internet node name `%s'.\n",
+ this_func, host));
+ FREE(host);
+ switch (lynx_nsl_status) {
+ case HT_NOT_ACCEPTABLE:
+ case HT_INTERRUPTED:
+ return lynx_nsl_status;
+ default:
+ return -1;
+ }
+}
+#endif /* !INET6 */
+
+#ifdef INET6
+
+static void dump_addrinfo(const char *tag, const void *data)
+{
+ const LYNX_ADDRINFO *res;
+ int count = 0;
+
+ CTRACE((tfp, "dump_addrinfo %s:\n", tag));
+ for (res = (const LYNX_ADDRINFO *) data; res; res = res->ai_next) {
+ char hostbuf[1024], portbuf[1024];
+
+ ++count;
+ hostbuf[0] = '\0';
+ portbuf[0] = '\0';
+ getnameinfo(res->ai_addr, res->ai_addrlen,
+ hostbuf, (socklen_t) sizeof(hostbuf),
+ portbuf, (socklen_t) sizeof(portbuf),
+ NI_NUMERICHOST | NI_NUMERICSERV);
+
+ CTRACE((tfp,
+ "\t[%d] family %d, socktype %d, protocol %d addr %s port %s\n",
+ count,
+ res->ai_family,
+ res->ai_socktype,
+ res->ai_protocol,
+ hostbuf,
+ portbuf));
+ }
+}
+
+#if defined(NSL_FORK)
+
+/*
+ * Copy the relevant information (on the child-side).
+ */
+static size_t fill_addrinfo(void **buffer,
+ const LYNX_ADDRINFO *phost)
+{
+ static const char *this_func = "fill_addinfo";
+
+ const LYNX_ADDRINFO *q;
+ LYNX_ADDRINFO *actual;
+ LYNX_ADDRINFO *result;
+ int count = 0;
+ int limit = 0;
+ size_t need = sizeof(LYNX_ADDRINFO);
+ char *heap;
+
+ CTRACE((tfp, "filladdr_info %p\n", (const void *) phost));
+ for (q = phost; q != 0; q = q->ai_next) {
+ ++limit;
+ need += phost->ai_addrlen;
+ need += sizeof(LYNX_ADDRINFO);
+ }
+ CTRACE((tfp, "...fill_addrinfo %d:%lu\n", limit, (unsigned long) need));
+
+ if ((result = (LYNX_ADDRINFO *) calloc(1, need)) == 0)
+ outofmem(__FILE__, this_func);
+
+ *buffer = actual = result;
+ heap = ((char *) actual) + ((size_t) limit * sizeof(LYNX_ADDRINFO));
+
+ for (count = 0; count < limit; ++count) {
+
+ /*
+ * copying the whole structure seems simpler but because it is not
+ * packed, uninitialized gaps make it hard to analyse with valgrind.
+ */
+ /* *INDENT-EQLS* */
+ actual->ai_flags = phost->ai_flags;
+ actual->ai_family = phost->ai_family;
+ actual->ai_socktype = phost->ai_socktype;
+ actual->ai_protocol = phost->ai_protocol;
+ actual->ai_addrlen = phost->ai_addrlen;
+ actual->ai_addr = (struct sockaddr *) (void *) heap;
+
+ MemCpy(heap, phost->ai_addr, phost->ai_addrlen);
+ heap += phost->ai_addrlen;
+
+ phost = phost->ai_next;
+
+ actual->ai_next = ((count + 1 < limit)
+ ? (actual + 1)
+ : 0);
+ ++actual;
+ }
+ return (size_t) (heap - (char *) result);
+}
+
+/*
+ * Read data, repair pointers as done in fill_addrinfo().
+ */
+static unsigned read_addrinfo(int fd, char *buffer, size_t length)
+{
+ unsigned result = read_bytes(fd, buffer, length);
+ LYNX_ADDRINFO *actual = (LYNX_ADDRINFO *) (void *) buffer;
+ LYNX_ADDRINFO *res;
+ int count = 0;
+ int limit;
+ char *heap;
+
+ CTRACE((tfp, "read_addrinfo length %lu\n", (unsigned long) length));
+ for (limit = 0; actual[limit].ai_next; ++limit) {
+ }
+ ++limit;
+ heap = (char *) (actual + limit);
+ CTRACE((tfp, "...read_addrinfo %d items\n", limit));
+
+ for (res = actual, count = 0; count < limit; ++count) {
+ res->ai_addr = (struct sockaddr *) (void *) heap;
+ heap += res->ai_addrlen;
+ if (count < limit - 1) {
+ res->ai_next = (res + 1);
+ ++res;
+ } else {
+ res->ai_next = 0;
+ }
+ }
+
+#ifdef DEBUG_HOSTENT
+ dump_addrinfo("read_addrinfo", buffer);
+#endif
+ return result;
+}
+
+/*
+ * This is called via the child-side of the fork.
+ */
+static void really_getaddrinfo(const char *host,
+ const char *port,
+ STATUSES * statuses,
+ void **result)
+{
+ LYNX_ADDRINFO hints, *res = 0;
+ int error;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ error = getaddrinfo(host, port, &hints, &res);
+ if (error) {
+ CTRACE((tfp, "HTGetAddrInfo: getaddrinfo(%s, %s): %s\n", host, port,
+ gai_strerror(error)));
+ } else {
+ statuses->child_errno = errno;
+ statuses->child_h_errno = h_errno;
+#ifdef HAVE_H_ERRNO
+ statuses->h_errno_valid = YES;
+#endif
+
+#ifdef DEBUG_HOSTENT_CHILD
+ dump_addrinfo("CHILD getaddrinfo", res);
+#endif
+ statuses->rehostentlen = fill_addrinfo(result, res);
+#ifdef DEBUG_HOSTENT_CHILD
+ dump_addrinfo("CHILD fill_addrinfo", (const LYNX_ADDRINFO *) (*result));
+#endif
+ if (statuses->rehostentlen <= sizeof(LYNX_ADDRINFO) || (*result) == NULL) {
+ statuses->rehostentlen = 0;
+ statuses->h_length = 0;
+ } else {
+ statuses->h_length = (int) (((LYNX_ADDRINFO *) (*result))->ai_addrlen);
+ }
+ freeaddrinfo(res);
+ }
+}
+#endif /* NSL_FORK */
+
+static LYNX_ADDRINFO *HTGetAddrInfo(const char *str,
+ const int defport)
+{
+#ifdef NSL_FORK
+ /* for transfer of result between from child to parent: */
+ void *readdrinfo = 0;
+
+#else
+ LYNX_ADDRINFO hints;
+ int error;
+#endif /* NSL_FORK */
+ LYNX_ADDRINFO *res;
+ char *p;
+ char *s = NULL;
+ char *host, *port;
+ char pbuf[80];
+
+ StrAllocCopy(s, str);
+
+ if (s[0] == '[' && (p = StrChr(s, ']')) != NULL) {
+ *p++ = '\0';
+ host = s + 1;
+ } else {
+ p = s;
+ host = &s[0];
+ }
+ port = strrchr(p, ':');
+ if (port) {
+ *port++ = '\0';
+ } else {
+ sprintf(pbuf, "%d", defport);
+ port = pbuf;
+ }
+
+#ifdef NSL_FORK
+ if (setup_nsl_fork(really_getaddrinfo,
+ read_addrinfo,
+ dump_addrinfo,
+ host, port, &readdrinfo)) {
+ res = readdrinfo;
+ } else {
+ res = NULL;
+ }
+#else
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ error = getaddrinfo(host, port, &hints, &res);
+ if (error || !res) {
+ CTRACE((tfp, "HTGetAddrInfo: getaddrinfo(%s, %s): %s\n", host, port,
+ gai_strerror(error)));
+ res = NULL;
+ }
+#endif
+
+ free(s);
+#ifdef DEBUG_HOSTENT
+ dump_addrinfo("HTGetAddrInfo", res);
+#endif
+ return res;
+}
+
+BOOLEAN HTCheckAddrInfo(const char *str, const int defport)
+{
+ LYNX_ADDRINFO *data = HTGetAddrInfo(str, defport);
+ BOOLEAN result = (data != 0);
+
+ FREE_NSL_FORK(data);
+ return result;
+}
+#endif /* INET6 */
+
+#ifdef LY_FIND_LEAKS
+/* Free our name for the host on which we are - FM
+ * -------------------------------------------
+ *
+ */
+static void free_HTTCP_hostname(void)
+{
+ FREE(hostname);
+}
+#endif /* LY_FIND_LEAKS */
+
+/* Derive the name of the host on which we are
+ * -------------------------------------------
+ *
+ */
+static void get_host_details(void)
+{
+ char name[MAXHOSTNAMELEN + 1]; /* The name of this host */
+
+#ifdef UCX
+ char *domain_name; /* The name of this host domain */
+#endif /* UCX */
+#ifdef NEED_HOST_ADDRESS /* no -- needs name server! */
+#ifdef INET6
+ LYNX_ADDRINFO hints, *res;
+ int error;
+
+#else
+ LYNX_HOSTENT *phost; /* Pointer to host -- See netdb.h */
+#endif /* INET6 */
+#endif /* NEED_HOST_ADDRESS */
+ size_t namelength = sizeof(name);
+
+ if (hostname)
+ return; /* Already done */
+ gethostname(name, namelength); /* Without domain */
+ StrAllocCopy(hostname, name);
+#ifdef LY_FIND_LEAKS
+ atexit(free_HTTCP_hostname);
+#endif
+#ifdef UCX
+ /*
+ * UCX doesn't give the complete domain name. Get rest from UCX$BIND_DOM
+ * logical.
+ */
+ if (StrChr(hostname, '.') == NULL) { /* Not full address */
+ domain_name = LYGetEnv("UCX$BIND_DOMAIN");
+ if (domain_name == NULL)
+ domain_name = LYGetEnv("TCPIP$BIND_DOMAIN");
+ if (domain_name != NULL) {
+ StrAllocCat(hostname, ".");
+ StrAllocCat(hostname, domain_name);
+ }
+ }
+#endif /* UCX */
+ CTRACE((tfp, "TCP: Local host name is %s\n", hostname));
+
+#ifndef DECNET /* Decnet ain't got no damn name server 8#OO */
+#ifdef NEED_HOST_ADDRESS /* no -- needs name server! */
+#ifdef INET6
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags = AI_CANONNAME;
+ error = getaddrinfo(name, NULL, &hints, &res);
+ if (error || !res || !res->ai_canonname) {
+ CTRACE((tfp, "TCP: %s: `%s'\n", gai_strerror(error), name));
+ if (res)
+ freeaddrinfo(res);
+ return; /* Fail! */
+ }
+ StrAllocCopy(hostname, res->ai_canonname);
+ MemCpy(&HTHostAddress, res->ai_addr, res->ai_addrlen);
+ freeaddrinfo(res);
+#else
+ phost = gethostbyname(name); /* See netdb.h */
+ if (!OK_HOST(phost)) {
+ CTRACE((tfp,
+ "TCP: Can't find my own internet node address for `%s'!!\n",
+ name));
+ return; /* Fail! */
+ }
+ StrAllocCopy(hostname, phost->h_name);
+ MemCpy(&HTHostAddress, &phost->h_addr_list[0], phost->h_length);
+#endif /* INET6 */
+ CTRACE((tfp, " Name server says that I am `%s' = %s\n",
+ hostname, HTInetString(&HTHostAddress)));
+#endif /* NEED_HOST_ADDRESS */
+
+#endif /* !DECNET */
+}
+
+const char *HTHostName(void)
+{
+ get_host_details();
+ return hostname;
+}
+
+#ifdef _WINDOWS
+#define SET_EINTR WSASetLastError(EINTR)
+#else
+#define SET_EINTR SOCKET_ERRNO = EINTR
+#endif
+
+static BOOL HTWasInterrupted(int *status)
+{
+ BOOL result = FALSE;
+
+ if (HTCheckForInterrupt()) {
+ result = TRUE;
+ *status = HT_INTERRUPTED;
+ SET_EINTR;
+ }
+ return result;
+}
+
+#define TRIES_PER_SECOND 10
+
+/*
+ * Set the select-timeout to 0.1 seconds.
+ */
+static void set_timeout(struct timeval *timeoutp)
+{
+ timeoutp->tv_sec = 0;
+ timeoutp->tv_usec = 100000;
+}
+
+#ifndef MULTINET /* SOCKET_ERRNO != errno ? */
+#if !defined(UCX) || !defined(VAXC) /* errno not modifiable ? */
+#define SOCKET_DEBUG_TRACE /* show errno status after some system calls */
+#endif /* UCX && VAXC */
+#endif /* MULTINET */
+/*
+ * Interruptible connect as implemented for Mosaic by Marc Andreesen
+ * and hacked in for Lynx years ago by Lou Montulli, and further
+ * modified over the years by numerous Lynx lovers. - FM
+ */
+int HTDoConnect(const char *url,
+ const char *protocol,
+ int default_port,
+ int *s)
+{
+ char *socks5_host = NULL;
+ unsigned socks5_host_len = 0;
+ int socks5_port;
+ const char *socks5_orig_url;
+ char *socks5_new_url = NULL;
+ char *socks5_protocol = NULL;
+ int status = HT_OK;
+ char *line = NULL;
+ char *p1 = NULL;
+ char *host = NULL;
+ char const *emsg;
+
+#ifdef INET6
+ LYNX_ADDRINFO *res = 0, *res0 = 0;
+
+#else
+ struct sockaddr_in sock_A;
+ struct sockaddr_in *soc_in = &sock_A;
+#endif
+
+ *s = -1; /* nothing is open yet */
+
+ /* In case of a present SOCKS5 proxy, marshal */
+ if (socks5_proxy == NULL)
+ socks5_proxy = LYGetEnv("SOCKS5_PROXY");
+ if ((socks5_orig_url = socks5_proxy) != NULL) {
+ int xport;
+
+ xport = default_port;
+ socks5_orig_url = url;
+ StrAllocCopy(socks5_new_url, url);
+
+ /* Get node name and optional port number of wanted URL */
+ if ((p1 = HTParse(socks5_new_url, "", PARSE_HOST)) != NULL) {
+ StrAllocCopy(socks5_host, p1);
+ strip_userid(socks5_host, FALSE);
+ FREE(p1);
+ }
+
+ if (isEmpty(socks5_host)) {
+ emsg = gettext("SOCKS5: no hostname found.");
+ status = HT_ERROR;
+ goto report_error;
+ }
+
+ if (strlen(socks5_host) > 255) {
+ emsg = gettext("SOCKS5: hostname too long.");
+ status = HT_ERROR;
+ goto report_error;
+ }
+ socks5_host_len = (unsigned) strlen(socks5_host);
+
+ if (HTParsePort(socks5_new_url, &socks5_port) == NULL)
+ socks5_port = xport;
+ FREE(socks5_new_url);
+
+ /* And switch over to our SOCKS5 config; in order to embed that into
+ * lynx environment, prepend protocol prefix */
+ default_port = 1080; /* RFC 1928 */
+ HTSACat(&socks5_new_url, "socks://");
+ HTSACat(&socks5_new_url, socks5_proxy);
+ url = socks5_new_url;
+
+ HTSprintf0(&socks5_protocol,
+ gettext("(for %s at %s) SOCKS5"),
+ protocol, socks5_host);
+ protocol = socks5_protocol;
+ }
+#ifndef INET6
+ /*
+ * Set up defaults.
+ */
+ memset(soc_in, 0, sizeof(*soc_in));
+ soc_in->sin_family = AF_INET;
+ soc_in->sin_port = htons((PortNumber) default_port);
+#endif /* INET6 */
+
+ /*
+ * Get node name and optional port number.
+ */
+ p1 = HTParse(url, "", PARSE_HOST);
+ StrAllocCopy(host, p1);
+ strip_userid(host, FALSE);
+ FREE(p1);
+
+ HTSprintf0(&line, "%s%s", WWW_FIND_MESSAGE, host);
+ _HTProgress(line);
+#ifdef INET6
+ /* HTParseInet() is useless! */
+ res0 = HTGetAddrInfo(host, default_port);
+ if (res0 == NULL) {
+ HTSprintf0(&line, gettext("Unable to locate remote host %s."), host);
+ _HTProgress(line);
+ status = HT_NO_DATA;
+ goto cleanup;
+ }
+#else
+ status = HTParseInet(soc_in, host);
+ if (status) {
+ if (status != HT_INTERRUPTED) {
+ if (status == HT_NOT_ACCEPTABLE) {
+ /* Not HTProgress, so warning won't be overwritten immediately;
+ * but not HTAlert, because typically there will be other
+ * alerts from the callers. - kw
+ */
+ HTUserMsg2(gettext("Invalid hostname %s"), host);
+ } else {
+ HTSprintf0(&line,
+ gettext("Unable to locate remote host %s."), host);
+ _HTProgress(line);
+ }
+ status = HT_NO_DATA;
+ }
+ goto cleanup;
+ }
+#endif /* INET6 */
+
+ HTSprintf0(&line, gettext("Making %s connection to %s"), protocol, host);
+ _HTProgress(line);
+
+ /*
+ * Now, let's get a socket set up from the server for the data.
+ */
+#ifndef INET6
+ *s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (*s == -1) {
+ status = HT_NO_DATA;
+ emsg = gettext("socket failed.");
+ goto report_error;
+ }
+#else
+ for (res = res0; res; res = res->ai_next) {
+ *s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
+ if (*s == -1) {
+ char hostbuf[1024], portbuf[1024];
+
+ getnameinfo(res->ai_addr, res->ai_addrlen,
+ hostbuf, (socklen_t) sizeof(hostbuf),
+ portbuf, (socklen_t) sizeof(portbuf),
+ NI_NUMERICHOST | NI_NUMERICSERV);
+ HTSprintf0(&line,
+ gettext("socket failed: family %d addr %s port %s."),
+ res->ai_family, hostbuf, portbuf);
+ _HTProgress(line);
+ continue;
+ }
+#endif /* INET6 */
+
+#if !defined(DOSPATH) || defined(__DJGPP__)
+#if !defined(NO_IOCTL) || defined(USE_FCNTL)
+ /*
+ * Make the socket non-blocking, so the connect can be canceled. This
+ * means that when we issue the connect we should NOT have to wait for
+ * the accept on the other end.
+ */
+ {
+#ifdef USE_FCNTL
+ int ret = fcntl(*s, F_SETFL, O_NONBLOCK);
+
+#else
+ int val = 1;
+ int ret = IOCTL(*s, FIONBIO, &val);
+#endif /* USE_FCNTL */
+ if (ret == -1)
+ _HTProgress(gettext("Could not make connection non-blocking."));
+ }
+#endif /* !NO_IOCTL || USE_FCNTL */
+#endif /* !DOSPATH || __DJGPP__ */
+
+ /*
+ * Issue the connect. Since the server can't do an instantaneous
+ * accept and we are non-blocking, this will almost certainly return a
+ * negative status.
+ */
+#ifdef SOCKS
+ if (socks_flag) {
+#ifdef INET6
+ status = Rconnect(*s, res->ai_addr, res->ai_addrlen);
+#else
+ status = Rconnect(*s, SOCKADDR_OF(sock_A), sizeof(sock_A));
+#endif /* INET6 */
+ } else
+#endif /* SOCKS */
+#ifdef INET6
+ status = connect(*s, res->ai_addr, res->ai_addrlen);
+#else
+ status = connect(*s, SOCKADDR_OF(sock_A), sizeof(sock_A));
+#endif /* INET6 */
+
+ /*
+ * According to the Sun man page for connect:
+ * EINPROGRESS The socket is non-blocking and the con-
+ * nection cannot be completed immediately.
+ * It is possible to select(2) for comple-
+ * tion by selecting the socket for writ-
+ * ing.
+ * According to the Motorola SVR4 man page for connect:
+ * EAGAIN The socket is non-blocking and the con-
+ * nection cannot be completed immediately.
+ * It is possible to select for completion
+ * by selecting the socket for writing.
+ * However, this is only possible if the
+ * socket STREAMS module is the topmost
+ * module on the protocol stack with a
+ * write service procedure. This will be
+ * the normal case.
+ */
+ CTRACE((tfp, "connect(): status: %d, SOCK_ERRNO: %d\n", status, SOCKET_ERRNO));
+
+ if ((status < 0) &&
+ (SOCKET_ERRNO == EINPROGRESS
+#ifdef EAGAIN
+ || SOCKET_ERRNO == EAGAIN
+#endif
+ )) {
+ struct timeval select_timeout;
+ int ret;
+ int tries = 0;
+
+#ifdef SOCKET_DEBUG_TRACE
+ if (SOCKET_ERRNO != EINPROGRESS) {
+ HTInetStatus("this socket's first connect");
+ }
+#endif /* SOCKET_DEBUG_TRACE */
+ ret = 0;
+ while (ret <= 0) {
+ fd_set writefds;
+
+ /*
+ * Protect against an infinite loop.
+ */
+ if ((tries++ / TRIES_PER_SECOND) >= connect_timeout) {
+ HTAlert(gettext("Connection failed (too many retries)."));
+#ifdef INET6
+#ifndef NSL_FORK
+ if (res0)
+ freeaddrinfo(res0);
+#endif
+#endif /* INET6 */
+ status = HT_NO_DATA;
+ goto cleanup;
+ }
+ set_timeout(&select_timeout);
+ FD_ZERO(&writefds);
+ FD_SET((LYNX_FD) *s, &writefds);
+#ifdef SOCKS
+ if (socks_flag)
+ ret = Rselect(*s + 1, NULL,
+ &writefds, NULL, &select_timeout);
+ else
+#endif /* SOCKS */
+ ret = select(*s + 1,
+ NULL,
+ &writefds,
+ NULL,
+ &select_timeout);
+
+#ifdef SOCKET_DEBUG_TRACE
+ if (tries == 1) {
+ if (SOCKET_ERRNO != EINPROGRESS) {
+ HTInetStatus("this socket's first select");
+ }
+ }
+#endif /* SOCKET_DEBUG_TRACE */
+ /*
+ * If we suspend, then it is possible that select will be
+ * interrupted. Allow for this possibility. - JED
+ */
+ if ((ret == -1) && (SOCKET_ERRNO == EINTR))
+ continue;
+
+#ifdef SOCKET_DEBUG_TRACE
+ if (ret < 0) {
+ HTInetStatus("failed select");
+ }
+#endif /* SOCKET_DEBUG_TRACE */
+ /*
+ * Again according to the Sun and Motorola man pages for
+ * connect:
+ * EALREADY The socket is non-blocking and a previ-
+ * ous connection attempt has not yet been
+ * completed.
+ * Thus if the SOCKET_ERRNO is NOT EALREADY we have a real
+ * error, and should break out here and return that error.
+ * Otherwise if it is EALREADY keep on trying to complete the
+ * connection.
+ */
+ if ((ret < 0) && (SOCKET_ERRNO != EALREADY)) {
+ status = ret;
+ break;
+ } else if (((SOCKET_ERRNO == EALREADY) ||
+ (SOCKET_ERRNO == EINPROGRESS)) &&
+ HTCheckForInterrupt()) {
+ status = HT_INTERRUPTED;
+ break;
+ } else if (ret > 0) {
+ /*
+ * Extra check here for connection success, if we try to
+ * connect again, and get EISCONN, it means we have a
+ * successful connection. But don't check with SOCKS.
+ */
+#ifdef SOCKS
+ if (socks_flag) {
+ status = 0;
+ } else {
+#endif /* SOCKS */
+#ifdef INET6
+ status = connect(*s, res->ai_addr, res->ai_addrlen);
+#else
+ status = connect(*s, SOCKADDR_OF(sock_A), sizeof(sock_A));
+#endif /* INET6 */
+#ifdef UCX
+ /*
+ * A UCX feature: Instead of returning EISCONN UCX
+ * returns EADDRINUSE. Test for this status also.
+ */
+ if ((status < 0) && ((SOCKET_ERRNO == EISCONN) ||
+ (SOCKET_ERRNO == EADDRINUSE)))
+#else
+ if ((status < 0) && (SOCKET_ERRNO == EISCONN))
+#endif /* UCX */
+ {
+ status = 0;
+ }
+
+ if (status && (SOCKET_ERRNO == EALREADY)) /* new stuff LJM */
+ ret = 0; /* keep going */
+ else {
+#ifdef SOCKET_DEBUG_TRACE
+ if (status < 0) {
+ HTInetStatus("confirm-ready connect");
+ }
+#endif /* SOCKET_DEBUG_TRACE */
+ break;
+ }
+#ifdef SOCKS
+ }
+#endif /* SOCKS */
+ }
+#ifdef SOCKS
+ else if (!socks_flag)
+#else
+ else
+#endif /* SOCKS */
+ {
+ /*
+ * The select says we aren't ready yet. Try to connect
+ * again to make sure. If we don't get EALREADY or
+ * EISCONN, something has gone wrong. Break out and report
+ * it.
+ *
+ * For some reason, SVR4 returns EAGAIN here instead of
+ * EALREADY, even though the man page says it should be
+ * EALREADY.
+ *
+ * For some reason, UCX pre 3 apparently returns errno =
+ * 18242 instead of EALREADY or EISCONN.
+ */
+#ifdef INET6
+ status = connect(*s, res->ai_addr, res->ai_addrlen);
+#else
+ status = connect(*s, SOCKADDR_OF(sock_A), sizeof(sock_A));
+#endif /* INET6 */
+ if ((status < 0) &&
+ (SOCKET_ERRNO != EALREADY
+#ifdef EAGAIN
+ && SOCKET_ERRNO != EAGAIN
+#endif
+ ) &&
+#ifdef UCX
+ (SOCKET_ERRNO != 18242) &&
+#endif /* UCX */
+ (SOCKET_ERRNO != EISCONN)) {
+#ifdef SOCKET_DEBUG_TRACE
+ HTInetStatus("confirm-not-ready connect");
+#endif /* SOCKET_DEBUG_TRACE */
+ break;
+ }
+ }
+ if (HTWasInterrupted(&status)) {
+ CTRACE((tfp, "*** INTERRUPTED in middle of connect.\n"));
+ break;
+ }
+ }
+ }
+#ifdef SOCKET_DEBUG_TRACE
+ else if (status < 0) {
+ HTInetStatus("this socket's first and only connect");
+ }
+#endif /* SOCKET_DEBUG_TRACE */
+#ifdef INET6
+ if (status < 0) {
+ NETCLOSE(*s);
+ *s = -1;
+ if (status != HT_INTERRUPTED)
+ continue;
+ }
+ break;
+ }
+#endif /* INET6 */
+
+#ifdef INET6
+ if (*s < 0)
+#else
+ if (status < 0)
+#endif /* INET6 */
+ {
+ /*
+ * The connect attempt failed or was interrupted, so close up the
+ * socket.
+ */
+ NETCLOSE(*s);
+ }
+#if !defined(DOSPATH) || defined(__DJGPP__)
+#if !defined(NO_IOCTL) || defined(USE_FCNTL)
+ else {
+ /*
+ * Make the socket blocking again on good connect.
+ */
+#ifdef USE_FCNTL
+ int ret = fcntl(*s, F_SETFL, 0);
+
+#else
+ int val = 0;
+ int ret = IOCTL(*s, FIONBIO, &val);
+#endif /* USE_FCNTL */
+ if (ret == -1)
+ _HTProgress(gettext("Could not restore socket to blocking."));
+ }
+#endif /* !NO_IOCTL || USE_FCNTL */
+#endif /* !DOSPATH || __DJGPP__ */
+
+#ifdef INET6
+#ifdef NSL_FORK
+ FREE_NSL_FORK(res0);
+#else
+ if (res0)
+ freeaddrinfo(res0);
+#endif
+#endif /* INET6 */
+
+ /* Now if this was a SOCKS5 proxy connection, go for the real one */
+ if (status >= 0 && socks5_orig_url != NULL) {
+ unsigned char pbuf[4 + 1 + 255 + 2];
+ unsigned i;
+
+ /* RFC 1928: version identifier/method selection message */
+ pbuf[0] = 0x05; /* VER: protocol version: X'05' */
+ pbuf[1] = 0x01; /* NMETHODS: 1 */
+ pbuf[2] = 0x00; /* METHOD: X'00' NO AUTHENTICATION REQUIRED */
+ if (NETWRITE(*s, (char *) pbuf, 3) != 3) {
+ goto report_system_err;
+ } else if (HTDoRead(*s, pbuf, 2) != 2) {
+ goto report_system_err;
+ } else if (pbuf[0] != 0x05 || pbuf[1] != 0x00) {
+ goto report_unexpected_reply;
+ }
+
+ /* RFC 1928: CONNECT request */
+ HTSprintf0(&line, gettext("SOCKS5: connecting to %s"), socks5_host);
+ _HTProgress(line);
+ pbuf[0] = 0x05; /* VER: protocol version: X'05' */
+ pbuf[1] = 0x01; /* CMD: CONNECT X'01' */
+ pbuf[2] = 0x00; /* RESERVED */
+ pbuf[3] = 0x03; /* ATYP: domain name */
+ pbuf[4] = (unsigned char) socks5_host_len;
+ memcpy(&pbuf[i = 5], socks5_host, socks5_host_len);
+ i += socks5_host_len;
+ /* C99 */ {
+ unsigned short x; /* XXX 16-bit? */
+
+ x = htons((PortNumber) socks5_port);
+ memcpy(&pbuf[i], (unsigned char *) &x, sizeof x);
+ i += (unsigned) sizeof(x);
+ }
+ if ((size_t) NETWRITE(*s, (char *) pbuf, i) != i) {
+ goto report_system_err;
+ } else if ((unsigned) HTDoRead(*s, pbuf, 4) != 4) {
+ goto report_system_err;
+ }
+ /* Version 5, reserved must be 0 */
+ if (pbuf[0] == 0x05 && pbuf[2] == 0x00) {
+ /* Result */
+ switch (pbuf[1]) {
+ case 0x00:
+ emsg = NULL;
+ break;
+ case 0x01:
+ emsg = gettext("SOCKS server failure");
+ break;
+ case 0x02:
+ emsg = gettext("connection not allowed by ruleset");
+ break;
+ case 0x03:
+ emsg = gettext("network unreachable");
+ break;
+ case 0x04:
+ emsg = gettext("host unreachable");
+ break;
+ case 0x05:
+ emsg = gettext("connection refused");
+ break;
+ case 0x06:
+ emsg = gettext("TTL expired");
+ break;
+ case 0x07:
+ emsg = gettext("command not supported");
+ break;
+ case 0x08:
+ emsg = gettext("address type not supported");
+ break;
+ default:
+ emsg = gettext("unknown SOCKS error code");
+ break;
+ }
+ if (emsg != NULL) {
+ goto report_no_connection;
+ }
+ } else {
+ goto report_unexpected_reply;
+ }
+
+ /* Address type variable; read the BND.PORT with it.
+ * This is actually false since RFC 1928 says that the BND.ADDR reply
+ * to CONNECT contains the IP address, so only 0x01 and 0x04 are
+ * allowed */
+ switch (pbuf[3]) {
+ case 0x01:
+ i = 4;
+ break;
+ case 0x03:
+ i = 1;
+ break;
+ case 0x04:
+ i = 16;
+ break;
+ default:
+ goto report_unexpected_reply;
+ }
+ i += (unsigned) sizeof(unsigned short);
+
+ if ((size_t) HTDoRead(*s, pbuf, i) != i) {
+ goto report_system_err;
+ } else if (i == 1 + sizeof(unsigned short)) {
+ i = pbuf[0];
+ if ((size_t) HTDoRead(*s, pbuf, i) != i) {
+ goto report_system_err;
+ }
+ }
+ }
+ goto cleanup;
+
+ report_system_err:
+ emsg = LYStrerror(errno);
+ goto report_no_connection;
+
+ report_unexpected_reply:
+ emsg = gettext("unexpected reply\n");
+ /* FALLTHRU */
+
+ report_no_connection:
+ status = HT_NO_CONNECTION;
+ /* FALLTHRU */
+
+ report_error:
+ HTAlert(emsg);
+ if (*s != -1) {
+ NETCLOSE(*s);
+ }
+
+ cleanup:
+ if (socks5_proxy != NULL) {
+ FREE(socks5_new_url);
+ FREE(socks5_protocol);
+ FREE(socks5_host);
+ }
+ FREE(host);
+ FREE(line);
+ return status;
+}
+
+/*
+ * This is interruptible so reads can be implemented cleanly.
+ */
+int HTDoRead(int fildes,
+ void *buf,
+ unsigned nbyte)
+{
+ int result;
+ BOOL ready;
+
+#if !defined(NO_IOCTL)
+ int ret;
+ fd_set readfds;
+ struct timeval select_timeout;
+ int tries = 0;
+
+#ifdef USE_READPROGRESS
+ int otries = 0;
+ time_t otime = time((time_t *) 0);
+ time_t start = otime;
+#endif
+#endif /* !NO_IOCTL */
+
+#if defined(UNIX) && !defined(__BEOS__)
+ if (fildes == 0) {
+ /*
+ * 0 can be a valid socket fd, but if it's a tty something must have
+ * gone wrong. - kw
+ */
+ if (isatty(fildes)) {
+ CTRACE((tfp, "HTDoRead - refusing to read fd 0 which is a tty!\n"));
+ return -1;
+ }
+ } else
+#endif
+ if (fildes <= 0) {
+ CTRACE((tfp, "HTDoRead - no file descriptor!\n"));
+ return -1;
+ }
+
+ if (HTWasInterrupted(&result)) {
+ CTRACE((tfp, "HTDoRead - interrupted before starting!\n"));
+ return (result);
+ }
+#if defined(NO_IOCTL)
+ ready = TRUE;
+#else
+ ready = FALSE;
+ while (!ready) {
+ /*
+ * Protect against an infinite loop.
+ */
+ if ((tries++ / TRIES_PER_SECOND) >= reading_timeout) {
+ HTAlert(gettext("Socket read failed (too many tries)."));
+ SET_EINTR;
+ result = HT_INTERRUPTED;
+ break;
+ }
+#ifdef USE_READPROGRESS
+ if (tries - otries > TRIES_PER_SECOND) {
+ time_t t = time((time_t *) 0);
+
+ otries = tries;
+ if (t - otime >= 5) {
+ otime = t;
+ HTReadProgress((off_t) (-1), (off_t) 0); /* Put "stalled" message */
+ }
+ }
+#endif
+
+ /*
+ * If we suspend, then it is possible that select will be interrupted.
+ * Allow for this possibility. - JED
+ */
+ do {
+ set_timeout(&select_timeout);
+ FD_ZERO(&readfds);
+ FD_SET((LYNX_FD) fildes, &readfds);
+#ifdef SOCKS
+ if (socks_flag)
+ ret = Rselect(fildes + 1,
+ &readfds, NULL, NULL, &select_timeout);
+ else
+#endif /* SOCKS */
+ ret = select(fildes + 1,
+ &readfds, NULL, NULL, &select_timeout);
+ } while ((ret == -1) && (errno == EINTR));
+
+ if (ret < 0) {
+ result = -1;
+ break;
+ } else if (ret > 0) {
+ ready = TRUE;
+ } else if (HTWasInterrupted(&result)) {
+ break;
+ }
+ }
+#endif /* !NO_IOCTL */
+
+ if (ready) {
+#if defined(UCX) && defined(VAXC)
+ /*
+ * VAXC and UCX problem only.
+ */
+ errno = vaxc$errno = 0;
+ result = SOCKET_READ(fildes, buf, nbyte);
+ CTRACE((tfp,
+ "Read - result,errno,vaxc$errno: %d %d %d\n", result, errno, vaxc$errno));
+ if ((result <= 0) && TRACE)
+ perror("HTTCP.C:HTDoRead:read"); /* RJF */
+ /*
+ * An errno value of EPIPE and result < 0 indicates end-of-file on VAXC.
+ */
+ if ((result <= 0) && (errno == EPIPE)) {
+ result = 0;
+ set_errno(0);
+ }
+#else
+#ifdef UNIX
+ while ((result = (int) SOCKET_READ(fildes, buf, nbyte)) == -1) {
+ if (errno == EINTR)
+ continue;
+#ifdef ERESTARTSYS
+ if (errno == ERESTARTSYS)
+ continue;
+#endif /* ERESTARTSYS */
+ HTInetStatus("read");
+ break;
+ }
+#else /* UNIX */
+ result = NETREAD(fildes, (char *) buf, nbyte);
+#endif /* !UNIX */
+#endif /* UCX && VAXC */
+ }
+#ifdef USE_READPROGRESS
+ CTRACE2(TRACE_TIMING, (tfp, "...HTDoRead returns %d (%" PRI_time_t
+ " seconds)\n",
+ result, CAST_time_t (time((time_t *)0) - start)));
+#endif
+ return result;
+}
+
+#ifdef SVR4_BSDSELECT
+/*
+ * This is a fix for the difference between BSD's select() and
+ * SVR4's select(). SVR4's select() can never return a value larger
+ * than the total number of file descriptors being checked. So, if
+ * you select for read and write on one file descriptor, and both
+ * are true, SVR4 select() will only return 1. BSD select in the
+ * same situation will return 2.
+ *
+ * Additionally, BSD select() on timing out, will zero the masks,
+ * while SVR4 does not. This is fixed here as well.
+ *
+ * Set your tabstops to 4 characters to have this code nicely formatted.
+ *
+ * Jerry Whelan, guru@bradley.edu, June 12th, 1993
+ */
+#ifdef select
+#undef select
+#endif /* select */
+
+#ifdef SOCKS
+#ifdef Rselect
+#undef Rselect
+#endif /* Rselect */
+#endif /* SOCKS */
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/select.h>
+
+int BSDselect(int nfds,
+ fd_set * readfds,
+ fd_set * writefds,
+ fd_set * exceptfds,
+ struct timeval *select_timeout)
+{
+ int rval, i;
+
+#ifdef SOCKS
+ if (socks_flag)
+ rval = Rselect(nfds, readfds, writefds, exceptfds, select_timeout);
+ else
+#endif /* SOCKS */
+ rval = select(nfds, readfds, writefds, exceptfds, select_timeout);
+
+ switch (rval) {
+ case -1:
+ return (rval);
+
+ case 0:
+ if (readfds != NULL)
+ FD_ZERO(readfds);
+ if (writefds != NULL)
+ FD_ZERO(writefds);
+ if (exceptfds != NULL)
+ FD_ZERO(exceptfds);
+ return (rval);
+
+ default:
+ for (i = 0, rval = 0; i < nfds; i++) {
+ if ((readfds != NULL) && FD_ISSET(i, readfds))
+ rval++;
+ if ((writefds != NULL) && FD_ISSET(i, writefds))
+ rval++;
+ if ((exceptfds != NULL) && FD_ISSET(i, exceptfds))
+ rval++;
+
+ }
+ return (rval);
+ }
+/* Should never get here */
+}
+#endif /* SVR4_BSDSELECT */
diff --git a/WWW/Library/Implementation/HTTCP.h b/WWW/Library/Implementation/HTTCP.h
new file mode 100644
index 0000000..a15b290
--- /dev/null
+++ b/WWW/Library/Implementation/HTTCP.h
@@ -0,0 +1,111 @@
+/*
+ * $LynxId: HTTCP.h,v 1.27 2018/05/16 19:48:16 tom Exp $
+ *
+ * /Net/dxcern/userd/timbl/hypertext/WWW/Library/src/HTTCP.html
+ * GENERIC TCP/IP COMMUNICATION
+ *
+ * This module has the common code for handling TCP/IP connections etc.
+ */
+#ifndef HTTCP_H
+#define HTTCP_H
+
+#ifndef HTUTILS_H
+#include <HTUtils.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+/* Produce a string for an internet address
+ * ---------------------------------------
+ *
+ * On exit:
+ * returns a pointer to a static string which must be copied if
+ * it is to be kept.
+ */
+ extern const char *HTInetString(LY_SOCKADDR * mysin);
+
+/* Encode INET status (as in sys/errno.h) inet_status()
+ * ------------------
+ *
+ * On entry:
+ * where gives a description of what caused the error
+ * global errno gives the error number in the unix way.
+ *
+ * On return:
+ * returns a negative status in the unix way.
+ */
+ extern int HTInetStatus(const char *where);
+
+/* Publicly accessible variables
+*/
+/* extern struct sockaddr_in HTHostAddress; */
+ /* The internet address of the host */
+ /* Valid after call to HTHostName() */
+
+/* Parse a cardinal value parse_cardinal()
+ * ----------------------
+ *
+ * On entry:
+ * *pp points to first character to be interpreted, terminated by
+ * non 0..9 character.
+ * *pstatus points to status already valid,
+ * maxvalue gives the largest allowable value.
+ *
+ * On exit:
+ * *pp points to first unread character,
+ * *pstatus points to status updated iff bad
+ */
+
+ extern unsigned int HTCardinal(int *pstatus,
+ char **pp,
+ unsigned int max_value);
+
+/* Check whether string is a valid Internet hostname
+ * -------------------------------------------------
+ */
+
+ extern BOOL valid_hostname(char *name);
+
+/* Resolve an internet hostname, like gethostbyname
+ * ------------------------------------------------
+ *
+ * On entry,
+ * str points to the given host name, not numeric address,
+ * without colon or port number.
+ *
+ * On exit,
+ * returns a pointer to a struct hostent in static storage,
+ * or NULL in case of error or user interruption.
+ *
+ * The interface is intended to be the same as for gethostbyname(),
+ * but additional status is returned in lynx_nsl_status.
+ */
+ extern int lynx_nsl_status;
+
+ extern BOOLEAN LYCheckHostByName(char *host);
+
+#ifdef INET6
+ extern BOOLEAN HTCheckAddrInfo(const char *str, const int defport);
+#endif
+
+/* Get Name of This Machine
+ * ------------------------
+ *
+ */
+
+ extern const char *HTHostName(void);
+
+ extern int HTDoConnect(const char *url,
+ const char *protocol,
+ int default_port,
+ int *s);
+
+ extern int HTDoRead(int fildes,
+ void *buf,
+ unsigned nbyte);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* HTTCP_H */
diff --git a/WWW/Library/Implementation/HTTP.c b/WWW/Library/Implementation/HTTP.c
new file mode 100644
index 0000000..e3c5a4a
--- /dev/null
+++ b/WWW/Library/Implementation/HTTP.c
@@ -0,0 +1,2838 @@
+/*
+ * $LynxId: HTTP.c,v 1.183 2022/04/01 00:10:19 tom Exp $
+ *
+ * HyperText Transfer Protocol - Client implementation HTTP.c
+ * ===========================
+ * Modified:
+ * 27 Jan 1994 PDM Added Ari Luotonen's Fix for Reload when using proxy
+ * servers.
+ * 28 Apr 1997 AJL,FM Do Proxy Authorisation.
+ */
+
+#include <HTUtils.h>
+#include <HTTP.h>
+#include <LYUtils.h>
+
+#ifdef USE_SSL
+#include <HTNews.h>
+#endif
+
+#define HTTP_PORT 80
+#define HTTPS_PORT 443
+#define SNEWS_PORT 563
+
+#define INIT_LINE_SIZE 1536 /* Start with line buffer this big */
+#define LINE_EXTEND_THRESH 256 /* Minimum read size */
+#define VERSION_LENGTH 20 /* for returned protocol version */
+
+#include <HTParse.h>
+#include <HTTCP.h>
+#include <HTFormat.h>
+#include <HTFile.h>
+#include <HTAlert.h>
+#include <HTMIME.h>
+#include <HTML.h>
+#include <HTInit.h>
+#include <HTAABrow.h>
+#include <HTAccess.h> /* Are we using an HTTP gateway? */
+
+#include <LYCookie.h>
+#include <LYGlobalDefs.h>
+#include <GridText.h>
+#include <LYStrings.h>
+#include <LYUtils.h>
+#include <LYrcFile.h>
+#include <LYLeaks.h>
+#include <LYCurses.h>
+
+#ifdef USE_SSL
+
+#ifdef USE_OPENSSL_INCL
+#include <openssl/x509v3.h>
+#endif
+
+#if defined(LIBRESSL_VERSION_NUMBER)
+/* OpenSSL and LibreSSL version numbers do not correspond */
+
+#if LIBRESSL_VERSION_NUMBER >= 0x2060100fL
+#define SSL_set_no_TLSV1() SSL_set_min_proto_version(handle, TLS1_1_VERSION)
+#endif
+
+#elif defined(OPENSSL_VERSION_NUMBER) && (OPENSSL_VERSION_NUMBER >= 0x10100000L)
+
+#define SSLEAY_VERSION_NUMBER OPENSSL_VERSION_NUMBER
+#undef SSL_load_error_strings
+#undef SSLeay_add_ssl_algorithms
+#define ASN1_STRING_data ASN1_STRING_get0_data
+#define TLS_client_method() SSLv23_client_method()
+#define SSL_load_error_strings() /* nothing */
+#define SSLeay_add_ssl_algorithms() /* nothing */
+#define SSL_set_no_TLSV1() SSL_set_min_proto_version(handle, TLS1_1_VERSION)
+
+#elif defined(SSLEAY_VERSION_NUMBER)
+
+#define TLS_client_method() SSLv23_client_method()
+
+#endif
+
+#ifndef SSL_set_no_TLSV1
+#define SSL_set_no_TLSV1() SSL_set_options(handle, SSL_OP_NO_TLSv1)
+#endif
+
+#ifdef USE_GNUTLS_INCL
+#include <gnutls/x509.h>
+#endif
+
+#endif /* USE_SSL */
+
+BOOLEAN reloading = FALSE; /* Reloading => send no-cache pragma to proxy */
+char *redirecting_url = NULL; /* Location: value. */
+BOOL permanent_redirection = FALSE; /* Got 301 status? */
+BOOL redirect_post_content = FALSE; /* Don't convert to GET? */
+
+#ifdef USE_SSL
+SSL_CTX *ssl_ctx = NULL; /* SSL ctx */
+SSL *SSL_handle = NULL;
+static int ssl_okay;
+
+static void free_ssl_ctx(void)
+{
+ if (ssl_ctx != NULL)
+ SSL_CTX_free(ssl_ctx);
+}
+
+static BOOL needs_limit(const char *actual)
+{
+ return ((int) strlen(actual) > LYcols - 7) ? TRUE : FALSE;
+}
+
+static char *limited_string(const char *source, const char *actual)
+{
+ int limit = ((int) strlen(source)
+ - ((int) strlen(actual) - (LYcols - 10)));
+ char *temp = NULL;
+
+ StrAllocCopy(temp, source);
+ if (limit < 0)
+ limit = 0;
+ strcpy(temp + limit, "...");
+ return temp;
+}
+
+/*
+ * If the error message is too long to fit in the line, truncate that to fit
+ * within the limits for prompting.
+ */
+static void SSL_single_prompt(char **target, const char *source)
+{
+ HTSprintf0(target, SSL_FORCED_PROMPT, source);
+ if (needs_limit(*target)) {
+ char *temp = limited_string(source, *target);
+
+ *target = NULL;
+ HTSprintf0(target, SSL_FORCED_PROMPT, temp);
+ free(temp);
+ }
+}
+
+static void SSL_double_prompt(char **target, const char *format, const char
+ *arg1, const char *arg2)
+{
+ HTSprintf0(target, format, arg1, arg2);
+ if (needs_limit(*target)) {
+ char *parg2 = limited_string(arg2, *target);
+
+ *target = NULL;
+ HTSprintf0(target, format, arg1, parg2);
+ if (needs_limit(*target)) {
+ char *parg1 = limited_string(arg1, *target);
+
+ *target = NULL;
+ HTSprintf0(target, format, parg1, parg2);
+ free(parg1);
+ }
+ free(parg2);
+ }
+}
+
+static int HTSSLCallback(int preverify_ok, X509_STORE_CTX * x509_ctx GCC_UNUSED)
+{
+ char *msg = NULL;
+ int result = 1;
+
+#ifdef USE_X509_SUPPORT
+ HTSprintf0(&msg,
+ gettext("SSL callback:%s, preverify_ok=%d, ssl_okay=%d"),
+ X509_verify_cert_error_string((long) X509_STORE_CTX_get_error(x509_ctx)),
+ preverify_ok, ssl_okay);
+ _HTProgress(msg);
+ FREE(msg);
+#endif
+
+#ifndef USE_NSS_COMPAT_INCL
+ if (!(preverify_ok || ssl_okay || ssl_noprompt)) {
+#ifdef USE_X509_SUPPORT
+ SSL_single_prompt(&msg,
+ X509_verify_cert_error_string((long)
+ X509_STORE_CTX_get_error(x509_ctx)));
+ if (HTForcedPrompt(ssl_noprompt, msg, NO))
+ ssl_okay = 1;
+ else
+ result = 0;
+#endif
+
+ FREE(msg);
+ }
+#endif
+ return result;
+}
+
+SSL *HTGetSSLHandle(void)
+{
+#ifdef USE_GNUTLS_INCL
+ static char *certfile = NULL;
+#endif
+ static char *client_keyfile = NULL;
+ static char *client_certfile = NULL;
+
+ if (ssl_ctx == NULL) {
+ /*
+ * First time only.
+ */
+#if SSLEAY_VERSION_NUMBER < 0x0800
+ if ((ssl_ctx = SSL_CTX_new()) != NULL) {
+ X509_set_default_verify_paths(ssl_ctx->cert);
+ }
+#else
+ SSLeay_add_ssl_algorithms();
+ if ((ssl_ctx = SSL_CTX_new(TLS_client_method())) != NULL) {
+#ifdef SSL_OP_NO_SSLv2
+ SSL_CTX_set_options(ssl_ctx, SSL_OP_ALL | SSL_OP_NO_SSLv2);
+#else
+ SSL_CTX_set_options(ssl_ctx, SSL_OP_ALL);
+#endif
+#ifdef SSL_OP_NO_COMPRESSION
+ SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_COMPRESSION);
+#endif
+#ifdef SSL_MODE_AUTO_RETRY
+ SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY);
+#endif
+#ifdef SSL_MODE_RELEASE_BUFFERS
+ SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS);
+#endif
+ SSL_CTX_set_default_verify_paths(ssl_ctx);
+ SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, HTSSLCallback);
+ }
+#endif /* SSLEAY_VERSION_NUMBER < 0x0800 */
+#if defined(USE_PROGRAM_DIR) & !defined(USE_GNUTLS_INCL)
+ if (ssl_ctx != NULL) {
+ X509_LOOKUP *lookup;
+
+ lookup = X509_STORE_add_lookup(ssl_ctx->cert_store,
+ X509_LOOKUP_file());
+ if (lookup != NULL) {
+ char *certfile = NULL;
+
+ HTSprintf0(&certfile, "%s\\cert.pem", program_dir);
+ X509_LOOKUP_load_file(lookup, certfile, X509_FILETYPE_PEM);
+ FREE(certfile);
+ }
+ }
+#endif
+#ifdef USE_GNUTLS_INCL
+ if ((certfile = LYGetEnv("SSL_CERT_FILE")) != NULL) {
+ CTRACE((tfp,
+ "HTGetSSLHandle: certfile is set to %s by SSL_CERT_FILE\n",
+ certfile));
+ } else {
+ if (non_empty(SSL_cert_file)) {
+ certfile = SSL_cert_file;
+ CTRACE((tfp,
+ "HTGetSSLHandle: certfile is set to %s by config SSL_CERT_FILE\n",
+ certfile));
+ }
+#if defined(USE_PROGRAM_DIR)
+ else {
+ HTSprintf0(&(certfile), "%s\\cert.pem", program_dir);
+ CTRACE((tfp,
+ "HTGetSSLHandle: certfile is set to %s by installed directory\n", certfile));
+ }
+#endif
+ }
+#endif
+ atexit(free_ssl_ctx);
+ }
+
+ if (non_empty(SSL_client_key_file)) {
+ client_keyfile = SSL_client_key_file;
+ CTRACE((tfp,
+ "HTGetSSLHandle: client key file is set to %s by config SSL_CLIENT_KEY_FILE\n",
+ client_keyfile));
+ }
+
+ if (non_empty(SSL_client_cert_file)) {
+ client_certfile = SSL_client_cert_file;
+ CTRACE((tfp,
+ "HTGetSSLHandle: client cert file is set to %s by config SSL_CLIENT_CERT_FILE\n",
+ client_certfile));
+ }
+#ifdef USE_GNUTLS_INCL
+ ssl_ctx->certfile = certfile;
+ ssl_ctx->certfile_type = GNUTLS_X509_FMT_PEM;
+ ssl_ctx->client_keyfile = client_keyfile;
+ ssl_ctx->client_keyfile_type = GNUTLS_X509_FMT_PEM;
+ ssl_ctx->client_certfile = client_certfile;
+ ssl_ctx->client_certfile_type = GNUTLS_X509_FMT_PEM;
+#elif SSLEAY_VERSION_NUMBER >= 0x0930
+ if (client_certfile != NULL) {
+ if (client_keyfile == NULL) {
+ client_keyfile = client_certfile;
+ }
+ SSL_CTX_use_certificate_chain_file(ssl_ctx, client_certfile);
+ SSL_CTX_use_PrivateKey_file(ssl_ctx, client_keyfile, SSL_FILETYPE_PEM);
+ }
+#endif
+ ssl_okay = 0;
+ return (SSL_new(ssl_ctx));
+}
+
+void HTSSLInitPRNG(void)
+{
+#if SSLEAY_VERSION_NUMBER >= 0x00905100
+ if (RAND_status() == 0) {
+ char rand_file[256];
+ time_t t;
+ long l, seed;
+
+#ifndef _WINDOWS
+ pid_t pid;
+
+#else
+ DWORD pid;
+#endif
+
+ t = time(NULL);
+
+#ifndef _WINDOWS
+ pid = getpid();
+#else
+ pid = GetCurrentThreadId();
+#endif
+
+ RAND_file_name(rand_file, 256L);
+ CTRACE((tfp, "HTTP: Seeding PRNG\n"));
+ /* Seed as much as 1024 bytes from RAND_file_name */
+ RAND_load_file(rand_file, 1024L);
+ /* Seed in time (mod_ssl does this) */
+ RAND_seed((unsigned char *) &t, (int) sizeof(time_t));
+
+ /* Seed in pid (mod_ssl does this) */
+ RAND_seed((unsigned char *) &pid, (int) sizeof(pid));
+ /* Initialize system's random number generator */
+ RAND_bytes((unsigned char *) &seed, (int) sizeof(long));
+
+ lynx_srand((unsigned) seed);
+ while (RAND_status() == 0) {
+ /* Repeatedly seed the PRNG using the system's random number generator until it has been seeded with enough data */
+ l = (long) lynx_rand();
+ RAND_seed((unsigned char *) &l, (int) sizeof(long));
+ }
+ /* Write a rand_file */
+ RAND_write_file(rand_file);
+ }
+#endif /* SSLEAY_VERSION_NUMBER >= 0x00905100 */
+ return;
+}
+
+#define HTTP_NETREAD(sock, buff, size, handle) \
+ (handle \
+ ? SSL_read(handle, buff, size) \
+ : NETREAD(sock, buff, size))
+
+#define HTTP_NETWRITE(sock, buff, size, handle) \
+ (handle \
+ ? SSL_write(handle, buff, size) \
+ : NETWRITE(sock, buff, size))
+
+#define HTTP_NETCLOSE(sock, handle) \
+ { (void)NETCLOSE(sock); \
+ if (handle) \
+ SSL_free(handle); \
+ SSL_handle = handle = NULL; \
+ }
+
+#else
+#define HTTP_NETREAD(a, b, c, d) NETREAD(a, b, c)
+#define HTTP_NETWRITE(a, b, c, d) NETWRITE(a, b, c)
+#define HTTP_NETCLOSE(a, b) (void)NETCLOSE(a)
+#endif /* USE_SSL */
+
+#ifdef _WINDOWS /* 1997/11/06 (Thu) 13:00:08 */
+
+#define BOX_TITLE "Lynx " __FILE__
+#define BOX_FLAG (MB_ICONINFORMATION | MB_SETFOREGROUND)
+
+typedef struct {
+ int fd;
+ char *buf;
+ int len;
+} recv_data_t;
+
+int ws_read_per_sec = 0;
+static int ws_errno = 0;
+
+static DWORD g_total_times = 0;
+static DWORD g_total_bytes = 0;
+
+/* The same like read, but takes care of EINTR and uses select to
+ timeout the stale connections. */
+
+static int ws_read(int fd, char *buf, int len)
+{
+ int res;
+ int retry = 3;
+
+ do {
+ res = recv(fd, buf, len, 0);
+ if (WSAEWOULDBLOCK == WSAGetLastError()) {
+ Sleep(100);
+ if (retry-- > 0)
+ continue;
+ }
+ } while (res == SOCKET_ERROR && SOCKET_ERRNO == EINTR);
+
+ return res;
+}
+
+#define DWORD_ERR ((DWORD)-1)
+
+static DWORD __stdcall _thread_func(void *p)
+{
+ DWORD result;
+ int i, val;
+ recv_data_t *q = (recv_data_t *) p;
+
+ i = 0;
+ i++;
+ val = ws_read(q->fd, q->buf, q->len);
+
+ if (val == SOCKET_ERROR) {
+ ws_errno = WSAGetLastError();
+#if 0
+ char buff[256];
+
+ sprintf(buff, "Thread read: %d, error (%ld), fd = %d, len = %d",
+ i, ws_errno, q->fd, q->len);
+ MessageBox(NULL, buff, BOX_TITLE, BOX_FLAG);
+#endif
+ result = DWORD_ERR;
+ } else {
+ result = val;
+ }
+
+ return result;
+}
+
+/* The same like read, but takes care of EINTR and uses select to
+ timeout the stale connections. */
+
+int ws_netread(int fd, char *buf, int len)
+{
+ int i;
+ char buff[256];
+
+ /* 1998/03/30 (Mon) 09:01:21 */
+ HANDLE hThread;
+ DWORD dwThreadID;
+ DWORD exitcode = 0;
+ DWORD ret_val = DWORD_ERR;
+ DWORD val, process_time, now_TickCount, save_TickCount;
+
+ static recv_data_t para;
+
+#define TICK 5
+#define STACK_SIZE 0x2000uL
+
+ EnterCriticalSection(&critSec_READ);
+
+ para.fd = fd;
+ para.buf = buf;
+ para.len = len;
+
+ ws_read_per_sec = 0;
+ save_TickCount = GetTickCount();
+
+ hThread = CreateThread(NULL, STACK_SIZE,
+ _thread_func,
+ (void *) &para, 0UL, &dwThreadID);
+
+ if (hThread == 0) {
+ HTInfoMsg("CreateThread Failed (read)");
+ goto read_exit;
+ }
+
+ i = 0;
+ while (1) {
+ val = WaitForSingleObject(hThread, 1000 / TICK);
+ i++;
+ if (val == WAIT_FAILED) {
+ HTInfoMsg("Wait Failed");
+ ret_val = DWORD_ERR;
+ break;
+ } else if (val == WAIT_TIMEOUT) {
+ i++;
+ if (i / TICK > (AlertSecs + 2)) {
+ sprintf(buff, "Read Waiting (%2d.%01d) for %d Bytes",
+ i / TICK, (i % TICK) * 10 / TICK, len);
+ SetConsoleTitle(buff);
+ }
+ if (win32_check_interrupt() || ((i / TICK) > lynx_timeout)) {
+ if (CloseHandle(hThread) == FALSE) {
+ HTInfoMsg("Thread terminate Failed");
+ }
+ WSASetLastError(ETIMEDOUT);
+ ret_val = HT_INTERRUPTED;
+ break;
+ }
+ } else if (val == WAIT_OBJECT_0) {
+ if (GetExitCodeThread(hThread, &exitcode) == FALSE) {
+ exitcode = DWORD_ERR;
+ }
+ if (CloseHandle(hThread) == FALSE) {
+ HTInfoMsg("Thread terminate Failed");
+ }
+ now_TickCount = GetTickCount();
+ if (now_TickCount >= save_TickCount)
+ process_time = now_TickCount - save_TickCount;
+ else
+ process_time = now_TickCount + (0xffffffff - save_TickCount);
+
+ if (process_time == 0)
+ process_time = 1;
+ g_total_times += process_time;
+
+ /*
+ * DWORD is unsigned, and could be an error code which is signed.
+ */
+ if ((long) exitcode > 0)
+ g_total_bytes += exitcode;
+
+ ws_read_per_sec = g_total_bytes;
+ if (ws_read_per_sec > 2000000) {
+ if (g_total_times > 1000)
+ ws_read_per_sec /= (g_total_times / 1000);
+ } else {
+ ws_read_per_sec *= 1000;
+ ws_read_per_sec /= g_total_times;
+ }
+
+ ret_val = exitcode;
+ break;
+ }
+ } /* end while(1) */
+
+ read_exit:
+ LeaveCriticalSection(&critSec_READ);
+ return ret_val;
+}
+#endif /* _WINDOWS */
+
+/*
+ * RFC-1738 says we can have user/password using these ASCII characters
+ * safe = "$" | "-" | "_" | "." | "+"
+ * extra = "!" | "*" | "'" | "(" | ")" | ","
+ * hex = digit | "A" | "B" | "C" | "D" | "E" | "F" |
+ * "a" | "b" | "c" | "d" | "e" | "f"
+ * escape = "%" hex hex
+ * unreserved = alpha | digit | safe | extra
+ * uchar = unreserved | escape
+ * user = *[ uchar | ";" | "?" | "&" | "=" ]
+ * password = *[ uchar | ";" | "?" | "&" | "=" ]
+ * and we cannot have a password without user, i.e., no leading ":"
+ * and ":", "@", "/" must be encoded, i.e., will not appear as such.
+ *
+ * However, in a URL
+ * //<user>:<password>@<host>:<port>/<url-path>
+ * valid characters in the host are different, not allowing most of those
+ * punctuation characters.
+ *
+ * RFC-3986 amends this, using
+ * userinfo = *( unreserved / pct-encoded / sub-delims / ":" )
+ * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
+ * reserved = gen-delims / sub-delims
+ * gen-delims = ":" / "/" / "?" / "#" / "[" / "]" / "@"
+ * sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
+ * / "*" / "+" / "," / ";" / "="
+ * and
+ * host = IP-literal / IPv4address / reg-name
+ * reg-name = *( unreserved / pct-encoded / sub-delims )
+ */
+char *HTSkipToAt(char *host, int *gen_delims)
+{
+ char *result = 0;
+ char *s = host;
+ int pass = 0;
+ int ch;
+ int last = -1;
+
+ *gen_delims = 0;
+ while ((ch = UCH(*s)) != '\0') {
+ if (ch == ':') {
+ if (pass++)
+ break;
+ } else if (ch == '@') {
+ if (s != host && last != ':')
+ result = s;
+ break;
+ } else if (RFC_3986_GEN_DELIMS(ch)) {
+ *gen_delims += 1;
+ if (!RFC_3986_GEN_DELIMS(s[1]))
+ break;
+ } else if (ch == '%') {
+ if (!(isxdigit(UCH(s[1])) && isxdigit(UCH(s[2]))))
+ break;
+ } else if (!(RFC_3986_UNRESERVED(ch) ||
+ RFC_3986_SUB_DELIMS(ch))) {
+ break;
+ }
+ ++s;
+ last = ch;
+ }
+ return result;
+}
+
+static char *fake_hostname(char *auth)
+{
+ char *result = NULL;
+ char *colon = NULL;
+
+ StrAllocCopy(result, auth);
+ if ((colon = strchr(result, ':')) != 0)
+ *colon = '\0';
+ if (strchr(result, '.') == 0)
+ FREE(result);
+ return result;
+}
+
+/*
+ * Strip any username from the given string so we retain only the host.
+ */
+void strip_userid(char *host, int parse_only)
+{
+ int gen_delims = 0;
+ char *p1 = host;
+ char *p2 = HTSkipToAt(host, &gen_delims);
+
+ if (p2 != 0) {
+ char *msg = NULL;
+ char *auth = NULL;
+ char *fake = NULL;
+ char *p3 = p2;
+ int sub_delims = 0;
+ int my_delimit = UCH(*p2);
+ int do_trimming = (my_delimit == '@');
+
+ *p2++ = '\0';
+
+ StrAllocCopy(auth, host);
+
+ /*
+ * Trailing "gen-delims" demonstrates that there is no user/password.
+ */
+ while ((p3 != host) && RFC_3986_GEN_DELIMS(p3[-1])) {
+ *(--p3) = '\0';
+ }
+ /*
+ * While legal, punctuation-only user/password is questionable.
+ */
+ while ((p3 != host) && RFC_3986_SUB_DELIMS(p3[-1])) {
+ ++sub_delims;
+ *(--p3) = '\0';
+ }
+ /*
+ * Trim trailing "gen-delims" from the real hostname.
+ */
+ for (p3 = p2; *p3 != '\0'; ++p3) {
+ if (RFC_3986_GEN_DELIMS(*p3)) {
+ *p3 = '\0';
+ break;
+ }
+ }
+ CTRACE((tfp, "trim auth: result:`%s'\n", host));
+
+ if (gen_delims || strcmp(host, auth)) {
+ do_trimming = !gen_delims;
+ }
+ if (*host == '\0' && sub_delims) {
+ HTSprintf0(&msg,
+ gettext("User/password contains only punctuation: %s"),
+ auth);
+ } else if ((fake = fake_hostname(host)) != NULL) {
+ HTSprintf0(&msg,
+ gettext("User/password may be confused with hostname: '%s' (e.g, '%s')"),
+ auth, fake);
+ }
+ if (msg != 0 && !parse_only)
+ HTAlert(msg);
+ if (do_trimming) {
+ while ((*p1++ = *p2++) != '\0') {
+ ;
+ }
+ CTRACE((tfp, "trim host: result:`%s'\n", host));
+ }
+ FREE(fake);
+ FREE(auth);
+ FREE(msg);
+ }
+}
+
+/*
+ * Check if the user's options specified to use the given encoding. Normally
+ * all encodings with compiled-in support are specified (encodingALL).
+ */
+static BOOL acceptEncoding(int code)
+{
+ BOOL result = FALSE;
+
+ if ((code & LYAcceptEncoding) != 0) {
+ const char *program = 0;
+
+ switch (code) {
+ case encodingGZIP:
+ program = HTGetProgramPath(ppGZIP);
+ break;
+ case encodingDEFLATE:
+ program = HTGetProgramPath(ppINFLATE);
+ break;
+ case encodingCOMPRESS:
+ program = HTGetProgramPath(ppCOMPRESS);
+ break;
+ case encodingBZIP2:
+ program = HTGetProgramPath(ppBZIP2);
+ break;
+ case encodingBROTLI:
+ program = HTGetProgramPath(ppBROTLI);
+ break;
+ default:
+ break;
+ }
+ /*
+ * FIXME: if lynx did not rely upon external programs to decompress
+ * files for external viewers, this check could be relaxed.
+ */
+ result = (BOOL) (program != 0);
+ }
+ return result;
+}
+
+#ifdef USE_SSL
+static void show_cert_issuer(X509 * peer_cert GCC_UNUSED)
+{
+#if defined(USE_OPENSSL_INCL) || defined(USE_GNUTLS_FUNCS)
+ char ssl_dn[1024];
+ char *msg = NULL;
+
+ X509_NAME_oneline(X509_get_issuer_name(peer_cert), ssl_dn, (int) sizeof(ssl_dn));
+ HTSprintf0(&msg, gettext("Certificate issued by: %s"), ssl_dn);
+ _HTProgress(msg);
+ FREE(msg);
+#elif defined(USE_GNUTLS_INCL)
+ /* the OpenSSL "compat" code compiles but dumps core with GNU TLS */
+#endif
+}
+#endif
+
+/*
+ * Remove IPv6 brackets (and any port-number) from the given host-string.
+ */
+#ifdef USE_SSL
+static char *StripIpv6Brackets(char *host)
+{
+ int port_number;
+ char *p;
+
+ if ((p = HTParsePort(host, &port_number)) != 0)
+ *p = '\0';
+
+ if (*host == '[') {
+ p = host + strlen(host) - 1;
+ if (*p == ']') {
+ *p = '\0';
+ for (p = host; (p[0] = p[1]) != '\0'; ++p) {
+ ; /* EMPTY */
+ }
+ }
+ }
+ return host;
+}
+#endif
+
+/*
+ * Remove user/password, if any, from the given host-string.
+ */
+#ifdef USE_SSL
+static char *StripUserAuthents(char *host)
+{
+ char *p = strchr(host, '@');
+
+ if (p != NULL) {
+ char *q = host;
+
+ while ((*q++ = *++p) != '\0') ;
+ }
+ return host;
+}
+#endif
+
+/* Load Document from HTTP Server HTLoadHTTP()
+ * ==============================
+ *
+ * Given a hypertext address, this routine loads a document.
+ *
+ *
+ * On entry,
+ * arg is the hypertext reference of the article to be loaded.
+ *
+ * On exit,
+ * returns >=0 If no error, a good socket number
+ * <0 Error.
+ *
+ * The socket must be closed by the caller after the document has been
+ * read.
+ *
+ */
+static int HTLoadHTTP(const char *arg,
+ HTParentAnchor *anAnchor,
+ HTFormat format_out,
+ HTStream *sink)
+{
+ static char empty[1];
+ int s; /* Socket number for returned data */
+ const char *url = arg; /* The URL which get_physical() returned */
+ bstring *command = NULL; /* The whole command */
+ char *eol; /* End of line if found */
+ char *start_of_data; /* Start of body of reply */
+ int status; /* tcp return */
+ off_t bytes_already_read;
+ char crlf[3]; /* A CR LF equivalent string */
+ HTStream *target; /* Unconverted data */
+ HTFormat format_in; /* Format arriving in the message */
+ BOOL do_head = FALSE; /* Whether or not we should do a head */
+ BOOL do_post = FALSE; /* ARE WE posting ? */
+ const char *METHOD;
+
+ char *line_buffer = NULL;
+ char *line_kept_clean = NULL;
+
+#ifdef SH_EX /* FIX BUG by kaz@maczuka.hitachi.ibaraki.jp */
+ int real_length_of_line = 0;
+#endif
+ BOOL extensions; /* Assume good HTTP server */
+ char *linebuf = NULL;
+ char temp[80];
+ BOOL first_Accept = TRUE;
+ BOOL show_401 = FALSE;
+ BOOL show_407 = FALSE;
+ BOOL auth_proxy = NO; /* Generate a proxy authorization. - AJL */
+
+ int length, rawlength, rv;
+ int server_status = 0;
+ BOOL doing_redirect, already_retrying = FALSE;
+ int len = 0;
+
+#ifdef USE_SSL
+ unsigned long SSLerror;
+ BOOL do_connect = FALSE; /* ARE WE going to use a proxy tunnel ? */
+ BOOL did_connect = FALSE; /* ARE WE actually using a proxy tunnel ? */
+ const char *connect_url = NULL; /* The URL being proxied */
+ char *connect_host = NULL; /* The host being proxied */
+ SSL *handle = NULL; /* The SSL handle */
+ X509 *peer_cert; /* The peer certificate */
+ char ssl_dn[1024];
+ char *cert_host;
+ char *ssl_host;
+ char *p;
+ char *msg = NULL;
+ int status_sslcertcheck;
+ char *ssl_dn_start;
+ char *ssl_all_cns = NULL;
+
+#ifdef USE_GNUTLS_INCL
+ int ret;
+ unsigned tls_status;
+#endif
+
+#if (SSLEAY_VERSION_NUMBER >= 0x0900) && !defined(USE_GNUTLS_FUNCS)
+ BOOL try_tls = TRUE;
+#endif /* SSLEAY_VERSION_NUMBER >= 0x0900 */
+ SSL_handle = NULL;
+#else
+ void *handle = NULL;
+#endif /* USE_SSL */
+
+ if (anAnchor->isHEAD)
+ do_head = TRUE;
+ else if (anAnchor->post_data)
+ do_post = TRUE;
+
+ if (!url) {
+ status = -3;
+ _HTProgress(BAD_REQUEST);
+ goto done;
+ }
+ if (!*url) {
+ status = -2;
+ _HTProgress(BAD_REQUEST);
+ goto done;
+ }
+#ifdef USE_SSL
+ if (using_proxy && !StrNCmp(url, "http://", 7)) {
+ int portnumber;
+
+ if ((connect_url = strstr((url + 7), "https://"))) {
+ do_connect = TRUE;
+ connect_host = HTParse(connect_url, "https", PARSE_HOST);
+ if (!HTParsePort(connect_host, &portnumber)) {
+ sprintf(temp, ":%d", HTTPS_PORT);
+ StrAllocCat(connect_host, temp);
+ }
+ CTRACE((tfp, "HTTP: connect_url = '%s'\n", connect_url));
+ CTRACE((tfp, "HTTP: connect_host = '%s'\n", connect_host));
+ } else if ((connect_url = strstr((url + 7), "snews://"))) {
+ do_connect = TRUE;
+ connect_host = HTParse(connect_url, "snews", PARSE_HOST);
+ if (!HTParsePort(connect_host, &portnumber)) {
+ sprintf(temp, ":%d", SNEWS_PORT);
+ StrAllocCat(connect_host, temp);
+ }
+ CTRACE((tfp, "HTTP: connect_url = '%s'\n", connect_url));
+ CTRACE((tfp, "HTTP: connect_host = '%s'\n", connect_host));
+ }
+ }
+#endif /* USE_SSL */
+
+ sprintf(crlf, "%c%c", CR, LF);
+
+ /*
+ * At this point, we're talking HTTP/1.0.
+ */
+ extensions = YES;
+
+ try_again:
+ /*
+ * All initializations are moved down here from up above, so we can start
+ * over here...
+ */
+ eol = 0;
+ length = 0;
+ doing_redirect = FALSE;
+ permanent_redirection = FALSE;
+ redirect_post_content = FALSE;
+ target = NULL;
+ line_buffer = NULL;
+ line_kept_clean = NULL;
+
+#ifdef USE_SSL
+ if (!StrNCmp(url, "https", 5))
+ status = HTDoConnect(url, "HTTPS", HTTPS_PORT, &s);
+ else
+ status = HTDoConnect(url, "HTTP", HTTP_PORT, &s);
+#else
+ if (!StrNCmp(url, "https", 5)) {
+ HTAlert(gettext("This client does not contain support for HTTPS URLs."));
+ status = HT_NOT_LOADED;
+ goto done;
+ }
+ status = HTDoConnect(arg, "HTTP", HTTP_PORT, &s);
+#endif /* USE_SSL */
+ if (status == HT_INTERRUPTED) {
+ /*
+ * Interrupt cleanly.
+ */
+ CTRACE((tfp, "HTTP: Interrupted on connect; recovering cleanly.\n"));
+ _HTProgress(CONNECTION_INTERRUPTED);
+ status = HT_NOT_LOADED;
+ goto done;
+ }
+ if (status < 0) {
+#ifdef _WINDOWS
+ CTRACE((tfp, "HTTP: Unable to connect to remote host for `%s'\n"
+ " (status = %d, sock_errno = %d).\n",
+ url, status, SOCKET_ERRNO));
+#else
+ CTRACE((tfp,
+ "HTTP: Unable to connect to remote host for `%s' (errno = %d).\n",
+ url, SOCKET_ERRNO));
+#endif
+ HTAlert(gettext("Unable to connect to remote host."));
+ status = HT_NOT_LOADED;
+ goto done;
+ }
+#ifdef USE_SSL
+ use_tunnel:
+ /*
+ * If this is an https document, then do the SSL stuff here.
+ */
+ if (did_connect || !StrNCmp(url, "https", 5)) {
+ SSL_handle = handle = HTGetSSLHandle();
+ SSL_set_fd(handle, s);
+ /* get host we're connecting to */
+ ssl_host = HTParse(url, "", PARSE_HOST);
+ ssl_host = StripIpv6Brackets(ssl_host);
+ ssl_host = StripUserAuthents(ssl_host);
+#if defined(USE_GNUTLS_FUNCS)
+ ret = gnutls_server_name_set(handle->gnutls_state,
+ GNUTLS_NAME_DNS,
+ ssl_host, strlen(ssl_host));
+ CTRACE((tfp, "...called gnutls_server_name_set(%s) ->%d\n", ssl_host, ret));
+#elif SSLEAY_VERSION_NUMBER >= 0x0900
+#ifndef USE_NSS_COMPAT_INCL
+ if (!try_tls) {
+ SSL_set_no_TLSV1();
+ CTRACE((tfp, "...adding SSL_OP_NO_TLSv1\n"));
+ }
+#if OPENSSL_VERSION_NUMBER >= 0x0090806fL && !defined(OPENSSL_NO_TLSEXT)
+ else {
+ int ret = (int) SSL_set_tlsext_host_name(handle, ssl_host);
+
+ CTRACE((tfp, "...called SSL_set_tlsext_host_name(%s) ->%d\n",
+ ssl_host, ret));
+ }
+#endif
+#endif
+#endif /* SSLEAY_VERSION_NUMBER >= 0x0900 */
+ HTSSLInitPRNG();
+ status = SSL_connect(handle);
+
+ if (status <= 0) {
+#if (SSLEAY_VERSION_NUMBER >= 0x0900)
+#if !defined(USE_GNUTLS_FUNCS)
+ if (try_tls) {
+ _HTProgress(gettext("Retrying connection without TLS."));
+ try_tls = FALSE;
+ if (did_connect)
+ HTTP_NETCLOSE(s, handle);
+ goto try_again;
+ } else
+#endif
+ {
+ CTRACE((tfp,
+ "HTTP: Unable to complete SSL handshake for '%s', SSL_connect=%d, SSL error stack dump follows\n",
+ url, status));
+ SSL_load_error_strings();
+ while ((SSLerror = ERR_get_error()) != 0) {
+ CTRACE((tfp, "HTTP: SSL: %s\n", ERR_error_string(SSLerror, NULL)));
+ }
+ HTAlert("Unable to make secure connection to remote host.");
+ if (did_connect)
+ HTTP_NETCLOSE(s, handle);
+ status = HT_NOT_LOADED;
+ goto done;
+ }
+#else
+ unsigned long SSLerror;
+
+ CTRACE((tfp,
+ "HTTP: Unable to complete SSL handshake for '%s', SSL_connect=%d, SSL error stack dump follows\n",
+ url, status));
+ SSL_load_error_strings();
+ while ((SSLerror = ERR_get_error()) != 0) {
+ CTRACE((tfp, "HTTP: SSL: %s\n", ERR_error_string(SSLerror, NULL)));
+ }
+ HTAlert("Unable to make secure connection to remote host.");
+ if (did_connect)
+ HTTP_NETCLOSE(s, handle);
+ status = HT_NOT_LOADED;
+ goto done;
+#endif /* SSLEAY_VERSION_NUMBER >= 0x0900 */
+ }
+#ifdef USE_GNUTLS_INCL
+ gnutls_certificate_set_verify_flags(handle->gnutls_cred,
+ GNUTLS_VERIFY_DO_NOT_ALLOW_SAME |
+ GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT);
+ ret = gnutls_certificate_verify_peers2(handle->gnutls_state, &tls_status);
+ if (ret < 0 || tls_status != 0) {
+ int flag_continue = 1;
+
+#if GNUTLS_VERSION_NUMBER >= 0x030104
+ int type;
+ gnutls_datum_t out;
+
+ if (ret < 0) {
+ SSL_single_prompt(&msg,
+ gettext("GnuTLS error when trying to verify certificate."));
+ } else {
+ type = gnutls_certificate_type_get(handle->gnutls_state);
+ (void) gnutls_certificate_verification_status_print(tls_status,
+ type,
+ &out, 0);
+ SSL_single_prompt(&msg, (const char *) out.data);
+ gnutls_free(out.data);
+ }
+#else
+ char *msg2;
+
+ if (ret == 0 && tls_status & GNUTLS_CERT_SIGNER_NOT_FOUND) {
+ msg2 = gettext("the certificate has no known issuer");
+ } else if (tls_status & GNUTLS_CERT_SIGNER_NOT_FOUND) {
+ msg2 = gettext("no issuer was found");
+ } else if (tls_status & GNUTLS_CERT_SIGNER_NOT_CA) {
+ msg2 = gettext("issuer is not a CA");
+ } else if (tls_status & GNUTLS_CERT_REVOKED) {
+ msg2 = gettext("the certificate has been revoked");
+ } else {
+ msg2 = gettext("the certificate is not trusted");
+ }
+ SSL_single_prompt(&msg, msg2);
+#endif
+ CTRACE((tfp, "HTLoadHTTP: %s\n", msg));
+ if (!ssl_noprompt) {
+ if (!HTForcedPrompt(ssl_noprompt, msg, NO)) {
+ flag_continue = 0;
+ }
+ } else if (ssl_noprompt == FORCE_PROMPT_NO) {
+ flag_continue = 0;
+ }
+ FREE(msg);
+ if (flag_continue == 0) {
+ status = HT_NOT_LOADED;
+ FREE(msg);
+ goto done;
+ }
+ }
+#endif
+
+ peer_cert = (X509 *) SSL_get_peer_certificate(handle);
+#if defined(USE_OPENSSL_INCL) || defined(USE_GNUTLS_FUNCS)
+ X509_NAME_oneline(X509_get_subject_name(peer_cert),
+ ssl_dn, (int) sizeof(ssl_dn));
+#elif defined(USE_GNUTLS_INCL)
+ X509_NAME_oneline(X509_get_subject_name(peer_cert),
+ ssl_dn + 1, (int) sizeof(ssl_dn) - 1);
+
+ /* Iterate over DN in incompatible GnuTLS format to bring it into OpenSSL format */
+ ssl_dn[0] = '/';
+ ssl_dn_start = ssl_dn;
+ while (*ssl_dn_start) {
+ if ((*ssl_dn_start == ',') && (*(ssl_dn_start + 1) == ' ')) {
+ *ssl_dn_start++ = '/';
+ if (*(p = ssl_dn_start) != 0) {
+ while ((p[0] = p[1]) != 0)
+ ++p;
+ }
+ } else {
+ ssl_dn_start++;
+ }
+ }
+#endif
+
+ /*
+ * X.509 DN validation taking ALL CN fields into account
+ * (c) 2006 Thorsten Glaser <tg@mirbsd.de>
+ */
+
+ /* initialise status information */
+ status_sslcertcheck = 0; /* 0 = no CN found in DN */
+ ssl_dn_start = ssl_dn;
+
+ /* validate all CNs found in DN */
+ CTRACE((tfp, "Validating CNs in '%s'\n", ssl_dn_start));
+ while ((cert_host = strstr(ssl_dn_start, "/CN=")) != NULL) {
+ status_sslcertcheck = 1; /* 1 = could not verify CN */
+ /* start of CommonName */
+ cert_host += 4;
+ /* find next part of DistinguishedName */
+ if ((p = StrChr(cert_host, '/')) != NULL) {
+ *p = '\0';
+ ssl_dn_start = p; /* yes this points to the NUL byte */
+ } else
+ ssl_dn_start = NULL;
+ cert_host = StripIpv6Brackets(cert_host);
+
+ /* verify this CN */
+ CTRACE((tfp, "Matching\n\tssl_host '%s'\n\tcert_host '%s'\n",
+ ssl_host, cert_host));
+ if (!strcasecomp_asterisk(ssl_host, cert_host)) {
+ status_sslcertcheck = 2; /* 2 = verified peer */
+ /* I think this is cool to have in the logs -TG */
+ HTSprintf0(&msg,
+ gettext("Verified connection to %s (cert=%s)"),
+ ssl_host, cert_host);
+ _HTProgress(msg);
+ FREE(msg);
+ /* no need to continue the verification loop */
+ break;
+ }
+
+ /* add this CN to list of failed CNs */
+ if (ssl_all_cns == NULL)
+ StrAllocCopy(ssl_all_cns, "CN<");
+ else
+ StrAllocCat(ssl_all_cns, ":CN<");
+ StrAllocCat(ssl_all_cns, cert_host);
+ StrAllocCat(ssl_all_cns, ">");
+ /* if we cannot retry, don't try it */
+ if (ssl_dn_start == NULL)
+ break;
+ /* now retry next CN found in DN */
+ *ssl_dn_start = '/'; /* formerly NUL byte */
+ }
+
+ /* check the X.509v3 Subject Alternative Name */
+#ifdef USE_GNUTLS_INCL
+ if (status_sslcertcheck < 2) {
+ int i;
+ size_t size;
+ gnutls_x509_crt_t cert;
+ static char buf[2048];
+
+ /* import the certificate to the x509_crt format */
+ if (gnutls_x509_crt_init(&cert) == 0) {
+
+ if (gnutls_x509_crt_import(cert, peer_cert,
+ GNUTLS_X509_FMT_DER) < 0) {
+ gnutls_x509_crt_deinit(cert);
+ goto done;
+ }
+
+ ret = 0;
+ for (i = 0; !(ret < 0); i++) {
+ size = sizeof(buf);
+ ret = gnutls_x509_crt_get_subject_alt_name(cert,
+ (unsigned) i,
+ buf, &size,
+ NULL);
+
+ if (strcasecomp_asterisk(ssl_host, buf) == 0) {
+ status_sslcertcheck = 2;
+ HTSprintf0(&msg,
+ gettext("Verified connection to %s (subj=%s)"),
+ ssl_host, buf);
+ _HTProgress(msg);
+ FREE(msg);
+ break;
+ }
+
+ }
+ }
+ }
+#endif
+#ifdef USE_OPENSSL_INCL
+ if (status_sslcertcheck < 2) {
+ STACK_OF(GENERAL_NAME) * gens;
+ int i, numalts;
+ const GENERAL_NAME *gn;
+
+ gens = (STACK_OF(GENERAL_NAME) *)
+ X509_get_ext_d2i(peer_cert, NID_subject_alt_name, NULL, NULL);
+
+ if (gens != NULL) {
+ numalts = sk_GENERAL_NAME_num(gens);
+ for (i = 0; i < numalts; ++i) {
+ gn = sk_GENERAL_NAME_value(gens, i);
+ if (gn->type == GEN_DNS)
+ cert_host = (char *) ASN1_STRING_data(gn->d.ia5);
+ else if (gn->type == GEN_IPADD) {
+ /* XXX untested -TG */
+ size_t j = (size_t) ASN1_STRING_length(gn->d.ia5);
+
+ cert_host = (char *) malloc(j + 1);
+ MemCpy(cert_host, ASN1_STRING_data(gn->d.ia5), j);
+ cert_host[j] = '\0';
+ } else
+ continue;
+ status_sslcertcheck = 1; /* got at least one */
+ /* verify this SubjectAltName (see above) */
+ cert_host = StripIpv6Brackets(cert_host);
+ if (!(gn->type == GEN_IPADD ? strcasecomp :
+ strcasecomp_asterisk) (ssl_host, cert_host)) {
+ status_sslcertcheck = 2;
+ HTSprintf0(&msg,
+ gettext("Verified connection to %s (subj=%s)"),
+ ssl_host, cert_host);
+ _HTProgress(msg);
+ FREE(msg);
+ if (gn->type == GEN_IPADD)
+ free(cert_host);
+ break;
+ }
+ /* add to list of failed CNs */
+ if (ssl_all_cns == NULL)
+ StrAllocCopy(ssl_all_cns, "SAN<");
+ else
+ StrAllocCat(ssl_all_cns, ":SAN<");
+ if (gn->type == GEN_DNS)
+ StrAllocCat(ssl_all_cns, "DNS=");
+ else if (gn->type == GEN_IPADD)
+ StrAllocCat(ssl_all_cns, "IP=");
+ StrAllocCat(ssl_all_cns, cert_host);
+ StrAllocCat(ssl_all_cns, ">");
+ if (gn->type == GEN_IPADD)
+ free(cert_host);
+ }
+ sk_GENERAL_NAME_free(gens);
+ }
+ }
+#endif /* USE_OPENSSL_INCL */
+
+ /* if an error occurred, format the appropriate message */
+ if (status_sslcertcheck == 0) {
+ SSL_single_prompt(&msg,
+ gettext("Can't find common name in certificate"));
+ } else if (status_sslcertcheck == 1) {
+ SSL_double_prompt(&msg,
+ gettext("SSL error:host(%s)!=cert(%s)-Continue?"),
+ ssl_host, ssl_all_cns);
+ }
+
+ /* if an error occurred, let the user decide how much he trusts */
+ if (status_sslcertcheck < 2) {
+ if (msg == NULL)
+ StrAllocCopy(msg, gettext("SSL error"));
+ if (!HTForcedPrompt(ssl_noprompt, msg, NO)) {
+ status = HT_NOT_LOADED;
+ FREE(msg);
+ FREE(ssl_all_cns);
+ goto done;
+ }
+ SSL_double_prompt(&msg,
+ gettext("UNVERIFIED connection to %s (cert=%s)"),
+ ssl_host, ssl_all_cns ? ssl_all_cns : "NONE");
+ _HTProgress(msg);
+ FREE(msg);
+ }
+
+ show_cert_issuer(peer_cert);
+
+ HTSprintf0(&msg,
+ gettext("Secure %d-bit %s (%s) HTTP connection"),
+ SSL_get_cipher_bits(handle, NULL),
+ SSL_get_cipher_version(handle),
+ SSL_get_cipher(handle));
+ _HTProgress(msg);
+ FREE(msg);
+ FREE(ssl_all_cns);
+ FREE(ssl_host);
+ }
+#endif /* USE_SSL */
+
+ /* Ask that node for the document, omitting the host name & anchor
+ */
+ {
+ char *p1 = (HTParse(url, "", PARSE_PATH | PARSE_PUNCTUATION));
+
+#ifdef USE_SSL
+ if (do_connect) {
+ METHOD = "CONNECT";
+ BStrCopy0(command, "CONNECT ");
+ } else
+#endif /* USE_SSL */
+ if (do_post) {
+ METHOD = "POST";
+ BStrCopy0(command, "POST ");
+ } else if (do_head) {
+ METHOD = "HEAD";
+ BStrCopy0(command, "HEAD ");
+ } else {
+ METHOD = "GET";
+ BStrCopy0(command, "GET ");
+ }
+
+ /*
+ * If we are using a proxy gateway don't copy in the first slash of
+ * say: /gopher://a;lkdjfl;ajdf;lkj/;aldk/adflj so that just
+ * gopher://.... is sent.
+ */
+#ifdef USE_SSL
+ if (using_proxy && !did_connect) {
+ if (do_connect)
+ BStrCat0(command, connect_host);
+ else
+ BStrCat0(command, p1 + 1);
+ }
+#else
+ if (using_proxy)
+ BStrCat0(command, p1 + 1);
+#endif /* USE_SSL */
+ else
+ BStrCat0(command, p1);
+ FREE(p1);
+ }
+ if (extensions) {
+ BStrCat0(command, " ");
+ BStrCat0(command, ((HTprotocolLevel == HTTP_1_0)
+ ? "HTTP/1.0"
+ : "HTTP/1.1"));
+ }
+
+ BStrCat0(command, crlf); /* CR LF, as in rfc 977 */
+
+ if (extensions) {
+ int n, i;
+ char *host = NULL;
+
+ if ((host = HTParse(anAnchor->address, "", PARSE_HOST)) != NULL) {
+ strip_userid(host, TRUE);
+ HTBprintf(&command, "Host: %s%c%c", host, CR, LF);
+ FREE(host);
+ }
+ if (HTprotocolLevel >= HTTP_1_1) {
+ HTBprintf(&command, "Connection: close%c%c", CR, LF);
+ }
+
+ if (!HTPresentations)
+ HTFormatInit();
+ n = HTList_count(HTPresentations);
+
+ first_Accept = TRUE;
+ len = 0;
+ for (i = 0; i < n; i++) {
+ HTPresentation *pres =
+ (HTPresentation *) HTList_objectAt(HTPresentations, i);
+
+ if (pres->get_accept) {
+ if (pres->quality < 1.0) {
+ if (pres->maxbytes > 0) {
+ sprintf(temp, ";q=%4.3f;mxb=%" PRI_off_t "",
+ pres->quality, CAST_off_t (pres->maxbytes));
+ } else {
+ sprintf(temp, ";q=%4.3f", pres->quality);
+ }
+ } else if (pres->maxbytes > 0) {
+ sprintf(temp, ";mxb=%" PRI_off_t "", CAST_off_t (pres->maxbytes));
+ } else {
+ temp[0] = '\0';
+ }
+ HTSprintf0(&linebuf, "%s%s%s",
+ (first_Accept ?
+ "Accept: " : ", "),
+ HTAtom_name(pres->rep),
+ temp);
+ len += (int) strlen(linebuf);
+ if (len > 252 && !first_Accept) {
+ BStrCat0(command, crlf);
+ HTSprintf0(&linebuf, "Accept: %s%s",
+ HTAtom_name(pres->rep),
+ temp);
+ len = (int) strlen(linebuf);
+ }
+ BStrCat0(command, linebuf);
+ first_Accept = FALSE;
+ }
+ }
+ HTBprintf(&command, "%s*/*;q=0.01%c%c",
+ (first_Accept ?
+ "Accept: " : ", "), CR, LF);
+
+ /*
+ * FIXME: suppressing the "Accept-Encoding" in this case is done to
+ * work around limitations of the presentation logic used for the
+ * command-line "-base" option. The remote site may transmit the
+ * document gzip'd, but the ensuing logic in HTSaveToFile() would see
+ * the mime-type as gzip rather than text/html, and not prepend the
+ * base URL. This is less efficient than accepting the compressed data
+ * and uncompressing it, adding the base URL but is simpler than
+ * augmenting the dump's presentation logic -TD
+ */
+ if (LYPrependBaseToSource && dump_output_immediately) {
+ CTRACE((tfp,
+ "omit Accept-Encoding to work-around interaction with -source\n"));
+ } else {
+ char *list = 0;
+ int j, k;
+
+ for (j = 1; j < encodingALL; j <<= 1) {
+ if (acceptEncoding(j)) {
+ for (k = 0; tbl_preferred_encoding[k].name != 0; ++k) {
+ if (tbl_preferred_encoding[k].value == j) {
+ if (list != 0)
+ StrAllocCat(list, ", ");
+ StrAllocCat(list, tbl_preferred_encoding[k].name);
+ break;
+ }
+ }
+ }
+ }
+
+ if (list != 0) {
+ HTBprintf(&command, "Accept-Encoding: %s%c%c", list, CR, LF);
+ free(list);
+ }
+ }
+
+ if (non_empty(language)) {
+ HTBprintf(&command, "Accept-Language: %s%c%c", language, CR, LF);
+ }
+
+ if (non_empty(pref_charset)) {
+ BStrCat0(command, "Accept-Charset: ");
+ StrAllocCopy(linebuf, pref_charset);
+ if (linebuf[strlen(linebuf) - 1] == ',')
+ linebuf[strlen(linebuf) - 1] = '\0';
+ LYLowerCase(linebuf);
+ if (strstr(linebuf, "iso-8859-1") == NULL)
+ StrAllocCat(linebuf, ", iso-8859-1;q=0.01");
+ if (strstr(linebuf, "us-ascii") == NULL)
+ StrAllocCat(linebuf, ", us-ascii;q=0.01");
+ BStrCat0(command, linebuf);
+ HTBprintf(&command, "%c%c", CR, LF);
+ }
+#if 0
+ /*
+ * Promote 300 (Multiple Choices) replies, if supported, over 406 (Not
+ * Acceptable) replies. - FM
+ *
+ * This used to be done in versions 2.7 and 2.8*, but violates the
+ * specs for transparent content negotiation and has the effect that
+ * servers supporting those specs will send 300 (Multiple Choices)
+ * instead of a normal response (e.g. 200 OK), since they will assume
+ * that the client wants to make the choice. It is not clear whether
+ * there are any servers or sites for which sending this header really
+ * improves anything.
+ *
+ * If there ever is a need to send "Negotiate: trans" and really mean
+ * it, we should send "Negotiate: trans,trans" or similar, since that
+ * is semantically equivalent and some servers may ignore "Negotiate:
+ * trans" as a special case when it comes from Lynx (to work around the
+ * old faulty behavior). - kw
+ *
+ * References:
+ * RFC 2295 (see also RFC 2296), and mail to lynx-dev and
+ * new-httpd@apache.org from Koen Holtman, Jan 1999.
+ */
+ if (!do_post) {
+ HTBprintf(&command, "Negotiate: trans%c%c", CR, LF);
+ }
+#endif /* 0 */
+
+ /*
+ * When reloading give no-cache pragma to proxy server to make it
+ * refresh its cache. -- Ari L. <luotonen@dxcern.cern.ch>
+ *
+ * Also send it as a Cache-Control header for HTTP/1.1. - FM
+ */
+ if (reloading) {
+ HTBprintf(&command, "Pragma: no-cache%c%c", CR, LF);
+ HTBprintf(&command, "Cache-Control: no-cache%c%c", CR, LF);
+ }
+
+ if (LYSendUserAgent || no_useragent) {
+ if (non_empty(LYUserAgent)) {
+ char *cp = LYSkipBlanks(LYUserAgent);
+
+ /* Won't send it at all if all blank - kw */
+ if (*cp != '\0')
+ HTBprintf(&command, "User-Agent: %.*s%c%c",
+ INIT_LINE_SIZE - 15, LYUserAgent, CR, LF);
+ } else {
+ HTBprintf(&command, "User-Agent: %s/%s libwww-FM/%s%c%c",
+ HTAppName ? HTAppName : "unknown",
+ HTAppVersion ? HTAppVersion : "0.0",
+ HTLibraryVersion, CR, LF);
+ }
+ }
+
+ if (non_empty(personal_mail_address) && !LYNoFromHeader) {
+ HTBprintf(&command, "From: %s%c%c", personal_mail_address, CR, LF);
+ }
+
+ if (!(LYUserSpecifiedURL ||
+ LYNoRefererHeader || LYNoRefererForThis) &&
+ strcmp(HTLoadedDocumentURL(), "")) {
+ const char *cp = LYRequestReferer;
+
+ if (!cp)
+ cp = HTLoadedDocumentURL(); /* @@@ Try both? - kw */
+ BStrCat0(command, "Referer: ");
+ if (isLYNXIMGMAP(cp)) {
+ char *pound = findPoundSelector(cp);
+ int nn = (pound ? (int) (pound - cp) : (int) strlen(cp));
+
+ HTSABCat(&command, cp + LEN_LYNXIMGMAP, nn);
+ } else {
+ BStrCat0(command, cp);
+ }
+ HTBprintf(&command, "%c%c", CR, LF);
+ } {
+ char *abspath;
+ char *docname;
+ char *hostname;
+ char *colon;
+ int portnumber;
+ char *auth, *cookie = NULL;
+ BOOL secure = (BOOL) (StrNCmp(anAnchor->address, "https", 5)
+ ? FALSE
+ : TRUE);
+
+ abspath = HTParse(arg, "", PARSE_PATH | PARSE_PUNCTUATION);
+ docname = HTParse(arg, "", PARSE_PATH);
+ hostname = HTParse(arg, "", PARSE_HOST);
+ if (hostname &&
+ NULL != (colon = HTParsePort(hostname, &portnumber))) {
+ *colon = '\0'; /* Chop off port number */
+ } else if (!StrNCmp(arg, "https", 5)) {
+ portnumber = HTTPS_PORT;
+ } else {
+ portnumber = HTTP_PORT;
+ }
+
+ /*
+ * Add Authorization, Proxy-Authorization, and/or Cookie headers,
+ * if applicable.
+ */
+ if (using_proxy) {
+ /*
+ * If we are using a proxy, first determine if we should
+ * include an Authorization header and/or Cookie header for the
+ * ultimate target of this request. - FM & AJL
+ */
+ char *host2 = NULL, *path2 = NULL;
+ int port2 = (StrNCmp(docname, "https", 5) ?
+ HTTP_PORT : HTTPS_PORT);
+
+ host2 = HTParse(docname, "", PARSE_HOST);
+ path2 = HTParse(docname, "", PARSE_PATH | PARSE_PUNCTUATION);
+ if ((colon = HTParsePort(host2, &port2)) != NULL) {
+ /* Use non-default port number */
+ *colon = '\0';
+ }
+
+ /*
+ * This composeAuth() does file access, i.e., for the ultimate
+ * target of the request. - AJL
+ */
+ auth_proxy = NO;
+ auth = HTAA_composeAuth(host2, port2, path2, auth_proxy);
+ if (auth == NULL) {
+ CTRACE((tfp, "HTTP: Not sending authorization (yet).\n"));
+ } else if (*auth != '\0') {
+ /*
+ * We have an Authorization header to be included.
+ */
+ HTBprintf(&command, "%s%c%c", auth, CR, LF);
+ CTRACE((tfp, "HTTP: Sending authorization: %s\n", auth));
+ } else {
+ /*
+ * The user either cancelled or made a mistake with the
+ * username and password prompt.
+ */
+ if (!(traversal || dump_output_immediately) &&
+ HTConfirm(CONFIRM_WO_PASSWORD)) {
+ show_401 = TRUE;
+ } else {
+ if (traversal || dump_output_immediately)
+ HTAlert(FAILED_NEED_PASSWD);
+#ifdef USE_SSL
+ if (did_connect)
+ HTTP_NETCLOSE(s, handle);
+#endif /* USE_SSL */
+ BStrFree(command);
+ FREE(hostname);
+ FREE(docname);
+ FREE(abspath);
+ FREE(host2);
+ FREE(path2);
+ status = HT_NOT_LOADED;
+ goto done;
+ }
+ }
+ /*
+ * Add 'Cookie:' header, if it's HTTP or HTTPS document being
+ * proxied.
+ */
+ if (!StrNCmp(docname, "http", 4)) {
+ cookie = LYAddCookieHeader(host2, path2, port2, secure);
+ }
+ FREE(host2);
+ FREE(path2);
+ /*
+ * The next composeAuth() will be for the proxy. - AJL
+ */
+ auth_proxy = YES;
+ } else {
+ /*
+ * Add cookie for a non-proxied request. - FM
+ */
+ cookie = LYAddCookieHeader(hostname, abspath, portnumber, secure);
+ auth_proxy = NO;
+ }
+ /*
+ * If we do have a cookie set, add it to the request buffer. - FM
+ */
+ if (cookie != NULL) {
+ if (*cookie != '$' && USE_RFC_2965) {
+ /*
+ * It's a historical cookie, so signal to the server that
+ * we support modern cookies. - FM
+ */
+ BStrCat0(command, "Cookie2: $Version=\"1\"");
+ BStrCat0(command, crlf);
+ CTRACE((tfp, "HTTP: Sending Cookie2: $Version =\"1\"\n"));
+ }
+ if (*cookie != '\0') {
+ /*
+ * It's not a zero-length string, so add the header. Note
+ * that any folding of long strings has been done already
+ * in LYCookie.c. - FM
+ */
+ BStrCat0(command, "Cookie: ");
+ BStrCat0(command, cookie);
+ BStrCat0(command, crlf);
+ CTRACE((tfp, "HTTP: Sending Cookie: %s\n", cookie));
+ }
+ FREE(cookie);
+ }
+ FREE(abspath);
+
+ /*
+ * If we are using a proxy, auth_proxy should be YES, and we check
+ * here whether we want a Proxy-Authorization header for it. If we
+ * are not using a proxy, auth_proxy should still be NO, and we
+ * check here for whether we want an Authorization header. - FM &
+ * AJL
+ */
+ if ((auth = HTAA_composeAuth(hostname,
+ portnumber,
+ docname,
+ auth_proxy)) != NULL &&
+ *auth != '\0') {
+ /*
+ * If auth is not NULL nor zero-length, it's an Authorization
+ * or Proxy-Authorization header to be included. - FM
+ */
+ HTBprintf(&command, "%s%c%c", auth, CR, LF);
+ CTRACE((tfp, (auth_proxy ?
+ "HTTP: Sending proxy authorization: %s\n" :
+ "HTTP: Sending authorization: %s\n"),
+ auth));
+ } else if (auth && *auth == '\0') {
+ /*
+ * If auth is a zero-length string, the user either cancelled
+ * or goofed at the username and password prompt. - FM
+ */
+ if (!(traversal || dump_output_immediately) && HTConfirm(CONFIRM_WO_PASSWORD)) {
+ if (auth_proxy == TRUE) {
+ show_407 = TRUE;
+ } else {
+ show_401 = TRUE;
+ }
+ } else {
+ if (traversal || dump_output_immediately)
+ HTAlert(FAILED_NEED_PASSWD);
+ BStrFree(command);
+ FREE(hostname);
+ FREE(docname);
+ status = HT_NOT_LOADED;
+ goto done;
+ }
+ } else {
+ CTRACE((tfp, (auth_proxy ?
+ "HTTP: Not sending proxy authorization (yet).\n" :
+ "HTTP: Not sending authorization (yet).\n")));
+ }
+ FREE(hostname);
+ FREE(docname);
+ }
+ }
+
+ if (
+#ifdef USE_SSL
+ !do_connect &&
+#endif /* USE_SSL */
+ do_post) {
+ CTRACE((tfp, "HTTP: Doing post, content-type '%s'\n",
+ anAnchor->post_content_type
+ ? anAnchor->post_content_type
+ : "lose"));
+ HTBprintf(&command, "Content-Type: %s%c%c",
+ anAnchor->post_content_type
+ ? anAnchor->post_content_type
+ : "lose",
+ CR, LF);
+
+ HTBprintf(&command, "Content-Length: %d%c%c",
+ !isBEmpty(anAnchor->post_data)
+ ? BStrLen(anAnchor->post_data)
+ : 0,
+ CR, LF);
+
+ BStrCat0(command, crlf); /* Blank line means "end" of headers */
+
+ BStrCat(command, anAnchor->post_data);
+ } else
+ BStrCat0(command, crlf); /* Blank line means "end" of headers */
+
+ if (TRACE) {
+ CTRACE((tfp, "Writing:\n"));
+ trace_bstring(command);
+#ifdef USE_SSL
+ CTRACE((tfp, "%s",
+ (anAnchor->post_data && !do_connect ? crlf : "")));
+#else
+ CTRACE((tfp, "%s",
+ (anAnchor->post_data ? crlf : "")));
+#endif /* USE_SSL */
+ CTRACE((tfp, "----------------------------------\n"));
+ }
+
+ _HTProgress(gettext("Sending HTTP request."));
+
+#ifdef NOT_ASCII /* S/390 -- gil -- 0548 */
+ {
+ char *p2;
+
+ for (p2 = BStrData(command);
+ p2 < BStrData(command) + BStrLen(command);
+ p2++)
+ *p2 = TOASCII(*p2);
+ }
+#endif /* NOT_ASCII */
+ status = (int) HTTP_NETWRITE(s,
+ BStrData(command),
+ BStrLen(command),
+ handle);
+ BStrFree(command);
+ FREE(linebuf);
+ if (status <= 0) {
+ if (status == 0) {
+ CTRACE((tfp, "HTTP: Got status 0 in initial write\n"));
+ /* Do nothing. */
+ } else if ((SOCKET_ERRNO == ENOTCONN ||
+ SOCKET_ERRNO == ECONNRESET ||
+ SOCKET_ERRNO == EPIPE) &&
+ !already_retrying &&
+ /* Don't retry if we're posting. */ !do_post) {
+ /*
+ * Arrrrgh, HTTP 0/1 compatibility problem, maybe.
+ */
+ CTRACE((tfp,
+ "HTTP: BONZO ON WRITE Trying again with HTTP0 request.\n"));
+ _HTProgress(RETRYING_AS_HTTP0);
+ HTTP_NETCLOSE(s, handle);
+ extensions = NO;
+ already_retrying = TRUE;
+ goto try_again;
+ } else {
+ CTRACE((tfp,
+ "HTTP: Hit unexpected network WRITE error; aborting connection.\n"));
+ HTTP_NETCLOSE(s, handle);
+ status = -1;
+ HTAlert(gettext("Unexpected network write error; connection aborted."));
+ goto done;
+ }
+ }
+
+ CTRACE((tfp, "HTTP: WRITE delivered OK\n"));
+ _HTProgress(gettext("HTTP request sent; waiting for response."));
+
+ /* Read the first line of the response
+ * -----------------------------------
+ */
+ {
+ /* Get numeric status etc */
+ BOOL end_of_file = NO;
+ int buffer_length = INIT_LINE_SIZE;
+
+ line_buffer = typecallocn(char, (size_t) buffer_length);
+
+ if (line_buffer == NULL)
+ outofmem(__FILE__, "HTLoadHTTP");
+
+ HTReadProgress(bytes_already_read = 0, (off_t) 0);
+ do { /* Loop to read in the first line */
+ /*
+ * Extend line buffer if necessary for those crazy WAIS URLs ;-)
+ */
+ if (buffer_length - length < LINE_EXTEND_THRESH) {
+ buffer_length = buffer_length + buffer_length;
+ line_buffer =
+ (char *) realloc(line_buffer, ((unsigned) buffer_length *
+ sizeof(char)));
+
+ if (line_buffer == NULL)
+ outofmem(__FILE__, "HTLoadHTTP");
+ }
+ CTRACE((tfp, "HTTP: Trying to read %d\n", buffer_length - length - 1));
+ status = HTTP_NETREAD(s,
+ line_buffer + length,
+ (buffer_length - length - 1),
+ handle);
+ CTRACE((tfp, "HTTP: Read %d\n", status));
+ if (status <= 0) {
+ /*
+ * Retry if we get nothing back too.
+ * Bomb out if we get nothing twice.
+ */
+ if (status == HT_INTERRUPTED) {
+ CTRACE((tfp, "HTTP: Interrupted initial read.\n"));
+ _HTProgress(CONNECTION_INTERRUPTED);
+ HTTP_NETCLOSE(s, handle);
+ status = HT_NO_DATA;
+ goto clean_up;
+ } else if (status < 0 &&
+ (SOCKET_ERRNO == ENOTCONN ||
+#ifdef _WINDOWS /* 1997/11/09 (Sun) 16:59:58 */
+ SOCKET_ERRNO == ETIMEDOUT ||
+#endif
+ SOCKET_ERRNO == ECONNRESET ||
+ SOCKET_ERRNO == EPIPE) &&
+ !already_retrying && !do_post) {
+ /*
+ * Arrrrgh, HTTP 0/1 compatibility problem, maybe.
+ */
+ CTRACE((tfp,
+ "HTTP: BONZO Trying again with HTTP0 request.\n"));
+ HTTP_NETCLOSE(s, handle);
+ FREE(line_buffer);
+ FREE(line_kept_clean);
+
+ extensions = NO;
+ already_retrying = TRUE;
+ _HTProgress(RETRYING_AS_HTTP0);
+ goto try_again;
+ }
+#ifdef USE_SSL
+ else if ((SSLerror = ERR_get_error()) != 0) {
+ CTRACE((tfp,
+ "HTTP: Hit unexpected network read error; aborting connection; status %d:%s.\n",
+ status, ERR_error_string(SSLerror, NULL)));
+ HTAlert(gettext("Unexpected network read error; connection aborted."));
+ HTTP_NETCLOSE(s, handle);
+ status = -1;
+ goto clean_up;
+ }
+#endif
+ else {
+ CTRACE((tfp,
+ "HTTP: Hit unexpected network read error; aborting connection; status %d.\n",
+ status));
+ HTAlert(gettext("Unexpected network read error; connection aborted."));
+ HTTP_NETCLOSE(s, handle);
+ status = -1;
+ goto clean_up;
+ }
+ }
+#ifdef NOT_ASCII /* S/390 -- gil -- 0564 */
+ {
+ char *p2;
+
+ for (p2 = line_buffer + length;
+ p2 < line_buffer + length + status;
+ p2++)
+ *p2 = FROMASCII(*p2);
+ }
+#endif /* NOT_ASCII */
+
+ bytes_already_read += status;
+ HTReadProgress(bytes_already_read, (off_t) 0);
+
+#ifdef UCX /* UCX returns -1 on EOF */
+ if (status == 0 || status == -1)
+#else
+ if (status == 0)
+#endif
+ {
+ break;
+ }
+ line_buffer[length + status] = 0;
+
+ if (line_buffer) {
+ FREE(line_kept_clean);
+ line_kept_clean = (char *) malloc((unsigned) buffer_length *
+ sizeof(char));
+
+ if (line_kept_clean == NULL)
+ outofmem(__FILE__, "HTLoadHTTP");
+ MemCpy(line_kept_clean, line_buffer, buffer_length);
+#ifdef SH_EX /* FIX BUG by kaz@maczuka.hitachi.ibaraki.jp */
+ real_length_of_line = length + status;
+#endif
+ }
+
+ eol = StrChr(line_buffer + length, LF);
+ /* Do we *really* want to do this? */
+ if (eol && eol != line_buffer && *(eol - 1) == CR)
+ *(eol - 1) = ' ';
+
+ length = length + status;
+
+ /* Do we really want to do *this*? */
+ if (eol)
+ *eol = 0; /* Terminate the line */
+ }
+ /* All we need is the first line of the response. If it's a HTTP/1.0
+ * response, then the first line will be absurdly short and therefore
+ * we can safely gate the number of bytes read through this code (as
+ * opposed to below) to ~1000.
+ *
+ * Well, let's try 100.
+ */
+ while (!eol && !end_of_file && bytes_already_read < 100);
+ } /* Scope of loop variables */
+
+ /* save total length, in case we decide later to show it all - kw */
+ rawlength = length;
+
+ /* We now have a terminated unfolded line. Parse it.
+ * --------------------------------------------------
+ */
+ CTRACE((tfp, "HTTP: Rx: %s\n", line_buffer));
+
+ /*
+ * Kludge to work with old buggy servers and the VMS Help gateway. They
+ * can't handle the third word, so we try again without it.
+ */
+ if (extensions && /* Old buggy server or Help gateway? */
+ (0 == StrNCmp(line_buffer, "<TITLE>Bad File Request</TITLE>", 31) ||
+ 0 == StrNCmp(line_buffer, "Address should begin with", 25) ||
+ 0 == StrNCmp(line_buffer, "<TITLE>Help ", 12) ||
+ 0 == strcmp(line_buffer,
+ "Document address invalid or access not authorised"))) {
+ FREE(line_buffer);
+ FREE(line_kept_clean);
+ extensions = NO;
+ already_retrying = TRUE;
+ CTRACE((tfp, "HTTP: close socket %d to retry with HTTP0\n", s));
+ HTTP_NETCLOSE(s, handle);
+ /* print a progress message */
+ _HTProgress(RETRYING_AS_HTTP0);
+ goto try_again;
+ } {
+ int fields;
+ char server_version[VERSION_LENGTH + 1];
+
+ server_version[0] = 0;
+
+ fields = sscanf(line_buffer, "%20s %d",
+ server_version,
+ &server_status);
+
+ CTRACE((tfp, "HTTP: Scanned %d fields from line_buffer\n", fields));
+
+ if (non_empty(http_error_file)) {
+ /* Make the status code externally available */
+ FILE *error_file;
+
+#ifdef SERVER_STATUS_ONLY
+ error_file = fopen(http_error_file, TXT_W);
+ if (error_file) { /* Managed to open the file */
+ fprintf(error_file, "error=%d\n", server_status);
+ fclose(error_file);
+ }
+#else
+ error_file = fopen(http_error_file, TXT_A);
+ if (error_file) { /* Managed to open the file */
+ fprintf(error_file, " URL=%s (%s)\n", url, METHOD);
+ fprintf(error_file, "STATUS=%s\n", line_buffer);
+ fclose(error_file);
+ }
+#endif /* SERVER_STATUS_ONLY */
+ }
+
+ /*
+ * Rule out a non-HTTP/1.n reply as best we can.
+ */
+ if (fields < 2 || !server_version[0] || server_version[0] != 'H' ||
+ server_version[1] != 'T' || server_version[2] != 'T' ||
+ server_version[3] != 'P' || server_version[4] != '/' ||
+ server_version[6] != '.') {
+ /*
+ * Ugh! An HTTP0 reply,
+ */
+ HTAtom *encoding;
+
+ CTRACE((tfp, "--- Talking HTTP0.\n"));
+
+ format_in = HTFileFormat(url, &encoding, NULL);
+ /*
+ * Treat all plain text as HTML. This sucks but its the only
+ * solution without without looking at content.
+ */
+ if (!StrNCmp(HTAtom_name(format_in), STR_PLAINTEXT, 10)) {
+ CTRACE((tfp, "HTTP: format_in being changed to text/HTML\n"));
+ format_in = WWW_HTML;
+ }
+ if (!IsUnityEnc(encoding)) {
+ /*
+ * Change the format to that for "www/compressed".
+ */
+ CTRACE((tfp, "HTTP: format_in is '%s',\n", HTAtom_name(format_in)));
+ StrAllocCopy(anAnchor->content_type, HTAtom_name(format_in));
+ StrAllocCopy(anAnchor->content_encoding, HTAtom_name(encoding));
+ format_in = HTAtom_for("www/compressed");
+ CTRACE((tfp, " Treating as '%s' with encoding '%s'\n",
+ "www/compressed", HTAtom_name(encoding)));
+ }
+
+ start_of_data = line_kept_clean;
+ } else {
+ /*
+ * Set up to decode full HTTP/1.n response. - FM
+ */
+ format_in = HTAtom_for("www/mime");
+ CTRACE((tfp, "--- Talking HTTP1.\n"));
+
+ /*
+ * We set start_of_data to "" when !eol here because there will be
+ * a put_block done below; we do *not* use the value of
+ * start_of_data (as a pointer) in the computation of length (or
+ * anything else) when !eol. Otherwise, set the value of length to
+ * what we have beyond eol (i.e., beyond the status line). - FM
+ */
+ if (eol != 0) {
+ start_of_data = (eol + 1);
+ } else {
+ start_of_data = empty;
+ }
+ length = (eol
+ ? length - (int) (start_of_data - line_buffer)
+ : 0);
+
+ /*
+ * Trim trailing spaces in line_buffer so that we can use it in
+ * messages which include the status line. - FM
+ */
+ while (line_buffer[strlen(line_buffer) - 1] == ' ')
+ line_buffer[strlen(line_buffer) - 1] = '\0';
+
+ /*
+ * Take appropriate actions based on the status. - FM
+ */
+ switch (server_status / 100) {
+ case 1:
+ /*
+ * HTTP/1.1 Informational statuses.
+ * 100 Continue.
+ * 101 Switching Protocols.
+ * > 101 is unknown.
+ * We should never get these, and they have only the status
+ * line and possibly other headers, so we'll deal with them by
+ * showing the full header to the user as text/plain. - FM
+ */
+ HTAlert(gettext("Got unexpected Informational Status."));
+ do_head = TRUE;
+ break;
+
+ case 2:
+ /*
+ * Good: Got MIME object! (Successful) - FM
+ */
+ if (do_head) {
+ /*
+ * If HEAD was requested, show headers (and possibly bogus
+ * body) for all 2xx status codes as text/plain - KW
+ */
+ HTProgress(line_buffer);
+ break;
+ }
+ switch (server_status) {
+ case 204:
+ /*
+ * No Content.
+ */
+ HTAlert(line_buffer);
+ HTTP_NETCLOSE(s, handle);
+ HTNoDataOK = 1;
+ status = HT_NO_DATA;
+ goto clean_up;
+
+ case 205:
+ /*
+ * Reset Content. The server has fulfilled the request but
+ * nothing is returned and we should reset any form
+ * content. We'll instruct the user to do that, and
+ * restore the current document. - FM
+ */
+ HTAlert(gettext("Request fulfilled. Reset Content."));
+ HTTP_NETCLOSE(s, handle);
+ status = HT_NO_DATA;
+ goto clean_up;
+
+ case 206:
+ /*
+ * Partial Content. We didn't send a Range so something
+ * went wrong somewhere. Show the status message and
+ * restore the current document. - FM
+ */
+ HTAlert(line_buffer);
+ HTTP_NETCLOSE(s, handle);
+ status = HT_NO_DATA;
+ goto clean_up;
+
+ default:
+ /*
+ * 200 OK.
+ * 201 Created.
+ * 202 Accepted.
+ * 203 Non-Authoritative Information.
+ * > 206 is unknown.
+ * All should return something to display.
+ */
+#if defined(USE_SSL) /* && !defined(DISABLE_NEWS) _H */
+ if (do_connect) {
+ CTRACE((tfp,
+ "HTTP: Proxy tunnel to '%s' established.\n",
+ connect_host));
+ do_connect = FALSE;
+ url = connect_url;
+ FREE(line_buffer);
+ FREE(line_kept_clean);
+#ifndef DISABLE_NEWS
+ if (!StrNCmp(connect_url, "snews", 5)) {
+ CTRACE((tfp,
+ " Will attempt handshake and snews connection.\n"));
+ status = HTNewsProxyConnect(s, url, anAnchor,
+ format_out, sink);
+ goto done;
+ }
+#endif /* DISABLE_NEWS */
+ did_connect = TRUE;
+ already_retrying = TRUE;
+ eol = 0;
+ length = 0;
+ doing_redirect = FALSE;
+ permanent_redirection = FALSE;
+ target = NULL;
+ CTRACE((tfp,
+ " Will attempt handshake and resubmit headers.\n"));
+ goto use_tunnel;
+ }
+#endif /* USE_SSL */
+ HTProgress(line_buffer);
+ } /* case 2 switch */
+ break;
+
+ case 3:
+ /*
+ * Various forms of Redirection. - FM
+ * 300 Multiple Choices.
+ * 301 Moved Permanently.
+ * 302 Found (temporary; we can, and do, use GET).
+ * 303 See Other (temporary; always use GET).
+ * 304 Not Modified.
+ * 305 Use Proxy.
+ * 306 Set Proxy.
+ * 307 Temporary Redirect with method retained.
+ * > 308 is unknown.
+ */
+ if (no_url_redirection || do_head || keep_mime_headers) {
+ /*
+ * If any of these flags are set, we do not redirect, but
+ * instead show what was returned to the user as
+ * text/plain. - FM
+ */
+ HTProgress(line_buffer);
+ break;
+ }
+
+ if (server_status == 300) { /* Multiple Choices */
+ /*
+ * For client driven content negotiation. The server
+ * should be sending some way for the user-agent to make a
+ * selection, so we'll show the user whatever the server
+ * returns. There might be a Location: header with the
+ * server's preference present, but the choice should be up
+ * to the user, someday based on an Alternates: header,
+ * and a body always should be present with descriptions
+ * and links for the choices (i.e., we use the latter, for
+ * now). - FM
+ */
+ HTAlert(line_buffer);
+ if (traversal) {
+ HTTP_NETCLOSE(s, handle);
+ status = -1;
+ goto clean_up;
+ }
+ if (!dump_output_immediately &&
+ format_out == WWW_DOWNLOAD) {
+ /*
+ * Convert a download request to a presentation request
+ * for interactive users. - FM
+ */
+ format_out = WWW_PRESENT;
+ }
+ break;
+ }
+
+ if (server_status == 304) { /* Not Modified */
+ /*
+ * We didn't send an "If-Modified-Since" header, so this
+ * status is inappropriate. We'll deal with it by showing
+ * the full header to the user as text/plain. - FM
+ */
+ HTAlert(gettext("Got unexpected 304 Not Modified status."));
+ do_head = TRUE;
+ break;
+ }
+
+ if (server_status == 305 ||
+ server_status == 306 ||
+ server_status > 307) {
+ /*
+ * Show user the content, if any, for 305, 306, or unknown
+ * status. - FM
+ */
+ HTAlert(line_buffer);
+ if (traversal) {
+ HTTP_NETCLOSE(s, handle);
+ status = -1;
+ goto clean_up;
+ }
+ if (!dump_output_immediately &&
+ format_out == WWW_DOWNLOAD) {
+ /*
+ * Convert a download request to a presentation request
+ * for interactive users. - FM
+ */
+ format_out = WWW_PRESENT;
+ }
+ break;
+ }
+
+ /*
+ * We do not load the file, but read the headers for the
+ * "Location:", check out that redirecting_url and if it's
+ * acceptable (e.g., not a telnet URL when we have that
+ * disabled), initiate a new fetch. If that's another
+ * redirecting_url, we'll repeat the checks, and fetch
+ * initiations if acceptable, until we reach the actual URL, or
+ * the redirection limit set in HTAccess.c is exceeded. If the
+ * status was 301 indicating that the relocation is permanent,
+ * we set the permanent_redirection flag to make it permanent
+ * for the current anchor tree (i.e., will persist until the
+ * tree is freed or the client exits). If the redirection
+ * would include POST content, we seek confirmation from an
+ * interactive user, with option to use 303 for 301 (but not
+ * for 307), and otherwise refuse the redirection. We also
+ * don't allow permanent redirection if we keep POST content.
+ * If we don't find the Location header or it's value is
+ * zero-length, we display whatever the server returned, and
+ * the user should RELOAD that to try again, or make a
+ * selection from it if it contains links, or Left-Arrow to the
+ * previous document. - FM
+ */
+ {
+ if ((dump_output_immediately || traversal) &&
+ do_post &&
+ server_status != 303 &&
+ server_status != 302 &&
+ server_status != 301) {
+ /*
+ * Don't redirect POST content without approval from an
+ * interactive user. - FM
+ */
+ HTTP_NETCLOSE(s, handle);
+ status = -1;
+ HTAlert(gettext("Redirection of POST content requires user approval."));
+ if (traversal)
+ HTProgress(line_buffer);
+ goto clean_up;
+ }
+
+ HTProgress(line_buffer);
+ if (server_status == 301) { /* Moved Permanently */
+ if (do_post) {
+ /*
+ * Don't make the redirection permanent if we have
+ * POST content. - FM
+ */
+ CTRACE((tfp,
+ "HTTP: Have POST content. Treating 301 (Permanent) as Temporary.\n"));
+ HTAlert(gettext("Have POST content. Treating Permanent Redirection as Temporary.\n"));
+ } else {
+ permanent_redirection = TRUE;
+ }
+ }
+ doing_redirect = TRUE;
+
+ break;
+ }
+
+ case 4:
+ /*
+ * "I think I goofed!" (Client Error) - FM
+ */
+ switch (server_status) {
+ case 401: /* Unauthorized */
+ /*
+ * Authorization for origin server required. If show_401
+ * is set, proceed to showing the 401 body. Otherwise, if
+ * we can set up authorization based on the
+ * WWW-Authenticate header, and the user provides a
+ * username and password, try again. Otherwise, check
+ * whether to show the 401 body or restore the current
+ * document - FM
+ */
+ if (show_401)
+ break;
+ if (HTAA_shouldRetryWithAuth(start_of_data, (size_t)
+ length, s, NO)) {
+
+ HTTP_NETCLOSE(s, handle);
+ if (dump_output_immediately &&
+ !HTAA_HaveUserinfo(HTParse(arg, "", PARSE_HOST)) &&
+ !authentication_info[0]) {
+ fprintf(stderr,
+ "HTTP: Access authorization required.\n");
+ fprintf(stderr,
+ " Use the -auth=id:pw parameter.\n");
+ status = HT_NO_DATA;
+ goto clean_up;
+ }
+
+ CTRACE((tfp, "%s %d %s\n",
+ "HTTP: close socket", s,
+ "to retry with Access Authorization"));
+
+ _HTProgress(gettext("Retrying with access authorization information."));
+ FREE(line_buffer);
+ FREE(line_kept_clean);
+#ifdef USE_SSL
+ if (using_proxy && !StrNCmp(url, "https://", 8)) {
+ url = arg;
+ do_connect = TRUE;
+ did_connect = FALSE;
+ }
+#endif /* USE_SSL */
+ goto try_again;
+ } else if (!(traversal || dump_output_immediately) &&
+ HTConfirm(gettext("Show the 401 message body?"))) {
+ break;
+ } else {
+ if (traversal || dump_output_immediately)
+ HTAlert(FAILED_RETRY_WITH_AUTH);
+ HTTP_NETCLOSE(s, handle);
+ status = -1;
+ goto clean_up;
+ }
+
+ case 407:
+ /*
+ * Authorization for proxy server required. If we are not
+ * in fact using a proxy, or show_407 is set, proceed to
+ * showing the 407 body. Otherwise, if we can set up
+ * authorization based on the Proxy-Authenticate header,
+ * and the user provides a username and password, try
+ * again. Otherwise, check whether to show the 401 body or
+ * restore the current document. - FM & AJL
+ */
+ if (!using_proxy || show_407)
+ break;
+ if (HTAA_shouldRetryWithAuth(start_of_data, (size_t)
+ length, s, YES)) {
+
+ HTTP_NETCLOSE(s, handle);
+ if (dump_output_immediately && !proxyauth_info[0]) {
+ fprintf(stderr,
+ "HTTP: Proxy authorization required.\n");
+ fprintf(stderr,
+ " Use the -pauth=id:pw parameter.\n");
+ status = HT_NO_DATA;
+ goto clean_up;
+ }
+
+ CTRACE((tfp, "%s %d %s\n",
+ "HTTP: close socket", s,
+ "to retry with Proxy Authorization"));
+
+ _HTProgress(HTTP_RETRY_WITH_PROXY);
+ FREE(line_buffer);
+ FREE(line_kept_clean);
+ goto try_again;
+ } else if (!(traversal || dump_output_immediately) &&
+ HTConfirm(gettext("Show the 407 message body?"))) {
+ if (!dump_output_immediately &&
+ format_out == WWW_DOWNLOAD) {
+ /*
+ * Convert a download request to a presentation
+ * request for interactive users. - FM
+ */
+ format_out = WWW_PRESENT;
+ }
+ break;
+ } else {
+ if (traversal || dump_output_immediately)
+ HTAlert(FAILED_RETRY_WITH_PROXY);
+ HTTP_NETCLOSE(s, handle);
+ status = -1;
+ goto clean_up;
+ }
+
+ case 408:
+ /*
+ * Request Timeout. Show the status message and restore
+ * the current document. - FM
+ */
+ HTAlert(line_buffer);
+ HTTP_NETCLOSE(s, handle);
+ status = HT_NO_DATA;
+ goto clean_up;
+
+ default:
+ /*
+ * 400 Bad Request.
+ * 402 Payment Required.
+ * 403 Forbidden.
+ * 404 Not Found.
+ * 405 Method Not Allowed.
+ * 406 Not Acceptable.
+ * 409 Conflict.
+ * 410 Gone.
+ * 411 Length Required.
+ * 412 Precondition Failed.
+ * 413 Request Entity Too Large.
+ * 414 Request-URI Too Long.
+ * 415 Unsupported Media Type.
+ * 416 List Response (for content negotiation).
+ * > 416 is unknown.
+ * Show the status message, and display the returned text
+ * if we are not doing a traversal. - FM
+ */
+ HTAlert(line_buffer);
+ if (traversal) {
+ HTTP_NETCLOSE(s, handle);
+ status = -1;
+ goto clean_up;
+ }
+ if (!dump_output_immediately &&
+ format_out == WWW_DOWNLOAD) {
+ /*
+ * Convert a download request to a presentation request
+ * for interactive users. - FM
+ */
+ format_out = WWW_PRESENT;
+ }
+ break;
+ } /* case 4 switch */
+ break;
+
+ case 5:
+ /*
+ * "I think YOU goofed!" (server error)
+ * 500 Internal Server Error
+ * 501 Not Implemented
+ * 502 Bad Gateway
+ * 503 Service Unavailable
+ * 504 Gateway Timeout
+ * 505 HTTP Version Not Supported
+ * > 505 is unknown.
+ * Should always include a message, which we always should
+ * display. - FM
+ */
+ HTAlert(line_buffer);
+ if (traversal) {
+ HTTP_NETCLOSE(s, handle);
+ status = -1;
+ goto clean_up;
+ }
+ if (!dump_output_immediately &&
+ format_out == WWW_DOWNLOAD) {
+ /*
+ * Convert a download request to a presentation request for
+ * interactive users. - FM
+ */
+ format_out = WWW_PRESENT;
+ }
+ break;
+
+ default:
+ /*
+ * Bad or unknown server_status number. Take a chance and hope
+ * there is something to display. - FM
+ */
+ HTAlert(gettext("Unknown status reply from server!"));
+ HTAlert(line_buffer);
+ if (traversal) {
+ HTTP_NETCLOSE(s, handle);
+ status = -1;
+ goto clean_up;
+ }
+ if (!dump_output_immediately &&
+ format_out == WWW_DOWNLOAD) {
+ /*
+ * Convert a download request to a presentation request for
+ * interactive users. - FM
+ */
+ format_out = WWW_PRESENT;
+ }
+ break;
+ } /* Switch on server_status/100 */
+
+ } /* Full HTTP reply */
+ } /* scope of fields */
+
+ /*
+ * The user may have pressed the 'z'ap key during the pause caused by one
+ * of the HTAlerts above if the server reported an error, to skip loading
+ * of the error response page. Checking here before setting up the stream
+ * stack and feeding it data avoids doing unnecessary work, it also can
+ * avoid unnecessarily pushing a loaded document out of the cache to make
+ * room for the unwanted error page. - kw
+ */
+ if (HTCheckForInterrupt()) {
+ HTTP_NETCLOSE(s, handle);
+ if (doing_redirect) {
+ /*
+ * Impatient user. - FM
+ */
+ CTRACE((tfp, "HTTP: Interrupted followup read.\n"));
+ _HTProgress(CONNECTION_INTERRUPTED);
+ }
+ status = HT_INTERRUPTED;
+ goto clean_up;
+ }
+ /*
+ * Set up the stream stack to handle the body of the message.
+ */
+ if (do_head || keep_mime_headers) {
+ /*
+ * It was a HEAD request, or we want the headers and source.
+ */
+ start_of_data = line_kept_clean;
+#ifdef SH_EX /* FIX BUG by kaz@maczuka.hitachi.ibaraki.jp */
+/* GIF file contains \0, so strlen does not return the data length */
+ length = real_length_of_line;
+#else
+ length = rawlength;
+#endif
+ format_in = HTAtom_for(STR_PLAINTEXT);
+
+ } else if (doing_redirect) {
+
+ format_in = HTAtom_for("message/x-http-redirection");
+ StrAllocCopy(anAnchor->content_type, HTAtom_name(format_in));
+ if (traversal) {
+ format_out = WWW_DEBUG;
+ if (!sink)
+ sink = HTErrorStream();
+ } else if (!dump_output_immediately &&
+ format_out == WWW_DOWNLOAD) {
+ /*
+ * Convert a download request to a presentation request for
+ * interactive users. - FM
+ */
+ format_out = WWW_PRESENT;
+ }
+ }
+
+ target = HTStreamStack(format_in,
+ format_out,
+ sink, anAnchor);
+
+ if (target == NULL) {
+ char *buffer = NULL;
+
+ HTTP_NETCLOSE(s, handle);
+ HTSprintf0(&buffer, CANNOT_CONVERT_I_TO_O,
+ HTAtom_name(format_in), HTAtom_name(format_out));
+ _HTProgress(buffer);
+ FREE(buffer);
+ status = -1;
+ goto clean_up;
+ }
+
+ /*
+ * Recycle the first chunk of data, in all cases.
+ */
+ (*target->isa->put_block) (target, start_of_data, length);
+
+ /*
+ * Go pull the bulk of the data down.
+ */
+ rv = HTCopy(anAnchor, s, (void *) handle, target);
+
+ /*
+ * If we get here with doing_redirect set, it means that we were looking
+ * for a Location header. We either have got it now in redirecting_url -
+ * in that case the stream should not have loaded any data. Or we didn't
+ * get it, in that case the stream may have presented the message body
+ * normally. - kw
+ */
+
+ if (rv == -1) {
+ /*
+ * Intentional interrupt before data were received, not an error
+ */
+ if (doing_redirect && traversal)
+ status = -1;
+ else
+ status = HT_INTERRUPTED;
+ HTTP_NETCLOSE(s, handle);
+ goto clean_up;
+ }
+
+ if (rv == -2) {
+ /*
+ * Aw hell, a REAL error, maybe cuz it's a dumb HTTP0 server
+ */
+ (*target->isa->_abort) (target, NULL);
+ if (doing_redirect && redirecting_url) {
+ /*
+ * Got a location before the error occurred? Then consider it an
+ * interrupt but proceed below as normal. - kw
+ */
+ /* do nothing here */
+ } else {
+ HTTP_NETCLOSE(s, handle);
+ if (!doing_redirect && !already_retrying && !do_post) {
+ CTRACE((tfp, "HTTP: Trying again with HTTP0 request.\n"));
+ /*
+ * May as well consider it an interrupt -- right?
+ */
+ FREE(line_buffer);
+ FREE(line_kept_clean);
+ extensions = NO;
+ already_retrying = TRUE;
+ _HTProgress(RETRYING_AS_HTTP0);
+ goto try_again;
+ } else {
+ status = HT_NOT_LOADED;
+ goto clean_up;
+ }
+ }
+ }
+
+ /*
+ * Free if complete transmission (socket was closed before return). Close
+ * socket if partial transmission (was freed on abort).
+ */
+ if (rv != HT_INTERRUPTED && rv != -2) {
+ (*target->isa->_free) (target);
+ } else {
+ HTTP_NETCLOSE(s, handle);
+ }
+
+ if (doing_redirect) {
+ if (redirecting_url) {
+ /*
+ * Set up for checking redirecting_url in LYGetFile.c for
+ * restrictions before we seek the document at that Location. - FM
+ */
+ CTRACE((tfp, "HTTP: Picked up location '%s'\n",
+ redirecting_url));
+ if (rv == HT_INTERRUPTED) {
+ /*
+ * Intentional interrupt after data were received, not an error
+ * (probably). We take it as a user request to abandon the
+ * redirection chain.
+ *
+ * This could reasonably be changed (by just removing this
+ * block), it would make sense if there are redirecting
+ * resources that "hang" after sending the headers. - kw
+ */
+ FREE(redirecting_url);
+ CTRACE((tfp, "HTTP: Interrupted followup read.\n"));
+ status = HT_INTERRUPTED;
+ goto clean_up;
+ }
+ HTProgress(line_buffer);
+ if (server_status == 305) { /* Use Proxy */
+ /*
+ * Make sure the proxy field ends with a slash. - FM
+ */
+ if (redirecting_url[strlen(redirecting_url) - 1]
+ != '/')
+ StrAllocCat(redirecting_url, "/");
+ /*
+ * Append our URL. - FM
+ */
+ StrAllocCat(redirecting_url, anAnchor->address);
+ CTRACE((tfp, "HTTP: Proxy URL is '%s'\n",
+ redirecting_url));
+ }
+ if (!do_post ||
+ server_status == 303 ||
+ server_status == 302) {
+ /*
+ * We don't have POST content (nor support PUT or DELETE), or
+ * the status is "See Other" or "General Redirection" and we
+ * can convert to GET, so go back and check out the new URL. -
+ * FM
+ */
+ status = HT_REDIRECTING;
+ goto clean_up;
+ }
+ /*
+ * Make sure the user wants to redirect the POST content, or treat
+ * as GET - FM & DK
+ */
+ switch (HTConfirmPostRedirect(redirecting_url,
+ server_status)) {
+ /*
+ * User failed to confirm. Abort the fetch.
+ */
+ case 0:
+ FREE(redirecting_url);
+ status = HT_NO_DATA;
+ goto clean_up;
+
+ /*
+ * User wants to treat as GET with no content. Go back to
+ * check out the URL.
+ */
+ case 303:
+ break;
+
+ /*
+ * Set the flag to retain the POST content and go back to check
+ * out the URL. - FM
+ */
+ default:
+ redirect_post_content = TRUE;
+ }
+
+ /* Lou's old comment: - FM */
+ /* OK, now we've got the redirection URL temporarily stored
+ in external variable redirecting_url, exported from HTMIME.c,
+ since there's no straightforward way to do this in the library
+ currently. Do the right thing. */
+
+ status = HT_REDIRECTING;
+
+ } else {
+ status = traversal ? -1 : HT_LOADED;
+ }
+
+ } else {
+ /*
+ * If any data were received, treat as a complete transmission
+ */
+ status = HT_LOADED;
+ }
+
+ /*
+ * Clean up
+ */
+ clean_up:
+ FREE(line_buffer);
+ FREE(line_kept_clean);
+
+ done:
+ /*
+ * Clear out on exit, just in case.
+ */
+ reloading = FALSE;
+#ifdef USE_SSL
+ FREE(connect_host);
+ if (handle) {
+ SSL_free(handle);
+ SSL_handle = handle = NULL;
+ }
+#endif /* USE_SSL */
+ dump_server_status = server_status;
+ return status;
+}
+
+/* Protocol descriptor
+*/
+#ifdef GLOBALDEF_IS_MACRO
+#define _HTTP_C_GLOBALDEF_1_INIT { "http", HTLoadHTTP, 0}
+GLOBALDEF(HTProtocol, HTTP, _HTTP_C_GLOBALDEF_1_INIT);
+#define _HTTP_C_GLOBALDEF_2_INIT { "https", HTLoadHTTP, 0}
+GLOBALDEF(HTProtocol, HTTPS, _HTTP_C_GLOBALDEF_2_INIT);
+#else
+GLOBALDEF HTProtocol HTTP =
+{"http", HTLoadHTTP, 0};
+GLOBALDEF HTProtocol HTTPS =
+{"https", HTLoadHTTP, 0};
+#endif /* GLOBALDEF_IS_MACRO */
diff --git a/WWW/Library/Implementation/HTTP.h b/WWW/Library/Implementation/HTTP.h
new file mode 100644
index 0000000..ea0f835
--- /dev/null
+++ b/WWW/Library/Implementation/HTTP.h
@@ -0,0 +1,47 @@
+/*
+ * $LynxId: HTTP.h,v 1.12 2020/01/21 21:59:06 tom Exp $
+ *
+ * /Net/dxcern/userd/timbl/hypertext/WWW/Library/Implementation/HTTP.html
+ * HYPERTEXT TRANSFER PROTOCOL
+ */
+#ifndef HTTP_H
+#define HTTP_H
+
+#include <HTAccess.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+#ifdef GLOBALREF_IS_MACRO
+ extern GLOBALREF (HTProtocol, HTTP);
+ extern GLOBALREF (HTProtocol, HTTPS);
+
+#else
+ GLOBALREF HTProtocol HTTP;
+ GLOBALREF HTProtocol HTTPS;
+#endif /* GLOBALREF_IS_MACRO */
+
+#define URL_GET_METHOD 1
+#define URL_POST_METHOD 2
+#define URL_MAIL_METHOD 3
+
+ /*
+ * Special value for 'reloading' used to tell HTLoadDocument() that the
+ * user asked for a reload, versus Lynx doing a reload for other reasons.
+ */
+#define REAL_RELOAD (TRUE + 1)
+
+ extern int ws_read_per_sec;
+ extern BOOLEAN reloading;
+ extern char *redirecting_url;
+ extern BOOL permanent_redirection;
+ extern BOOL redirect_post_content;
+
+#ifdef USE_SSL
+ extern SSL *SSL_handle;
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* HTTP_H */
diff --git a/WWW/Library/Implementation/HTTelnet.c b/WWW/Library/Implementation/HTTelnet.c
new file mode 100644
index 0000000..fb0098a
--- /dev/null
+++ b/WWW/Library/Implementation/HTTelnet.c
@@ -0,0 +1,553 @@
+/*
+ * $LynxId: HTTelnet.c,v 1.41 2013/11/28 11:15:19 tom Exp $
+ *
+ * Telnet Access, Rlogin, etc HTTelnet.c
+ * ==========================
+ *
+ * Authors
+ * TBL Tim Berners-Lee timbl@info.cern.ch
+ * JFG Jean-Francois Groff jgh@next.com
+ * DD Denis DeLaRoca (310) 825-4580 <CSP1DWD@mvs.oac.ucla.edu>
+ * History
+ * 8 Jun 92 Telnet hopping prohibited as telnet is not secure (TBL)
+ * 26 Jun 92 When over DECnet, suppressed FTP, Gopher and News. (JFG)
+ * 6 Oct 92 Moved HTClientHost and logfile into here. (TBL)
+ * 17 Dec 92 Tn3270 added, bug fix. (DD)
+ * 2 Feb 93 Split from HTAccess.c. Registration.(TBL)
+ */
+
+#include <HTUtils.h>
+#include <LYUtils.h>
+
+/* Implements:
+*/
+#include <HTTelnet.h>
+
+#include <HTParse.h>
+#include <HTAnchor.h>
+#include <HTTP.h>
+#include <HTFile.h>
+
+#include <HTTCP.h>
+#include <HText.h>
+
+#include <HTAccess.h>
+#include <HTAlert.h>
+
+#include <LYStrings.h>
+#include <LYClean.h>
+#include <LYLeaks.h>
+
+#ifdef __GNUC__
+static void do_system(char *) GCC_UNUSED;
+#endif
+
+static void do_system(char *command)
+{
+ if (non_empty(command)) {
+ CTRACE((tfp, "HTTelnet: Command is: %s\n\n", command));
+ LYSystem(command);
+ }
+ FREE(command);
+}
+
+/* Telnet or "rlogin" access
+ * -------------------------
+ */
+static int remote_session(char *acc_method, char *host)
+{
+ const char *program;
+ char *user = host;
+ char *password = NULL;
+ char *cp;
+ char *hostname;
+ char *port;
+ char *command = NULL;
+ enum _login_protocol {
+ telnet,
+ rlogin,
+ tn3270
+ } login_protocol =
+ strcmp(acc_method, "rlogin") == 0 ? rlogin :
+ strcmp(acc_method, "tn3270") == 0 ? tn3270 : telnet;
+
+ /*
+ * Modified to allow for odd chars in a username only if exists.
+ * 05-28-94 Lynx 2-3-1 Garrett Arch Blythe
+ */
+ /* prevent telnet://hostname;rm -rf * URL's (VERY BAD)
+ * *cp=0; // terminate at any ;,<,>,`,|,",' or space or return
+ * or tab to prevent security hole
+ */
+ for (cp = (StrChr(host, '@') ? StrChr(host, '@') : host); *cp != '\0';
+ cp++) {
+ if (!isalnum(UCH(*cp)) && *cp != '_' && *cp != '-' &&
+ *cp != ':' && *cp != '.' && *cp != '@') {
+ *cp = '\0';
+ break;
+ }
+ }
+
+ hostname = StrChr(host, '@');
+
+ if (hostname) {
+ *hostname++ = '\0'; /* Split */
+ } else {
+ hostname = host;
+ user = NULL; /* No user specified */
+ }
+
+ port = StrChr(hostname, ':');
+ if (port)
+ *port++ = '\0'; /* Split */
+
+ if (*hostname == '\0') {
+ CTRACE((tfp, "HTTelnet: No host specified!\n"));
+ return HT_NO_DATA;
+ } else if (!valid_hostname(hostname)) {
+ char *prefix = NULL;
+ char *line = NULL;
+
+ CTRACE((tfp, "HTTelnet: Invalid hostname %s!\n", host));
+ HTSprintf0(&prefix,
+ gettext("remote %s session:"), acc_method);
+ HTSprintf0(&line,
+ gettext("Invalid hostname %s"), host);
+ HTAlwaysAlert(prefix, line);
+ FREE(prefix);
+ FREE(line);
+ return HT_NO_DATA;
+ }
+
+ if (user) {
+ password = StrChr(user, ':');
+ if (password) {
+ *password++ = '\0';
+ }
+ }
+
+ /* If the person is already telnetting etc, forbid hopping */
+ /* This is a security precaution, for us and remote site */
+
+ if (HTSecure) {
+
+#ifdef TELNETHOPPER_MAIL
+ HTSprintf0(&command,
+ "finger @%s | mail -s \"**telnethopper %s\" tbl@dxcern.cern.ch",
+ HTClientHost, HTClientHost);
+ do_system(command);
+#endif
+ printf("\n\nSorry, but the service you have selected is one\n");
+ printf("to which you have to log in. If you were running www\n");
+ printf("on your own computer, you would be automatically connected.\n");
+ printf("For security reasons, this is not allowed when\n");
+ printf("you log in to this information service remotely.\n\n");
+
+ printf("You can manually connect to this service using %s\n",
+ acc_method);
+ printf("to host %s", hostname);
+ if (user)
+ printf(", user name %s", user);
+ if (password)
+ printf(", password %s", password);
+ if (port)
+ printf(", port %s", port);
+ printf(".\n\n");
+ return HT_NO_DATA;
+ }
+
+ /* Not all telnet servers get it even if user name is specified so we
+ * always tell the guy what to log in as.
+ */
+ if (user && login_protocol != rlogin)
+ printf("When you are connected, log in as: %s\n", user);
+ if (password && login_protocol != rlogin)
+ printf(" The password is: %s\n", password);
+ fflush(stdout);
+
+/*
+ * NeXTSTEP is the implied version of the NeXT operating system.
+ * You may need to define this yourself.
+ */
+#if !defined(TELNET_DONE) && (defined(NeXT) && defined(NeXTSTEP) && NeXTSTEP<=20100)
+#define FMT_TELNET "%s%s%s %s %s"
+
+ if ((program = HTGetProgramPath(ppTELNET)) != NULL) {
+ HTAddParam(&command, FMT_TELNET, 1, program);
+ HTOptParam(&command, FMT_TELNET, 2, user ? " -l " : "");
+ HTAddParam(&command, FMT_TELNET, 3, user);
+ HTAddParam(&command, FMT_TELNET, 4, hostname);
+ HTAddParam(&command, FMT_TELNET, 5, port);
+ HTEndParam(&command, FMT_TELNET, 5);
+ }
+ do_system(command);
+#define TELNET_DONE
+#endif
+
+/* Most unix machines support username only with rlogin */
+#if !defined(TELNET_DONE) && (defined(UNIX) || defined(DOSPATH) || defined(__CYGWIN__))
+
+#define FMT_RLOGIN "%s %s%s%s"
+#define FMT_TN3270 "%s %s %s"
+#define FMT_TELNET "%s %s %s"
+
+ switch (login_protocol) {
+ case rlogin:
+ if ((program = HTGetProgramPath(ppRLOGIN)) != NULL) {
+ HTAddParam(&command, FMT_RLOGIN, 1, program);
+ HTAddParam(&command, FMT_RLOGIN, 2, hostname);
+ HTOptParam(&command, FMT_RLOGIN, 3, user ? " -l " : "");
+ HTAddParam(&command, FMT_RLOGIN, 4, user);
+ HTEndParam(&command, FMT_RLOGIN, 4);
+ }
+ break;
+
+ case tn3270:
+ if ((program = HTGetProgramPath(ppTN3270)) != NULL) {
+ HTAddParam(&command, FMT_TN3270, 1, program);
+ HTAddParam(&command, FMT_TN3270, 2, hostname);
+ HTAddParam(&command, FMT_TN3270, 3, port);
+ HTEndParam(&command, FMT_TN3270, 3);
+ }
+ break;
+
+ case telnet:
+ if ((program = HTGetProgramPath(ppTELNET)) != NULL) {
+ HTAddParam(&command, FMT_TELNET, 1, program);
+ HTAddParam(&command, FMT_TELNET, 2, hostname);
+ HTAddParam(&command, FMT_TELNET, 3, port);
+ HTEndParam(&command, FMT_TELNET, 3);
+ }
+ break;
+ }
+
+ LYSystem(command);
+#define TELNET_DONE
+#endif /* unix */
+
+/* VMS varieties */
+#if !defined(TELNET_DONE) && (defined(MULTINET))
+ if (login_protocol == rlogin) {
+ HTSprintf0(&command, "RLOGIN%s%s%s%s%s %s", /*lm 930713 */
+ user ? "/USERNAME=\"" : "",
+ NonNull(user),
+ user ? "\"" : "",
+ port ? "/PORT=" : "",
+ NonNull(port),
+ hostname);
+
+ } else if (login_protocol == tn3270) {
+ HTSprintf0(&command, "TELNET/TN3270 %s%s %s",
+ port ? "/PORT=" : "",
+ NonNull(port),
+ hostname);
+
+ } else { /* TELNET */
+ HTSprintf0(&command, "TELNET %s%s %s",
+ port ? "/PORT=" : "",
+ NonNull(port),
+ hostname);
+ }
+
+ do_system(command);
+#define TELNET_DONE
+#endif /* MULTINET */
+
+#if !defined(TELNET_DONE) && defined(WIN_TCP)
+ if ((cp = getenv("WINTCP_COMMAND_STYLE")) != NULL &&
+ 0 == strncasecomp(cp, "VMS", 3)) { /* VMS command syntax */
+ if (login_protocol == rlogin) {
+ HTSprintf0(&command, "RLOGIN%s%s%s%s%s %s", /*lm 930713 */
+ user ? "/USERNAME=\"" : "",
+ NonNull(user),
+ user ? "\"" : "",
+ port ? "/PORT=" : "",
+ NonNull(port),
+ hostname);
+
+ } else if (login_protocol == tn3270) {
+ HTSprintf0(&command, "TELNET/TN3270 %s%s %s",
+ port ? "/PORT=" : "",
+ NonNull(port),
+ hostname);
+
+ } else { /* TELNET */
+ HTSprintf0(&command, "TELNET %s%s %s",
+ port ? "/PORT=" : "",
+ NonNull(port),
+ hostname);
+ }
+
+ } else { /* UNIX command syntax */
+ if (login_protocol == rlogin) {
+ HTSprintf0(&command, "RLOGIN %s%s%s%s%s",
+ hostname,
+ user ? " -l " : "",
+ user ? "\"" : "",
+ NonNull(user),
+ user ? "\"" : "");
+
+ } else if (login_protocol == tn3270) {
+ HTSprintf0(&command, "TN3270 %s %s",
+ hostname,
+ NonNull(port));
+
+ } else { /* TELNET */
+ HTSprintf0(&command, "TELNET %s %s",
+ hostname,
+ NonNull(port));
+ }
+ }
+
+ do_system(command);
+#define TELNET_DONE
+#endif /* WIN_TCP */
+
+#if !defined(TELNET_DONE) && defined(UCX)
+ if (login_protocol == rlogin) {
+ HTSprintf0(&command, "RLOGIN%s%s%s %s %s",
+ user ? "/USERNAME=\"" : "",
+ NonNull(user),
+ user ? "\"" : "",
+ hostname,
+ NonNull(port));
+
+ } else if (login_protocol == tn3270) {
+ HTSprintf0(&command, "TN3270 %s %s",
+ hostname,
+ NonNull(port));
+
+ } else { /* TELNET */
+ HTSprintf0(&command, "TELNET %s %s",
+ hostname,
+ NonNull(port));
+ }
+
+ do_system(command);
+#define TELNET_DONE
+#endif /* UCX */
+
+#if !defined(TELNET_DONE) && defined(CMU_TCP)
+ if (login_protocol == telnet) {
+ HTSprintf0(&command, "TELNET %s%s %s",
+ port ? "/PORT=" : "",
+ NonNull(port),
+ hostname);
+ do_system(command);
+ } else {
+ printf("\nSorry, this browser was compiled without the %s access option.\n",
+ acc_method);
+ printf("\nPress <return> to return to Lynx.");
+ LYgetch();
+ HadVMSInterrupt = FALSE;
+ }
+#define TELNET_DONE
+#endif /* CMU_TCP */
+
+#if !defined(TELNET_DONE) && defined(SOCKETSHR_TCP)
+ if (getenv("MULTINET_SOCKET_LIBRARY") != NULL) {
+ if (login_protocol == rlogin) {
+ HTSprintf0(&command, "MULTINET RLOGIN%s%s%s%s %s", /*lm 930713 */
+ user ? "/USERNAME=" : "",
+ NonNull(user),
+ port ? "/PORT=" : "",
+ NonNull(port),
+ hostname);
+
+ } else if (login_protocol == tn3270) {
+ HTSprintf0(&command, "MULTINET TELNET/TN3270 %s%s %s",
+ port ? "/PORT=" : "",
+ NonNull(port),
+ hostname);
+
+ } else { /* TELNET */
+ HTSprintf0(&command, "MULTINET TELNET %s%s %s",
+ port ? "/PORT=" : "",
+ NonNull(port),
+ hostname);
+ }
+
+ do_system(command);
+ return HT_NO_DATA; /* Ok - it was done but no data */
+ } else if ((cp = getenv("WINTCP_COMMAND_STYLE")) != NULL) {
+ if (0 == strncasecomp(cp, "VMS", 3)) { /* VMS command syntax */
+ if (login_protocol == rlogin) {
+ HTSprintf0(&command, "RLOGIN%s%s%s%s %s", /*lm 930713 */
+ user ? "/USERNAME=" : "",
+ NonNull(user),
+ port ? "/PORT=" : "",
+ NonNull(port),
+ hostname);
+ } else if (login_protocol == tn3270) {
+ HTSprintf0(&command, "TELNET/TN3270 %s%s %s",
+ port ? "/PORT=" : "",
+ NonNull(port),
+ hostname);
+ } else { /* TELNET */
+ HTSprintf0(&command, "TELNET %s%s %s",
+ port ? "/PORT=" : "",
+ NonNull(port),
+ hostname);
+ }
+ } else { /* UNIX command syntax */
+ if (login_protocol == rlogin) {
+ HTSprintf0(&command, "RLOGIN %s%s%s",
+ hostname,
+ user ? " -l " : "",
+ NonNull(user));
+ } else if (login_protocol == tn3270) {
+ HTSprintf0(&command, "TN3270 %s %s",
+ hostname,
+ NonNull(port));
+ } else { /* TELNET */
+ HTSprintf0(&command, "TELNET %s %s",
+ hostname,
+ NonNull(port));
+ }
+ }
+
+ do_system(command);
+ return HT_NO_DATA; /* Ok - it was done but no data */
+ } else if (getenv("UCX$DEVICE") != NULL
+ || getenv("TCPIP$DEVICE") != NULL) {
+ if (login_protocol == rlogin) {
+ HTSprintf0(&command, "RLOGIN%s%s %s %s",
+ user ? "/USERNAME=" : "",
+ NonNull(user),
+ hostname,
+ NonNull(port));
+
+ } else if (login_protocol == tn3270) {
+ HTSprintf0(&command, "TN3270 %s %s",
+ hostname,
+ NonNull(port));
+
+ } else { /* TELNET */
+ HTSprintf0(&command, "TELNET %s %s",
+ hostname,
+ NonNull(port));
+ }
+
+ do_system(command);
+ return HT_NO_DATA; /* Ok - it was done but no data */
+ } else if (getenv("CMUTEK_ROOT") != NULL) {
+ if (login_protocol == telnet) {
+ HTSprintf0(&command, "TELNET %s%s %s",
+ port ? "/PORT=" : "",
+ NonNull(port),
+ hostname);
+ do_system(command);
+ } else {
+ printf("\nSorry, this browser was compiled without the %s access option.\n",
+ acc_method);
+ printf("\nPress <return> to return to Lynx.");
+ LYgetch();
+ HadVMSInterrupt = FALSE;
+ }
+ } else {
+ if (login_protocol == telnet) {
+ HTSprintf0(&command, "TELNET %s%s %s",
+ port ? "/PORT=" : "",
+ NonNull(port),
+ hostname);
+ do_system(command);
+ } else {
+ printf("\nSorry, this browser was compiled without the %s access option.\n",
+ acc_method);
+ printf("\nPress <return> to return to Lynx.");
+ LYgetch();
+ HadVMSInterrupt = FALSE;
+ }
+ }
+#define TELNET_DONE
+#endif /* SOCKETSHR_TCP */
+
+#if !defined(TELNET_DONE) && (defined(SIMPLE_TELNET) || defined(VM))
+ if (login_protocol == telnet) { /* telnet only */
+ HTSprintf0(&command, "TELNET %s", /* @@ Bug: port ignored */
+ hostname);
+ do_system(command);
+ return HT_NO_DATA; /* Ok - it was done but no data */
+ }
+#define TELNET_DONE
+#endif
+
+#ifndef TELNET_DONE
+ printf("\nSorry, this browser was compiled without the %s access option.\n",
+ acc_method);
+ printf("\nTo access the information you must %s to %s", acc_method, hostname);
+ if (port)
+ printf(" (port %s)", port);
+ if (user)
+ printf("\nlogging in with username %s", user);
+ printf(".\n");
+ {
+ printf("\nPress <return> to return to Lynx.");
+ fflush(stdout);
+ LYgetch();
+#ifdef VMS
+ HadVMSInterrupt = FALSE;
+#endif /* VMS */
+ }
+#endif /* !TELNET_DONE */
+ return HT_NO_DATA;
+}
+
+/* "Load a document" -- establishes a session
+ * ------------------------------------------
+ *
+ * On entry,
+ * addr must point to the fully qualified hypertext reference.
+ *
+ * On exit,
+ * returns <0 Error has occurred.
+ * >=0 Value of file descriptor or socket to be used
+ * to read data.
+ * *pFormat Set to the format of the file, if known.
+ * (See WWW.h)
+ *
+ */
+static int HTLoadTelnet(const char *addr,
+ HTParentAnchor *anchor GCC_UNUSED,
+ HTFormat format_out GCC_UNUSED,
+ HTStream *sink) /* Ignored */
+{
+ char *acc_method;
+ char *host;
+ int status;
+
+ if (sink) {
+ CTRACE((tfp,
+ "HTTelnet: Can't output a live session -- must be interactive!\n"));
+ return HT_NO_DATA;
+ }
+ acc_method = HTParse(addr, STR_FILE_URL, PARSE_ACCESS);
+
+ host = HTParse(addr, "", PARSE_HOST);
+ if (!host || *host == '\0') {
+ status = HT_NO_DATA;
+ CTRACE((tfp, "HTTelnet: No host specified!\n"));
+ } else {
+ status = remote_session(acc_method, host);
+ }
+
+ FREE(host);
+ FREE(acc_method);
+ return status;
+}
+
+#ifdef GLOBALDEF_IS_MACRO
+#define _HTTELNET_C_1_INIT { "telnet", HTLoadTelnet, NULL }
+#define _HTTELNET_C_2_INIT { "rlogin", HTLoadTelnet, NULL }
+#define _HTTELNET_C_3_INIT { "tn3270", HTLoadTelnet, NULL }
+GLOBALDEF(HTProtocol, HTTelnet, _HTTELNET_C_1_INIT);
+GLOBALDEF(HTProtocol, HTRlogin, _HTTELNET_C_2_INIT);
+GLOBALDEF(HTProtocol, HTTn3270, _HTTELNET_C_3_INIT);
+#else
+GLOBALDEF HTProtocol HTTelnet =
+{"telnet", HTLoadTelnet, NULL};
+GLOBALDEF HTProtocol HTRlogin =
+{"rlogin", HTLoadTelnet, NULL};
+GLOBALDEF HTProtocol HTTn3270 =
+{"tn3270", HTLoadTelnet, NULL};
+#endif /* GLOBALDEF_IS_MACRO */
diff --git a/WWW/Library/Implementation/HTTelnet.h b/WWW/Library/Implementation/HTTelnet.h
new file mode 100644
index 0000000..f2208d8
--- /dev/null
+++ b/WWW/Library/Implementation/HTTelnet.h
@@ -0,0 +1,28 @@
+/* /Net/dxcern/userd/timbl/hypertext/WWW/Library/Implementation/HTTelnet.html
+ TELNET AND SIMILAR ACCESS METHODS
+
+ */
+
+#ifndef HTTELNET_H
+#define HTTELNET_H
+
+#include <HTAccess.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+#ifdef GLOBALREF_IS_MACRO
+ extern GLOBALREF (HTProtocol, HTTelnet);
+ extern GLOBALREF (HTProtocol, HTRlogin);
+ extern GLOBALREF (HTProtocol, HTTn3270);
+
+#else
+ GLOBALREF HTProtocol HTTelnet;
+ GLOBALREF HTProtocol HTRlogin;
+ GLOBALREF HTProtocol HTTn3270;
+#endif /* GLOBALREF_IS_MACRO */
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* HTTELNET_H */
diff --git a/WWW/Library/Implementation/HTUU.c b/WWW/Library/Implementation/HTUU.c
new file mode 100644
index 0000000..4848cce
--- /dev/null
+++ b/WWW/Library/Implementation/HTUU.c
@@ -0,0 +1,210 @@
+/*
+ * $LynxId: HTUU.c,v 1.11 2010/09/21 23:55:12 tom Exp $
+ *
+ * MODULE HTUU.c
+ * UUENCODE AND UUDECODE
+ *
+ * ACKNOWLEDGEMENT:
+ * This code is taken from rpem distribution, and was originally
+ * written by Mark Riordan.
+ *
+ * AUTHORS:
+ * MR Mark Riordan riordanmr@clvax1.cl.msu.edu
+ * AL Ari Luotonen luotonen@dxcern.cern.ch
+ *
+ * HISTORY:
+ * Added as part of the WWW library and edited to conform
+ * with the WWW project coding standards by: AL 5 Aug 1993
+ * Originally written by: MR 12 Aug 1990
+ * Original header text:
+ * -------------------------------------------------------------
+ * File containing routines to convert a buffer
+ * of bytes to/from RFC 1113 printable encoding format.
+ *
+ * This technique is similar to the familiar Unix uuencode
+ * format in that it maps 6 binary bits to one ASCII
+ * character (or more aptly, 3 binary bytes to 4 ASCII
+ * characters). However, RFC 1113 does not use the same
+ * mapping to printable characters as uuencode.
+ *
+ * Mark Riordan 12 August 1990 and 17 Feb 1991.
+ * This code is hereby placed in the public domain.
+ * -------------------------------------------------------------
+ */
+
+#include <HTUtils.h>
+#include <HTUU.h>
+
+#include <LYLeaks.h>
+
+static char six2pr[64] =
+{
+ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
+ 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
+ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
+ 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'
+};
+
+static unsigned char pr2six[256];
+
+/*--- function HTUU_encode -----------------------------------------------
+ *
+ * Encode a single line of binary data to a standard format that
+ * uses only printing ASCII characters (but takes up 33% more bytes).
+ *
+ * Entry bufin points to a buffer of bytes. If nbytes is not
+ * a multiple of three, then the byte just beyond
+ * the last byte in the buffer must be 0.
+ * nbytes is the number of bytes in that buffer.
+ * This cannot be more than 48.
+ * bufcoded points to an output buffer. Be sure that this
+ * can hold at least 1 + (4*nbytes)/3 characters.
+ *
+ * Exit bufcoded contains the coded line. The first 4*nbytes/3 bytes
+ * contain printing ASCII characters representing
+ * those binary bytes. This may include one or
+ * two '=' characters used as padding at the end.
+ * The last byte is a zero byte.
+ * Returns the number of ASCII characters in "bufcoded".
+ */
+int HTUU_encode(unsigned char *bufin,
+ size_t nbytes,
+ char *bufcoded)
+{
+/* ENC is the basic 1 character encoding function to make a char printing */
+#define ENC(c) six2pr[c]
+
+ register char *outptr = bufcoded;
+ size_t i;
+
+ /* This doesn't seem to be needed (AL): register unsigned char *inptr = bufin; */
+
+ for (i = 0; i < nbytes; i += 3) {
+ *(outptr++) = ENC(*bufin >> 2); /* c1 */
+ *(outptr++) = ENC(((*bufin << 4) & 060) | ((bufin[1] >> 4) & 017)); /*c2 */
+ *(outptr++) = ENC(((bufin[1] << 2) & 074) | ((bufin[2] >> 6) & 03)); /*c3 */
+ *(outptr++) = ENC(bufin[2] & 077); /* c4 */
+
+ bufin += 3;
+ }
+
+ /* If nbytes was not a multiple of 3, then we have encoded too
+ * many characters. Adjust appropriately.
+ */
+ if (i == nbytes + 1) {
+ /* There were only 2 bytes in that last group */
+ outptr[-1] = '=';
+ } else if (i == nbytes + 2) {
+ /* There was only 1 byte in that last group */
+ outptr[-1] = '=';
+ outptr[-2] = '=';
+ }
+ *outptr = '\0';
+ return (int) (outptr - bufcoded);
+}
+
+/*--- function HTUU_decode ------------------------------------------------
+ *
+ * Decode an ASCII-encoded buffer back to its original binary form.
+ *
+ * Entry bufcoded points to a uuencoded string. It is
+ * terminated by any character not in
+ * the printable character table six2pr, but
+ * leading whitespace is stripped.
+ * bufplain points to the output buffer; must be big
+ * enough to hold the decoded string (generally
+ * shorter than the encoded string) plus
+ * as many as two extra bytes used during
+ * the decoding process.
+ * outbufsize is the maximum number of bytes that
+ * can fit in bufplain.
+ *
+ * Exit Returns the number of binary bytes decoded.
+ * bufplain contains these bytes.
+ */
+int HTUU_decode(char *bufcoded,
+ unsigned char *bufplain,
+ int outbufsize)
+{
+/* single character decode */
+#define DEC(c) pr2six[(int)c]
+#define MAXVAL 63
+
+ static int first = 1;
+
+ int nbytesdecoded, j;
+ register char *bufin;
+ register unsigned char *bufout = bufplain;
+ register int nprbytes;
+
+ /* If this is the first call, initialize the mapping table.
+ * This code should work even on non-ASCII machines.
+ */
+ if (first) {
+ first = 0;
+ for (j = 0; j < 256; j++)
+ pr2six[j] = MAXVAL + 1;
+
+ for (j = 0; j < 64; j++)
+ pr2six[UCH(six2pr[j])] = UCH(j);
+#if 0
+ /* *INDENT-OFF* */
+ pr2six['A']= 0; pr2six['B']= 1; pr2six['C']= 2; pr2six['D']= 3;
+ pr2six['E']= 4; pr2six['F']= 5; pr2six['G']= 6; pr2six['H']= 7;
+ pr2six['I']= 8; pr2six['J']= 9; pr2six['K']=10; pr2six['L']=11;
+ pr2six['M']=12; pr2six['N']=13; pr2six['O']=14; pr2six['P']=15;
+ pr2six['Q']=16; pr2six['R']=17; pr2six['S']=18; pr2six['T']=19;
+ pr2six['U']=20; pr2six['V']=21; pr2six['W']=22; pr2six['X']=23;
+ pr2six['Y']=24; pr2six['Z']=25; pr2six['a']=26; pr2six['b']=27;
+ pr2six['c']=28; pr2six['d']=29; pr2six['e']=30; pr2six['f']=31;
+ pr2six['g']=32; pr2six['h']=33; pr2six['i']=34; pr2six['j']=35;
+ pr2six['k']=36; pr2six['l']=37; pr2six['m']=38; pr2six['n']=39;
+ pr2six['o']=40; pr2six['p']=41; pr2six['q']=42; pr2six['r']=43;
+ pr2six['s']=44; pr2six['t']=45; pr2six['u']=46; pr2six['v']=47;
+ pr2six['w']=48; pr2six['x']=49; pr2six['y']=50; pr2six['z']=51;
+ pr2six['0']=52; pr2six['1']=53; pr2six['2']=54; pr2six['3']=55;
+ pr2six['4']=56; pr2six['5']=57; pr2six['6']=58; pr2six['7']=59;
+ pr2six['8']=60; pr2six['9']=61; pr2six['+']=62; pr2six['/']=63;
+ /* *INDENT-ON* */
+
+#endif
+ }
+
+ /* Strip leading whitespace. */
+
+ while (*bufcoded == ' ' || *bufcoded == '\t')
+ bufcoded++;
+
+ /* Figure out how many characters are in the input buffer.
+ * If this would decode into more bytes than would fit into
+ * the output buffer, adjust the number of input bytes downwards.
+ */
+ bufin = bufcoded;
+ while (pr2six[UCH(*(bufin++))] <= MAXVAL) ;
+ nprbytes = (int) (bufin - bufcoded - 1);
+ nbytesdecoded = ((nprbytes + 3) / 4) * 3;
+ if (nbytesdecoded > outbufsize) {
+ nprbytes = (outbufsize * 4) / 3;
+ }
+
+ bufin = bufcoded;
+
+ while (nprbytes > 0) {
+ *(bufout++) = UCH((DEC(bufin[0]) << 2) | (DEC(bufin[1]) >> 4));
+ *(bufout++) = UCH((DEC(bufin[1]) << 4) | (DEC(bufin[2]) >> 2));
+ *(bufout++) = UCH((DEC(bufin[2]) << 6) | (DEC(bufin[3])));
+ bufin += 4;
+ nprbytes -= 4;
+ }
+
+ if (nprbytes & 03) {
+ if (pr2six[(int) bufin[-2]] > MAXVAL) {
+ nbytesdecoded -= 2;
+ } else {
+ nbytesdecoded -= 1;
+ }
+ }
+
+ return (nbytesdecoded);
+}
diff --git a/WWW/Library/Implementation/HTUU.h b/WWW/Library/Implementation/HTUU.h
new file mode 100644
index 0000000..93ba435
--- /dev/null
+++ b/WWW/Library/Implementation/HTUU.h
@@ -0,0 +1,36 @@
+/*
+ * $LynxId: HTUU.h,v 1.11 2010/09/25 11:43:27 tom Exp $
+ *
+ * ENCODING TO PRINTABLE CHARACTERS
+
+ File module provides functions HTUU_encode() and HTUU_decode() which convert
+ a buffer of bytes to/from RFC 1113 printable encoding format. This
+ technique is similar to the familiar Unix uuencode format in that it maps 6
+ binary bits to one ASCII character (or more aptly, 3 binary bytes to 4 ASCII
+ characters). However, RFC 1113 does not use the same mapping to printable
+ characters as uuencode.
+
+ */
+
+#ifndef HTUU_H
+#define HTUU_H
+
+#ifndef HTUTILS_H
+#include <HTUtils.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+ extern int HTUU_encode(unsigned char *bufin,
+ size_t nbytes,
+ char *bufcoded);
+
+ extern int HTUU_decode(char *bufcoded,
+ unsigned char *bufplain,
+ int outbufsize);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* HTUU_H */
diff --git a/WWW/Library/Implementation/HTUtils.h b/WWW/Library/Implementation/HTUtils.h
new file mode 100644
index 0000000..d01d0dd
--- /dev/null
+++ b/WWW/Library/Implementation/HTUtils.h
@@ -0,0 +1,847 @@
+/*
+ * $LynxId: HTUtils.h,v 1.134 2021/06/29 22:01:12 tom Exp $
+ *
+ * Utility macros for the W3 code library
+ * MACROS FOR GENERAL USE
+ *
+ * See also: the system dependent file "www_tcp.h", which is included here.
+ */
+
+#ifndef NO_LYNX_TRACE
+#define DEBUG /* Turns on trace; turn off for smaller binary */
+#endif
+
+#ifndef HTUTILS_H
+#define HTUTILS_H
+
+#ifdef HAVE_CONFIG_H
+#include <lynx_cfg.h> /* generated by autoconf 'configure' script */
+
+#ifdef HAVE_STDNORETURN_H
+#include <stdnoreturn.h>
+#undef GCC_NORETURN
+#define GCC_NORETURN _Noreturn
+#endif
+
+/* see AC_FUNC_ALLOCA macro */
+#ifdef __GNUC__
+# define alloca(size) __builtin_alloca(size)
+#else
+# ifdef _MSC_VER
+# include <malloc.h>
+# ifndef alloca
+# define alloca(size) _alloca(size)
+# endif
+# else
+# if HAVE_ALLOCA_H
+# include <alloca.h>
+# else
+# ifdef _AIX
+#pragma alloca
+# else
+# ifndef alloca /* predefined by HP cc +Olibcalls */
+char *alloca();
+
+# endif
+# endif
+# endif
+# endif
+#endif
+
+#include <sys/types.h>
+#include <stdio.h>
+
+#else /* HAVE_CONFIG_H */
+
+#ifdef DJGPP
+#include <sys/config.h> /* pseudo-autoconf values for DJGPP libc/headers */
+#define HAVE_TRUNCATE 1
+#define HAVE_ALLOCA 1
+#include <limits.h>
+#endif /* DJGPP */
+
+#include <sys/types.h>
+#include <stdio.h>
+
+/* Explicit system-configure */
+#ifdef VMS
+#define NO_SIZECHANGE
+
+#if defined(VAXC) && !defined(__DECC)
+#define NO_UNISTD_H /* DEC C has unistd.h, but not VAX C */
+#endif
+
+#define NO_KEYPAD
+#define NO_UTMP
+
+#undef NO_FILIO_H
+#define NO_FILIO_H
+
+#define NOUSERS
+#define DISP_PARTIAL /* experimental */
+#endif
+
+#if defined(VMS) || defined(_WINDOWS)
+#define HAVE_STDLIB_H 1
+#endif
+
+/* Accommodate non-autoconf'd Makefile's (VMS, DJGPP, etc) */
+
+#ifndef NO_ARPA_INET_H
+#define HAVE_ARPA_INET_H 1
+#endif
+
+#ifndef NO_CBREAK
+#define HAVE_CBREAK 1
+#endif
+
+#ifndef NO_CUSERID
+#define HAVE_CUSERID 1
+#endif
+
+#ifndef NO_FILIO_H
+#define HAVE_SYS_FILIO_H 1
+#endif
+
+#ifndef NO_GETCWD
+#define HAVE_GETCWD 1
+#endif
+
+#ifndef USE_SLANG
+#ifndef NO_KEYPAD
+#define HAVE_KEYPAD 1
+#endif
+#ifndef NO_TTYTYPE
+#define HAVE_TTYTYPE 1
+#endif
+#endif /* USE_SLANG */
+
+#ifndef NO_PUTENV
+#define HAVE_PUTENV 1
+#endif
+
+#ifndef NO_SIZECHANGE
+#define HAVE_SIZECHANGE 1
+#endif
+
+#ifndef NO_UNISTD_H
+#undef HAVE_UNISTD_H
+#define HAVE_UNISTD_H 1
+#endif
+
+#ifndef NO_UTMP
+#define HAVE_UTMP 1
+#endif
+
+#endif /* HAVE_CONFIG_H */
+
+#include <assert.h>
+
+/* suppress inadvertent use of gettext in makeuctb when cross-compiling */
+#ifdef DONT_USE_GETTEXT
+#undef HAVE_GETTEXT
+#undef HAVE_LIBGETTEXT_H
+#undef HAVE_LIBINTL_H
+#endif
+
+#ifndef HAVE_ICONV
+#undef USE_JAPANESEUTF8_SUPPORT
+#endif
+
+#ifndef lynx_srand
+#define lynx_srand srand
+#endif
+
+#ifndef lynx_rand
+#define lynx_rand rand
+#endif
+
+#if '0' != 48
+#define NOT_ASCII
+#endif
+
+#if '0' == 240
+#define EBCDIC
+#endif
+
+#ifndef LY_MAXPATH
+#define LY_MAXPATH 256
+#endif
+
+#ifndef GCC_NORETURN
+#define GCC_NORETURN /* nothing */
+#endif
+
+#ifndef GCC_UNUSED
+#define GCC_UNUSED /* nothing */
+#endif
+
+#if defined(__GNUC__) && defined(_FORTIFY_SOURCE)
+#define USE_IGNORE_RC
+extern int ignore_unused;
+
+#define IGNORE_RC(func) ignore_unused = (int) func
+#else
+#define IGNORE_RC(func) (void) func
+#endif /* gcc workarounds */
+
+#if defined(__CYGWIN32__) && ! defined(__CYGWIN__)
+#define __CYGWIN__ 1
+#endif
+
+#if defined(__CYGWIN__) /* 1998/12/31 (Thu) 16:13:46 */
+#ifdef USE_OPENSSL_INCL
+#define NOCRYPT /* workaround for openssl 1.0.1e bug */
+#endif
+#include <windows.h> /* #include "windef.h" */
+#define BOOLEAN_DEFINED
+#undef HAVE_POPEN /* FIXME: does this not work, or is it missing */
+#undef small /* see <w32api/rpcndr.h> */
+#endif
+
+#if defined(__DARWIN_NO_LONG_LONG)
+#undef HAVE_ATOLL
+#endif
+
+#if defined(HAVE_ATOLL)
+#define LYatoll(n) atoll(n)
+#else
+extern off_t LYatoll(const char *value);
+#endif
+
+/* cygwin, mingw32, etc. */
+#ifdef FILE_DOES_NOT_EXIST
+#undef FILE_DOES_NOT_EXIST /* see <w32api/winnt.h> */
+#endif
+
+/*
+ * VS .NET 2003 includes winsock.h unconditionally from windows.h,
+ * so we do not want to include windows.h if we want winsock2.h
+ */
+#if defined(_WINDOWS) && !defined(__CYGWIN__)
+
+#ifndef __GNUC__
+#pragma warning (disable : 4100) /* unreferenced formal parameter */
+#pragma warning (disable : 4127) /* conditional expression is constant */
+#pragma warning (disable : 4201) /* nameless struct/union */
+#pragma warning (disable : 4214) /* bit field types other than int */
+#pragma warning (disable : 4310) /* cast truncates constant value */
+#pragma warning (disable : 4514) /* unreferenced inline function has been removed */
+#pragma warning (disable : 4996) /* This function or variable may be unsafe. ... */
+#endif
+
+#if defined(USE_WINSOCK2_H) && (_MSC_VER >= 1300) && (_MSC_VER < 1400)
+#include <winsock2.h> /* includes windows.h, in turn windef.h */
+#else
+#include <windows.h> /* #include "windef.h" */
+#endif
+
+#define BOOLEAN_DEFINED
+
+#if !_WIN_CC /* 1999/09/29 (Wed) 22:00:53 */
+#include <dos.h>
+#endif
+
+#if defined(DECL_SLEEP) && defined(HAVE_CONFIG_H)
+# undef sleep
+# if defined(__MINGW32__)
+# define sleep(n) Sleep((n)*100)
+# else
+extern void sleep(unsigned __seconds);
+# endif
+#elif !defined(__MINGW32__)
+# undef sleep
+extern void sleep(unsigned __seconds);
+#endif
+
+#define popen _popen
+#define pclose _pclose
+
+#if defined(_MSC_VER) && (_MSC_VER > 0)
+typedef unsigned short mode_t;
+#endif
+
+#endif /* _WINDOWS */
+
+#if defined(USE_DEFAULT_COLORS) && !defined(HAVE_USE_DEFAULT_COLORS)
+ /* if we don't have use_default_colors() */
+# undef USE_DEFAULT_COLORS
+#endif
+
+#ifndef USE_COLOR_STYLE
+ /* it's useless for such setup */
+# define NO_EMPTY_HREFLESS_A
+#endif
+
+#if defined(__EMX__) || defined(WIN_EX) || defined(HAVE_POPEN)
+# define CAN_CUT_AND_PASTE
+#endif
+
+#if defined(USE_SLANG) || (defined(USE_COLOR_STYLE) && defined(__EMX__))
+# define USE_BLINK
+#endif
+
+#if defined(DOSPATH) || defined(__EMX__)
+# define USE_DOS_DRIVES /* we allow things like "c:" in paths */
+#endif
+
+#if defined(UNIX)
+# if (defined(__BEOS__) || defined(__CYGWIN__) || defined(__DJGPP__) || defined(__EMX__) || defined(__MINGW32__))
+# define SINGLE_USER_UNIX /* well, at least they try */
+# else
+# define MULTI_USER_UNIX
+# endif
+#endif
+
+/*
+
+ ERROR TYPE
+
+ This is passed back when streams are aborted. It might be nice to have some structure
+ of error messages, numbers, and recursive pointers to reasons. Currently this is a
+ placeholder for something more sophisticated.
+
+ */
+typedef void *HTError; /* Unused at present -- best definition? */
+
+/*
+
+Standard C library for malloc() etc
+
+ */
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#ifndef EXIT_SUCCESS
+#define EXIT_SUCCESS 0
+#endif
+
+#ifndef EXIT_FAILURE
+#define EXIT_FAILURE 1
+#endif
+
+#ifdef __EMX__
+#include <unistd.h> /* should be re-include protected under EMX */
+#define getcwd _getcwd2
+#define chdir _chdir2
+#endif
+
+#ifdef vax
+#ifdef unix
+#define ultrix /* Assume vax+unix=ultrix */
+#endif /* unix */
+#endif /* vax */
+
+#ifndef VMS
+#ifndef ultrix
+
+#ifdef NeXT
+#include <libc.h> /* NeXT */
+#endif /* NeXT */
+
+#else /* ultrix: */
+
+#include <malloc.h>
+#include <memory.h>
+
+#endif /* !ultrix */
+#else /* VMS: */
+
+#include <unixlib.h>
+#if defined(VAXC) && !defined(__DECC)
+#define malloc VAXC$MALLOC_OPT
+#define calloc VAXC$CALLOC_OPT
+#define free VAXC$FREE_OPT
+#define cfree VAXC$CFREE_OPT
+#define realloc VAXC$REALLOC_OPT
+#endif /* VAXC && !__DECC */
+
+#endif /* !VMS */
+
+#ifndef NULL
+#define NULL ((void *)0)
+#endif
+
+#define DeConst(p) (void *)(intptr_t)(p)
+
+#define isEmpty(s) ((s) == 0 || *(s) == 0)
+#define non_empty(s) !isEmpty(s)
+
+#define NonNull(s) (((s) != 0) ? s : "")
+#define NONNULL(s) (((s) != 0) ? s : "(null)")
+
+/* array/table size */
+#define TABLESIZE(v) (sizeof(v)/sizeof(v[0]))
+
+#define typecalloc(cast) (cast *)calloc((size_t)1, sizeof(cast))
+#define typecallocn(cast,ntypes) (cast *)calloc((size_t)(ntypes),sizeof(cast))
+
+#define typeRealloc(cast,ptr,ntypes) (cast *)realloc(ptr, (size_t)(ntypes)*sizeof(cast))
+
+#define typeMalloc(cast) (cast *)malloc(sizeof(cast))
+#define typeMallocn(cast,ntypes) (cast *)malloc((size_t)(ntypes)*sizeof(cast))
+
+/*
+
+OFTEN USED INTEGER MACROS
+
+ Min and Max functions
+
+ */
+#ifndef HTMIN
+#define HTMIN(a,b) ((a) <= (b) ? (a) : (b))
+#define HTMAX(a,b) ((a) >= (b) ? (a) : (b))
+#endif
+/*
+
+Booleans
+
+ */
+/* Note: GOOD and BAD are already defined (differently) on RS6000 aix */
+/* #define GOOD(status) ((status)38;1) VMS style status: test bit 0 */
+/* #define BAD(status) (!GOOD(status)) Bit 0 set if OK, otherwise clear */
+
+#ifndef _WINDOWS
+#ifndef BOOLEAN_DEFINED
+typedef char BOOLEAN; /* Logical value */
+
+#ifndef CURSES
+#ifndef TRUE
+#define TRUE (BOOLEAN)1
+#define FALSE (BOOLEAN)0
+#endif
+#endif /* CURSES */
+#endif /* BOOLEAN_DEFINED */
+#define BOOLEAN_DEFINED
+#endif /* _WINDOWS */
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1300)
+/* it declares BOOL/BOOLEAN as BYTE/int */
+#else
+#ifndef BOOL
+#define BOOL BOOLEAN
+#endif
+#endif
+
+#ifndef YES
+#define YES (BOOLEAN)1
+#define NO (BOOLEAN)0
+#endif
+
+#define STRING1PTR const char *
+#define STRING2PTR const char * const *
+
+extern BOOL LYOutOfMemory; /* Declared in LYexit.c - FM */
+
+#define TCP_PORT 80 /* Allocated to http by Jon Postel/ISI 24-Jan-92 */
+#define OLD_TCP_PORT 2784 /* Try the old one if no answer on 80 */
+#define DNP_OBJ 80 /* This one doesn't look busy, but we must check */
+ /* That one was for decnet */
+
+/* Inline Function WHITE: Is character c white space? */
+/* For speed, include all control characters */
+
+#define WHITE(c) ((UCH(TOASCII(c))) <= 32)
+
+/* Inline Function LYIsASCII: Is character c a traditional ASCII
+ * character (i.e. <128) after converting from host character set. */
+
+#define LYIsASCII(c) (TOASCII(UCH(c)) < 128)
+
+/*
+
+Success (>=0) and failure (<0) codes
+
+Some of the values are chosen to be HTTP-like, but status return values
+are generally not the response status from any specific protocol.
+
+ */
+
+#define HT_PARSER_OTHER_CONTENT 701 /* tells SGML to change content model */
+#define HT_PARSER_REOPEN_ELT 700 /* tells SGML parser to keep tag open */
+#define HT_REDIRECTING 399
+#define HT_PARTIAL_CONTENT 206 /* Partial Content */
+#define HT_LOADED 200 /* Instead of a socket */
+
+#define HT_OK 0 /* Generic success */
+
+#define HT_ERROR -1 /* Generic failure */
+#define HT_CANNOT_TRANSLATE -4
+#define HT_BAD_EOF -12 /* Premature EOF */
+#define HT_NO_CONNECTION -99 /* ERR no connection available - */
+#define HT_NO_DATA -204 /* OK but no data was loaded - */
+ /* possibly other app started or forked */
+#define HT_NO_ACCESS -401 /* Access not available */
+#define HT_FORBIDDEN -403 /* Access forbidden */
+#define HT_NOT_ACCEPTABLE -406 /* Not Acceptable */
+#define HT_H_ERRNO_VALID -800 /* see h_errno for resolver error */
+#define HT_INTERNAL -900 /* Weird -- should never happen. */
+#define HT_INTERRUPTED -29998
+#define HT_NOT_LOADED -29999
+
+#ifndef va_arg
+# include <stdarg.h>
+#endif
+
+#define LYva_start(ap,format) va_start(ap,format)
+
+/*
+ * GCC can be told that some functions are like printf (and do type-checking on
+ * their parameters).
+ */
+#ifndef GCC_PRINTFLIKE
+#if defined(GCC_PRINTF) && !defined(printf) && !defined(HAVE_LIBUTF8_H)
+#define GCC_PRINTFLIKE(fmt,var) __attribute__((format(printf,fmt,var)))
+#else
+#define GCC_PRINTFLIKE(fmt,var) /*nothing */
+#endif
+#endif
+
+#include <HTString.h> /* String utilities */
+
+/*
+
+Out Of Memory checking for malloc() return:
+
+ */
+#ifndef __FILE__
+#define __FILE__ ""
+#define __LINE__ ""
+#endif
+
+#include <LYexit.h>
+
+/*
+ * Upper- and Lowercase macros
+ *
+ * The problem here is that toupper(x) is not defined officially unless
+ * isupper(x) is. These macros are CERTAINLY needed on #if defined(pyr) ||
+ * define(mips) or BDSI platforms. For safefy, we make them mandatory.
+ *
+ * Note: Pyramid and Mips can't uppercase non-alpha.
+ */
+#include <ctype.h>
+#include <string.h>
+
+#ifndef TOLOWER
+
+#ifdef USE_ASCII_CTYPES
+
+#define TOLOWER(c) ascii_tolower(UCH(c))
+#define TOUPPER(c) ascii_toupper(UCH(c))
+#define ISUPPER(c) ascii_isupper(UCH(c))
+
+#else
+
+#define TOLOWER(c) (isupper(UCH(c)) ? tolower(UCH(c)) : UCH(c))
+#define TOUPPER(c) (islower(UCH(c)) ? toupper(UCH(c)) : UCH(c))
+#define ISUPPER(c) (isupper(UCH(c)))
+
+#endif
+
+#endif /* TOLOWER */
+
+#define FREE(x) {if (x != 0) {free((char *)x); x = NULL;}}
+
+/*
+
+The local equivalents of CR and LF
+
+ We can check for these after net ascii text has been converted to the local
+ representation. Similarly, we include them in strings to be sent as net ascii after
+ translation.
+
+ */
+#define LF FROMASCII('\012') /* ASCII line feed LOCAL EQUIVALENT */
+#define CR FROMASCII('\015') /* Will be converted to ^M for transmission */
+
+/*
+ * Debug message control.
+ */
+#ifdef NO_LYNX_TRACE
+#define WWW_TraceFlag 0
+#define WWW_TraceMask 0
+#define LYTraceLogFP 0
+#else
+extern BOOLEAN WWW_TraceFlag;
+extern int WWW_TraceMask;
+#endif
+
+#define TRACE (WWW_TraceFlag)
+#define TRACE_bit(n) (TRACE && (WWW_TraceMask & (1 << n)) != 0)
+#define TRACE_SGML (TRACE_bit(0))
+#define TRACE_STYLE (TRACE_bit(1))
+#define TRACE_TRST (TRACE_bit(2))
+#define TRACE_CFG (TRACE_bit(3))
+#define TRACE_BSTRING (TRACE_bit(4))
+#define TRACE_COOKIES (TRACE_bit(5))
+#define TRACE_CHARSETS (TRACE_bit(6))
+#define TRACE_GRIDTEXT (TRACE_bit(7))
+#define TRACE_TIMING (TRACE_bit(8))
+#define TRACE_HTPARSE (TRACE_bit(9))
+
+/*
+ * Get printing/scanning formats.
+ */
+#if defined(HAVE_INTTYPES_H)
+#include <inttypes.h>
+#endif
+
+#define DigitsOf(type) (int)((sizeof(type)*8)/3)
+
+/*
+ * Printing/scanning-formats for "off_t", as well as cast needed to fit.
+ */
+#if defined(HAVE_LONG_LONG) && defined(HAVE_INTTYPES_H) && defined(SIZEOF_OFF_T)
+#if (SIZEOF_OFF_T == 8) && defined(PRId64) && defined(SCNd64)
+
+#define PRI_off_t PRId64
+#define SCN_off_t SCNd64
+#define CAST_off_t(n) (int64_t)(n)
+
+#elif (SIZEOF_OFF_T == 4) && defined(PRId32)
+
+#define PRI_off_t PRId32
+#define SCN_off_t SCNd32
+
+#if (SIZEOF_INT == 4)
+#define CAST_off_t(n) (int)(n)
+#elif (SIZEOF_LONG == 4)
+#define CAST_off_t(n) (long)(n)
+#else
+#define CAST_off_t(n) (int32_t)(n)
+#endif
+
+#endif
+#endif
+
+#ifndef PRI_off_t
+#define GUESS_PRI_off_t
+#if (SIZEOF_OFF_T == SIZEOF_LONG)
+#define PRI_off_t "ld"
+#define SCN_off_t "ld"
+#define CAST_off_t(n) (long)(n)
+#elif defined(HAVE_LONG_LONG)
+#define PRI_off_t "lld"
+#define SCN_off_t "lld"
+#define CAST_off_t(n) (long long)(n)
+#else
+#define PRI_off_t "ld"
+/* SCN_off_t requires workaround */
+#define CAST_off_t(n) (long)(n)
+#endif
+#endif
+
+/*
+ * MinGW-32 uses only 32-bit DLL, which limits printing.
+ */
+#if defined(__MINGW32__)
+#undef PRI_off_t
+#undef CAST_off_t
+#define PRI_off_t "ld"
+#define CAST_off_t(n) (long)(n)
+#endif
+
+/*
+ * Printing-format for "time_t", as well as cast needed to fit.
+ */
+#if defined(HAVE_LONG_LONG) && defined(HAVE_INTTYPES_H) && defined(SIZEOF_TIME_T)
+#if (SIZEOF_TIME_T == 8) && defined(PRId64)
+
+#define PRI_time_t PRId64
+#define SCN_time_t SCNd64
+#define CAST_time_t(n) (int64_t)(n)
+
+#elif (SIZEOF_TIME_T == 4) && defined(PRId32)
+
+#define PRI_time_t PRId32
+#define SCN_time_t SCNd32
+
+#if (SIZEOF_INT == 4)
+#define CAST_time_t(n) (int)(n)
+#elif (SIZEOF_LONG == 4)
+#define CAST_time_t(n) (long)(n)
+#else
+#define CAST_time_t(n) (int32_t)(n)
+#endif
+
+#endif
+#endif
+
+#ifndef PRI_time_t
+#if defined(HAVE_LONG_LONG) && (SIZEOF_TIME_T > SIZEOF_LONG)
+#define PRI_time_t "lld"
+#define SCN_time_t "lld"
+#define CAST_time_t(n) (long long)(n)
+#else
+#define PRI_time_t "ld"
+#define SCN_time_t "ld"
+#define CAST_time_t(n) (long)(n)
+#endif
+#endif
+
+/*
+ * Printing-format for "UCode_t".
+ */
+#define PRI_UCode_t "lX"
+#define CAST_UCode_t(n) (unsigned long)(n)
+
+/*
+ * Verbose-tracing.
+ */
+#if defined(USE_VERTRACE) && !defined(LY_TRACELINE)
+#define LY_TRACELINE __LINE__
+#endif
+
+#if defined(LY_TRACELINE)
+#define LY_SHOWWHERE fprintf( tfp, "%s: %d: ", __FILE__, LY_TRACELINE ),
+#else
+#define LY_SHOWWHERE /* nothing */
+#endif
+
+#define CTRACE(p) ((void)((TRACE) && ( LY_SHOWWHERE fprintf p )))
+#define CTRACE2(m,p) ((void)((m) && ( LY_SHOWWHERE fprintf p )))
+#define tfp TraceFP()
+#define CTRACE_SLEEP(secs) if (TRACE && LYTraceLogFP == 0) sleep((unsigned)secs)
+#define CTRACE_FLUSH(fp) if (TRACE) fflush(fp)
+
+#include <www_tcp.h>
+
+/*
+ * We force this include-ordering since socks.h contains redefinitions of
+ * functions that probably are prototyped via other includes. The socks.h
+ * definitions have to be included everywhere, since they're making wrappers
+ * for the stdio functions as well as the network functions.
+ */
+#if defined(USE_SOCKS5)
+#define SOCKS4TO5 /* turn on the Rxxxx definitions used in Lynx */
+#include <socks.h>
+
+/*
+ * The AIX- and SOCKS4-specific definitions in socks.h are inconsistent.
+ * Repair them so they're consistent (and usable).
+ */
+#if defined(_AIX) && !defined(USE_SOCKS4_PREFIX)
+#undef Raccept
+#define Raccept accept
+#undef Rgetsockname
+#define Rgetsockname getsockname
+#undef Rgetpeername
+#define Rgetpeername getpeername
+#endif
+
+/*
+ * Workaround for order-of-evaluation problem with gcc and socks5 headers
+ * which breaks the Rxxxx names by attaching the prefix twice:
+ */
+#ifdef INCLUDE_PROTOTYPES
+#undef Raccept
+#undef Rbind
+#undef Rconnect
+#undef Rlisten
+#undef Rselect
+#undef Rgetpeername
+#undef Rgetsockname
+#define Raccept accept
+#define Rbind bind
+#define Rconnect connect
+#define Rgetpeername getpeername
+#define Rgetsockname getsockname
+#define Rlisten listen
+#define Rselect select
+#endif
+
+#elif defined(SOCKS)
+#include <socks.h>
+#endif /* USE_SOCKS5 */
+
+#ifdef USE_SSL
+
+#define free_func free__func
+
+#ifdef USE_OPENSSL_INCL
+#include <openssl/ssl.h>
+#include <openssl/crypto.h>
+#include <openssl/rand.h>
+#include <openssl/err.h>
+
+#else
+
+#if defined(USE_GNUTLS_FUNCS)
+#include <tidy_tls.h>
+#define USE_GNUTLS_INCL 1 /* do this for the ".c" ifdef's */
+#elif defined(USE_GNUTLS_INCL)
+#include <gnutls/openssl.h>
+/*
+ * GNUTLS's implementation of OpenSSL is very incomplete and rudimentary.
+ * For a start, let's make it compile (TD - 2003/4/13).
+ */
+#ifndef SSL_VERIFY_PEER
+#define SSL_VERIFY_PEER 0x01
+#endif
+#else
+
+#ifdef USE_NSS_COMPAT_INCL
+#include <nss_compat_ossl/nss_compat_ossl.h>
+
+#else /* assume SSLeay */
+#include <ssl.h>
+#include <crypto.h>
+#include <rand.h>
+#include <err.h>
+#endif
+#endif
+#endif /* USE_OPENSSL_INCL */
+
+#undef free_func
+#endif /* USE_SSL */
+
+#ifdef HAVE_BSD_STDLIB_H
+#include <bsd/stdlib.h> /* prototype for arc4random.h */
+#elif defined(HAVE_BSD_RANDOM_H)
+#include <bsd/random.h> /* prototype for arc4random.h */
+#endif
+
+#ifdef HAVE_LIBDMALLOC
+#include <dmalloc.h> /* Gray Watson's library */
+#define show_alloc() dmalloc_log_unfreed()
+#endif
+
+#ifdef HAVE_LIBDBMALLOC
+#include <dbmalloc.h> /* Conor Cahill's library */
+#define show_alloc() malloc_dump(fileno(stderr))
+#endif
+
+#ifndef show_alloc
+#define show_alloc() /* nothing */
+#endif
+
+#include <userdefs.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+#ifndef TOLOWER
+#ifdef USE_ASCII_CTYPES
+ extern int ascii_toupper(int);
+ extern int ascii_tolower(int);
+ extern int ascii_isupper(int);
+#endif
+#endif
+
+ extern FILE *TraceFP(void);
+
+ extern char *HTSkipToAt(char *host, int *gen_delims);
+ extern void strip_userid(char *host, int warn);
+
+#ifdef USE_SSL
+ extern SSL *HTGetSSLHandle(void);
+ extern void HTSSLInitPRNG(void);
+ extern int HTGetSSLCharacter(void *handle);
+#endif /* USE_SSL */
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* HTUTILS_H */
diff --git a/WWW/Library/Implementation/HTVMSUtils.c b/WWW/Library/Implementation/HTVMSUtils.c
new file mode 100644
index 0000000..38e583a
--- /dev/null
+++ b/WWW/Library/Implementation/HTVMSUtils.c
@@ -0,0 +1,1131 @@
+/*
+ * $LynxId: HTVMSUtils.c,v 1.40 2020/01/21 21:58:29 tom Exp $
+ *
+ * MODULE HTVMSUtil.c
+ * VMS Utility Routines
+ *
+ * AUTHORS:
+ * MD Mark Donszelmann duns@vxdeop.cern.ch
+ *
+ * HISTORY:
+ * 14 Nov 93 MD Written
+ *
+ * BUGS:
+ *
+ *
+ */
+
+#include <HTUtils.h>
+#ifdef VMS
+#include <HTFormat.h>
+#include <HTStream.h>
+#include <UCDefs.h>
+#include <UCMap.h>
+#include <UCAux.h>
+#include <HTFTP.h>
+#include <HTTCP.h>
+#include <HTVMSUtils.h>
+#include <ssdef.h>
+#include <jpidef.h>
+#include <prvdef.h>
+#include <acldef.h>
+#include <chpdef.h>
+#include <descrip.h>
+#include <lib$routines.h>
+#include <starlet.h>
+#include <rmsdef.h>
+
+#include <LYGlobalDefs.h>
+#include <LYUtils.h>
+#include <LYLeaks.h>
+#include <LYStrings.h>
+
+BOOL HTVMSFileVersions = FALSE; /* Include version numbers in listing? */
+
+typedef struct {
+ unsigned long BufferLength:16;
+ unsigned long ItemCode:16;
+ unsigned long BufferAddress:32;
+ unsigned long ReturnLengthAddress:32;
+} ItemStruct;
+
+/* PUBLIC HTVMS_authSysPrv()
+ * CHECKS IF THIS PROCESS IS AUTHORIZED TO ENABLE SYSPRV
+ * ON ENTRY:
+ * No arguments.
+ *
+ * ON EXIT:
+ * returns YES if SYSPRV is authorized
+ */
+BOOL HTVMS_authSysPrv(void)
+{
+ unsigned long Result;
+ ItemStruct ItemList[2];
+ unsigned long Length;
+ unsigned long Buffer[2];
+
+ /* fill Item */
+ ItemList[0].BufferLength = sizeof(Buffer);
+ ItemList[0].BufferAddress = (unsigned long) Buffer;
+ ItemList[0].ReturnLengthAddress = (unsigned long) &Length;
+ ItemList[0].ItemCode = JPI$_AUTHPRIV;
+
+ /* terminate list */
+ ItemList[1].ItemCode = 0;
+ ItemList[1].BufferLength = 0;
+
+ /* call system */
+ Result = sys$getjpiw(0, 0, 0, ItemList, 0, 0, 0);
+
+ if (Result != SS$_NORMAL)
+ return (NO);
+
+ if (Buffer[0] & PRV$M_SYSPRV)
+ return (YES);
+
+ return (NO);
+}
+
+/* PUBLIC HTVMS_enableSysPrv()
+ * ENABLES SYSPRV
+ * ON ENTRY:
+ * No arguments.
+ *
+ * ON EXIT:
+ *
+ */
+void HTVMS_enableSysPrv(void)
+{
+ unsigned long Result;
+ unsigned long Prv[2], PreviousPrv[2];
+
+ Prv[0] = PRV$M_SYSPRV;
+ Prv[1] = 0;
+ Result = sys$setprv(1, &Prv, 0, &PreviousPrv);
+
+ if (Result == SS$_NORMAL) {
+ if (!(PreviousPrv[0] & PRV$M_SYSPRV)) {
+ CTRACE((tfp, "HTVMS_enableSysPrv: Enabled SYSPRV\n"));
+ }
+ }
+}
+
+/* PUBLIC HTVMS_disableSysPrv()
+ * DISABLES SYSPRV
+ * ON ENTRY:
+ * No arguments.
+ *
+ * ON EXIT:
+ *
+ */
+void HTVMS_disableSysPrv(void)
+{
+ unsigned long Result;
+ unsigned long Prv[2], PreviousPrv[2];
+
+ Prv[0] = PRV$M_SYSPRV;
+ Prv[1] = 0;
+ Result = sys$setprv(0, &Prv, 0, &PreviousPrv);
+
+ if (Result == SS$_NORMAL) {
+ if (PreviousPrv[0] & PRV$M_SYSPRV) {
+ CTRACE((tfp, "HTVMS_disableSysPrv: Disabled SYSPRV\n"));
+ }
+ }
+}
+
+/* PUBLIC HTVMS_checkAccess()
+ * CHECKS ACCESS TO FILE FOR CERTAIN USER
+ * ON ENTRY:
+ * FileName The file to be accessed
+ * UserName Name of the user to check access for.
+ * User nobody, represented by "" is given NO for an answer
+ * Method Name of the method to be checked
+ *
+ * ON EXIT:
+ * returns YES if access is allowed
+ *
+ */
+BOOL HTVMS_checkAccess(const char *FileName,
+ const char *UserName,
+ const char *Method)
+{
+ unsigned long Result;
+ ItemStruct ItemList[2];
+ unsigned long Length;
+ unsigned long Buffer;
+ unsigned long ObjType;
+
+ char *VmsName;
+
+ struct dsc$descriptor_s FileNameDesc;
+ struct dsc$descriptor_s UserNameDesc;
+
+ char *colon;
+
+ /* user nobody should access as from account under which server is running */
+ if (0 == strcmp(UserName, ""))
+ return (NO);
+
+ /* check Filename and convert */
+ colon = StrChr(FileName, ':');
+ if (colon)
+ VmsName = HTVMS_name("", colon + 1);
+ else
+ VmsName = HTVMS_name("", FileName);
+
+ /* check for GET */
+ if (0 == strcmp(Method, "GET")) {
+ /* fill Item */
+ ItemList[0].BufferLength = sizeof(Buffer);
+ ItemList[0].BufferAddress = (unsigned long) &Buffer;
+ ItemList[0].ReturnLengthAddress = (unsigned long) &Length;
+ ItemList[0].ItemCode = CHP$_FLAGS;
+
+ /* terminate list */
+ ItemList[1].ItemCode = 0;
+ ItemList[1].BufferLength = 0;
+
+ /* fill input */
+ ObjType = ACL$C_FILE;
+ Buffer = CHP$M_READ;
+ UserNameDesc.dsc$w_length = strlen(UserName);
+ UserNameDesc.dsc$b_dtype = DSC$K_DTYPE_T;
+ UserNameDesc.dsc$b_class = DSC$K_CLASS_S;
+ UserNameDesc.dsc$a_pointer = (char *) UserName;
+ FileNameDesc.dsc$w_length = strlen(VmsName);
+ FileNameDesc.dsc$b_dtype = DSC$K_DTYPE_T;
+ FileNameDesc.dsc$b_class = DSC$K_CLASS_S;
+ FileNameDesc.dsc$a_pointer = VmsName;
+
+ /* call system */
+ Result = sys$check_access(&ObjType, &FileNameDesc, &UserNameDesc, ItemList);
+
+ if (Result == SS$_NORMAL)
+ return (YES);
+ else
+ return (NO);
+ }
+
+ return (NO);
+}
+
+/* PUBLIC HTVMS_wwwName()
+ * CONVERTS VMS Name into WWW Name
+ * ON ENTRY:
+ * vmsname VMS file specification (NO NODE)
+ *
+ * ON EXIT:
+ * returns www file specification
+ *
+ * EXAMPLES:
+ * vmsname wwwname
+ * DISK$USER disk$user
+ * DISK$USER: /disk$user/
+ * DISK$USER:[DUNS] /disk$user/duns
+ * DISK$USER:[DUNS.ECHO] /disk$user/duns/echo
+ * [DUNS] duns
+ * [DUNS.ECHO] duns/echo
+ * [DUNS.ECHO.-.TRANS] duns/echo/../trans
+ * [DUNS.ECHO.--.TRANS] duns/echo/../../trans
+ * [.DUNS] duns
+ * [.DUNS.ECHO] duns/echo
+ * [.DUNS.ECHO]TEST.COM duns/echo/test.com
+ * TEST.COM test.com
+ *
+ *
+ */
+const char *HTVMS_wwwName(const char *vmsname)
+{
+ static char wwwname[LY_MAXPATH];
+ const char *src;
+ char *dst;
+ int dir;
+
+ dst = wwwname;
+ src = vmsname;
+ dir = 0;
+ if (StrChr(src, ':'))
+ *(dst++) = '/';
+ for (; *src != '\0'; src++) {
+ switch (*src) {
+ case ':':
+ *(dst++) = '/';
+ break;
+ case '-':
+ if (dir) {
+ if ((*(src - 1) == '[' ||
+ *(src - 1) == '.' ||
+ *(src - 1) == '-') &&
+ (*(src + 1) == '.' ||
+ *(src + 1) == '-')) {
+ *(dst++) = '/';
+ *(dst++) = '.';
+ *(dst++) = '.';
+ } else
+ *(dst++) = '-';
+ } else {
+ if (*(src - 1) == ']')
+ *(dst++) = '/';
+ *(dst++) = '-';
+ }
+ break;
+ case '.':
+ if (dir) {
+ if (*(src - 1) != '[')
+ *(dst++) = '/';
+ } else {
+ if (*(src - 1) == ']')
+ *(dst++) = '/';
+ *(dst++) = '.';
+ }
+ break;
+ case '[':
+ dir = 1;
+ break;
+ case ']':
+ dir = 0;
+ break;
+ default:
+ if (*(src - 1) == ']')
+ *(dst++) = '/';
+ *(dst++) = *src;
+ break;
+ }
+ }
+ *(dst++) = '\0';
+ return (wwwname);
+}
+
+/*
+ * The code below is for directory browsing by VMS Curses clients.
+ * It is based on the newer WWWLib's HTDirBrw.c. - Foteos Macrides
+ */
+int HTStat(const char *filename,
+ struct stat *info)
+{
+ /*
+ the following stuff does not work in VMS with a normal stat...
+ --> /disk$user/duns/www if www is a directory
+ is statted like: /disk$user/duns/www.dir
+ after a normal stat has failed
+ --> /disk$user/duns if duns is a toplevel directory
+ is statted like: /disk$user/000000/duns.dir
+ --> /disk$user since disk$user is a device
+ is statted like: /disk$user/000000/000000.dir
+ --> /
+ searches all devices, no solution yet...
+ --> /vxcern!/disk$cr/wwwteam/login.com
+ is not statted but granted with fake information...
+ */
+ int Result;
+ int Len;
+ char *Ptr, *Ptr2;
+ static char *Name;
+
+ /* try normal stat... */
+ Result = stat((char *) filename, info);
+ if (Result == 0)
+ return (Result);
+
+ /* make local copy */
+ StrAllocCopy(Name, filename);
+
+ /* failed,so do device search in case root is requested */
+ if (!strcmp(Name, "/")) { /* root requested */
+ return (-1);
+ }
+
+ /* failed so this might be a directory, add '.dir' */
+ Len = strlen(Name);
+ if (Name[Len - 1] == '/')
+ Name[Len - 1] = '\0';
+
+ /* fail in case of device */
+ Ptr = StrChr(Name + 1, '/');
+ if ((Ptr == NULL) && (Name[0] == '/')) { /* device only... */
+ StrAllocCat(Name, "/000000/000000");
+ }
+
+ if (Ptr != NULL) { /* correct filename in case of toplevel dir */
+ Ptr2 = StrChr(Ptr + 1, '/');
+ if ((Ptr2 == NULL) && (Name[0] == '/')) {
+ char End[256];
+
+ LYStrNCpy(End, Ptr, sizeof(End) - 1);
+ *(Ptr + 1) = '\0';
+ StrAllocCat(Name, "000000");
+ StrAllocCat(Name, End);
+ }
+ }
+
+ /* try in case a file on toplevel directory or .DIR was already specified */
+ Result = stat(Name, info);
+ if (Result == 0)
+ return (Result);
+
+ /* add .DIR and try again */
+ StrAllocCat(Name, ".dir");
+ Result = stat(Name, info);
+ return (Result);
+}
+
+#ifndef _POSIX_SOURCE
+#define d_ino d_fileno /* compatibility */
+#ifndef NULL
+#define NULL 0
+#endif
+#endif /* !_POSIX_SOURCE */
+
+typedef struct __dirdesc {
+ long context; /* context descriptor for LIB$FIND_FILE calls */
+ char dirname[255 + 1]; /* keeps the directory name, including *.* */
+ struct dsc$descriptor_s dirname_desc; /* descriptor of dirname */
+} DIR;
+
+static DIR *HTVMSopendir(char *dirname);
+static struct dirent *HTVMSreaddir(DIR *dirp);
+static int HTVMSclosedir(DIR *dirp);
+
+/*** #include <sys_dirent.h> ***/
+/*** "sys_dirent.h" ***/
+struct dirent {
+ unsigned long d_fileno; /* file number of entry */
+ unsigned short d_namlen; /* length of string in d_name */
+ char d_name[255 + 1]; /* name (up to MAXNAMLEN + 1) */
+};
+
+#ifndef _POSIX_SOURCE
+/*
+ * It's unlikely to change, but make sure that sizeof d_name above is
+ * at least MAXNAMLEN + 1 (more may be added for padding).
+ */
+#define MAXNAMLEN 255
+/*
+ * The macro DIRSIZ(dp) gives the minimum amount of space required to represent
+ * a directory entry. For any directory entry dp->d_reclen >= DIRSIZ(dp).
+ * Specific filesystem types may use this macro to construct the value
+ * for d_reclen.
+ */
+#undef DIRSIZ
+#define DIRSIZ(dp) \
+ (((sizeof(struct dirent) - (MAXNAMLEN+1) + ((dp)->d_namlen+1)) +3) & ~3)
+
+#endif /* !_POSIX_SOURCE */
+
+static DIR *HTVMSopendir(char *dirname)
+{
+ static DIR dir;
+ char *closebracket;
+ long status;
+ struct dsc$descriptor_s entryname_desc;
+ struct dsc$descriptor_s dirname_desc;
+ static char *DirEntry;
+ char Actual[256];
+ char VMSentry[256];
+ char UnixEntry[256];
+ int index;
+ char *dot;
+
+ /* check if directory exists */
+ /* dirname can look like /disk$user/duns/www/test/multi */
+ /* or like /disk$user/duns/www/test/multi/ */
+ /* DirEntry should look like disk$user:[duns.www.test]multi in both cases */
+ /* dir.dirname should look like disk$user:[duns.www.test.multi] */
+ sprintf(UnixEntry, "%.*s", sizeof(UnixEntry) - 2, dirname);
+ if (UnixEntry[strlen(UnixEntry) - 1] != '/')
+ strcat(UnixEntry, "/");
+
+ StrAllocCopy(DirEntry, HTVMS_name("", UnixEntry));
+ if (strlen(DirEntry) > sizeof(dir.dirname) - 1)
+ return (NULL);
+ strcpy(dir.dirname, DirEntry);
+ index = strlen(DirEntry) - 1;
+
+ if (DirEntry[index] == ']')
+ DirEntry[index] = '\0';
+
+ if ((dot = strrchr(DirEntry, '.')) == NULL) { /* convert disk$user:[duns] into disk$user:[000000]duns.dir */
+ char *openbr = strrchr(DirEntry, '[');
+
+ if (!openbr) { /* convert disk$user: into disk$user:[000000]000000.dir */
+ if (strlen(dir.dirname) > sizeof(dir.dirname) - 10)
+ return (NULL);
+ sprintf(dir.dirname, "%.*s[000000]", sizeof(dir.dirname) - 9, DirEntry);
+ StrAllocCat(DirEntry, "[000000]000000.dir");
+ } else {
+ char End[256];
+
+ strcpy(End, openbr + 1);
+ *(openbr + 1) = '\0';
+ StrAllocCat(DirEntry, "000000]");
+ StrAllocCat(DirEntry, End);
+ StrAllocCat(DirEntry, ".dir");
+ }
+ } else {
+ *dot = ']';
+ StrAllocCat(DirEntry, ".dir");
+ }
+ /* lib$find_file needs a fixed-size buffer */
+ LYStrNCpy(Actual, DirEntry, sizeof(Actual) - 1);
+
+ dir.context = 0;
+ dirname_desc.dsc$w_length = strlen(Actual);
+ dirname_desc.dsc$b_dtype = DSC$K_DTYPE_T;
+ dirname_desc.dsc$b_class = DSC$K_CLASS_S;
+ dirname_desc.dsc$a_pointer = (char *) &(Actual);
+
+ /* look for the directory */
+ entryname_desc.dsc$w_length = 255;
+ entryname_desc.dsc$b_dtype = DSC$K_DTYPE_T;
+ entryname_desc.dsc$b_class = DSC$K_CLASS_S;
+ entryname_desc.dsc$a_pointer = VMSentry;
+
+ status = lib$find_file(&(dirname_desc),
+ &entryname_desc,
+ &(dir.context),
+ 0, 0, 0, 0);
+ if (!(status & 0x01)) { /* directory not found */
+ return (NULL);
+ }
+
+ if (strlen(dir.dirname) > sizeof(dir.dirname) - 10)
+ return (NULL);
+ if (HTVMSFileVersions)
+ strcat(dir.dirname, "*.*;*");
+ else
+ strcat(dir.dirname, "*.*");
+ dir.context = 0;
+ dir.dirname_desc.dsc$w_length = strlen(dir.dirname);
+ dir.dirname_desc.dsc$b_dtype = DSC$K_DTYPE_T;
+ dir.dirname_desc.dsc$b_class = DSC$K_CLASS_S;
+ dir.dirname_desc.dsc$a_pointer = (char *) &(dir.dirname);
+ return (&dir);
+}
+
+static struct dirent *HTVMSreaddir(DIR *dirp)
+{
+ static struct dirent entry;
+ long status;
+ struct dsc$descriptor_s entryname_desc;
+ char *space, *slash;
+ char VMSentry[256];
+ const char *UnixEntry;
+
+ entryname_desc.dsc$w_length = 255;
+ entryname_desc.dsc$b_dtype = DSC$K_DTYPE_T;
+ entryname_desc.dsc$b_class = DSC$K_CLASS_S;
+ entryname_desc.dsc$a_pointer = VMSentry;
+
+ status = lib$find_file(&(dirp->dirname_desc),
+ &entryname_desc,
+ &(dirp->context),
+ 0, 0, 0, 0);
+ if (status == RMS$_NMF) { /* no more files */
+ return (NULL);
+ } else { /* ok */
+ if (!(status & 0x01))
+ return (0);
+ if (HTVMSFileVersions)
+ space = StrChr(VMSentry, ' ');
+ else
+ space = StrChr(VMSentry, ';');
+ if (space)
+ *space = '\0';
+
+ /* convert to unix style... */
+ UnixEntry = HTVMS_wwwName(VMSentry);
+ slash = strrchr(UnixEntry, '/') + 1;
+ strcpy(entry.d_name, slash);
+ entry.d_namlen = strlen(entry.d_name);
+ entry.d_fileno = 1;
+ return (&entry);
+ }
+}
+
+static int HTVMSclosedir(DIR *dirp)
+{
+ long status;
+
+ status = lib$find_file_end(&(dirp->context));
+ if (!(status & 0x01))
+ exit_immediately(status);
+ dirp->context = 0;
+ return (0);
+}
+
+#include <HTAnchor.h>
+#include <HTParse.h>
+#include <HTBTree.h>
+#include <HTFile.h> /* For HTFileFormat() */
+#include <HTAlert.h>
+/*
+ * Hypertext object building machinery.
+ */
+#include <HTML.h>
+#define PUTC(c) (*targetClass.put_character)(target, c)
+#define PUTS(s) (*targetClass.put_string)(target, s)
+#define START(e) (*targetClass.start_element)(target, e, 0, 0, -1, 0)
+#define END(e) (*targetClass.end_element)(target, e, 0)
+#define FREE_TARGET (*targetClass._free)(target)
+#define ABORT_TARGET (*targetClass._free)(target)
+struct _HTStructured {
+ const HTStructuredClass *isa;
+ /* ... */
+};
+
+#define STRUCT_DIRENT struct dirent
+
+static char *months[12] =
+{
+ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+};
+
+typedef struct _VMSEntryInfo {
+ char *filename;
+ char *type;
+ char *date;
+ unsigned int size;
+ BOOLEAN display; /* show this entry? */
+} VMSEntryInfo;
+
+static void free_VMSEntryInfo_contents(VMSEntryInfo * entry_info)
+{
+ if (entry_info) {
+ FREE(entry_info->filename);
+ FREE(entry_info->type);
+ FREE(entry_info->date);
+ }
+ /* don't free the struct */
+}
+
+int compare_VMSEntryInfo_structs(VMSEntryInfo * entry1, VMSEntryInfo * entry2)
+{
+ int i, status;
+ char date1[16], date2[16], time1[8], time2[8], month[4];
+
+ switch (HTfileSortMethod) {
+ case FILE_BY_SIZE:
+ /* both equal or both 0 */
+ if (entry1->size == entry2->size)
+ return (strcasecomp(entry1->filename,
+ entry2->filename));
+ else if (entry1->size > entry2->size)
+ return (1);
+ else
+ return (-1);
+ case FILE_BY_TYPE:
+ if (entry1->type && entry2->type) {
+ status = strcasecomp(entry1->type, entry2->type);
+ if (status)
+ return (status);
+ /* else fall to filename comparison */
+ }
+ return (strcasecomp(entry1->filename,
+ entry2->filename));
+ case FILE_BY_DATE:
+ if (entry1->date && entry2->date) {
+ /*
+ * Make sure we have the correct length. - FM
+ */
+ if (strlen(entry1->date) != 12 ||
+ strlen(entry2->date) != 12) {
+ return (strcasecomp(entry1->filename,
+ entry2->filename));
+ }
+ /*
+ * Set up for sorting in reverse
+ * chronological order. - FM
+ */
+ if (entry1->date[7] != ' ') {
+ strcpy(date1, "9999");
+ strcpy(time1, (char *) &entry1->date[7]);
+ } else {
+ strcpy(date1, (char *) &entry1->date[8]);
+ strcpy(time1, "00:00");
+ }
+ LYStrNCpy(month, entry1->date, 3);
+ for (i = 0; i < 12; i++) {
+ if (!strcasecomp(month, months[i])) {
+ break;
+ }
+ }
+ i++;
+ sprintf(month, "%02d", i);
+ strcat(date1, month);
+ StrNCat(date1, (char *) &entry1->date[4], 2);
+ date1[8] = '\0';
+ if (date1[6] == ' ') {
+ date1[6] = '0';
+ }
+ strcat(date1, time1);
+ if (entry2->date[7] != ' ') {
+ strcpy(date2, "9999");
+ strcpy(time2, (char *) &entry2->date[7]);
+ } else {
+ strcpy(date2, (char *) &entry2->date[8]);
+ strcpy(time2, "00:00");
+ }
+ LYStrNCpy(month, entry2->date, 3);
+ for (i = 0; i < 12; i++) {
+ if (!strcasecomp(month, months[i])) {
+ break;
+ }
+ }
+ i++;
+ sprintf(month, "%02d", i);
+ strcat(date2, month);
+ StrNCat(date2, (char *) &entry2->date[4], 2);
+ date2[8] = '\0';
+ if (date2[6] == ' ') {
+ date2[6] = '0';
+ }
+ strcat(date2, time2);
+ /*
+ * Do the comparison. - FM
+ */
+ status = strcasecomp(date2, date1);
+ if (status)
+ return (status);
+ /* else fall to filename comparison */
+ }
+ return (strcasecomp(entry1->filename,
+ entry2->filename));
+ case FILE_BY_NAME:
+ default:
+ return (strcmp(entry1->filename,
+ entry2->filename));
+ }
+}
+
+/* HTVMSBrowseDir()
+ *
+ * This function generates a directory listing as an HTML-object
+ * for local file URL's. It assumes the first two elements of
+ * of the path are a device followed by a directory:
+ *
+ * file://localhost/device/directory[/[foo]]
+ *
+ * Will not accept 000000 as a directory name.
+ * Will offer links to parent through the top directory, unless
+ * a terminal slash was included in the calling URL.
+ *
+ * Returns HT_LOADED on success, HTLoadError() messages on error.
+ *
+ * Developed for Lynx by Foteos Macrides (macrides@sci.wfeb.edu).
+ */
+int HTVMSBrowseDir(const char *address,
+ HTParentAnchor *anchor,
+ HTFormat format_out,
+ HTStream *sink)
+{
+ HTStructured *target;
+ HTStructuredClass targetClass;
+ char *pathname = HTParse(address, "", PARSE_PATH + PARSE_PUNCTUATION);
+ char *tail = NULL;
+ char *title = NULL;
+ char *header = NULL;
+ char *parent = NULL;
+ char *relative = NULL;
+ char *cp, *cp1;
+ int pathend, len;
+ DIR *dp;
+ struct stat file_info;
+ time_t NowTime;
+ static char ThisYear[8];
+ VMSEntryInfo *entry_info = 0;
+ char string_buffer[64];
+
+ HTUnEscape(pathname);
+ CTRACE((tfp, "HTVMSBrowseDir: Browsing `%s\'\n", pathname));
+
+ /*
+ * Require at least two elements (presumably a device and directory) and
+ * disallow the device root (000000 directory). Symbolic paths (e.g.,
+ * sys$help) should have been translated and expanded (e.g., to
+ * /sys$sysroot/syshlp) before calling this routine.
+ */
+ if (((*pathname != '/') ||
+ (cp = StrChr(pathname + 1, '/')) == NULL ||
+ *(cp + 1) == '\0' ||
+ 0 == StrNCmp((cp + 1), "000000", 6)) ||
+ (dp = HTVMSopendir(pathname)) == NULL) {
+ FREE(pathname);
+ return HTLoadError(sink, 403, COULD_NOT_ACCESS_DIR);
+ }
+
+ /*
+ * Set up the output stream.
+ */
+ _HTProgress(BUILDING_DIR_LIST);
+ if (UCLYhndl_HTFile_for_unspec >= 0) {
+ HTAnchor_setUCInfoStage(anchor,
+ UCLYhndl_HTFile_for_unspec,
+ UCT_STAGE_PARSER,
+ UCT_SETBY_DEFAULT);
+ }
+ target = HTML_new(anchor, format_out, sink);
+ targetClass = *(target->isa);
+
+ /*
+ * Set up the offset string of the anchor reference, and strings for the
+ * title and header.
+ */
+ cp = strrchr(pathname, '/'); /* find lastslash */
+ StrAllocCopy(tail, (cp + 1)); /* take slash off the beginning */
+ if (*tail != '\0') {
+ StrAllocCopy(title, tail);
+ *cp = '\0';
+ if ((cp1 = strrchr(pathname, '/')) != NULL &&
+ cp1 != pathname &&
+ StrNCmp((cp1 + 1), "000000", 6))
+ StrAllocCopy(parent, (cp1 + 1));
+ *cp = '/';
+ } else {
+ pathname[strlen(pathname) - 1] = '\0';
+ cp = strrchr(pathname, '/');
+ StrAllocCopy(title, (cp + 1));
+ pathname[strlen(pathname)] = '/';
+ }
+ StrAllocCopy(header, pathname);
+
+ /*
+ * Initialize path name for HTStat().
+ */
+ pathend = strlen(pathname);
+ if (*(pathname + pathend - 1) != '/') {
+ StrAllocCat(pathname, "/");
+ pathend++;
+ }
+
+ /*
+ * Output the title and header.
+ */
+ START(HTML_HTML);
+ PUTC('\n');
+ START(HTML_HEAD);
+ PUTC('\n');
+ HTUnEscape(title);
+ START(HTML_TITLE);
+ PUTS(title);
+ PUTS(" directory");
+ END(HTML_TITLE);
+ PUTC('\n');
+ FREE(title);
+ END(HTML_HEAD);
+ PUTC('\n');
+ START(HTML_BODY);
+ PUTC('\n');
+ HTUnEscape(header);
+ START(HTML_H1);
+ PUTS(header);
+ END(HTML_H1);
+ PUTC('\n');
+ if (HTDirReadme == HT_DIR_README_TOP) {
+ FILE *fp;
+
+ if (header[strlen(header) - 1] != '/')
+ StrAllocCat(header, "/");
+ StrAllocCat(header, HT_DIR_README_FILE);
+ if ((fp = fopen(header, "r")) != NULL) {
+ START(HTML_PRE);
+ for (;;) {
+ char c = fgetc(fp);
+
+ if (c == (char) EOF)
+ break;
+#ifdef NOTDEFINED
+ switch (c) {
+ case '&':
+ case '<':
+ case '>':
+ PUTC('&');
+ PUTC('#');
+ PUTC((char) (c / 10));
+ PUTC((char) (c % 10));
+ PUTC(';');
+ break;
+ default:
+ PUTC(c);
+ }
+#else
+ PUTC(c);
+#endif /* NOTDEFINED */
+ }
+ END(HTML_PRE);
+ fclose(fp);
+ }
+ }
+ FREE(header);
+ if (parent) {
+ HTSprintf0(&relative, "%s/..", tail);
+ HTStartAnchor(target, "", relative);
+ PUTS("Up to ");
+ HTUnEscape(parent);
+ PUTS(parent);
+ END(HTML_A);
+ START(HTML_P);
+ PUTC('\n');
+ FREE(relative);
+ FREE(parent);
+ }
+
+ /*
+ * Set up the date comparison.
+ */
+ NowTime = time(NULL);
+ strcpy(ThisYear, (char *) ctime(&NowTime) + 20);
+ ThisYear[4] = '\0';
+
+ /*
+ * Now, generate the Btree and put it out to the output stream.
+ */
+ {
+ char dottest = 2; /* To avoid two strcmp() each time */
+ STRUCT_DIRENT *dirbuf;
+ HTBTree *bt;
+
+ /* Set up sort key and initialize BTree */
+ bt = HTBTree_new((HTComparer) compare_VMSEntryInfo_structs);
+
+ /* Build tree */
+ while ((dirbuf = HTVMSreaddir(dp))) {
+ HTAtom *encoding = NULL;
+ HTFormat format;
+
+ /* Skip if not used */
+ if (!dirbuf->d_ino) {
+ continue;
+ }
+
+ /* Current and parent directories are never shown in list */
+ if (dottest && (!strcmp(dirbuf->d_name, ".") ||
+ !strcmp(dirbuf->d_name, ".."))) {
+ dottest--;
+ continue;
+ }
+
+ /* Don't show the selective enabling file
+ * unless version numbers are included */
+ if (!strcasecomp(dirbuf->d_name, HT_DIR_ENABLE_FILE)) {
+ continue;
+ }
+
+ /* Skip files beginning with a dot? */
+ if ((no_dotfiles || !show_dotfiles) && *dirbuf->d_name == '.') {
+ continue;
+ }
+
+ /* OK, make an lstat() and get a key ready. */
+ *(pathname + pathend) = '\0';
+ StrAllocCat(pathname, dirbuf->d_name);
+ if (HTStat(pathname, &file_info)) {
+ /* for VMS the failure here means the file is not readable...
+ we however continue to browse through the directory... */
+ continue;
+ }
+ entry_info = (VMSEntryInfo *) malloc(sizeof(VMSEntryInfo));
+ if (entry_info == NULL)
+ outofmem(__FILE__, "HTVMSBrowseDir");
+ entry_info->type = 0;
+ entry_info->size = 0;
+ entry_info->date = 0;
+ entry_info->filename = 0;
+ entry_info->display = TRUE;
+
+ /* Get the type */
+ format = HTFileFormat(dirbuf->d_name, &encoding,
+ (const char **) &cp);
+ if (!cp) {
+ if (!StrNCmp(HTAtom_name(format), "application", 11)) {
+ cp = HTAtom_name(format) + 12;
+ if (!StrNCmp(cp, "x-", 2))
+ cp += 2;
+ } else
+ cp = HTAtom_name(format);
+ }
+ StrAllocCopy(entry_info->type, cp);
+
+ StrAllocCopy(entry_info->filename, dirbuf->d_name);
+ if (S_ISDIR(file_info.st_mode)) {
+ /* strip .DIR part... */
+ char *dot;
+
+ dot = strstr(entry_info->filename, ".DIR");
+ if (dot)
+ *dot = '\0';
+ LYLowerCase(entry_info->filename);
+ StrAllocCopy(entry_info->type, "Directory");
+ } else {
+ if ((cp = strstr(entry_info->filename, "READ")) == NULL) {
+ cp = entry_info->filename;
+ } else {
+ cp += 4;
+ if (!StrNCmp(cp, "ME", 2)) {
+ cp += 2;
+ while (cp && *cp && *cp != '.') {
+ cp++;
+ }
+ } else if (!StrNCmp(cp, ".ME", 3)) {
+ cp = (entry_info->filename +
+ strlen(entry_info->filename));
+ } else {
+ cp = entry_info->filename;
+ }
+ }
+ LYLowerCase(cp);
+ if (((len = strlen(entry_info->filename)) > 2) &&
+ entry_info->filename[len - 1] == 'z') {
+ if (entry_info->filename[len - 2] == '.' ||
+ entry_info->filename[len - 2] == '_')
+ entry_info->filename[len - 1] = 'Z';
+ }
+ }
+
+ /* Get the date */
+ {
+ char *t = (char *) ctime((const time_t *) &file_info.st_ctime);
+
+ *(t + 24) = '\0';
+
+ StrAllocCopy(entry_info->date, (t + 4));
+ *((entry_info->date) + 7) = '\0';
+ if ((atoi((t + 19))) < atoi(ThisYear))
+ StrAllocCat(entry_info->date, (t + 19));
+ else {
+ StrAllocCat(entry_info->date, (t + 11));
+ *((entry_info->date) + 12) = '\0';
+ }
+ }
+
+ /* Get the size */
+ if (!S_ISDIR(file_info.st_mode))
+ entry_info->size = (unsigned int) file_info.st_size;
+ else
+ entry_info->size = 0;
+
+ /* Now, update the BTree etc. */
+ if (entry_info->display) {
+ CTRACE((tfp, "Adding file to BTree: %s\n",
+ entry_info->filename));
+ HTBTree_add(bt, entry_info);
+ }
+
+ } /* End while HTVMSreaddir() */
+
+ FREE(pathname);
+ HTVMSclosedir(dp);
+
+ START(HTML_PRE);
+ /*
+ * Run through the BTree printing out in order
+ */
+ {
+ HTBTElement *ele;
+ int i;
+
+ for (ele = HTBTree_next(bt, NULL);
+ ele != NULL;
+ ele = HTBTree_next(bt, ele)) {
+ entry_info = (VMSEntryInfo *) HTBTree_object(ele);
+
+ /* Output the date */
+ if (entry_info->date) {
+ PUTS(entry_info->date);
+ PUTS(" ");
+ } else
+ PUTS(" * ");
+
+ /* Output the type */
+ if (entry_info->type) {
+ for (i = 0; entry_info->type[i] != '\0' && i < 15; i++)
+ PUTC(entry_info->type[i]);
+ for (; i < 17; i++)
+ PUTC(' ');
+
+ }
+
+ /* Output the link for the name */
+ HTDirEntry(target, tail, entry_info->filename);
+ PUTS(entry_info->filename);
+ END(HTML_A);
+
+ /* Output the size */
+ if (entry_info->size) {
+ if (entry_info->size < 1024)
+ sprintf(string_buffer, " %d bytes",
+ entry_info->size);
+ else
+ sprintf(string_buffer, " %dKb",
+ entry_info->size / 1024);
+ PUTS(string_buffer);
+ }
+
+ PUTC('\n'); /* end of this entry */
+
+ free_VMSEntryInfo_contents(entry_info);
+ }
+ }
+
+ HTBTreeAndObject_free(bt);
+
+ } /* End of both BTree loops */
+
+ /*
+ * Complete the output stream.
+ */
+ END(HTML_PRE);
+ PUTC('\n');
+ END(HTML_BODY);
+ PUTC('\n');
+ END(HTML_HTML);
+ PUTC('\n');
+ FREE(tail);
+ FREE_TARGET;
+
+ return HT_LOADED;
+
+} /* End of directory reading section */
+
+/*
+ * Remove all versions of the given file. We assume there are no permissions
+ * problems, since we do this mainly for removing temporary files.
+ */
+int HTVMS_remove(char *filename)
+{
+ int code = remove(filename); /* return the first status code */
+
+ while (remove(filename) == 0) ;
+ return code;
+}
+
+/*
+ * Remove all older versions of the given file. We may fail to remove some
+ * version due to permissions -- the loop stops either at that point, or when
+ * we run out of older versions to remove.
+ */
+void HTVMS_purge(char *filename)
+{
+ char *older_file = 0;
+ char *oldest_file = 0;
+ struct stat sb;
+
+ StrAllocCopy(older_file, filename);
+ StrAllocCat(older_file, ";-1");
+
+ while (remove(older_file) == 0) ;
+ /*
+ * If we do not have any more older versions, it is safe to rename the
+ * current file to version #1.
+ */
+ if (stat(older_file, &sb) != 0) {
+ StrAllocCopy(oldest_file, filename);
+ StrAllocCat(oldest_file, ";1");
+ rename(older_file, oldest_file);
+ FREE(oldest_file);
+ }
+
+ FREE(older_file);
+}
+#endif /* VMS */
diff --git a/WWW/Library/Implementation/HTVMSUtils.h b/WWW/Library/Implementation/HTVMSUtils.h
new file mode 100644
index 0000000..d7efe8c
--- /dev/null
+++ b/WWW/Library/Implementation/HTVMSUtils.h
@@ -0,0 +1,101 @@
+/* VMS specific routines
+
+ */
+
+#ifndef HTVMSUTIL_H
+#define HTVMSUTIL_H
+
+#ifndef HTUTILS_H
+#include <HTUtils.h>
+#endif
+
+#include <HTAnchor.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+ extern BOOL HTVMSFileVersions; /* Include version numbers in listing? */
+
+/* PUBLIC HTVMS_authSysPrv()
+ * CHECKS IF THIS PROCESS IS AUTHORIZED TO ENABLE SYSPRV
+ * ON ENTRY:
+ * No arguments.
+ *
+ * ON EXIT:
+ * returns YES if SYSPRV is authorized
+ */
+ extern BOOL HTVMS_authSysPrv(void);
+
+/* PUBLIC HTVMS_enableSysPrv()
+ * ENABLES SYSPRV
+ * ON ENTRY:
+ * No arguments.
+ *
+ * ON EXIT:
+ *
+ */
+ extern void HTVMS_enableSysPrv(void);
+
+/* PUBLIC HTVMS_disableSysPrv()
+ * DISABLES SYSPRV
+ * ON ENTRY:
+ * No arguments.
+ *
+ * ON EXIT:
+ *
+ */
+ extern void HTVMS_disableSysPrv(void);
+
+/* PUBLIC HTVMS_checkAccess()
+ * CHECKS ACCESS TO FILE FOR CERTAIN USER
+ * ON ENTRY:
+ * FileName The file to be accessed
+ * UserName Name of the user to check access for
+ *
+ * ON EXIT:
+ * returns YES if access is allowed
+ *
+ */
+ extern BOOL HTVMS_checkAccess(const char *FileName,
+ const char *UserName,
+ const char *Method);
+
+/* PUBLIC HTVMS_wwwName()
+ * CONVERTS VMS Name into WWW Name
+ * ON ENTRY:
+ * vmsname VMS file specification (NO NODE)
+ *
+ * ON EXIT:
+ * returns www file specification
+ *
+ * EXAMPLES:
+ * vmsname wwwname
+ * DISK$USER disk$user
+ * DISK$USER: /disk$user/
+ * DISK$USER:[DUNS] /disk$user/duns
+ * DISK$USER:[DUNS.ECHO] /disk$user/duns/echo
+ * [DUNS] duns
+ * [DUNS.ECHO] duns/echo
+ * [DUNS.ECHO.-.TRANS] duns/echo/../trans
+ * [DUNS.ECHO.--.TRANS] duns/echo/../../trans
+ * [.DUNS] duns
+ * [.DUNS.ECHO] duns/echo
+ * [.DUNS.ECHO]TEST.COM duns/echo/test.com
+ * TEST.COM test.com
+ *
+ *
+ */
+ const extern char *HTVMS_wwwName(const char *vmsname);
+
+ extern int HTVMSBrowseDir(const char *address,
+ HTParentAnchor *anchor,
+ HTFormat format_out,
+ HTStream *sink);
+
+ extern int HTVMS_remove(char *filename);
+ extern void HTVMS_purge(char *filename);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* not HTVMSUTIL_H */
diff --git a/WWW/Library/Implementation/HTVMS_WaisProt.c b/WWW/Library/Implementation/HTVMS_WaisProt.c
new file mode 100644
index 0000000..687c9df
--- /dev/null
+++ b/WWW/Library/Implementation/HTVMS_WaisProt.c
@@ -0,0 +1,2469 @@
+/*
+ * $LynxId: HTVMS_WaisProt.c,v 1.10 2020/01/21 22:10:03 tom Exp $
+ *
+ * HTVMS_WAISProt.c
+ *
+ * Adaptation for Lynx by F.Macrides (macrides@sci.wfeb.edu)
+ *
+ * 31-May-1994 FM Initial version.
+ *
+ *----------------------------------------------------------------------*/
+
+/*
+ * Routines originally from WProt.c -- FM
+ *
+ *----------------------------------------------------------------------*/
+/* WIDE AREA INFORMATION SERVER SOFTWARE:
+ * No guarantees or restrictions. See the readme file for the full standard
+ * disclaimer.
+
+ * 3.26.90 Harry Morris, morris@think.com
+ * 3.30.90 Harry Morris
+ * - removed chunk code from WAISSearchAPDU,
+ * - added makeWAISQueryType1Query() and readWAISType1Query() which replace
+ * makeWAISQueryTerms() and makeWAISQueryDocs().
+ * 4.11.90 HWM - generalized conditional includes (see c-dialect.h)
+ * - renamed makeWAISType1Query() to makeWAISTextQuery()
+ * renamed readWAISType1Query() to readWAISTextQuery()
+ * 5.29.90 TS - fixed bug in makeWAISQueryDocs
+ * added CSTFreeWAISFoo functions
+ */
+
+#define _C_WAIS_protocol_
+
+/* This file implements the Z39.50 extensions required for WAIS
+*/
+
+#include <HTUtils.h>
+#include <HTVMS_WaisUI.h>
+#include <HTVMS_WaisProt.h>
+
+#include <LYLeaks.h>
+
+/* very rough estimates of the size of an object */
+#define DefWAISInitResponseSize (size_t)200
+#define DefWAISSearchSize (size_t)3000
+#define DefWAISSearchResponseSize (size_t)6000
+#define DefWAISPresentSize (size_t)1000
+#define DefWAISPresentResponseSize (size_t)6000
+#define DefWAISDocHeaderSize (size_t)500
+#define DefWAISShortHeaderSize (size_t)200
+#define DefWAISLongHeaderSize (size_t)800
+#define DefWAISDocTextSize (size_t)6000
+#define DefWAISDocHeadlineSize (size_t)500
+#define DefWAISDocCodeSize (size_t)500
+
+#define RESERVE_SPACE_FOR_WAIS_HEADER(len) \
+ if (*len > 0) \
+ *len -= header_len;
+
+#define S_MALLOC(type) (type*)s_malloc(sizeof(type))
+#define S_MALLOC2(type) (type*)s_malloc(sizeof(type) * 2)
+
+#define S_REALLOC2(type, ptr, num) (type*)s_realloc((char*)ptr, (sizeof(type) * (num + 2)))
+
+/*----------------------------------------------------------------------*/
+
+static unsigned long userInfoTagSize(data_tag tag,
+ unsigned long length)
+/* return the number of bytes required to write the user info tag and
+ length
+ */
+{
+ unsigned long size;
+
+ /* calculate bytes required to represent tag. max tag is 16K */
+ size = writtenCompressedIntSize(tag);
+ size += writtenCompressedIntSize(length);
+
+ return (size);
+}
+
+/*----------------------------------------------------------------------*/
+
+static char *writeUserInfoHeader(data_tag tag,
+ long infoSize,
+ long estHeaderSize,
+ char *buffer,
+ long *len)
+/* write the tag and size, making sure the info fits. return the true end
+ of the info (after adjustment) note that the argument infoSize includes
+ estHeaderSize. Note that the argument len is the number of bytes remaining
+ in the buffer. Since we write the tag and size at the beginning of the
+ buffer (in space that we reserved) we don't want to pass len the calls which
+ do that writing.
+ */
+{
+ long dummyLen = 100; /* plenty of space for a tag and size */
+ char *buf = buffer;
+ long realSize = infoSize - estHeaderSize;
+ long realHeaderSize = userInfoTagSize(tag, realSize);
+
+ if (buffer == NULL || *len == 0)
+ return (NULL);
+
+ /* write the tag */
+ buf = writeTag(tag, buf, &dummyLen);
+
+ /* see if the if the header size was correct. if not,
+ we have to shift the info to fit the real header size */
+ if (estHeaderSize != realHeaderSize) { /* make sure there is enough space */
+ CHECK_FOR_SPACE_LEFT(realHeaderSize - estHeaderSize, len);
+ memmove(buffer + realHeaderSize, buffer + estHeaderSize, (size_t) (realSize));
+ }
+
+ /* write the size */
+ writeCompressedInteger(realSize, buf, &dummyLen);
+
+ /* return the true end of buffer */
+ return (buffer + realHeaderSize + realSize);
+}
+
+/*----------------------------------------------------------------------*/
+
+static char *readUserInfoHeader(data_tag *tag,
+ unsigned long *num,
+ char *buffer)
+/* read the tag and size */
+{
+ char *buf = buffer;
+
+ buf = readTag(tag, buf);
+ buf = readCompressedInteger(num, buf);
+ return (buf);
+}
+
+/*----------------------------------------------------------------------*/
+
+WAISInitResponse *makeWAISInitResponse(long chunkCode,
+ long chunkIDLen,
+ char *chunkMarker,
+ char *highlightMarker,
+ char *deHighlightMarker,
+ char *newLineChars)
+/* create a WAIS init response object */
+{
+ WAISInitResponse *init = S_MALLOC(WAISInitResponse);
+
+ init->ChunkCode = chunkCode; /* note: none are copied! */
+ init->ChunkIDLength = chunkIDLen;
+ init->ChunkMarker = chunkMarker;
+ init->HighlightMarker = highlightMarker;
+ init->DeHighlightMarker = deHighlightMarker;
+ init->NewlineCharacters = newLineChars;
+
+ return (init);
+}
+
+/*----------------------------------------------------------------------*/
+
+void freeWAISInitResponse(WAISInitResponse *init)
+/* free an object made with makeWAISInitResponse */
+{
+ s_free(init->ChunkMarker);
+ s_free(init->HighlightMarker);
+ s_free(init->DeHighlightMarker);
+ s_free(init->NewlineCharacters);
+ s_free(init);
+}
+
+/*----------------------------------------------------------------------*/
+
+char *writeInitResponseInfo(InitResponseAPDU *init,
+ char *buffer,
+ long *len)
+/* write an init response object */
+{
+ unsigned long header_len = userInfoTagSize(DT_UserInformationLength,
+ DefWAISInitResponseSize);
+ char *buf = buffer + header_len;
+ WAISInitResponse *info = (WAISInitResponse *) init->UserInformationField;
+ unsigned long size;
+
+ RESERVE_SPACE_FOR_WAIS_HEADER(len);
+
+ buf = writeNum(info->ChunkCode, DT_ChunkCode, buf, len);
+ buf = writeNum(info->ChunkIDLength, DT_ChunkIDLength, buf, len);
+ buf = writeString(info->ChunkMarker, DT_ChunkMarker, buf, len);
+ buf = writeString(info->HighlightMarker, DT_HighlightMarker, buf, len);
+ buf = writeString(info->DeHighlightMarker, DT_DeHighlightMarker, buf, len);
+ buf = writeString(info->NewlineCharacters, DT_NewlineCharacters, buf, len);
+
+ /* now write the header and size */
+ size = buf - buffer;
+ buf = writeUserInfoHeader(DT_UserInformationLength,
+ size,
+ header_len,
+ buffer,
+ len);
+
+ return (buf);
+}
+
+/*----------------------------------------------------------------------*/
+
+char *readInitResponseInfo(void **info,
+ char *buffer)
+/* read an init response object */
+{
+ char *buf = buffer;
+ unsigned long size;
+ unsigned long headerSize;
+ long chunkCode, chunkIDLen;
+ data_tag tag1;
+ char *chunkMarker = NULL;
+ char *highlightMarker = NULL;
+ char *deHighlightMarker = NULL;
+ char *newLineChars = NULL;
+
+ chunkCode = chunkIDLen = UNUSED;
+
+ buf = readUserInfoHeader(&tag1, &size, buf);
+ headerSize = buf - buffer;
+
+ while (buf < (buffer + size + headerSize)) {
+ data_tag tag = peekTag(buf);
+
+ switch (tag) {
+ case DT_ChunkCode:
+ buf = readNum(&chunkCode, buf);
+ break;
+ case DT_ChunkIDLength:
+ buf = readNum(&chunkIDLen, buf);
+ break;
+ case DT_ChunkMarker:
+ buf = readString(&chunkMarker, buf);
+ break;
+ case DT_HighlightMarker:
+ buf = readString(&highlightMarker, buf);
+ break;
+ case DT_DeHighlightMarker:
+ buf = readString(&deHighlightMarker, buf);
+ break;
+ case DT_NewlineCharacters:
+ buf = readString(&newLineChars, buf);
+ break;
+ default:
+ s_free(highlightMarker);
+ s_free(deHighlightMarker);
+ s_free(newLineChars);
+ REPORT_READ_ERROR(buf);
+ break;
+ }
+ }
+
+ *info = (void *) makeWAISInitResponse(chunkCode, chunkIDLen, chunkMarker,
+ highlightMarker, deHighlightMarker,
+ newLineChars);
+ return (buf);
+}
+
+/*----------------------------------------------------------------------*/
+
+WAISSearch *makeWAISSearch(char *seedWords,
+ DocObj **docs,
+ char **textList,
+ long dateFactor,
+ char *beginDateRange,
+ char *endDateRange,
+ long maxDocsRetrieved)
+
+/* create a type 3 query object */
+{
+ WAISSearch *query = S_MALLOC(WAISSearch);
+
+ query->SeedWords = seedWords; /* not copied! */
+ query->Docs = docs; /* not copied! */
+ query->TextList = textList; /* not copied! */
+ query->DateFactor = dateFactor;
+ query->BeginDateRange = beginDateRange;
+ query->EndDateRange = endDateRange;
+ query->MaxDocumentsRetrieved = maxDocsRetrieved;
+
+ return (query);
+}
+
+/*----------------------------------------------------------------------*/
+
+void freeWAISSearch(WAISSearch *query)
+
+/* destroy an object made with makeWAISSearch() */
+{
+ void *ptr = NULL;
+ long i;
+
+ s_free(query->SeedWords);
+
+ if (query->Docs != NULL)
+ for (i = 0, ptr = (void *) query->Docs[i];
+ ptr != NULL;
+ ptr = (void *) query->Docs[++i])
+ freeDocObj((DocObj *) ptr);
+ s_free(query->Docs);
+
+ if (query->TextList != NULL) /* XXX revisit when textlist is fully defined */
+ for (i = 0, ptr = (void *) query->TextList[i];
+ ptr != NULL;
+ ptr = (void *) query->TextList[++i])
+ s_free(ptr);
+ s_free(query->TextList);
+
+ s_free(query->BeginDateRange);
+ s_free(query->EndDateRange);
+ s_free(query);
+}
+
+/*----------------------------------------------------------------------*/
+
+DocObj *makeDocObjUsingWholeDocument(any *docID,
+ char *type)
+
+/* construct a document object using byte chunks - only for use by
+ servers */
+{
+ DocObj *doc = S_MALLOC(DocObj);
+
+ doc->DocumentID = docID; /* not copied! */
+ doc->Type = type; /* not copied! */
+ doc->ChunkCode = CT_document;
+ return (doc);
+}
+
+/*----------------------------------------------------------------------*/
+
+DocObj *makeDocObjUsingLines(any *docID,
+ char *type,
+ long start,
+ long end)
+
+/* construct a document object using line chunks - only for use by
+ servers */
+{
+ DocObj *doc = S_MALLOC(DocObj);
+
+ doc->ChunkCode = CT_line;
+ doc->DocumentID = docID; /* not copied */
+ doc->Type = type; /* not copied! */
+ doc->ChunkStart.Pos = start;
+ doc->ChunkEnd.Pos = end;
+ return (doc);
+}
+
+/*----------------------------------------------------------------------*/
+
+DocObj *makeDocObjUsingBytes(any *docID,
+ char *type,
+ long start,
+ long end)
+
+/* construct a document object using byte chunks - only for use by
+ servers */
+{
+ DocObj *doc = S_MALLOC(DocObj);
+
+ doc->ChunkCode = CT_byte;
+ doc->DocumentID = docID; /* not copied */
+ doc->Type = type; /* not copied! */
+ doc->ChunkStart.Pos = start;
+ doc->ChunkEnd.Pos = end;
+ return (doc);
+}
+
+/*----------------------------------------------------------------------*/
+
+DocObj *makeDocObjUsingParagraphs(any *docID,
+ char *type,
+ any *start,
+ any *end)
+
+/* construct a document object using byte chunks - only for use by
+ servers */
+{
+ DocObj *doc = S_MALLOC(DocObj);
+
+ doc->ChunkCode = CT_paragraph;
+ doc->DocumentID = docID; /* not copied */
+ doc->Type = type;
+ doc->ChunkStart.ID = start;
+ doc->ChunkEnd.ID = end;
+ return (doc);
+}
+
+/*----------------------------------------------------------------------*/
+
+void freeDocObj(DocObj *doc)
+
+/* free a docObj */
+{
+ freeAny(doc->DocumentID);
+ s_free(doc->Type);
+ if (doc->ChunkCode == CT_paragraph) {
+ freeAny(doc->ChunkStart.ID);
+ freeAny(doc->ChunkEnd.ID);
+ }
+ s_free(doc);
+}
+
+/*----------------------------------------------------------------------*/
+
+static char *writeDocObj(DocObj *doc,
+ char *buffer,
+ long *len)
+
+/* write as little as we can about the doc obj */
+{
+ char *buf = buffer;
+
+ /* we always have to write the id, but its tag depends on if it's a chunk */
+ if (doc->ChunkCode == CT_document)
+ buf = writeAny(doc->DocumentID, DT_DocumentID, buf, len);
+ else
+ buf = writeAny(doc->DocumentID, DT_DocumentIDChunk, buf, len);
+
+ if (doc->Type != NULL)
+ buf = writeString(doc->Type, DT_TYPE, buf, len);
+
+ switch (doc->ChunkCode) {
+ case CT_document:
+ /* do nothing - there is no chunk data */
+ break;
+ case CT_byte:
+ case CT_line:
+ buf = writeNum(doc->ChunkCode, DT_ChunkCode, buf, len);
+ buf = writeNum(doc->ChunkStart.Pos, DT_ChunkStartID, buf, len);
+ buf = writeNum(doc->ChunkEnd.Pos, DT_ChunkEndID, buf, len);
+ break;
+ case CT_paragraph:
+ buf = writeNum(doc->ChunkCode, DT_ChunkCode, buf, len);
+ buf = writeAny(doc->ChunkStart.ID, DT_ChunkStartID, buf, len);
+ buf = writeAny(doc->ChunkEnd.ID, DT_ChunkEndID, buf, len);
+ break;
+ default:
+ panic("Implementation error: unknown chuck type %ld",
+ doc->ChunkCode);
+ break;
+ }
+
+ return (buf);
+}
+
+/*----------------------------------------------------------------------*/
+
+static char *readDocObj(DocObj **doc,
+ char *buffer)
+
+/* read whatever we have about the new document */
+{
+ char *buf = buffer;
+ data_tag tag;
+
+ *doc = S_MALLOC(DocObj);
+
+ tag = peekTag(buf);
+ buf = readAny(&((*doc)->DocumentID), buf);
+
+ if (tag == DT_DocumentID) {
+ (*doc)->ChunkCode = CT_document;
+ tag = peekTag(buf);
+ if (tag == DT_TYPE) /* XXX depends on DT_TYPE != what comes next */
+ buf = readString(&((*doc)->Type), buf);
+ /* ChunkStart and ChunkEnd are undefined */
+ } else if (tag == DT_DocumentIDChunk) {
+ boolean readParagraphs = false; /* for cleanup */
+
+ tag = peekTag(buf);
+ if (tag == DT_TYPE) /* XXX depends on DT_TYPE != CT_FOO */
+ buf = readString(&((*doc)->Type), buf);
+ buf = readNum(&((*doc)->ChunkCode), buf);
+ switch ((*doc)->ChunkCode) {
+ case CT_byte:
+ case CT_line:
+ buf = readNum(&((*doc)->ChunkStart.Pos), buf);
+ buf = readNum(&((*doc)->ChunkEnd.Pos), buf);
+ break;
+ case CT_paragraph:
+ buf = readAny(&((*doc)->ChunkStart.ID), buf);
+ buf = readAny(&((*doc)->ChunkEnd.ID), buf);
+ break;
+ default:
+ freeAny((*doc)->DocumentID);
+ if (readParagraphs) {
+ freeAny((*doc)->ChunkStart.ID);
+ freeAny((*doc)->ChunkEnd.ID);
+ }
+ s_free(doc);
+ REPORT_READ_ERROR(buf);
+ break;
+ }
+ } else {
+ freeAny((*doc)->DocumentID);
+ s_free(*doc);
+ REPORT_READ_ERROR(buf);
+ }
+ return (buf);
+}
+
+/*----------------------------------------------------------------------*/
+
+char *writeSearchInfo(SearchAPDU *query,
+ char *buffer,
+ long *len)
+
+/* write out a WAIS query (type 1 or 3) */
+{
+ if (strcmp(query->QueryType, QT_TextRetrievalQuery) == 0) {
+ return (writeAny((any *) query->Query, DT_Query, buffer, len));
+ } else {
+ unsigned long header_len = userInfoTagSize(DT_UserInformationLength,
+ DefWAISSearchSize);
+ char *buf = buffer + header_len;
+ WAISSearch *info = (WAISSearch *) query->Query;
+ unsigned long size;
+ long i;
+
+ RESERVE_SPACE_FOR_WAIS_HEADER(len);
+
+ buf = writeString(info->SeedWords, DT_SeedWords, buf, len);
+
+ if (info->Docs != NULL) {
+ for (i = 0; info->Docs[i] != NULL; i++) {
+ buf = writeDocObj(info->Docs[i], buf, len);
+ }
+ }
+
+ /* XXX text list */
+
+ buf = writeNum(info->DateFactor,
+ DT_DateFactor,
+ buf,
+ len);
+ buf = writeString(info->BeginDateRange,
+ DT_BeginDateRange,
+ buf,
+ len);
+ buf = writeString(info->EndDateRange,
+ DT_EndDateRange,
+ buf,
+ len);
+ buf = writeNum(info->MaxDocumentsRetrieved,
+ DT_MaxDocumentsRetrieved,
+ buf,
+ len);
+
+ /* now write the header and size */
+ size = buf - buffer;
+ buf = writeUserInfoHeader(DT_UserInformationLength,
+ size,
+ header_len,
+ buffer,
+ len);
+
+ return (buf);
+ }
+}
+
+/*----------------------------------------------------------------------*/
+
+char *readSearchInfo(void **info,
+ char *buffer)
+
+/* read a WAIS query (type 1 or 3) */
+{
+ data_tag type = peekTag(buffer);
+
+ if (type == DT_Query) /* this is a type 1 query */
+ {
+ char *buf = buffer;
+ any *query = NULL;
+
+ buf = readAny(&query, buf);
+ *info = (void *) query;
+ return (buf);
+ } else { /* a type 3 query */
+ char *buf = buffer;
+ unsigned long size;
+ unsigned long headerSize;
+ data_tag tag1;
+ char *seedWords = NULL;
+ char *beginDateRange = NULL;
+ char *endDateRange = NULL;
+ long dateFactor, maxDocsRetrieved;
+ char **textList = NULL;
+ DocObj **docIDs = NULL;
+ DocObj *doc = NULL;
+ long docs = 0;
+ long i;
+ void *ptr = NULL;
+
+ dateFactor = maxDocsRetrieved = UNUSED;
+
+ buf = readUserInfoHeader(&tag1, &size, buf);
+ headerSize = buf - buffer;
+
+ while (buf < (buffer + size + headerSize)) {
+ data_tag tag = peekTag(buf);
+
+ switch (tag) {
+ case DT_SeedWords:
+ buf = readString(&seedWords, buf);
+ break;
+ case DT_DocumentID:
+ case DT_DocumentIDChunk:
+ if (docIDs == NULL) /* create a new doc list */
+ {
+ docIDs = S_MALLOC2(DocObj *);
+ } else { /* grow the doc list */
+ docIDs = S_REALLOC2(DocObj *, docIDs, docs);
+ }
+ buf = readDocObj(&doc, buf);
+ if (buf == NULL) {
+ s_free(seedWords);
+ s_free(beginDateRange);
+ s_free(endDateRange);
+ if (docIDs != NULL)
+ for (i = 0, ptr = (void *) docIDs[i];
+ ptr != NULL;
+ ptr = (void *) docIDs[++i])
+ freeDocObj((DocObj *) ptr);
+ s_free(docIDs);
+ /* XXX should also free textlist when it is fully defined */
+ }
+ RETURN_ON_NULL(buf);
+ docIDs[docs++] = doc; /* put it in the list */
+ docIDs[docs] = NULL;
+ break;
+ case DT_TextList:
+ /* XXX */
+ break;
+ case DT_DateFactor:
+ buf = readNum(&dateFactor, buf);
+ break;
+ case DT_BeginDateRange:
+ buf = readString(&beginDateRange, buf);
+ break;
+ case DT_EndDateRange:
+ buf = readString(&endDateRange, buf);
+ break;
+ case DT_MaxDocumentsRetrieved:
+ buf = readNum(&maxDocsRetrieved, buf);
+ break;
+ default:
+ s_free(seedWords);
+ s_free(beginDateRange);
+ s_free(endDateRange);
+ if (docIDs != NULL)
+ for (i = 0, ptr = (void *) docIDs[i];
+ ptr != NULL;
+ ptr = (void *) docIDs[++i])
+ freeDocObj((DocObj *) ptr);
+ s_free(docIDs);
+ /* XXX should also free textlist when it is fully defined */
+ REPORT_READ_ERROR(buf);
+ break;
+ }
+ }
+
+ *info = (void *) makeWAISSearch(seedWords, docIDs, textList,
+ dateFactor, beginDateRange, endDateRange,
+ maxDocsRetrieved);
+ return (buf);
+ }
+}
+
+/*----------------------------------------------------------------------*/
+
+WAISDocumentHeader *makeWAISDocumentHeader(any *docID,
+ long versionNumber,
+ long score,
+ long bestMatch,
+ long docLen,
+ long lines,
+ char **types,
+ char *source,
+ char *date,
+ char *headline,
+ char *originCity)
+
+/* construct a standard document header, note that no fields are copied!
+ if the application needs to save these fields, it should copy them,
+ or set the field in this object to NULL before freeing it.
+ */
+{
+ WAISDocumentHeader *header = S_MALLOC(WAISDocumentHeader);
+
+ header->DocumentID = docID;
+ header->VersionNumber = versionNumber;
+ header->Score = score;
+ header->BestMatch = bestMatch;
+ header->DocumentLength = docLen;
+ header->Lines = lines;
+ header->Types = types;
+ header->Source = source;
+ header->Date = date;
+ header->Headline = headline;
+ header->OriginCity = originCity;
+
+ return (header);
+}
+
+/*----------------------------------------------------------------------*/
+
+void freeWAISDocumentHeader(WAISDocumentHeader *header)
+{
+ freeAny(header->DocumentID);
+ doList((void **) header->Types, fs_free); /* can't use the macro here ! */
+ s_free(header->Types);
+ s_free(header->Source);
+ s_free(header->Date);
+ s_free(header->Headline);
+ s_free(header->OriginCity);
+ s_free(header);
+}
+
+/*----------------------------------------------------------------------*/
+
+char *writeWAISDocumentHeader(WAISDocumentHeader *header, char *buffer,
+ long *len)
+{
+ unsigned long header_len = userInfoTagSize(DT_DocumentHeaderGroup,
+ DefWAISDocHeaderSize);
+ char *buf = buffer + header_len;
+ unsigned long size1;
+
+ RESERVE_SPACE_FOR_WAIS_HEADER(len);
+
+ buf = writeAny(header->DocumentID, DT_DocumentID, buf, len);
+ buf = writeNum(header->VersionNumber, DT_VersionNumber, buf, len);
+ buf = writeNum(header->Score, DT_Score, buf, len);
+ buf = writeNum(header->BestMatch, DT_BestMatch, buf, len);
+ buf = writeNum(header->DocumentLength, DT_DocumentLength, buf, len);
+ buf = writeNum(header->Lines, DT_Lines, buf, len);
+ if (header->Types != NULL) {
+ long size;
+ char *ptr = NULL;
+ long i;
+
+ buf = writeTag(DT_TYPE_BLOCK, buf, len);
+ for (i = 0, size = 0, ptr = header->Types[i];
+ ptr != NULL;
+ ptr = header->Types[++i]) {
+ long typeSize = strlen(ptr);
+
+ size += writtenTagSize(DT_TYPE);
+ size += writtenCompressedIntSize(typeSize);
+ size += typeSize;
+ }
+ buf = writeCompressedInteger((unsigned long) size, buf, len);
+ for (i = 0, ptr = header->Types[i]; ptr != NULL; ptr = header->Types[++i])
+ buf = writeString(ptr, DT_TYPE, buf, len);
+ }
+ buf = writeString(header->Source, DT_Source, buf, len);
+ buf = writeString(header->Date, DT_Date, buf, len);
+ buf = writeString(header->Headline, DT_Headline, buf, len);
+ buf = writeString(header->OriginCity, DT_OriginCity, buf, len);
+
+ /* now write the header and size */
+ size1 = buf - buffer;
+ buf = writeUserInfoHeader(DT_DocumentHeaderGroup,
+ size1,
+ header_len,
+ buffer,
+ len);
+
+ return (buf);
+}
+
+/*----------------------------------------------------------------------*/
+
+char *readWAISDocumentHeader(WAISDocumentHeader **header, char *buffer)
+{
+ char *buf = buffer;
+ unsigned long size1;
+ unsigned long headerSize;
+ data_tag tag1;
+ any *docID = NULL;
+ long versionNumber, score, bestMatch, docLength, lines;
+ char **types = NULL;
+ char *source = NULL;
+ char *date = NULL;
+ char *headline = NULL;
+ char *originCity = NULL;
+
+ versionNumber = score = bestMatch = docLength = lines = UNUSED;
+
+ buf = readUserInfoHeader(&tag1, &size1, buf);
+ headerSize = buf - buffer;
+
+ while (buf < (buffer + size1 + headerSize)) {
+ data_tag tag = peekTag(buf);
+
+ switch (tag) {
+ case DT_DocumentID:
+ buf = readAny(&docID, buf);
+ break;
+ case DT_VersionNumber:
+ buf = readNum(&versionNumber, buf);
+ break;
+ case DT_Score:
+ buf = readNum(&score, buf);
+ break;
+ case DT_BestMatch:
+ buf = readNum(&bestMatch, buf);
+ break;
+ case DT_DocumentLength:
+ buf = readNum(&docLength, buf);
+ break;
+ case DT_Lines:
+ buf = readNum(&lines, buf);
+ break;
+ case DT_TYPE_BLOCK:
+ {
+ unsigned long size = -1;
+ long numTypes = 0;
+
+ buf = readTag(&tag, buf);
+ buf = readCompressedInteger(&size, buf);
+ while (size > 0) {
+ char *type = NULL;
+ char *originalBuf = buf;
+
+ buf = readString(&type, buf);
+ types = S_REALLOC2(char *, types, numTypes);
+
+ types[numTypes++] = type;
+ types[numTypes] = NULL;
+ size -= (buf - originalBuf);
+ }
+ }
+ /* FALLTHRU */
+ case DT_Source:
+ buf = readString(&source, buf);
+ break;
+ case DT_Date:
+ buf = readString(&date, buf);
+ break;
+ case DT_Headline:
+ buf = readString(&headline, buf);
+ break;
+ case DT_OriginCity:
+ buf = readString(&originCity, buf);
+ break;
+ default:
+ freeAny(docID);
+ s_free(source);
+ s_free(date);
+ s_free(headline);
+ s_free(originCity);
+ REPORT_READ_ERROR(buf);
+ break;
+ }
+ }
+
+ *header = makeWAISDocumentHeader(docID, versionNumber, score, bestMatch,
+ docLength, lines, types, source, date, headline,
+ originCity);
+ return (buf);
+}
+
+/*----------------------------------------------------------------------*/
+
+WAISDocumentShortHeader *makeWAISDocumentShortHeader(any *docID,
+ long versionNumber,
+ long score,
+ long bestMatch,
+ long docLen,
+ long lines)
+/* construct a short document header, note that no fields are copied!
+ if the application needs to save these fields, it should copy them,
+ or set the field in this object to NULL before freeing it.
+ */
+{
+ WAISDocumentShortHeader *header = S_MALLOC(WAISDocumentShortHeader);
+
+ header->DocumentID = docID;
+ header->VersionNumber = versionNumber;
+ header->Score = score;
+ header->BestMatch = bestMatch;
+ header->DocumentLength = docLen;
+ header->Lines = lines;
+
+ return (header);
+}
+
+/*----------------------------------------------------------------------*/
+
+void freeWAISDocumentShortHeader(WAISDocumentShortHeader *header)
+{
+ freeAny(header->DocumentID);
+ s_free(header);
+}
+
+/*----------------------------------------------------------------------*/
+
+char *writeWAISDocumentShortHeader(WAISDocumentShortHeader *header, char *buffer,
+ long *len)
+{
+ unsigned long header_len = userInfoTagSize(DT_DocumentShortHeaderGroup,
+ DefWAISShortHeaderSize);
+ char *buf = buffer + header_len;
+ unsigned long size;
+
+ RESERVE_SPACE_FOR_WAIS_HEADER(len);
+
+ buf = writeAny(header->DocumentID, DT_DocumentID, buf, len);
+ buf = writeNum(header->VersionNumber, DT_VersionNumber, buf, len);
+ buf = writeNum(header->Score, DT_Score, buf, len);
+ buf = writeNum(header->BestMatch, DT_BestMatch, buf, len);
+ buf = writeNum(header->DocumentLength, DT_DocumentLength, buf, len);
+ buf = writeNum(header->Lines, DT_Lines, buf, len);
+
+ /* now write the header and size */
+ size = buf - buffer;
+ buf = writeUserInfoHeader(DT_DocumentShortHeaderGroup,
+ size,
+ header_len,
+ buffer,
+ len);
+
+ return (buf);
+}
+
+/*----------------------------------------------------------------------*/
+
+char *readWAISDocumentShortHeader(WAISDocumentShortHeader **header, char *buffer)
+{
+ char *buf = buffer;
+ unsigned long size;
+ unsigned long headerSize;
+ data_tag tag1;
+ any *docID = NULL;
+ long versionNumber, score, bestMatch, docLength, lines;
+
+ versionNumber = score = bestMatch = docLength = lines = UNUSED;
+
+ buf = readUserInfoHeader(&tag1, &size, buf);
+ headerSize = buf - buffer;
+
+ while (buf < (buffer + size + headerSize)) {
+ data_tag tag = peekTag(buf);
+
+ switch (tag) {
+ case DT_DocumentID:
+ buf = readAny(&docID, buf);
+ break;
+ case DT_VersionNumber:
+ buf = readNum(&versionNumber, buf);
+ break;
+ case DT_Score:
+ buf = readNum(&score, buf);
+ break;
+ case DT_BestMatch:
+ buf = readNum(&bestMatch, buf);
+ break;
+ case DT_DocumentLength:
+ buf = readNum(&docLength, buf);
+ break;
+ case DT_Lines:
+ buf = readNum(&lines, buf);
+ break;
+ default:
+ freeAny(docID);
+ REPORT_READ_ERROR(buf);
+ break;
+ }
+ }
+
+ *header = makeWAISDocumentShortHeader(docID, versionNumber, score, bestMatch,
+ docLength, lines);
+ return (buf);
+}
+
+/*----------------------------------------------------------------------*/
+
+WAISDocumentLongHeader *makeWAISDocumentLongHeader(any *docID,
+ long versionNumber,
+ long score,
+ long bestMatch,
+ long docLen,
+ long lines,
+ char **types,
+ char *source,
+ char *date,
+ char *headline,
+ char *originCity,
+ char *stockCodes,
+ char *companyCodes,
+ char *industryCodes)
+/* construct a long document header, note that no fields are copied!
+ if the application needs to save these fields, it should copy them,
+ or set the field in this object to NULL before freeing it.
+ */
+{
+ WAISDocumentLongHeader *header = S_MALLOC(WAISDocumentLongHeader);
+
+ header->DocumentID = docID;
+ header->VersionNumber = versionNumber;
+ header->Score = score;
+ header->BestMatch = bestMatch;
+ header->DocumentLength = docLen;
+ header->Lines = lines;
+ header->Types = types;
+ header->Source = source;
+ header->Date = date;
+ header->Headline = headline;
+ header->OriginCity = originCity;
+ header->StockCodes = stockCodes;
+ header->CompanyCodes = companyCodes;
+ header->IndustryCodes = industryCodes;
+
+ return (header);
+}
+
+/*----------------------------------------------------------------------*/
+
+void freeWAISDocumentLongHeader(WAISDocumentLongHeader *header)
+{
+ freeAny(header->DocumentID);
+ doList((void **) header->Types, fs_free); /* can't use the macro here! */
+ s_free(header->Source);
+ s_free(header->Date);
+ s_free(header->Headline);
+ s_free(header->OriginCity);
+ s_free(header->StockCodes);
+ s_free(header->CompanyCodes);
+ s_free(header->IndustryCodes);
+ s_free(header);
+}
+
+/*----------------------------------------------------------------------*/
+
+char *writeWAISDocumentLongHeader(WAISDocumentLongHeader *header, char *buffer,
+ long *len)
+{
+ unsigned long header_len = userInfoTagSize(DT_DocumentLongHeaderGroup,
+ DefWAISLongHeaderSize);
+ char *buf = buffer + header_len;
+ unsigned long size1;
+
+ RESERVE_SPACE_FOR_WAIS_HEADER(len);
+
+ buf = writeAny(header->DocumentID, DT_DocumentID, buf, len);
+ buf = writeNum(header->VersionNumber, DT_VersionNumber, buf, len);
+ buf = writeNum(header->Score, DT_Score, buf, len);
+ buf = writeNum(header->BestMatch, DT_BestMatch, buf, len);
+ buf = writeNum(header->DocumentLength, DT_DocumentLength, buf, len);
+ buf = writeNum(header->Lines, DT_Lines, buf, len);
+ if (header->Types != NULL) {
+ long size;
+ char *ptr = NULL;
+ long i;
+
+ buf = writeTag(DT_TYPE_BLOCK, buf, len);
+ for (i = 0, size = 0, ptr = header->Types[i];
+ ptr != NULL;
+ ptr = header->Types[++i]) {
+ long typeSize = strlen(ptr);
+
+ size += writtenTagSize(DT_TYPE);
+ size += writtenCompressedIntSize(typeSize);
+ size += typeSize;
+ }
+ buf = writeCompressedInteger((unsigned long) size, buf, len);
+ for (i = 0, ptr = header->Types[i]; ptr != NULL; ptr = header->Types[++i])
+ buf = writeString(ptr, DT_TYPE, buf, len);
+ }
+ buf = writeString(header->Source, DT_Source, buf, len);
+ buf = writeString(header->Date, DT_Date, buf, len);
+ buf = writeString(header->Headline, DT_Headline, buf, len);
+ buf = writeString(header->OriginCity, DT_OriginCity, buf, len);
+ buf = writeString(header->StockCodes, DT_StockCodes, buf, len);
+ buf = writeString(header->CompanyCodes, DT_CompanyCodes, buf, len);
+ buf = writeString(header->IndustryCodes, DT_IndustryCodes, buf, len);
+
+ /* now write the header and size */
+ size1 = buf - buffer;
+ buf = writeUserInfoHeader(DT_DocumentLongHeaderGroup,
+ size1,
+ header_len,
+ buffer,
+ len);
+
+ return (buf);
+}
+
+/*----------------------------------------------------------------------*/
+
+char *readWAISDocumentLongHeader(WAISDocumentLongHeader **header, char *buffer)
+{
+ char *buf = buffer;
+ unsigned long size1;
+ unsigned long headerSize;
+ data_tag tag1;
+ any *docID;
+ long versionNumber, score, bestMatch, docLength, lines;
+ char **types;
+ char *source, *date, *headline, *originCity, *stockCodes, *companyCodes, *industryCodes;
+
+ docID = NULL;
+ versionNumber =
+ score =
+ bestMatch =
+ docLength =
+ lines = UNUSED;
+ types = NULL;
+ source =
+ date =
+ headline =
+ originCity =
+ stockCodes =
+ companyCodes =
+ industryCodes = NULL;
+
+ buf = readUserInfoHeader(&tag1, &size1, buf);
+ headerSize = buf - buffer;
+
+ while (buf < (buffer + size1 + headerSize)) {
+ data_tag tag = peekTag(buf);
+
+ switch (tag) {
+ case DT_DocumentID:
+ buf = readAny(&docID, buf);
+ break;
+ case DT_VersionNumber:
+ buf = readNum(&versionNumber, buf);
+ break;
+ case DT_Score:
+ buf = readNum(&score, buf);
+ break;
+ case DT_BestMatch:
+ buf = readNum(&bestMatch, buf);
+ break;
+ case DT_DocumentLength:
+ buf = readNum(&docLength, buf);
+ break;
+ case DT_Lines:
+ buf = readNum(&lines, buf);
+ break;
+ case DT_TYPE_BLOCK:
+ {
+ unsigned long size = -1;
+ long numTypes = 0;
+
+ buf = readTag(&tag, buf);
+ readCompressedInteger(&size, buf);
+ while (size > 0) {
+ char *type = NULL;
+ char *originalBuf = buf;
+
+ buf = readString(&type, buf);
+ types = S_REALLOC2(char *, types, numTypes);
+
+ types[numTypes++] = type;
+ types[numTypes] = NULL;
+ size -= (buf - originalBuf);
+ }
+ }
+ /* FALLTHRU */
+ case DT_Source:
+ buf = readString(&source, buf);
+ break;
+ case DT_Date:
+ buf = readString(&date, buf);
+ break;
+ case DT_Headline:
+ buf = readString(&headline, buf);
+ break;
+ case DT_OriginCity:
+ buf = readString(&originCity, buf);
+ break;
+ case DT_StockCodes:
+ buf = readString(&stockCodes, buf);
+ break;
+ case DT_CompanyCodes:
+ buf = readString(&companyCodes, buf);
+ break;
+ case DT_IndustryCodes:
+ buf = readString(&industryCodes, buf);
+ break;
+ default:
+ freeAny(docID);
+ s_free(source);
+ s_free(date);
+ s_free(headline);
+ s_free(originCity);
+ s_free(stockCodes);
+ s_free(companyCodes);
+ s_free(industryCodes);
+ REPORT_READ_ERROR(buf);
+ break;
+ }
+ }
+
+ *header = makeWAISDocumentLongHeader(docID,
+ versionNumber,
+ score,
+ bestMatch,
+ docLength,
+ lines,
+ types,
+ source,
+ date,
+ headline,
+ originCity,
+ stockCodes,
+ companyCodes,
+ industryCodes);
+ return (buf);
+}
+
+/*----------------------------------------------------------------------*/
+
+WAISSearchResponse *
+ makeWAISSearchResponse(
+ char *seedWordsUsed,
+ WAISDocumentHeader **docHeaders,
+ WAISDocumentShortHeader **shortHeaders,
+ WAISDocumentLongHeader **longHeaders,
+ WAISDocumentText **text,
+ WAISDocumentHeadlines **headlines,
+ WAISDocumentCodes **codes,
+ diagnosticRecord ** diagnostics)
+{
+ WAISSearchResponse *response = S_MALLOC(WAISSearchResponse);
+
+ response->SeedWordsUsed = seedWordsUsed;
+ response->DocHeaders = docHeaders;
+ response->ShortHeaders = shortHeaders;
+ response->LongHeaders = longHeaders;
+ response->Text = text;
+ response->Headlines = headlines;
+ response->Codes = codes;
+ response->Diagnostics = diagnostics;
+
+ return (response);
+}
+
+/*----------------------------------------------------------------------*/
+
+void freeWAISSearchResponse(WAISSearchResponse * response)
+{
+ void *ptr = NULL;
+ long i;
+
+ s_free(response->SeedWordsUsed);
+
+ if (response->DocHeaders != NULL)
+ for (i = 0, ptr = (void *) response->DocHeaders[i];
+ ptr != NULL;
+ ptr = (void *) response->DocHeaders[++i])
+ freeWAISDocumentHeader((WAISDocumentHeader *) ptr);
+ s_free(response->DocHeaders);
+
+ if (response->ShortHeaders != NULL)
+ for (i = 0, ptr = (void *) response->ShortHeaders[i];
+ ptr != NULL;
+ ptr = (void *) response->ShortHeaders[++i])
+ freeWAISDocumentShortHeader((WAISDocumentShortHeader *) ptr);
+ s_free(response->ShortHeaders);
+
+ if (response->LongHeaders != NULL)
+ for (i = 0, ptr = (void *) response->LongHeaders[i];
+ ptr != NULL;
+ ptr = (void *) response->LongHeaders[++i])
+ freeWAISDocumentLongHeader((WAISDocumentLongHeader *) ptr);
+ s_free(response->LongHeaders);
+
+ if (response->Text != NULL)
+ for (i = 0, ptr = (void *) response->Text[i];
+ ptr != NULL;
+ ptr = (void *) response->Text[++i])
+ freeWAISDocumentText((WAISDocumentText *) ptr);
+ s_free(response->Text);
+
+ if (response->Headlines != NULL)
+ for (i = 0, ptr = (void *) response->Headlines[i];
+ ptr != NULL;
+ ptr = (void *) response->Headlines[++i])
+ freeWAISDocumentHeadlines((WAISDocumentHeadlines *) ptr);
+ s_free(response->Headlines);
+
+ if (response->Codes != NULL)
+ for (i = 0, ptr = (void *) response->Codes[i];
+ ptr != NULL;
+ ptr = (void *) response->Codes[++i])
+ freeWAISDocumentCodes((WAISDocumentCodes *) ptr);
+ s_free(response->Codes);
+
+ if (response->Diagnostics != NULL)
+ for (i = 0, ptr = (void *) response->Diagnostics[i];
+ ptr != NULL;
+ ptr = (void *) response->Diagnostics[++i])
+ freeDiag((diagnosticRecord *) ptr);
+ s_free(response->Diagnostics);
+
+ s_free(response);
+}
+
+/*----------------------------------------------------------------------*/
+
+char *writeSearchResponseInfo(SearchResponseAPDU *query,
+ char *buffer,
+ long *len)
+{
+ unsigned long header_len = userInfoTagSize(DT_UserInformationLength,
+ DefWAISSearchResponseSize);
+ char *buf = buffer + header_len;
+ WAISSearchResponse *info = (WAISSearchResponse *) query->DatabaseDiagnosticRecords;
+ unsigned long size;
+ void *header = NULL;
+ long i;
+
+ RESERVE_SPACE_FOR_WAIS_HEADER(len);
+
+ buf = writeString(info->SeedWordsUsed, DT_SeedWordsUsed, buf, len);
+
+ /* write out all the headers */
+ if (info->DocHeaders != NULL) {
+ for (i = 0, header = (void *) info->DocHeaders[i];
+ header != NULL;
+ header = (void *) info->DocHeaders[++i])
+ buf = writeWAISDocumentHeader((WAISDocumentHeader *) header, buf, len);
+ }
+
+ if (info->ShortHeaders != NULL) {
+ for (i = 0, header = (void *) info->ShortHeaders[i];
+ header != NULL;
+ header = (void *) info->ShortHeaders[++i])
+ buf = writeWAISDocumentShortHeader((WAISDocumentShortHeader *) header,
+ buf,
+ len);
+ }
+
+ if (info->LongHeaders != NULL) {
+ for (i = 0, header = (void *) info->LongHeaders[i];
+ header != NULL;
+ header = (void *) info->LongHeaders[++i])
+ buf = writeWAISDocumentLongHeader((WAISDocumentLongHeader *) header,
+ buf,
+ len);
+ }
+
+ if (info->Text != NULL) {
+ for (i = 0, header = (void *) info->Text[i];
+ header != NULL;
+ header = (void *) info->Text[++i])
+ buf = writeWAISDocumentText((WAISDocumentText *) header, buf, len);
+ }
+
+ if (info->Headlines != NULL) {
+ for (i = 0, header = (void *) info->Headlines[i];
+ header != NULL;
+ header = (void *) info->Headlines[++i])
+ buf = writeWAISDocumentHeadlines((WAISDocumentHeadlines *) header,
+ buf,
+ len);
+ }
+
+ if (info->Codes != NULL) {
+ for (i = 0, header = (void *) info->Codes[i];
+ header != NULL;
+ header = (void *) info->Codes[++i])
+ buf = writeWAISDocumentCodes((WAISDocumentCodes *) header, buf, len);
+ }
+
+ if (info->Diagnostics != NULL) {
+ for (i = 0, header = (void *) info->Diagnostics[i];
+ header != NULL;
+ header = (void *) info->Diagnostics[++i])
+ buf = writeDiag((diagnosticRecord *) header, buf, len);
+ }
+
+ /* now write the header and size */
+ size = buf - buffer;
+ buf = writeUserInfoHeader(DT_UserInformationLength,
+ size,
+ header_len,
+ buffer,
+ len);
+
+ return (buf);
+}
+
+/*----------------------------------------------------------------------*/
+
+static void cleanUpWaisSearchResponse(char *buf,
+ char *seedWordsUsed,
+ WAISDocumentHeader **docHeaders,
+ WAISDocumentShortHeader **shortHeaders,
+ WAISDocumentLongHeader **longHeaders,
+ WAISDocumentText **text,
+ WAISDocumentHeadlines **headlines,
+ WAISDocumentCodes **codes,
+ diagnosticRecord ** diags)
+/* if buf is NULL, we have just gotten a read error, and need to clean up
+ any state we have built. If not, then everything is going fine, and
+ we should just hang loose
+ */
+{
+ void *ptr = NULL;
+ long i;
+
+ if (buf == NULL) {
+ s_free(seedWordsUsed);
+ if (docHeaders != NULL)
+ for (i = 0, ptr = (void *) docHeaders[i]; ptr != NULL;
+ ptr = (void *) docHeaders[++i])
+ freeWAISDocumentHeader((WAISDocumentHeader *) ptr);
+ s_free(docHeaders);
+ if (shortHeaders != NULL)
+ for (i = 0, ptr = (void *) shortHeaders[i]; ptr != NULL;
+ ptr = (void *) shortHeaders[++i])
+ freeWAISDocumentShortHeader((WAISDocumentShortHeader *) ptr);
+ s_free(shortHeaders);
+ if (longHeaders != NULL)
+ for (i = 0, ptr = (void *) longHeaders[i]; ptr != NULL;
+ ptr = (void *) longHeaders[++i])
+ freeWAISDocumentLongHeader((WAISDocumentLongHeader *) ptr);
+ s_free(longHeaders);
+ if (text != NULL)
+ for (i = 0, ptr = (void *) text[i];
+ ptr != NULL;
+ ptr = (void *) text[++i])
+ freeWAISDocumentText((WAISDocumentText *) ptr);
+ s_free(text);
+ if (headlines != NULL)
+ for (i = 0, ptr = (void *) headlines[i]; ptr != NULL;
+ ptr = (void *) headlines[++i])
+ freeWAISDocumentHeadlines((WAISDocumentHeadlines *) ptr);
+ s_free(headlines);
+ if (codes != NULL)
+ for (i = 0, ptr = (void *) codes[i]; ptr != NULL;
+ ptr = (void *) codes[++i])
+ freeWAISDocumentCodes((WAISDocumentCodes *) ptr);
+ s_free(codes);
+ if (diags != NULL)
+ for (i = 0, ptr = (void *) diags[i]; ptr != NULL;
+ ptr = (void *) diags[++i])
+ freeDiag((diagnosticRecord *) ptr);
+ s_free(diags);
+ }
+}
+
+/*----------------------------------------------------------------------*/
+
+char *readSearchResponseInfo(void **info,
+ char *buffer)
+{
+ char *buf = buffer;
+ unsigned long size;
+ unsigned long headerSize;
+ data_tag tag1;
+ void *header = NULL;
+ WAISDocumentHeader **docHeaders = NULL;
+ WAISDocumentShortHeader **shortHeaders = NULL;
+ WAISDocumentLongHeader **longHeaders = NULL;
+ WAISDocumentText **text = NULL;
+ WAISDocumentHeadlines **headlines = NULL;
+ WAISDocumentCodes **codes = NULL;
+ long numDocHeaders, numLongHeaders, numShortHeaders, numText, numHeadlines;
+ long numCodes;
+ char *seedWordsUsed = NULL;
+ diagnosticRecord **diags = NULL;
+ diagnosticRecord *diag = NULL;
+ long numDiags = 0;
+
+ numDocHeaders =
+ numLongHeaders =
+ numShortHeaders =
+ numText =
+ numHeadlines =
+ numCodes = 0;
+
+ buf = readUserInfoHeader(&tag1, &size, buf);
+ headerSize = buf - buffer;
+
+ while (buf < (buffer + size + headerSize)) {
+ data_tag tag = peekTag(buf);
+
+ switch (tag) {
+ case DT_SeedWordsUsed:
+ buf = readString(&seedWordsUsed, buf);
+ break;
+ case DT_DatabaseDiagnosticRecords:
+ if (diags == NULL) /* create a new diag list */
+ {
+ diags = S_MALLOC2(diagnosticRecord *);
+ } else { /* grow the diag list */
+ diags = S_REALLOC2(diagnosticRecord *, diags, numDiags);
+ }
+ buf = readDiag(&diag, buf);
+ diags[numDiags++] = diag; /* put it in the list */
+ diags[numDiags] = NULL;
+ break;
+ case DT_DocumentHeaderGroup:
+ if (docHeaders == NULL) /* create a new header list */
+ {
+ docHeaders = S_MALLOC2(WAISDocumentHeader *);
+ } else { /* grow the doc list */
+ docHeaders = S_REALLOC2(WAISDocumentHeader *, docHeaders, numDocHeaders);
+ }
+ buf = readWAISDocumentHeader((WAISDocumentHeader **) &header, buf);
+ cleanUpWaisSearchResponse(buf,
+ seedWordsUsed,
+ docHeaders,
+ shortHeaders,
+ longHeaders,
+ text,
+ headlines,
+ codes,
+ diags);
+ RETURN_ON_NULL(buf);
+ docHeaders[numDocHeaders++] =
+ (WAISDocumentHeader *) header; /* put it in the list */
+ docHeaders[numDocHeaders] = NULL;
+ break;
+ case DT_DocumentShortHeaderGroup:
+ if (shortHeaders == NULL) /* create a new header list */
+ {
+ shortHeaders = S_MALLOC2(WAISDocumentShortHeader *);
+ } else { /* grow the doc list */
+ shortHeaders = S_REALLOC2(WAISDocumentShortHeader *,
+ shortHeaders,
+ numShortHeaders);
+ }
+ buf = readWAISDocumentShortHeader((WAISDocumentShortHeader **) &header,
+ buf);
+ cleanUpWaisSearchResponse(buf,
+ seedWordsUsed,
+ docHeaders,
+ shortHeaders,
+ longHeaders,
+ text,
+ headlines,
+ codes,
+ diags);
+ RETURN_ON_NULL(buf);
+ shortHeaders[numShortHeaders++] =
+ (WAISDocumentShortHeader *) header; /* put it in the list */
+ shortHeaders[numShortHeaders] = NULL;
+ break;
+ case DT_DocumentLongHeaderGroup:
+ if (longHeaders == NULL) /* create a new header list */
+ {
+ longHeaders = S_MALLOC2(WAISDocumentLongHeader *);
+ } else { /* grow the doc list */
+ longHeaders = S_REALLOC2(WAISDocumentLongHeader *,
+ longHeaders,
+ numLongHeaders);
+ }
+ buf = readWAISDocumentLongHeader((WAISDocumentLongHeader **) &header,
+ buf);
+ cleanUpWaisSearchResponse(buf,
+ seedWordsUsed,
+ docHeaders,
+ shortHeaders,
+ longHeaders,
+ text,
+ headlines,
+ codes,
+ diags);
+ RETURN_ON_NULL(buf);
+ longHeaders[numLongHeaders++] =
+ (WAISDocumentLongHeader *) header; /* put it in the list */
+ longHeaders[numLongHeaders] = NULL;
+ break;
+ case DT_DocumentTextGroup:
+ if (text == NULL) /* create a new list */
+ {
+ text = S_MALLOC2(WAISDocumentText *);
+ } else { /* grow the list */
+ text = S_REALLOC2(WAISDocumentText *, text, numText);
+ }
+ buf = readWAISDocumentText((WAISDocumentText **) &header, buf);
+ cleanUpWaisSearchResponse(buf,
+ seedWordsUsed,
+ docHeaders,
+ shortHeaders,
+ longHeaders,
+ text,
+ headlines,
+ codes,
+ diags);
+ RETURN_ON_NULL(buf);
+ text[numText++] =
+ (WAISDocumentText *) header; /* put it in the list */
+ text[numText] = NULL;
+ break;
+ case DT_DocumentHeadlineGroup:
+ if (headlines == NULL) /* create a new list */
+ {
+ headlines = S_MALLOC2(WAISDocumentHeadlines *);
+ } else { /* grow the list */
+ headlines = S_REALLOC2(WAISDocumentHeadlines *, headlines, numHeadlines);
+ }
+ buf = readWAISDocumentHeadlines((WAISDocumentHeadlines **) &header,
+ buf);
+ cleanUpWaisSearchResponse(buf,
+ seedWordsUsed,
+ docHeaders,
+ shortHeaders,
+ longHeaders,
+ text,
+ headlines,
+ codes,
+ diags);
+ RETURN_ON_NULL(buf);
+ headlines[numHeadlines++] =
+ (WAISDocumentHeadlines *) header; /* put it in the list */
+ headlines[numHeadlines] = NULL;
+ break;
+ case DT_DocumentCodeGroup:
+ if (codes == NULL) /* create a new list */
+ {
+ codes = S_MALLOC2(WAISDocumentCodes *);
+ } else { /* grow the list */
+ codes = S_REALLOC2(WAISDocumentCodes *, codes, numCodes);
+ }
+ buf = readWAISDocumentCodes((WAISDocumentCodes **) &header, buf);
+ cleanUpWaisSearchResponse(buf,
+ seedWordsUsed,
+ docHeaders,
+ shortHeaders,
+ longHeaders,
+ text,
+ headlines,
+ codes,
+ diags);
+ RETURN_ON_NULL(buf);
+ codes[numCodes++] =
+ (WAISDocumentCodes *) header; /* put it in the list */
+ codes[numCodes] = NULL;
+ break;
+ default:
+ cleanUpWaisSearchResponse(buf,
+ seedWordsUsed,
+ docHeaders,
+ shortHeaders,
+ longHeaders,
+ text,
+ headlines,
+ codes,
+ diags);
+ REPORT_READ_ERROR(buf);
+ break;
+ }
+ }
+
+ *info = (void *) makeWAISSearchResponse(seedWordsUsed,
+ docHeaders,
+ shortHeaders,
+ longHeaders,
+ text,
+ headlines,
+ codes,
+ diags);
+
+ return (buf);
+}
+
+/*----------------------------------------------------------------------*/
+
+WAISDocumentText *makeWAISDocumentText(any *docID,
+ long versionNumber,
+ any *documentText)
+{
+ WAISDocumentText *docText = S_MALLOC(WAISDocumentText);
+
+ docText->DocumentID = docID;
+ docText->VersionNumber = versionNumber;
+ docText->DocumentText = documentText;
+
+ return (docText);
+}
+
+/*----------------------------------------------------------------------*/
+
+void freeWAISDocumentText(WAISDocumentText *docText)
+{
+ freeAny(docText->DocumentID);
+ freeAny(docText->DocumentText);
+ s_free(docText);
+}
+
+/*----------------------------------------------------------------------*/
+
+char *writeWAISDocumentText(WAISDocumentText *docText, char *buffer,
+ long *len)
+{
+ unsigned long header_len = userInfoTagSize(DT_DocumentTextGroup,
+ DefWAISDocTextSize);
+ char *buf = buffer + header_len;
+ unsigned long size;
+
+ RESERVE_SPACE_FOR_WAIS_HEADER(len);
+
+ buf = writeAny(docText->DocumentID, DT_DocumentID, buf, len);
+ buf = writeNum(docText->VersionNumber, DT_VersionNumber, buf, len);
+ buf = writeAny(docText->DocumentText, DT_DocumentText, buf, len);
+
+ /* now write the header and size */
+ size = buf - buffer;
+ buf = writeUserInfoHeader(DT_DocumentTextGroup, size, header_len, buffer, len);
+
+ return (buf);
+}
+
+/*----------------------------------------------------------------------*/
+
+char *readWAISDocumentText(WAISDocumentText **docText, char *buffer)
+{
+ char *buf = buffer;
+ unsigned long size;
+ unsigned long headerSize;
+ data_tag tag1;
+ any *docID, *documentText;
+ long versionNumber;
+
+ docID = documentText = NULL;
+ versionNumber = UNUSED;
+
+ buf = readUserInfoHeader(&tag1, &size, buf);
+ headerSize = buf - buffer;
+
+ while (buf < (buffer + size + headerSize)) {
+ data_tag tag = peekTag(buf);
+
+ switch (tag) {
+ case DT_DocumentID:
+ buf = readAny(&docID, buf);
+ break;
+ case DT_VersionNumber:
+ buf = readNum(&versionNumber, buf);
+ break;
+ case DT_DocumentText:
+ buf = readAny(&documentText, buf);
+ break;
+ default:
+ freeAny(docID);
+ freeAny(documentText);
+ REPORT_READ_ERROR(buf);
+ break;
+ }
+ }
+
+ *docText = makeWAISDocumentText(docID, versionNumber, documentText);
+ return (buf);
+}
+
+/*----------------------------------------------------------------------*/
+
+WAISDocumentHeadlines *makeWAISDocumentHeadlines(any *docID,
+ long versionNumber,
+ char *source,
+ char *date,
+ char *headline,
+ char *originCity)
+{
+ WAISDocumentHeadlines *docHeadline = S_MALLOC(WAISDocumentHeadlines);
+
+ docHeadline->DocumentID = docID;
+ docHeadline->VersionNumber = versionNumber;
+ docHeadline->Source = source;
+ docHeadline->Date = date;
+ docHeadline->Headline = headline;
+ docHeadline->OriginCity = originCity;
+
+ return (docHeadline);
+}
+
+/*----------------------------------------------------------------------*/
+
+void freeWAISDocumentHeadlines(WAISDocumentHeadlines *docHeadline)
+{
+ freeAny(docHeadline->DocumentID);
+ s_free(docHeadline->Source);
+ s_free(docHeadline->Date);
+ s_free(docHeadline->Headline);
+ s_free(docHeadline->OriginCity);
+ s_free(docHeadline);
+}
+
+/*----------------------------------------------------------------------*/
+
+char *writeWAISDocumentHeadlines(WAISDocumentHeadlines *docHeadline, char *buffer,
+ long *len)
+{
+ unsigned long header_len = userInfoTagSize(DT_DocumentHeadlineGroup,
+ DefWAISDocHeadlineSize);
+ char *buf = buffer + header_len;
+ unsigned long size;
+
+ RESERVE_SPACE_FOR_WAIS_HEADER(len);
+
+ buf = writeAny(docHeadline->DocumentID, DT_DocumentID, buf, len);
+ buf = writeNum(docHeadline->VersionNumber, DT_VersionNumber, buf, len);
+ buf = writeString(docHeadline->Source, DT_Source, buf, len);
+ buf = writeString(docHeadline->Date, DT_Date, buf, len);
+ buf = writeString(docHeadline->Headline, DT_Headline, buf, len);
+ buf = writeString(docHeadline->OriginCity, DT_OriginCity, buf, len);
+
+ /* now write the header and size */
+ size = buf - buffer;
+ buf = writeUserInfoHeader(DT_DocumentHeadlineGroup,
+ size,
+ header_len,
+ buffer,
+ len);
+
+ return (buf);
+}
+
+/*----------------------------------------------------------------------*/
+
+char *readWAISDocumentHeadlines(WAISDocumentHeadlines **docHeadline, char *buffer)
+{
+ char *buf = buffer;
+ unsigned long size;
+ unsigned long headerSize;
+ data_tag tag1;
+ any *docID;
+ long versionNumber;
+ char *source, *date, *headline, *originCity;
+
+ docID = NULL;
+ versionNumber = UNUSED;
+ source = date = headline = originCity = NULL;
+
+ buf = readUserInfoHeader(&tag1, &size, buf);
+ headerSize = buf - buffer;
+
+ while (buf < (buffer + size + headerSize)) {
+ data_tag tag = peekTag(buf);
+
+ switch (tag) {
+ case DT_DocumentID:
+ buf = readAny(&docID, buf);
+ break;
+ case DT_VersionNumber:
+ buf = readNum(&versionNumber, buf);
+ break;
+ case DT_Source:
+ buf = readString(&source, buf);
+ break;
+ case DT_Date:
+ buf = readString(&date, buf);
+ break;
+ case DT_Headline:
+ buf = readString(&headline, buf);
+ break;
+ case DT_OriginCity:
+ buf = readString(&originCity, buf);
+ break;
+ default:
+ freeAny(docID);
+ s_free(source);
+ s_free(date);
+ s_free(headline);
+ s_free(originCity);
+ REPORT_READ_ERROR(buf);
+ break;
+ }
+ }
+
+ *docHeadline = makeWAISDocumentHeadlines(docID, versionNumber, source, date,
+ headline, originCity);
+ return (buf);
+}
+
+/*----------------------------------------------------------------------*/
+
+WAISDocumentCodes *makeWAISDocumentCodes(any *docID,
+ long versionNumber,
+ char *stockCodes,
+ char *companyCodes,
+ char *industryCodes)
+{
+ WAISDocumentCodes *docCodes = S_MALLOC(WAISDocumentCodes);
+
+ docCodes->DocumentID = docID;
+ docCodes->VersionNumber = versionNumber;
+ docCodes->StockCodes = stockCodes;
+ docCodes->CompanyCodes = companyCodes;
+ docCodes->IndustryCodes = industryCodes;
+
+ return (docCodes);
+}
+
+/*----------------------------------------------------------------------*/
+
+void freeWAISDocumentCodes(WAISDocumentCodes *docCodes)
+{
+ freeAny(docCodes->DocumentID);
+ s_free(docCodes->StockCodes);
+ s_free(docCodes->CompanyCodes);
+ s_free(docCodes->IndustryCodes);
+ s_free(docCodes);
+}
+
+/*----------------------------------------------------------------------*/
+
+char *writeWAISDocumentCodes(WAISDocumentCodes *docCodes,
+ char *buffer,
+ long *len)
+{
+ unsigned long header_len = userInfoTagSize(DT_DocumentCodeGroup,
+ DefWAISDocCodeSize);
+ char *buf = buffer + header_len;
+ unsigned long size;
+
+ RESERVE_SPACE_FOR_WAIS_HEADER(len);
+
+ buf = writeAny(docCodes->DocumentID, DT_DocumentID, buf, len);
+ buf = writeNum(docCodes->VersionNumber, DT_VersionNumber, buf, len);
+ buf = writeString(docCodes->StockCodes, DT_StockCodes, buf, len);
+ buf = writeString(docCodes->CompanyCodes, DT_CompanyCodes, buf, len);
+ buf = writeString(docCodes->IndustryCodes, DT_IndustryCodes, buf, len);
+
+ /* now write the header and size */
+ size = buf - buffer;
+ buf = writeUserInfoHeader(DT_DocumentCodeGroup, size, header_len, buffer, len);
+
+ return (buf);
+}
+
+/*----------------------------------------------------------------------*/
+
+char *readWAISDocumentCodes(WAISDocumentCodes **docCodes,
+ char *buffer)
+{
+ char *buf = buffer;
+ unsigned long size;
+ unsigned long headerSize;
+ data_tag tag1;
+ any *docID;
+ long versionNumber;
+ char *stockCodes, *companyCodes, *industryCodes;
+
+ docID = NULL;
+ versionNumber = UNUSED;
+ stockCodes = companyCodes = industryCodes = NULL;
+
+ buf = readUserInfoHeader(&tag1, &size, buf);
+ headerSize = buf - buffer;
+
+ while (buf < (buffer + size + headerSize)) {
+ data_tag tag = peekTag(buf);
+
+ switch (tag) {
+ case DT_DocumentID:
+ buf = readAny(&docID, buf);
+ break;
+ case DT_VersionNumber:
+ buf = readNum(&versionNumber, buf);
+ break;
+ case DT_StockCodes:
+ buf = readString(&stockCodes, buf);
+ break;
+ case DT_CompanyCodes:
+ buf = readString(&companyCodes, buf);
+ break;
+ case DT_IndustryCodes:
+ buf = readString(&industryCodes, buf);
+ break;
+ default:
+ freeAny(docID);
+ s_free(stockCodes);
+ s_free(companyCodes);
+ s_free(industryCodes);
+ REPORT_READ_ERROR(buf);
+ break;
+ }
+ }
+
+ *docCodes = makeWAISDocumentCodes(docID, versionNumber, stockCodes,
+ companyCodes, industryCodes);
+ return (buf);
+}
+
+/*----------------------------------------------------------------------*/
+
+char *writePresentInfo(PresentAPDU * present GCC_UNUSED, char *buffer,
+ long *len GCC_UNUSED)
+{
+ /* The WAIS protocol doesn't use present info */
+ return (buffer);
+}
+
+/*----------------------------------------------------------------------*/
+
+char *readPresentInfo(void **info,
+ char *buffer)
+{
+ /* The WAIS protocol doesn't use present info */
+ *info = NULL;
+ return (buffer);
+}
+
+/*----------------------------------------------------------------------*/
+
+char *writePresentResponseInfo(PresentResponseAPDU * response GCC_UNUSED, char *buffer,
+ long *len GCC_UNUSED)
+{
+ /* The WAIS protocol doesn't use presentResponse info */
+ return (buffer);
+}
+
+/*----------------------------------------------------------------------*/
+
+char *readPresentResponseInfo(void **info,
+ char *buffer)
+{
+ /* The WAIS protocol doesn't use presentResponse info */
+ *info = NULL;
+ return (buffer);
+}
+
+/*----------------------------------------------------------------------*/
+
+/* support for type 1 queries */
+
+/* new use values (for the chunk types) */
+#define BYTE "wb"
+#define LINE "wl"
+#define PARAGRAPH "wp"
+#define DATA_TYPE "wt"
+
+/* WAIS supports the following semantics for type 1 queries:
+
+ 1. retrieve the header/codes from a document:
+
+ System_Control_Number = docID
+ Data Type = type (optional)
+ And
+
+ 2. retrieve a fragment of the text of a document:
+
+ System_Control_Number = docID
+ Data Type = type (optional)
+ And
+ Chunk >= start
+ And
+ Chunk < end
+ And
+
+ Information from multiple documents may be requested by using
+ groups of the above joined by:
+
+ OR
+
+ ( XXX does an OR come after every group but the first, or do they
+ all come at the end? )
+
+ ( XXX return type could be in the element set)
+*/
+
+static query_term **makeWAISQueryTerms(DocObj **docs)
+/* given a null terminated list of docObjs, construct the appropriate
+ query of the form given above
+ */
+{
+ query_term **terms = NULL;
+ long numTerms = 0;
+ DocObj *doc = NULL;
+ long i;
+
+ if (docs == NULL)
+ return ((query_term **) NULL);
+
+ terms = (query_term **) s_malloc((size_t) (sizeof(query_term *) * 1));
+
+ terms[numTerms] = NULL;
+
+ /* loop through the docs making terms for them all */
+ for (i = 0, doc = docs[i]; doc != NULL; doc = docs[++i]) {
+ any *type = NULL;
+
+ if (doc->Type != NULL)
+ type = stringToAny(doc->Type);
+
+ if (doc->ChunkCode == CT_document) /* a whole document */
+ {
+ terms = S_REALLOC2(query_term *, terms, numTerms + 2);
+
+ terms[numTerms++] = makeAttributeTerm(SYSTEM_CONTROL_NUMBER,
+ EQUAL, IGNORE, IGNORE,
+ IGNORE, IGNORE, doc->DocumentID);
+ if (type != NULL) {
+ terms[numTerms++] = makeAttributeTerm(DATA_TYPE, EQUAL,
+ IGNORE, IGNORE, IGNORE,
+ IGNORE, type);
+ terms[numTerms++] = makeOperatorTerm(AND);
+ }
+ terms[numTerms] = NULL;
+ } else { /* a document fragment */
+ char chunk_att[ATTRIBUTE_SIZE];
+ any *startChunk = NULL;
+ any *endChunk = NULL;
+
+ terms = S_REALLOC2(query_term *, terms, numTerms + 6);
+
+ switch (doc->ChunkCode) {
+ case CT_byte:
+ case CT_line:
+ {
+ char start[20], end[20];
+
+ (doc->ChunkCode == CT_byte) ?
+ StrNCpy(chunk_att, BYTE, ATTRIBUTE_SIZE) :
+ StrNCpy(chunk_att, LINE, ATTRIBUTE_SIZE);
+ sprintf(start, "%ld", doc->ChunkStart.Pos);
+ startChunk = stringToAny(start);
+ sprintf(end, "%ld", doc->ChunkEnd.Pos);
+ endChunk = stringToAny(end);
+ }
+ break;
+ case CT_paragraph:
+ StrNCpy(chunk_att, PARAGRAPH, ATTRIBUTE_SIZE);
+ startChunk = doc->ChunkStart.ID;
+ endChunk = doc->ChunkEnd.ID;
+ break;
+ default:
+ /* error */
+ break;
+ }
+
+ terms[numTerms++] = makeAttributeTerm(SYSTEM_CONTROL_NUMBER,
+ EQUAL, IGNORE, IGNORE,
+ IGNORE,
+ IGNORE, doc->DocumentID);
+ if (type != NULL) {
+ terms[numTerms++] = makeAttributeTerm(DATA_TYPE, EQUAL, IGNORE,
+ IGNORE, IGNORE, IGNORE,
+ type);
+ terms[numTerms++] = makeOperatorTerm(AND);
+ }
+ terms[numTerms++] = makeAttributeTerm(chunk_att,
+ GREATER_THAN_OR_EQUAL,
+ IGNORE, IGNORE, IGNORE,
+ IGNORE,
+ startChunk);
+ terms[numTerms++] = makeOperatorTerm(AND);
+ terms[numTerms++] = makeAttributeTerm(chunk_att, LESS_THAN,
+ IGNORE, IGNORE, IGNORE,
+ IGNORE,
+ endChunk);
+ terms[numTerms++] = makeOperatorTerm(AND);
+ terms[numTerms] = NULL;
+
+ if (doc->ChunkCode == CT_byte || doc->ChunkCode == CT_line) {
+ freeAny(startChunk);
+ freeAny(endChunk);
+ }
+ }
+
+ freeAny(type);
+
+ if (i != 0) /* multiple independent queries, need a disjunction */
+ {
+ terms = S_REALLOC2(query_term *, terms, numTerms);
+
+ terms[numTerms++] = makeOperatorTerm(OR);
+ terms[numTerms] = NULL;
+ }
+ }
+
+ return (terms);
+}
+
+/*----------------------------------------------------------------------*/
+
+static DocObj **makeWAISQueryDocs(query_term **terms)
+/* given a list of terms in the form given above, convert them to
+ DocObjs.
+ */
+{
+ query_term *docTerm = NULL;
+ query_term *fragmentTerm = NULL;
+ DocObj **docs = NULL;
+ DocObj *doc = NULL;
+ long docNum, termNum;
+
+ docNum = termNum = 0;
+
+ docs = S_MALLOC(DocObj *);
+
+ docs[docNum] = NULL;
+
+ /* translate the terms into DocObjs */
+ while (true) {
+ query_term *typeTerm = NULL;
+ char *type = NULL;
+ long startTermOffset;
+
+ docTerm = terms[termNum];
+
+ if (docTerm == NULL)
+ break; /* we're done converting */
+
+ typeTerm = terms[termNum + 1]; /* get the lead Term if it exists */
+
+ if (strcmp(typeTerm->Use, DATA_TYPE) == 0) /* we do have a type */
+ {
+ startTermOffset = 3;
+ type = anyToString(typeTerm->Term);
+ } else { /* no type */
+ startTermOffset = 1;
+ typeTerm = NULL;
+ type = NULL;
+ }
+
+ /* grow the doc list */
+ docs = S_REALLOC2(DocObj *, docs, docNum);
+
+ /* figure out what kind of docObj to build - and build it */
+ fragmentTerm = terms[termNum + startTermOffset];
+ if (fragmentTerm != NULL && fragmentTerm->TermType == TT_Attribute) { /* build a document fragment */
+ query_term *startTerm = fragmentTerm;
+ query_term *endTerm = terms[termNum + startTermOffset + 2];
+
+ if (strcmp(startTerm->Use, BYTE) == 0) { /* a byte chunk */
+ doc = makeDocObjUsingBytes(duplicateAny(docTerm->Term),
+ type,
+ anyToLong(startTerm->Term),
+ anyToLong(endTerm->Term));
+ log_write("byte");
+ } else if (strcmp(startTerm->Use, LINE) == 0) { /* a line chunk */
+ doc = makeDocObjUsingLines(duplicateAny(docTerm->Term),
+ type,
+ anyToLong(startTerm->Term),
+ anyToLong(endTerm->Term));
+ log_write("line");
+ } else {
+ log_write("chunk"); /* a paragraph chunk */
+ doc = makeDocObjUsingParagraphs(duplicateAny(docTerm->Term),
+ type,
+ duplicateAny(startTerm->Term),
+ duplicateAny(endTerm->Term));
+ }
+ termNum += (startTermOffset + 4); /* point to next term */
+ } else { /* build a full document */
+ doc = makeDocObjUsingWholeDocument(duplicateAny(docTerm->Term),
+ type);
+ log_write("whole doc");
+ termNum += startTermOffset; /* point to next term */
+ }
+
+ docs[docNum++] = doc; /* insert the new document */
+
+ docs[docNum] = NULL; /* keep the doc list terminated */
+
+ if (terms[termNum] != NULL)
+ termNum++; /* skip the OR operator it necessary */
+ else
+ break; /* we are done */
+ }
+
+ return (docs);
+}
+
+/*----------------------------------------------------------------------*/
+
+any *makeWAISTextQuery(DocObj **docs)
+/* given a list of DocObjs, return an any whose contents is the corresponding
+ type 1 query
+ */
+{
+ any *buf = NULL;
+ query_term **terms = NULL;
+
+ terms = makeWAISQueryTerms(docs);
+ buf = writeQuery(terms);
+
+ doList((void **) terms, freeTerm);
+ s_free(terms);
+
+ return (buf);
+}
+
+/*----------------------------------------------------------------------*/
+
+DocObj **readWAISTextQuery(any *buf)
+/* given an any whose contents are type 1 queries of the WAIS sort,
+ construct a list of the corresponding DocObjs
+ */
+{
+ query_term **terms = NULL;
+ DocObj **docs = NULL;
+
+ terms = readQuery(buf);
+ docs = makeWAISQueryDocs(terms);
+
+ doList((void **) terms, freeTerm);
+ s_free(terms);
+
+ return (docs);
+}
+
+/*----------------------------------------------------------------------*/
+/* Customized free WAIS object routines: */
+/* */
+/* This set of procedures is for applications to free a WAIS object */
+/* which was made with makeWAISFOO. */
+/* Each procedure frees only the memory that was allocated in its */
+/* associated makeWAISFOO routine, thus it's not necessary for the */
+/* caller to assign nulls to the pointer fields of the WAIS object. */
+/*----------------------------------------------------------------------*/
+
+void CSTFreeWAISInitResponse(WAISInitResponse *init)
+/* free an object made with makeWAISInitResponse */
+{
+ s_free(init);
+}
+
+/*----------------------------------------------------------------------*/
+
+void CSTFreeWAISSearch(WAISSearch *query)
+/* destroy an object made with makeWAISSearch() */
+{
+ s_free(query);
+}
+
+/*----------------------------------------------------------------------*/
+
+void CSTFreeDocObj(DocObj *doc)
+/* free a docObj */
+{
+ s_free(doc);
+}
+
+/*----------------------------------------------------------------------*/
+
+void CSTFreeWAISDocumentHeader(WAISDocumentHeader *header)
+{
+ s_free(header);
+}
+
+/*----------------------------------------------------------------------*/
+
+void CSTFreeWAISDocumentShortHeader(WAISDocumentShortHeader *header)
+{
+ s_free(header);
+}
+
+/*----------------------------------------------------------------------*/
+
+void CSTFreeWAISDocumentLongHeader(WAISDocumentLongHeader *header)
+{
+ s_free(header);
+}
+
+/*----------------------------------------------------------------------*/
+
+void CSTFreeWAISSearchResponse(WAISSearchResponse * response)
+{
+ s_free(response);
+}
+
+/*----------------------------------------------------------------------*/
+
+void CSTFreeWAISDocumentText(WAISDocumentText *docText)
+{
+ s_free(docText);
+}
+
+/*----------------------------------------------------------------------*/
+
+void CSTFreeWAISDocHeadlines(WAISDocumentHeadlines *docHeadline)
+{
+ s_free(docHeadline);
+}
+
+/*----------------------------------------------------------------------*/
+
+void CSTFreeWAISDocumentCodes(WAISDocumentCodes *docCodes)
+{
+ s_free(docCodes);
+}
+
+/*----------------------------------------------------------------------*/
+
+void CSTFreeWAISTextQuery(any *query)
+{
+ freeAny(query);
+}
+
+/*----------------------------------------------------------------------*/
+
+/*
+ * Routines originally from WMessage.c -- FM
+ *
+ *----------------------------------------------------------------------*/
+/* WIDE AREA INFORMATION SERVER SOFTWARE
+ * No guarantees or restrictions. See the readme file for the full standard
+ * disclaimer.
+ * 3.26.90
+ */
+
+/* This file is for reading and writing the wais packet header.
+ * Morris@think.com
+ */
+
+/* to do:
+ * add check sum
+ * what do you do when checksum is wrong?
+ */
+
+/*---------------------------------------------------------------------*/
+
+void readWAISPacketHeader(char *msgBuffer,
+ WAISMessage * header_struct)
+{
+ /* msgBuffer is a string containing at least HEADER_LENGTH bytes. */
+
+ memmove(header_struct->msg_len, msgBuffer, (size_t) 10);
+ header_struct->msg_type = char_downcase((unsigned long) msgBuffer[10]);
+ header_struct->hdr_vers = char_downcase((unsigned long) msgBuffer[11]);
+ memmove(header_struct->server, (void *) (msgBuffer + 12), (size_t) 10);
+ header_struct->compression = char_downcase((unsigned long) msgBuffer[22]);
+ header_struct->encoding = char_downcase((unsigned long) msgBuffer[23]);
+ header_struct->msg_checksum = char_downcase((unsigned long) msgBuffer[24]);
+}
+
+/*---------------------------------------------------------------------*/
+
+/* this modifies the header argument. See wais-message.h for the different
+ * options for the arguments.
+ */
+
+void writeWAISPacketHeader(char *header,
+ long dataLen,
+ long type,
+ char *server,
+ long compression,
+ long encoding,
+ long version)
+/* Puts together the new wais before-the-z39-packet header. */
+{
+ char lengthBuf[11];
+ char serverBuf[11];
+
+ long serverLen = strlen(server);
+
+ if (serverLen > 10)
+ serverLen = 10;
+
+ sprintf(lengthBuf, "%010ld", dataLen);
+ StrNCpy(header, lengthBuf, 10);
+
+ header[10] = type & 0xFF;
+ header[11] = version & 0xFF;
+
+ StrNCpy(serverBuf, server, serverLen);
+ StrNCpy((char *) (header + 12), serverBuf, serverLen);
+
+ header[22] = compression & 0xFF;
+ header[23] = encoding & 0xFF;
+ header[24] = '0'; /* checkSum(header + HEADER_LENGTH,dataLen); XXX the result must be ascii */
+}
+
+/*---------------------------------------------------------------------*/
diff --git a/WWW/Library/Implementation/HTVMS_WaisProt.h b/WWW/Library/Implementation/HTVMS_WaisProt.h
new file mode 100644
index 0000000..cea2a32
--- /dev/null
+++ b/WWW/Library/Implementation/HTVMS_WaisProt.h
@@ -0,0 +1,425 @@
+/* HTVMS_WAISProt.h
+ *
+ * Adaptation for Lynx by F.Macrides (macrides@sci.wfeb.edu)
+ *
+ * 31-May-1994 FM Initial version.
+ *
+ *----------------------------------------------------------------------*/
+
+/*
+ * Routines originally from WProt.h -- FM
+ *
+ *----------------------------------------------------------------------*/
+/* WIDE AREA INFORMATION SERVER SOFTWARE:
+ * No guarantees or restrictions. See the readme file for the full standard
+ * disclaimer.
+ *
+ * 3.26.90 Harry Morris, morris@think.com
+ * 3.30.90 Harry Morris
+ * - removed chunk code from WAISSearchAPDU,
+ * - added makeWAISQueryType1Query() and readWAISType1Query() which
+ * replace makeWAISQueryTerms() and makeWAISQueryDocs().
+ * 4.11.90 HWM - added definitions of wais element set names
+ * 4.14.90 HWM - changed symbol for relevance feedback query from QT_3 to
+ * QT_RelevanceFeedbackQuery added QT_TextRetrievalQuery as a
+ * synonym for QT_BooleanQuery
+ * - renamed makeWAISType1Query() to makeWAISTextQuery()
+ * renamed readWAISType1Query() to readWAISTextQuery()
+ * 5.29.90 TS - added CSTFreeWAISFoo functions
+ */
+
+#ifndef _H_WAIS_protocol_
+#define _H_WAIS_protocol_
+
+#ifndef HTUTILS_H
+#include <HTUtils.h>
+#endif
+
+#include <HTVMS_WaisUI.h>
+
+/*----------------------------------------------------------------------*/
+/* Data types / constants */
+
+/* date factor constants */
+#define DF_INDEPENDENT 1
+#define DF_LATER 2
+#define DF_EARLIER 3
+#define DF_SPECIFIED_RANGE 4
+
+/* chunk types */
+#define CT_document 0
+#define CT_byte 1
+#define CT_line 2
+#define CT_paragraph 3
+
+/* relevance feedback query */
+#define QT_RelevanceFeedbackQuery "3"
+#define QT_TextRetrievalQuery QT_BooleanQuery
+
+/* new data tags */
+#define DT_UserInformationLength (data_tag)99
+#define DT_ChunkCode (data_tag)100
+#define DT_ChunkIDLength (data_tag)101
+#define DT_ChunkMarker (data_tag)102
+#define DT_HighlightMarker (data_tag)103
+#define DT_DeHighlightMarker (data_tag)104
+#define DT_NewlineCharacters (data_tag)105
+#define DT_SeedWords (data_tag)106
+#define DT_DocumentIDChunk (data_tag)107
+#define DT_ChunkStartID (data_tag)108
+#define DT_ChunkEndID (data_tag)109
+#define DT_TextList (data_tag)110
+#define DT_DateFactor (data_tag)111
+#define DT_BeginDateRange (data_tag)112
+#define DT_EndDateRange (data_tag)113
+#define DT_MaxDocumentsRetrieved (data_tag)114
+#define DT_SeedWordsUsed (data_tag)115
+#define DT_DocumentID (data_tag)116
+#define DT_VersionNumber (data_tag)117
+#define DT_Score (data_tag)118
+#define DT_BestMatch (data_tag)119
+#define DT_DocumentLength (data_tag)120
+#define DT_Source (data_tag)121
+#define DT_Date (data_tag)122
+#define DT_Headline (data_tag)123
+#define DT_OriginCity (data_tag)124
+#define DT_PresentStartByte (data_tag)125
+#define DT_TextLength (data_tag)126
+#define DT_DocumentText (data_tag)127
+#define DT_StockCodes (data_tag)128
+#define DT_CompanyCodes (data_tag)129
+#define DT_IndustryCodes (data_tag)130
+
+/* added by harry */
+#define DT_DocumentHeaderGroup (data_tag)150
+#define DT_DocumentShortHeaderGroup (data_tag)151
+#define DT_DocumentLongHeaderGroup (data_tag)152
+#define DT_DocumentTextGroup (data_tag)153
+#define DT_DocumentHeadlineGroup (data_tag)154
+#define DT_DocumentCodeGroup (data_tag)155
+#define DT_Lines (data_tag)131
+#define DT_TYPE_BLOCK (data_tag)132
+#define DT_TYPE (data_tag)133
+
+/* wais element sets */
+#define ES_DocumentHeader "Document Header"
+#define ES_DocumentShortHeader "Document Short Header"
+#define ES_DocumentLongHeader "Document Long Header"
+#define ES_DocumentText "Document Text"
+#define ES_DocumentHeadline "Document Headline"
+#define ES_DocumentCodes "Document Codes"
+
+typedef struct DocObj { /* specifies a section of a document */
+ any *DocumentID;
+ char *Type;
+ long ChunkCode;
+ union {
+ long Pos;
+ any *ID;
+ } ChunkStart;
+ union {
+ long Pos;
+ any *ID;
+ } ChunkEnd;
+} DocObj;
+
+/*----------------------------------------------------------------------*/
+/* WAIS APDU extensions */
+
+typedef struct WAISInitResponse {
+ long ChunkCode;
+ long ChunkIDLength;
+ char *ChunkMarker;
+ char *HighlightMarker;
+ char *DeHighlightMarker;
+ char *NewlineCharacters;
+ /* XXX need to add UpdateFrequency and Update Time */
+} WAISInitResponse;
+
+typedef struct WAISSearch {
+ char *SeedWords;
+ DocObj **Docs;
+ char **TextList;
+ long DateFactor;
+ char *BeginDateRange;
+ char *EndDateRange;
+ long MaxDocumentsRetrieved;
+} WAISSearch;
+
+typedef struct WAISDocumentHeader {
+ any *DocumentID;
+ long VersionNumber;
+ long Score;
+ long BestMatch;
+ long DocumentLength;
+ long Lines;
+ char **Types;
+ char *Source;
+ char *Date;
+ char *Headline;
+ char *OriginCity;
+} WAISDocumentHeader;
+
+typedef struct WAISDocumentShortHeader {
+ any *DocumentID;
+ long VersionNumber;
+ long Score;
+ long BestMatch;
+ long DocumentLength;
+ long Lines;
+} WAISDocumentShortHeader;
+
+typedef struct WAISDocumentLongHeader {
+ any *DocumentID;
+ long VersionNumber;
+ long Score;
+ long BestMatch;
+ long DocumentLength;
+ long Lines;
+ char **Types;
+ char *Source;
+ char *Date;
+ char *Headline;
+ char *OriginCity;
+ char *StockCodes;
+ char *CompanyCodes;
+ char *IndustryCodes;
+} WAISDocumentLongHeader;
+
+typedef struct WAISDocumentText {
+ any *DocumentID;
+ long VersionNumber;
+ any *DocumentText;
+} WAISDocumentText;
+
+typedef struct WAISDocumentHeadlines {
+ any *DocumentID;
+ long VersionNumber;
+ char *Source;
+ char *Date;
+ char *Headline;
+ char *OriginCity;
+} WAISDocumentHeadlines;
+
+typedef struct WAISDocumentCodes {
+ any *DocumentID;
+ long VersionNumber;
+ char *StockCodes;
+ char *CompanyCodes;
+ char *IndustryCodes;
+} WAISDocumentCodes;
+
+typedef struct WAISSearchResponse {
+ char *SeedWordsUsed;
+ WAISDocumentHeader **DocHeaders;
+ WAISDocumentShortHeader **ShortHeaders;
+ WAISDocumentLongHeader **LongHeaders;
+ WAISDocumentText **Text;
+ WAISDocumentHeadlines **Headlines;
+ WAISDocumentCodes **Codes;
+ diagnosticRecord **Diagnostics;
+} WAISSearchResponse;
+
+/*----------------------------------------------------------------------*/
+/* Functions */
+
+char *generate_search_apdu(char *buff, /* buffer to hold the apdu */
+ long *buff_len, /* number of bytes written to the buffer */
+ char *seed_words, /* string of the seed words */
+ char *database_name,
+ DocObj **docobjs,
+ long maxDocsRetrieved);
+
+DocObj *makeDocObjUsingWholeDocument(any *aDocID, char *type);
+DocObj *makeDocObjUsingBytes(any *aDocID, char *type, long start, long end);
+DocObj *makeDocObjUsingLines(any *aDocID, char *type, long start, long end);
+DocObj *makeDocObjUsingParagraphs(any *aDocID, char *type, any *start, any *end);
+void freeDocObj(DocObj *doc);
+
+WAISInitResponse *makeWAISInitResponse(long chunkCode, long chunkIDLen,
+ char *chunkMarker, char *highlightMarker,
+ char *deHighlightMarker, char *newLineChars);
+void freeWAISInitResponse(WAISInitResponse *init);
+
+WAISSearch *makeWAISSearch(char *seedWords,
+ DocObj **docs,
+ char **textList,
+ long dateFactor,
+ char *beginDateRange,
+ char *endDateRange,
+ long maxDocsRetrieved);
+void freeWAISSearch(WAISSearch *query);
+
+WAISDocumentHeader *makeWAISDocumentHeader(any *aDocID,
+ long versionNumber,
+ long score,
+ long bestMatch,
+ long docLen,
+ long lines,
+ char **types,
+ char *source,
+ char *date,
+ char *headline,
+ char *originCity);
+void freeWAISDocumentHeader(WAISDocumentHeader *header);
+char *writeWAISDocumentHeader(WAISDocumentHeader *header, char *buffer, long *len);
+char *readWAISDocumentHeader(WAISDocumentHeader **header, char *buffer);
+
+WAISDocumentShortHeader *makeWAISDocumentShortHeader(any *aDocID,
+ long versionNumber,
+ long score,
+ long bestMatch,
+ long docLen,
+ long lines);
+void freeWAISDocumentShortHeader(WAISDocumentShortHeader *header);
+char *writeWAISDocumentShortHeader(WAISDocumentShortHeader *header,
+ char *buffer, long *len);
+char *readWAISDocumentShortHeader(WAISDocumentShortHeader **header, char *buffer);
+
+WAISDocumentLongHeader *makeWAISDocumentLongHeader(any *aDocID,
+ long versionNumber,
+ long score,
+ long bestMatch,
+ long docLen,
+ long lines,
+ char **types,
+ char *source,
+ char *date, char *headline,
+ char *originCity,
+ char *stockCodes,
+ char *companyCodes,
+ char *industryCodes);
+void freeWAISDocumentLongHeader(WAISDocumentLongHeader *header);
+char *writeWAISDocumentLongHeader(WAISDocumentLongHeader *header,
+ char *buffer,
+ long *len);
+char *readWAISDocumentLongHeader(WAISDocumentLongHeader **header, char *buffer);
+
+WAISSearchResponse *makeWAISSearchResponse(char *seedWordsUsed,
+ WAISDocumentHeader **docHeaders,
+ WAISDocumentShortHeader **shortHeaders,
+ WAISDocumentLongHeader **longHeaders,
+ WAISDocumentText **text, WAISDocumentHeadlines **headlines,
+ WAISDocumentCodes **codes,
+ diagnosticRecord ** diagnostics);
+void freeWAISSearchResponse(WAISSearchResponse * response);
+
+WAISDocumentText *makeWAISDocumentText(any *aDocID, long versionNumber,
+ any *documentText);
+void freeWAISDocumentText(WAISDocumentText *docText);
+char *writeWAISDocumentText(WAISDocumentText *docText, char *buffer, long *len);
+char *readWAISDocumentText(WAISDocumentText **docText, char *buffer);
+
+WAISDocumentHeadlines *makeWAISDocumentHeadlines(any *aDocID,
+ long versionNumber,
+ char *source,
+ char *date,
+ char *headline,
+ char *originCity);
+void freeWAISDocumentHeadlines(WAISDocumentHeadlines *docHeadline);
+char *writeWAISDocumentHeadlines(WAISDocumentHeadlines *docHeadline,
+ char *buffer,
+ long *len);
+char *readWAISDocumentHeadlines(WAISDocumentHeadlines **docHeadline, char *buffer);
+
+WAISDocumentCodes *makeWAISDocumentCodes(any *aDocID,
+ long versionNumber,
+ char *stockCodes,
+ char *companyCodes,
+ char *industryCodes);
+void freeWAISDocumentCodes(WAISDocumentCodes *docCodes);
+char *writeWAISDocumentCodes(WAISDocumentCodes *docCodes, char *buffer, long *len);
+char *readWAISDocumentCodes(WAISDocumentCodes **docCodes, char *buffer);
+
+any *makeWAISTextQuery(DocObj **docs);
+DocObj **readWAISTextQuery(any *terms);
+
+void CSTFreeWAISInitResponse(WAISInitResponse *init);
+void CSTFreeWAISSearch(WAISSearch *query);
+void CSTFreeDocObj(DocObj *doc);
+void CSTFreeWAISDocumentHeader(WAISDocumentHeader *header);
+void CSTFreeWAISDocumentShortHeader(WAISDocumentShortHeader *header);
+void CSTFreeWAISDocumentLongHeader(WAISDocumentLongHeader *header);
+void CSTFreeWAISSearchResponse(WAISSearchResponse * response);
+void CSTFreeWAISDocumentText(WAISDocumentText *docText);
+void CSTFreeWAISDocHeadlines(WAISDocumentHeadlines *docHeadline);
+void CSTFreeWAISDocumentCodes(WAISDocumentCodes *docCodes);
+void CSTFreeWAISTextQuery(any *query);
+
+/*----------------------------------------------------------------------*/
+
+#endif /* ndef _H_WAIS_protocol_ */
+
+/*
+ * Routines originally from WMessage.h -- FM
+ *
+ *----------------------------------------------------------------------*/
+/* WIDE AREA INFORMATION SERVER SOFTWARE
+ * No guarantees or restrictions. See the readme file for the full standard
+ * disclaimer.
+ * 3.26.90
+ */
+
+/* wais-message.h
+ *
+ * This is the header outside of WAIS Z39.50 messages. The header will be
+ * printable ascii, so as to be transportable. This header will precede each
+ * Z39.50 APDU, or zero-length message if it is an ACK or NACK. Be sure to
+ * change hdr_vers current value if you change the structure of the header.
+ *
+ * The characters in the header are case insensitive so that the systems from
+ * the past that only handle one case can at least read the header.
+ *
+ * 7.5.90 HWM - added constants
+ * 7/5/90 brewster added function prototypes and comments
+ * 11/30/90 HWM - went to version 2 (inits and typed retrieval)
+ */
+
+#ifndef WMESSAGE_H
+#define WMESSAGE_H
+
+#ifndef HTUTILS_H
+#include <HTUtils.h>
+#endif
+
+#include <HTVMS_WaisUI.h>
+
+typedef struct wais_header {
+ char msg_len[10]; /* length in bytes of following message */
+ char msg_type; /* type of message: 'z'=Z39.50 APDU,
+ 'a'=ACK, 'n'=NACK */
+ char hdr_vers; /* version of this header, currently = '2' */
+ char server[10]; /* name or address of server */
+ char compression; /* <sp>=no compression, 'u'=unix compress */
+ char encoding; /* <sp>=no encoding, 'h'=hexize,
+ 'u'=uuencode */
+ char msg_checksum; /* XOR of every byte of message */
+} WAISMessage;
+
+#define HEADER_LENGTH 25 /* number of bytes needed to write a
+ wais-header (not sizeof(wais_header)) */
+
+#define HEADER_VERSION (long)'2'
+
+/* message type */
+#define Z3950 'z'
+#define ACK 'a'
+#define NAK 'n'
+
+/* compression */
+#define NO_COMPRESSION ' '
+#define UNIX_COMPRESSION 'u'
+
+/* encoding */
+#define NO_ENCODING ' '
+#define HEX_ENCODING 'h' /* Swartz 4/3 encoding */
+#define IBM_HEXCODING 'i' /* same as h but uses characters acceptable for IBM mainframes */
+#define UUENCODE 'u'
+
+void readWAISPacketHeader(char *msgBuffer, WAISMessage * header_struct);
+long getWAISPacketLength(WAISMessage * header);
+void writeWAISPacketHeader(char *header, long dataLen, long type,
+ char *server, long compression,
+ long encoding, long version);
+
+#endif /* ndef WMESSAGE_H */
diff --git a/WWW/Library/Implementation/HTVMS_WaisUI.c b/WWW/Library/Implementation/HTVMS_WaisUI.c
new file mode 100644
index 0000000..716bbbb
--- /dev/null
+++ b/WWW/Library/Implementation/HTVMS_WaisUI.c
@@ -0,0 +1,2279 @@
+/*
+ * $LynxId: HTVMS_WaisUI.c,v 1.21 2020/01/21 22:00:50 tom Exp $
+ * HTVMS_WAISUI.c
+ *
+ * Adaptation for Lynx by F.Macrides (macrides@sci.wfeb.edu)
+ *
+ * 30-May-1994 FM Initial version.
+ *
+ *----------------------------------------------------------------------*/
+
+/*
+ * Routines originally from UI.c -- FM
+ *
+ *----------------------------------------------------------------------*/
+/* WIDE AREA INFORMATION SERVER SOFTWARE:
+ * No guarantees or restrictions. See the readme file for the full standard
+ * disclaimer.
+ *
+ * Brewster@think.com
+ */
+
+/*
+ * this is a simple ui toolkit for building other ui's on top.
+ * -brewster
+ *
+ * top level functions:
+ * generate_search_apdu
+ * generate_retrieval_apdu
+ * interpret_message
+ *
+ */
+
+/* to do:
+ * generate multiple queries for long documents.
+ * this will crash if the file being retrieved is larger than 100k.
+ * do log_write()
+ *
+ */
+
+#include <HTUtils.h>
+
+#ifdef VMS
+#include <HTVMS_WaisUI.h>
+#include <HTVMS_WaisProt.h>
+#include <HTTCP.h>
+
+#undef MAXINT /* we don't need it here, and www_tcp.h may conflict */
+#include <math.h>
+
+#include <LYexit.h>
+#include <LYLeaks.h>
+
+void log_write(char *s GCC_UNUSED)
+{
+ return;
+}
+
+/*----------------------------------------------------------------------*/
+
+/* returns a pointer in the buffer of the first free byte.
+ if it overflows, then NULL is returned
+ */
+char *generate_search_apdu(char *buff, /* buffer to hold the apdu */
+ long *buff_len, /* length of the buffer changed to reflect new data written */
+ char *seed_words, /* string of the seed words */
+ char *database_name,
+ DocObj **docobjs,
+ long maxDocsRetrieved)
+{
+ /* local variables */
+
+ SearchAPDU *search3;
+ char *end_ptr;
+ static char *database_names[2] =
+ {"", 0};
+ any refID;
+ WAISSearch *query;
+
+ refID.size = 1;
+ refID.bytes = "3";
+
+ database_names[0] = database_name;
+ query = makeWAISSearch(seed_words,
+ docobjs, /* DocObjsPtr */
+ 0,
+ 1, /* DateFactor */
+ 0, /* BeginDateRange */
+ 0, /* EndDateRange */
+ maxDocsRetrieved
+ );
+
+ search3 = makeSearchAPDU(30,
+ 5000, /* should be large */
+ 30,
+ 1, /* replace indicator */
+ "", /* result set name */
+ database_names, /* database name */
+ QT_RelevanceFeedbackQuery, /* query_type */
+ 0, /* element name */
+ NULL, /* reference ID */
+ query);
+
+ end_ptr = writeSearchAPDU(search3, buff, buff_len);
+
+ CSTFreeWAISSearch(query);
+ freeSearchAPDU(search3);
+ return (end_ptr);
+}
+
+/*----------------------------------------------------------------------*/
+
+/* returns a pointer into the buffer of the next free byte.
+ if it overflowed, then NULL is returned
+ */
+
+char *generate_retrieval_apdu(char *buff,
+ long *buff_len, /* length of the buffer changed to reflect new data written */
+ any *docID,
+ long chunk_type,
+ long start,
+ long end,
+ char *type,
+ char *database_name)
+{
+ SearchAPDU *search;
+ char *end_ptr;
+
+ static char *database_names[2];
+ static char *element_names[3];
+ any refID;
+
+ DocObj *DocObjs[2];
+ any *query; /* changed from char* by brewster */
+
+ if (NULL == type)
+ type = s_strdup("TEXT");
+
+ database_names[0] = database_name;
+ database_names[1] = NULL;
+
+ element_names[0] = " ";
+ element_names[1] = ES_DocumentText;
+ element_names[2] = NULL;
+
+ refID.size = 1;
+ refID.bytes = "3";
+
+ switch (chunk_type) {
+ case CT_line:
+ DocObjs[0] = makeDocObjUsingLines(docID, type, start, end);
+ break;
+ case CT_byte:
+ DocObjs[0] = makeDocObjUsingBytes(docID, type, start, end);
+ break;
+ }
+ DocObjs[1] = NULL;
+
+ query = makeWAISTextQuery(DocObjs);
+ search = makeSearchAPDU(10, 16, 15,
+ 1, /* replace indicator */
+ "FOO", /* result set name */
+ database_names, /* database name */
+ QT_TextRetrievalQuery, /* query_type */
+ element_names, /* element name */
+ &refID, /* reference ID */
+ query);
+ end_ptr = writeSearchAPDU(search, buff, buff_len);
+ CSTFreeWAISTextQuery(query);
+ freeSearchAPDU(search);
+ return (end_ptr);
+}
+
+/*----------------------------------------------------------------------*/
+
+/* this is a safe version of unix 'read' it does all the checking
+ * and looping necessary
+ * to those trying to modify the transport code to use non-UNIX streams:
+ * This is the function to modify!
+ */
+static long read_from_stream(int d, char *buf, long nbytes)
+{
+ long didRead;
+ long toRead = nbytes;
+ long totalRead = 0; /* paranoia */
+
+ while (toRead > 0) {
+ didRead = NETREAD(d, buf, (int) toRead);
+ if (didRead == HT_INTERRUPTED)
+ return (HT_INTERRUPTED);
+ if (didRead == -1) /* error */
+ return (-1);
+ if (didRead == 0) /* eof */
+ return (-2); /* maybe this should return 0? */
+ toRead -= didRead;
+ buf += didRead;
+ totalRead += didRead;
+ }
+ if (totalRead != nbytes) /* we overread for some reason */
+ return (-totalRead); /* bad news */
+ return (totalRead);
+}
+
+/*----------------------------------------------------------------------*/
+
+/* returns the length of the response, 0 if an error */
+
+static long transport_message(long connection,
+ char *request_message,
+ long request_length,
+ char *response_message,
+ long response_buffer_length)
+{
+ WAISMessage header;
+ long response_length;
+ int rv;
+
+ /* Write out message. Read back header. Figure out response length. */
+
+ if (request_length + HEADER_LENGTH !=
+ NETWRITE(connection, request_message,
+ (int) (request_length + HEADER_LENGTH)))
+ return 0;
+
+ /* read for the first '0' */
+
+ while (1) {
+ rv = read_from_stream(connection, response_message, 1);
+ if (rv == HT_INTERRUPTED)
+ return HT_INTERRUPTED;
+ if (rv < 0)
+ return 0;
+ if ('0' == response_message[0])
+ break;
+ }
+
+ rv = read_from_stream(connection, response_message + 1, HEADER_LENGTH - 1);
+ if (rv == HT_INTERRUPTED)
+ return HT_INTERRUPTED;
+ if (rv < 0)
+ return 0;
+
+ readWAISPacketHeader(response_message, &header);
+ {
+ char length_array[11];
+
+ LYStrNCpy(length_array, header.msg_len, 10);
+ response_length = atol(length_array);
+ /*
+ if(verbose){
+ printf("WAIS header: '%s' length_array: '%s'\n",
+ response_message, length_array);
+ }
+ */
+ if (response_length > response_buffer_length) {
+ /* we got a message that is too long, therefore empty the message out,
+ and return 0 */
+ long i;
+
+ for (i = 0; i < response_length; i++) {
+ rv = read_from_stream(connection,
+ response_message + HEADER_LENGTH,
+ 1);
+ if (rv == HT_INTERRUPTED)
+ return HT_INTERRUPTED;
+ if (rv < 0)
+ return 0;
+ }
+ return (0);
+ }
+ }
+ rv = read_from_stream(connection,
+ response_message + HEADER_LENGTH,
+ response_length);
+ if (rv == HT_INTERRUPTED)
+ return HT_INTERRUPTED;
+ if (rv < 0)
+ return 0;
+ return (response_length);
+}
+
+/*----------------------------------------------------------------------*/
+
+/* returns the number of bytes written. 0 if an error */
+long interpret_message(char *request_message,
+ long request_length, /* length of the buffer */
+ char *response_message,
+ long response_buffer_length,
+ long connection,
+ boolean verbose GCC_UNUSED)
+{
+ long response_length;
+
+ /* ?
+ if(verbose){
+ printf ("sending");
+ if(hostname_internal && strlen(hostname_internal) > 0)
+ printf(" to host %s", hostname_internal);
+ if(service_name && strlen(service_name) > 0)
+ printf(" for service %s", service_name);
+ printf("\n");
+ twais_dsply_rsp_apdu(request_message + HEADER_LENGTH,
+ request_length);
+ }
+
+ */
+
+ writeWAISPacketHeader(request_message,
+ request_length,
+ (long) 'z', /* Z39.50 */
+ "wais ", /* server name */
+ (long) NO_COMPRESSION, /* no compression */
+ (long) NO_ENCODING, (long) HEADER_VERSION);
+ if (connection != 0) {
+ response_length = transport_message(connection, request_message,
+ request_length,
+ response_message,
+ response_buffer_length);
+ if (response_length == HT_INTERRUPTED)
+ return (HT_INTERRUPTED);
+ } else
+ return (0);
+
+ return (response_length);
+}
+
+/*----------------------------------------------------------------------*/
+
+/* modifies the string to exclude all seeker codes. sets length to
+ the new length. */
+static char *delete_seeker_codes(char *string, long *length)
+{
+ long original_count; /* index into the original string */
+ long new_count = 0; /* index into the collapsed string */
+
+ for (original_count = 0; original_count < *length; original_count++) {
+ if (27 == string[original_count]) {
+ /* then we have an escape code */
+ /* if the next letter is '(' or ')', then ignore two letters */
+ if ('(' == string[original_count + 1] ||
+ ')' == string[original_count + 1])
+ original_count += 1; /* it is a term marker */
+ else
+ original_count += 4; /* it is a paragraph marker */
+ } else
+ string[new_count++] = string[original_count];
+ }
+ *length = new_count;
+ return (string);
+}
+
+/*----------------------------------------------------------------------*/
+
+#if defined(VMS) && defined(__GNUC__) /* 10-AUG-1995 [pr] */
+/*
+ Workaround for an obscure bug in gcc's 2.6.[123] and 2.7.0 vax/vms port;
+ sometimes global variables will end up not being defined properly,
+ causing first gas to assume they're routines, then the linker to complain
+ about unresolved symbols, and finally the program to reference the wrong
+ objects (provoking ACCVIO). It's triggered by the specific ordering of
+ variable usage in the source code, hence rarely appears. This bug is
+ fixed in gcc 2.7.1, and was not present in 2.6.0 and earlier.
+
+ Make a reference to VAXCRTL's _ctype_[], and also one to this dummy
+ variable itself to prevent any "defined but not used" warning.
+ */
+static __const void *__const ctype_dummy[] =
+{&_ctype_, &ctype_dummy};
+#endif /* VMS && __GNUC__ */
+
+/* returns a pointer to a string with good stuff */
+char *trim_junk(char *headline)
+{
+ long length = strlen(headline) + 1; /* include the trailing null */
+ size_t i;
+
+ headline = delete_seeker_codes(headline, &length);
+ /* delete leading spaces */
+ for (i = 0; i < strlen(headline); i++) {
+ if (isprint(headline[i])) {
+ break;
+ }
+ }
+ headline = headline + i;
+ /* delete trailing stuff */
+ for (i = strlen(headline) - 1; i > 0; i--) {
+ if (isprint(headline[i])) {
+ break;
+ }
+ headline[i] = '\0';
+ }
+ return (headline);
+}
+
+/*----------------------------------------------------------------------*/
+
+/*
+ * Routines originally from ZProt.c -- FM
+ *
+ *----------------------------------------------------------------------*/
+/* WIDE AREA INFORMATION SERVER SOFTWARE:`
+ * No guarantees or restrictions. See the readme file for the full standard
+ * disclaimer.
+ *
+ * 3.26.90 Harry Morris, morris@think.com
+ * 3.30.90 Harry Morris - Changed any->bits to any->bytes
+ * 4.11.90 HWM - generalized conditional includes (see c-dialect.h)
+ */
+
+#define RESERVE_SPACE_FOR_HEADER(spaceLeft) \
+ *spaceLeft -= HEADER_LEN;
+
+#define RELEASE_HEADER_SPACE(spaceLeft) \
+ if (*spaceLeft > 0) \
+ *spaceLeft += HEADER_LEN;
+
+/*----------------------------------------------------------------------*/
+
+InitResponseAPDU *makeInitResponseAPDU(boolean result,
+ boolean search,
+ boolean present,
+ boolean deleteIt,
+ boolean accessControl,
+ boolean resourceControl,
+ long prefSize,
+ long maxMsgSize,
+ char *auth,
+ char *id,
+ char *name,
+ char *version,
+ any *refID,
+ void *userInfo)
+/* build an initResponse APDU with user specified information */
+{
+ InitResponseAPDU *init = (InitResponseAPDU *) s_malloc((size_t) sizeof(InitResponseAPDU));
+
+ init->PDUType = initResponseAPDU;
+ init->Result = result;
+ init->willSearch = search;
+ init->willPresent = present;
+ init->willDelete = deleteIt;
+ init->supportAccessControl = accessControl;
+ init->supportResourceControl = resourceControl;
+ init->PreferredMessageSize = prefSize;
+ init->MaximumRecordSize = maxMsgSize;
+ init->IDAuthentication = s_strdup(auth);
+ init->ImplementationID = s_strdup(id);
+ init->ImplementationName = s_strdup(name);
+ init->ImplementationVersion = s_strdup(version);
+ init->ReferenceID = duplicateAny(refID);
+ init->UserInformationField = userInfo; /* not copied! */
+
+ return (init);
+}
+
+/*----------------------------------------------------------------------*/
+
+void freeInitResponseAPDU(InitResponseAPDU *init)
+/* free an initAPDU */
+{
+ s_free(init->IDAuthentication);
+ s_free(init->ImplementationID);
+ s_free(init->ImplementationName);
+ s_free(init->ImplementationVersion);
+ freeAny(init->ReferenceID);
+ s_free(init);
+}
+
+/*----------------------------------------------------------------------*/
+
+char *writeInitResponseAPDU(InitResponseAPDU *init, char *buffer, long *len)
+/* write the initResponse to a buffer, adding system information */
+{
+ char *buf = buffer + HEADER_LEN; /* leave room for the header-length-indicator */
+ long size;
+ bit_map *optionsBM = NULL;
+
+ RESERVE_SPACE_FOR_HEADER(len);
+
+ buf = writePDUType(init->PDUType, buf, len);
+ buf = writeBoolean(init->Result, buf, len);
+ buf = writeProtocolVersion(buf, len);
+
+ optionsBM = makeBitMap((unsigned long) 5, init->willSearch, init->willPresent,
+ init->willDelete, init->supportAccessControl,
+ init->supportResourceControl);
+ buf = writeBitMap(optionsBM, DT_Options, buf, len);
+ freeBitMap(optionsBM);
+
+ buf = writeNum(init->PreferredMessageSize,
+ DT_PreferredMessageSize,
+ buf,
+ len);
+ buf = writeNum(init->MaximumRecordSize,
+ DT_MaximumRecordSize,
+ buf,
+ len);
+ buf = writeString(init->IDAuthentication,
+ DT_IDAuthentication,
+ buf,
+ len);
+ buf = writeString(init->ImplementationID,
+ DT_ImplementationID,
+ buf,
+ len);
+ buf = writeString(init->ImplementationName,
+ DT_ImplementationName,
+ buf,
+ len);
+ buf = writeString(init->ImplementationVersion,
+ DT_ImplementationVersion,
+ buf,
+ len);
+ buf = writeAny(init->ReferenceID,
+ DT_ReferenceID,
+ buf,
+ len);
+
+ /* go back and write the header-length-indicator */
+ RELEASE_HEADER_SPACE(len);
+ size = buf - buffer - HEADER_LEN;
+ writeBinaryInteger(size, HEADER_LEN, buffer, len);
+
+ if (init->UserInformationField != NULL)
+ buf = writeInitResponseInfo(init, buf, len);
+
+ return (buf);
+}
+
+/*----------------------------------------------------------------------*/
+
+char *readInitResponseAPDU(InitResponseAPDU **init, char *buffer)
+{
+ char *buf = buffer;
+ boolean search, present, delete, accessControl, resourceControl;
+ long prefSize, maxMsgSize;
+ char *auth, *id, *name, *version;
+ long size;
+ pdu_type pduType;
+ bit_map *versionBM = NULL;
+ bit_map *optionsBM = NULL;
+ boolean result;
+ any *refID = NULL;
+ void *userInfo = NULL;
+
+ auth = id = name = version = NULL;
+ refID = NULL;
+
+ /* read required part */
+ buf = readBinaryInteger(&size, HEADER_LEN, buf);
+ buf = readPDUType(&pduType, buf);
+ buf = readBoolean(&result, buf);
+ buf = readBitMap(&versionBM, buf);
+ buf = readBitMap(&optionsBM, buf);
+ buf = readNum(&prefSize, buf);
+ buf = readNum(&maxMsgSize, buf);
+
+ /* decode optionsBM */
+ search = bitAtPos(0, optionsBM);
+ present = bitAtPos(1, optionsBM);
+ delete = bitAtPos(2, optionsBM);
+ accessControl = bitAtPos(3, optionsBM);
+ resourceControl = bitAtPos(4, optionsBM);
+
+ /* read optional part */
+ while (buf < (buffer + size + HEADER_LEN)) {
+ data_tag tag = peekTag(buf);
+
+ switch (tag) {
+ case DT_IDAuthentication:
+ buf = readString(&auth, buf);
+ break;
+ case DT_ImplementationID:
+ buf = readString(&id, buf);
+ break;
+ case DT_ImplementationName:
+ buf = readString(&name, buf);
+ break;
+ case DT_ImplementationVersion:
+ buf = readString(&version, buf);
+ break;
+ case DT_ReferenceID:
+ buf = readAny(&refID, buf);
+ break;
+ default:
+ freeBitMap(versionBM);
+ freeBitMap(optionsBM);
+ s_free(auth);
+ s_free(id);
+ s_free(name);
+ s_free(version);
+ freeAny(refID);
+ REPORT_READ_ERROR(buf);
+ break;
+ }
+ }
+
+ buf = readInitResponseInfo(&userInfo, buf);
+ if (buf == NULL) {
+ freeBitMap(versionBM);
+ freeBitMap(optionsBM);
+ s_free(auth);
+ s_free(id);
+ s_free(name);
+ s_free(version);
+ freeAny(refID);
+ }
+ RETURN_ON_NULL(buf);
+
+ /* construct the basic init object */
+ *init = makeInitResponseAPDU(result,
+ search,
+ present,
+ delete,
+ accessControl,
+ resourceControl,
+ prefSize,
+ maxMsgSize,
+ auth,
+ id,
+ name,
+ version,
+ refID,
+ userInfo);
+
+ freeBitMap(versionBM);
+ freeBitMap(optionsBM);
+ s_free(auth);
+ s_free(id);
+ s_free(name);
+ s_free(version);
+ freeAny(refID);
+
+ return (buf);
+}
+
+/*----------------------------------------------------------------------*/
+
+InitResponseAPDU *replyToInitAPDU(InitAPDU * init, boolean result, void *userInfo)
+/* respond to an init message in the default way - echoing back
+ the init info
+ */
+{
+ InitResponseAPDU *initResp;
+
+ initResp = makeInitResponseAPDU(result,
+ init->willSearch,
+ init->willPresent,
+ init->willDelete,
+ init->supportAccessControl,
+ init->supportResourceControl,
+ init->PreferredMessageSize,
+ init->MaximumRecordSize,
+ init->IDAuthentication,
+ defaultImplementationID(),
+ defaultImplementationName(),
+ defaultImplementationVersion(),
+ init->ReferenceID,
+ userInfo);
+ return (initResp);
+}
+
+/*----------------------------------------------------------------------*/
+
+SearchAPDU *makeSearchAPDU(long small,
+ long large,
+ long medium,
+ boolean replace,
+ char *name,
+ char **databases,
+ char *type,
+ char **elements,
+ any *refID,
+ void *queryInfo)
+{
+ char *ptr = NULL;
+ long i;
+ SearchAPDU *query = (SearchAPDU *) s_malloc((size_t) sizeof(SearchAPDU));
+
+ query->PDUType = searchAPDU;
+ query->SmallSetUpperBound = small;
+ query->LargeSetLowerBound = large;
+ query->MediumSetPresentNumber = medium;
+ query->ReplaceIndicator = replace;
+ query->ResultSetName = s_strdup(name);
+ query->DatabaseNames = NULL;
+ if (databases != NULL) {
+ for (i = 0, ptr = databases[i]; ptr != NULL; ptr = databases[++i]) {
+ if (query->DatabaseNames == NULL)
+ query->DatabaseNames = (char **) s_malloc((size_t) (sizeof(char
+ *)
+ * 2));
+
+ else
+ query->DatabaseNames = (char **) s_realloc((char *) query->DatabaseNames,
+ (size_t) (sizeof(char
+ *) *
+ (i + 2)));
+
+ query->DatabaseNames[i] = s_strdup(ptr);
+ query->DatabaseNames[i + 1] = NULL;
+ }
+ }
+ query->QueryType = s_strdup(type);
+ query->ElementSetNames = NULL;
+ if (elements != NULL) {
+ for (i = 0, ptr = elements[i]; ptr != NULL; ptr = elements[++i]) {
+ if (query->ElementSetNames == NULL)
+ query->ElementSetNames =
+ (char **) s_malloc((size_t) (sizeof(char *) * 2));
+
+ else
+ query->ElementSetNames = (char **) s_realloc((char *) query->ElementSetNames,
+ (size_t) (sizeof(char
+ *) *
+ (i + 2)));
+
+ query->ElementSetNames[i] = s_strdup(ptr);
+ query->ElementSetNames[i + 1] = NULL;
+ }
+ }
+ query->ReferenceID = duplicateAny(refID);
+ query->Query = queryInfo; /* not copied! */
+ return (query);
+}
+
+/*----------------------------------------------------------------------*/
+
+void freeSearchAPDU(SearchAPDU *query)
+{
+ s_free(query->ResultSetName);
+ s_free(query->QueryType);
+ doList((void **) query->DatabaseNames, fs_free); /* can't use the macro here ! */
+ s_free(query->DatabaseNames);
+ doList((void **) query->ElementSetNames, fs_free); /* can't use the macro here ! */
+ s_free(query->ElementSetNames);
+ freeAny(query->ReferenceID);
+ s_free(query);
+}
+
+/*----------------------------------------------------------------------*/
+
+#define DB_DELIMITER "\037" /* hex 1F occurs between each database name */
+#define ES_DELIMITER_1 "\037" /* separates database name from element name */
+#define ES_DELIMITER_2 "\036" /* hex 1E separates <db,es> groups from one another */
+
+char *writeSearchAPDU(SearchAPDU *query, char *buffer, long *len)
+{
+ char *buf = buffer + HEADER_LEN; /* leave room for the header-length-indicator */
+ long size, i;
+ char *ptr = NULL;
+ char *scratch = NULL;
+
+ RESERVE_SPACE_FOR_HEADER(len);
+
+ buf = writePDUType(query->PDUType, buf, len);
+ buf = writeBinaryInteger(query->SmallSetUpperBound, (size_t) 3, buf, len);
+ buf = writeBinaryInteger(query->LargeSetLowerBound, (size_t) 3, buf, len);
+ buf = writeBinaryInteger(query->MediumSetPresentNumber, (size_t) 3, buf, len);
+ buf = writeBoolean(query->ReplaceIndicator, buf, len);
+ buf = writeString(query->ResultSetName, DT_ResultSetName, buf, len);
+ /* write database names */
+ if (query->DatabaseNames != NULL) {
+ for (i = 0, scratch = NULL, ptr = query->DatabaseNames[i]; ptr != NULL;
+ ptr = query->DatabaseNames[++i]) {
+ if (scratch == NULL)
+ scratch = s_strdup(ptr);
+ else {
+ size_t newScratchSize = (size_t) (strlen(scratch) +
+ strlen(ptr) + 2);
+
+ scratch = (char *) s_realloc(scratch, newScratchSize);
+ s_strncat(scratch, DB_DELIMITER, 2, newScratchSize);
+ s_strncat(scratch, ptr, strlen(ptr) + 1, newScratchSize);
+ }
+ }
+ buf = writeString(scratch, DT_DatabaseNames, buf, len);
+ s_free(scratch);
+ }
+ buf = writeString(query->QueryType, DT_QueryType, buf, len);
+ /* write element set names */
+ if (query->ElementSetNames != NULL) {
+ for (i = 0, scratch = NULL, ptr = query->ElementSetNames[i];
+ ptr != NULL;
+ ptr = query->ElementSetNames[++i]) {
+ if (scratch == NULL) {
+ if (query->ElementSetNames[i + 1] == NULL) /* there is a single element set name */
+ {
+ scratch = (char *) s_malloc((size_t) strlen(ptr) + 2);
+ StrNCpy(scratch, ES_DELIMITER_1, 2);
+ s_strncat(scratch, ptr, strlen(ptr) + 1, strlen(ptr) + 2);
+ } else { /* this is the first of a series of element set names */
+ size_t newScratchSize = (size_t) (strlen(ptr) +
+ strlen(query->ElementSetNames[i
+ + 1])
+ + 2);
+
+ scratch = s_strdup(ptr); /* the database name */
+ ptr = query->ElementSetNames[++i]; /* the element set name */
+ scratch = (char *) s_realloc(scratch, newScratchSize);
+ s_strncat(scratch, ES_DELIMITER_1, 2, newScratchSize);
+ s_strncat(scratch, ptr, strlen(ptr) + 1, newScratchSize);
+ }
+ } else {
+ char *esPtr = query->ElementSetNames[++i]; /* the element set name */
+ size_t newScratchSize = (size_t) (strlen(scratch) +
+ strlen(ptr) +
+ strlen(esPtr) +
+ 3);
+
+ scratch = (char *) s_realloc(scratch, newScratchSize);
+ s_strncat(scratch, ES_DELIMITER_2, 2, newScratchSize);
+ s_strncat(scratch, ptr, strlen(ptr) + 1, newScratchSize);
+ s_strncat(scratch, ES_DELIMITER_1, 2, newScratchSize);
+ s_strncat(scratch, esPtr, strlen(esPtr) + 1, newScratchSize);
+ }
+ }
+ buf = writeString(scratch, DT_ElementSetNames, buf, len);
+ s_free(scratch);
+ }
+ buf = writeAny(query->ReferenceID, DT_ReferenceID, buf, len);
+
+ /* go back and write the header-length-indicator */
+ RELEASE_HEADER_SPACE(len);
+ size = buf - buffer - HEADER_LEN;
+ writeBinaryInteger(size, HEADER_LEN, buffer, len);
+
+ if (query->Query != NULL)
+ buf = writeSearchInfo(query, buf, len);
+
+ return (buf);
+}
+
+/*----------------------------------------------------------------------*/
+
+SearchResponseAPDU *makeSearchResponseAPDU(long result,
+ long count,
+ long recordsReturned,
+ long nextPos,
+ long resultStatus,
+ long presentStatus,
+ any *refID,
+ void *records)
+{
+ SearchResponseAPDU *query =
+ (SearchResponseAPDU *) s_malloc((size_t) sizeof(SearchResponseAPDU));
+
+ query->PDUType = searchResponseAPDU;
+ query->SearchStatus = result;
+ query->ResultCount = count;
+ query->NumberOfRecordsReturned = recordsReturned;
+ query->NextResultSetPosition = nextPos;
+ query->ResultSetStatus = resultStatus;
+ query->PresentStatus = presentStatus;
+ query->ReferenceID = duplicateAny(refID);
+ query->DatabaseDiagnosticRecords = records;
+ return (query);
+}
+
+/*----------------------------------------------------------------------*/
+
+void freeSearchResponseAPDU(SearchResponseAPDU *queryResponse)
+{
+ freeAny(queryResponse->ReferenceID);
+ s_free(queryResponse);
+}
+
+/*----------------------------------------------------------------------*/
+
+char *writeSearchResponseAPDU(SearchResponseAPDU *queryResponse, char *buffer,
+ long *len)
+{
+ char *buf = buffer + HEADER_LEN; /* leave room for the header-length-indicator */
+ long size;
+
+ RESERVE_SPACE_FOR_HEADER(len);
+
+ buf = writePDUType(queryResponse->PDUType,
+ buf,
+ len);
+ buf = writeBinaryInteger(queryResponse->SearchStatus,
+ (size_t) 1,
+ buf,
+ len);
+ buf = writeBinaryInteger(queryResponse->ResultCount,
+ (size_t) 3,
+ buf,
+ len);
+ buf = writeBinaryInteger(queryResponse->NumberOfRecordsReturned,
+ (size_t) 3,
+ buf,
+ len);
+ buf = writeBinaryInteger(queryResponse->NextResultSetPosition,
+ (size_t) 3,
+ buf,
+ len);
+ buf = writeNum(queryResponse->ResultSetStatus,
+ DT_ResultSetStatus,
+ buf,
+ len);
+ buf = writeNum(queryResponse->PresentStatus,
+ DT_PresentStatus,
+ buf,
+ len);
+ buf = writeAny(queryResponse->ReferenceID,
+ DT_ReferenceID,
+ buf,
+ len);
+
+ /* go back and write the header-length-indicator */
+ RELEASE_HEADER_SPACE(len);
+ size = buf - buffer - HEADER_LEN;
+ writeBinaryInteger(size, HEADER_LEN, buffer, len);
+
+ if (queryResponse->DatabaseDiagnosticRecords != NULL)
+ buf = writeSearchResponseInfo(queryResponse, buf, len);
+
+ return (buf);
+}
+
+/*----------------------------------------------------------------------*/
+
+char *readSearchResponseAPDU(SearchResponseAPDU **queryResponse, char *buffer)
+{
+ char *buf = buffer;
+ long size;
+ pdu_type pduType;
+ long result, count, recordsReturned, nextPos;
+ long resultStatus, presentStatus;
+ any *refID = NULL;
+ void *userInfo = NULL;
+
+ /* read required part */
+ buf = readBinaryInteger(&size, HEADER_LEN, buf);
+ buf = readPDUType(&pduType, buf);
+ buf = readBinaryInteger(&result, (size_t) 1, buf);
+ buf = readBinaryInteger(&count, (size_t) 3, buf);
+ buf = readBinaryInteger(&recordsReturned, (size_t) 3, buf);
+ buf = readBinaryInteger(&nextPos, (size_t) 3, buf);
+
+ resultStatus = presentStatus = UNUSED;
+ refID = NULL;
+
+ /* read optional part */
+ while (buf < (buffer + size + HEADER_LEN)) {
+ data_tag tag = peekTag(buf);
+
+ switch (tag) {
+ case DT_ResultSetStatus:
+ buf = readNum(&resultStatus, buf);
+ break;
+ case DT_PresentStatus:
+ buf = readNum(&presentStatus, buf);
+ break;
+ case DT_ReferenceID:
+ buf = readAny(&refID, buf);
+ break;
+ default:
+ freeAny(refID);
+ REPORT_READ_ERROR(buf);
+ break;
+ }
+ }
+
+ buf = readSearchResponseInfo(&userInfo, buf);
+ if (buf == NULL)
+ freeAny(refID);
+ RETURN_ON_NULL(buf);
+
+ /* construct the search object */
+ *queryResponse = makeSearchResponseAPDU(result,
+ count,
+ recordsReturned,
+ nextPos,
+ (long) resultStatus,
+ (long) presentStatus,
+ refID,
+ userInfo);
+
+ freeAny(refID);
+
+ return (buf);
+}
+
+/*
+ * Routines originally from ZUtil.c -- FM
+ *
+ *----------------------------------------------------------------------*/
+/* WIDE AREA INFORMATION SERVER SOFTWARE:
+ * No guarantees or restrictions. See the readme file for the full standard
+ * disclaimer.
+ *
+ * 3.26.90 Harry Morris, morris@think.com
+ * 3.30.90 Harry Morris - Changed any->bits to any->bytes
+ * 4.11.90 HWM - fixed include file names, changed
+ * - writeCompressedIntegerWithPadding() to
+ * writeCompressedIntWithPadding()
+ * - generalized conditional includes (see c-dialect.h)
+ * 3.7.91 Jonny Goldman. Replaced "short" in makeBitMap with "int" line 632.
+ */
+
+char *readErrorPosition = NULL; /* pos where buf stopped making sense */
+
+/*----------------------------------------------------------------------*/
+/* A note on error handling
+ read - these are low level routines, they do not check the type tags
+ which (sometimes) precede the data (this is done by the higher
+ level functions which call these functions). There is no
+ attempt made to check that the reading does not exceed the read
+ buffer. Such cases should be very rare and usually will be
+ caught by the calling functions. (note - it is unlikely that
+ a series of low level reads will go far off the edge without
+ triggering a type error. However, it is possible for a single
+ bad read in an array function (eg. readAny) to attempt to read a
+ large amount, possibly causing a segmentation violation or out
+ of memory condition.
+ */
+/*----------------------------------------------------------------------*/
+
+diagnosticRecord *makeDiag(boolean surrogate, char *code, char *addInfo)
+{
+ diagnosticRecord *diag =
+ (diagnosticRecord *) s_malloc((size_t) sizeof(diagnosticRecord));
+
+ diag->SURROGATE = surrogate;
+ MemCpy(diag->DIAG, code, DIAGNOSTIC_CODE_SIZE);
+ diag->ADDINFO = s_strdup(addInfo);
+
+ return (diag);
+}
+
+/*----------------------------------------------------------------------*/
+
+void freeDiag(diagnosticRecord * diag)
+{
+ if (diag != NULL) {
+ if (diag->ADDINFO != NULL)
+ s_free(diag->ADDINFO);
+ s_free(diag);
+ }
+}
+
+/*----------------------------------------------------------------------*/
+
+#define END_OF_RECORD 0x1D
+
+char *writeDiag(diagnosticRecord * diag, char *buffer, long *len)
+/* diagnostics (as per Appendix D) have a very weird format - this changes
+ in SR-1
+ */
+{
+ char *buf = buffer;
+ long length;
+
+ if (diag == NULL) /* handle unspecified optional args */
+ return (buf);
+
+ buf = writeTag(DT_DatabaseDiagnosticRecords, buf, len);
+ CHECK_FOR_SPACE_LEFT(0, len);
+
+ length = 3;
+ if (diag->ADDINFO != NULL)
+ length += strlen(diag->ADDINFO);
+
+ if (length >= 0xFFFF) /* make sure the length is reasonable */
+ {
+ length = 0xFFFF - 1;
+ diag->ADDINFO[0xFFFF - 3 - 1] = '\0';
+ }
+
+ buf = writeBinaryInteger(length, 2, buf, len);
+
+ CHECK_FOR_SPACE_LEFT(1, len);
+ buf[0] = diag->DIAG[0];
+ buf++;
+
+ CHECK_FOR_SPACE_LEFT(1, len);
+ buf[0] = diag->DIAG[1];
+ buf++;
+
+ if (length > 3) {
+ CHECK_FOR_SPACE_LEFT(3, len);
+ MemCpy(buf, diag->ADDINFO, length - 3);
+ buf += length - 3;
+ }
+
+ CHECK_FOR_SPACE_LEFT(1, len);
+ buf[0] = diag->SURROGATE;
+ buf++;
+
+ CHECK_FOR_SPACE_LEFT(1, len);
+ buf[0] = END_OF_RECORD;
+ buf++;
+
+ return (buf);
+}
+
+/*----------------------------------------------------------------------*/
+
+char *readDiag(diagnosticRecord ** diag, char *buffer)
+{
+ char *buf = buffer;
+ diagnosticRecord *d = (diagnosticRecord *) s_malloc((size_t) sizeof(diagnosticRecord));
+ data_tag tag;
+ long len;
+
+ buf = readTag(&tag, buf);
+
+ buf = readBinaryInteger(&len, 2, buf);
+
+ d->DIAG[0] = buf[0];
+ d->DIAG[1] = buf[1];
+ d->DIAG[2] = '\0';
+
+ if (len > 3) {
+ d->ADDINFO = (char *) s_malloc((size_t) (len - 3 + 1));
+ MemCpy(d->ADDINFO, (char *) (buf + 2), len - 3);
+ d->ADDINFO[len - 3] = '\0';
+ } else
+ d->ADDINFO = NULL;
+
+ d->SURROGATE = buf[len - 1];
+
+ *diag = d;
+
+ return (buf + len + 1);
+}
+
+/*----------------------------------------------------------------------*/
+
+#define continueBit 0x80
+#define dataMask 0x7F
+#define dataBits 7
+
+char *writeCompressedInteger(unsigned long num, char *buf, long *len)
+/* write a binary integer in the format described on p. 40.
+ this might be sped up
+*/
+{
+ char byte;
+ unsigned long i;
+ unsigned long size;
+
+ size = writtenCompressedIntSize(num);
+ CHECK_FOR_SPACE_LEFT(size, len);
+
+ for (i = size - 1; i != 0; i--) {
+ byte = num & dataMask;
+ if (i != (size - 1)) /* turn on continue bit */
+ byte = (char) (byte | continueBit);
+ buf[i] = byte;
+ num = num >> dataBits; /* don't and here */
+ }
+
+ return (buf + size);
+}
+
+/*----------------------------------------------------------------------*/
+
+char *readCompressedInteger(unsigned long *num, char *buf)
+/* read a binary integer in the format described on p. 40.
+ this might be sped up
+*/
+{
+ long i = 0;
+ unsigned char byte;
+
+ *num = 0;
+
+ do {
+ byte = buf[i++];
+ *num = *num << dataBits;
+ *num += (byte & dataMask);
+ }
+ while (byte & continueBit);
+
+ return (buf + i);
+}
+
+/*----------------------------------------------------------------------*/
+
+#define pad 128 /* high bit is set */
+
+char *writeCompressedIntWithPadding(unsigned long num,
+ unsigned long size,
+ char *buffer,
+ long *len)
+/* Like writeCompressedInteger, except writes padding (128) to make
+ sure that size bytes are used. This can be read correctly by
+ readCompressedInteger()
+*/
+{
+ char *buf = buffer;
+ unsigned long needed, padding;
+ long i;
+
+ CHECK_FOR_SPACE_LEFT(size, len);
+
+ needed = writtenCompressedIntSize(num);
+ padding = size - needed;
+ i = padding - 1;
+
+ for (i = padding - 1; i >= 0; i--) {
+ buf[i] = pad;
+ }
+
+ buf = writeCompressedInteger(num, buf + padding, len);
+
+ return (buf);
+}
+
+/*----------------------------------------------------------------------*/
+
+unsigned long writtenCompressedIntSize(unsigned long num)
+/* return the number of bytes needed to represent the value num in
+ compressed format. currently limited to 4 bytes
+ */
+{
+ if (num < CompressedInt1Byte)
+ return (1);
+ else if (num < CompressedInt2Byte)
+ return (2);
+ else if (num < CompressedInt3Byte)
+ return (3);
+ else
+ return (4);
+}
+
+/*----------------------------------------------------------------------*/
+
+char *writeTag(data_tag tag, char *buf, long *len)
+/* write out a data tag */
+{
+ return (writeCompressedInteger(tag, buf, len));
+}
+
+/*----------------------------------------------------------------------*/
+
+char *readTag(data_tag *tag, char *buf)
+/* read a data tag */
+{
+ return (readCompressedInteger(tag, buf));
+}
+
+/*----------------------------------------------------------------------*/
+
+unsigned long writtenTagSize(data_tag tag)
+{
+ return (writtenCompressedIntSize(tag));
+}
+
+/*----------------------------------------------------------------------*/
+
+data_tag peekTag(char *buf)
+/* read a data tag without advancing the buffer */
+{
+ data_tag tag;
+
+ readTag(&tag, buf);
+ return (tag);
+}
+
+/*----------------------------------------------------------------------*/
+
+any *makeAny(unsigned long size, char *data)
+{
+ any *a = (any *) s_malloc((size_t) sizeof(any));
+
+ a->size = size;
+ a->bytes = data;
+ return (a);
+}
+
+/*----------------------------------------------------------------------*/
+
+void freeAny(any *a)
+/* destroy an any and its associated data. Assumes a->bytes was
+ allocated using the s_malloc family of libraries
+ */
+{
+ if (a != NULL) {
+ if (a->bytes != NULL)
+ s_free(a->bytes);
+ s_free(a);
+ }
+}
+
+/*----------------------------------------------------------------------*/
+
+any *duplicateAny(any *a)
+{
+ any *copy = NULL;
+
+ if (a == NULL)
+ return (NULL);
+
+ copy = (any *) s_malloc((size_t) sizeof(any));
+
+ copy->size = a->size;
+ if (a->bytes == NULL)
+ copy->bytes = NULL;
+ else {
+ copy->bytes = (char *) s_malloc((size_t) copy->size);
+ MemCpy(copy->bytes, a->bytes, copy->size);
+ }
+ return (copy);
+}
+
+/*----------------------------------------------------------------------*/
+
+char *writeAny(any *a, data_tag tag, char *buffer, long *len)
+/* write an any + tag and size info */
+{
+ char *buf = buffer;
+
+ if (a == NULL) /* handle unspecified optional args */
+ return (buf);
+
+ /* write the tags */
+ buf = writeTag(tag, buf, len);
+ buf = writeCompressedInteger(a->size, buf, len);
+
+ /* write the bytes */
+ CHECK_FOR_SPACE_LEFT(a->size, len);
+ MemCpy(buf, a->bytes, a->size);
+
+ return (buf + a->size);
+}
+
+/*----------------------------------------------------------------------*/
+
+char *readAny(any **anAny, char *buffer)
+/* read an any + tag and size info */
+{
+ char *buf;
+ any *a;
+ data_tag tag;
+
+ a = (any *) s_malloc((size_t) sizeof(any));
+
+ buf = buffer;
+
+ buf = readTag(&tag, buf);
+
+ buf = readCompressedInteger(&a->size, buf);
+
+ /* now simply copy the bytes */
+ a->bytes = (char *) s_malloc((size_t) a->size);
+ MemCpy(a->bytes, buf, a->size);
+ *anAny = a;
+
+ return (buf + a->size);
+}
+
+/*----------------------------------------------------------------------*/
+
+unsigned long writtenAnySize(data_tag tag, any *a)
+{
+ unsigned long size;
+
+ if (a == NULL)
+ return (0);
+
+ size = writtenTagSize(tag);
+ size += writtenCompressedIntSize(a->size);
+ size += a->size;
+ return (size);
+}
+
+/*----------------------------------------------------------------------*/
+
+any *stringToAny(char *s)
+{
+ any *a = NULL;
+
+ if (s == NULL)
+ return (NULL);
+
+ a = (any *) s_malloc((size_t) sizeof(any));
+
+ a->size = strlen(s);
+ a->bytes = (char *) s_malloc((size_t) a->size);
+ MemCpy(a->bytes, s, a->size);
+ return (a);
+}
+
+/*----------------------------------------------------------------------*/
+
+char *anyToString(any *a)
+{
+ char *s = NULL;
+
+ if (a == NULL)
+ return (NULL);
+
+ s = s_malloc((size_t) (a->size + 1));
+ MemCpy(s, a->bytes, a->size);
+ s[a->size] = '\0';
+ return (s);
+}
+
+/*----------------------------------------------------------------------*/
+
+char *writeString(char *s, data_tag tag, char *buffer, long *len)
+/* Write a C style string. The terminating null is not written.
+ This function is not part of the Z39.50 spec. It is provided
+ for the convenience of those wishing to pass C strings in
+ the place of an any.
+ */
+{
+ char *buf = buffer;
+ any *data = NULL;
+
+ if (s == NULL)
+ return (buffer); /* handle unused optional item before making an any */
+ data = (any *) s_malloc((size_t) sizeof(any));
+
+ data->size = strlen(s);
+ data->bytes = s; /* save a copy here by not using stringToAny() */
+ buf = writeAny(data, tag, buf, len);
+ s_free(data); /* don't use freeAny() since it will free s too */
+ return (buf);
+}
+
+/*----------------------------------------------------------------------*/
+
+char *readString(char **s, char *buffer)
+/* Read an any and convert it into a C style string.
+ This function is not part of the Z39.50 spec. It is provided
+ for the convenience of those wishing to pass C strings in
+ the place of an any.
+ */
+{
+ any *data = NULL;
+ char *buf = readAny(&data, buffer);
+
+ *s = anyToString(data);
+ freeAny(data);
+ return (buf);
+}
+
+/*----------------------------------------------------------------------*/
+
+unsigned long writtenStringSize(data_tag tag, char *s)
+{
+ unsigned long size;
+
+ if (s == NULL)
+ return (0);
+
+ size = writtenTagSize(tag);
+ size += writtenCompressedIntSize(size);
+ size += strlen(s);
+ return (size);
+}
+
+/*----------------------------------------------------------------------*/
+
+any *longToAny(long num)
+/* a convenience function */
+{
+ char s[40];
+
+ sprintf(s, "%ld", num);
+
+ return (stringToAny(s));
+}
+
+/*----------------------------------------------------------------------*/
+
+long anyToLong(any *a)
+/* a convenience function */
+{
+ long num;
+ char *str = NULL;
+
+ str = anyToString(a);
+ sscanf(str, "%ld", &num); /* could check the result and return
+ an error */
+ s_free(str);
+ return (num);
+}
+
+/*----------------------------------------------------------------------*/
+
+#define bitsPerByte 8
+
+bit_map *makeBitMap(unsigned long numBits, ...)
+/* construct and return a bitmap with numBits elements */
+{
+ va_list ap;
+ unsigned long i, j;
+ bit_map *bm = NULL;
+
+ LYva_start(ap, numBits);
+
+ bm = (bit_map *) s_malloc((size_t) sizeof(bit_map));
+
+ bm->size = (unsigned long) (ceil((double) numBits / bitsPerByte));
+ bm->bytes = (char *) s_malloc((size_t) bm->size);
+
+ /* fill up the bits */
+ for (i = 0; i < bm->size; i++) /* iterate over bytes */
+ {
+ char byte = 0;
+
+ for (j = 0; j < bitsPerByte; j++) /* iterate over bits */
+ {
+ if ((i * bitsPerByte + j) < numBits) {
+ boolean bit = false;
+
+ bit = (boolean) va_arg(ap, boolean);
+
+ if (bit) {
+ byte = byte | (1 << (bitsPerByte - j - 1));
+ }
+ }
+ }
+ bm->bytes[i] = byte;
+ }
+
+ va_end(ap);
+ return (bm);
+}
+
+/*----------------------------------------------------------------------*/
+
+void freeBitMap(bit_map *bm)
+/* destroy a bit map created by makeBitMap() */
+{
+ s_free(bm->bytes);
+ s_free(bm);
+}
+
+/*----------------------------------------------------------------------*/
+
+/* use this routine to interpret a bit map. pos specifies the bit
+ number. bit 0 is the Leftmost bit of the first byte.
+ Could do bounds checking.
+ */
+
+boolean bitAtPos(unsigned long pos, bit_map *bm)
+{
+ if (pos > bm->size * bitsPerByte)
+ return false;
+ else
+ return ((bm->bytes[(pos / bitsPerByte)] &
+ (0x80 >> (pos % bitsPerByte))) ?
+ true : false);
+}
+
+/*----------------------------------------------------------------------*/
+
+char *writeBitMap(bit_map *bm, data_tag tag, char *buffer, long *len)
+/* write a bitmap + type and size info */
+{
+ return (writeAny((any *) bm, tag, buffer, len));
+}
+
+/*----------------------------------------------------------------------*/
+
+char *readBitMap(bit_map **bm, char *buffer)
+/* read a bitmap + type and size info */
+{
+ char *c;
+
+ c = readAny((any **) bm, buffer);
+ return (c);
+}
+
+/*----------------------------------------------------------------------*/
+
+char *writeByte(unsigned long byte, char *buf, long *len)
+{
+ CHECK_FOR_SPACE_LEFT(1, len);
+ buf[0] = byte & 0xFF; /* we really only want the first byte */
+ return (buf + 1);
+}
+
+/*----------------------------------------------------------------------*/
+
+char *readByte(unsigned char *byte, char *buf)
+{
+ *byte = buf[0];
+ return (buf + 1);
+}
+
+/*----------------------------------------------------------------------*/
+
+char *writeBoolean(boolean flag, char *buf, long *len)
+{
+ return (writeByte(flag, buf, len));
+}
+
+/*----------------------------------------------------------------------*/
+
+char *readBoolean(boolean *flag, char *buffer)
+{
+ unsigned char byte;
+ char *buf = readByte(&byte, buffer);
+
+ *flag = (byte == true) ? true : false;
+ return (buf);
+}
+
+/*----------------------------------------------------------------------*/
+
+char *writePDUType(pdu_type pduType, char *buf, long *len)
+/* PDUType is a single byte */
+{
+ return (writeBinaryInteger((long) pduType, (unsigned long) 1, buf, len));
+}
+
+/*----------------------------------------------------------------------*/
+
+char *readPDUType(pdu_type *pduType, char *buf)
+/* PDUType is a single byte */
+{
+ return (readBinaryInteger((long *) pduType, (unsigned long) 1, buf));
+}
+
+/*----------------------------------------------------------------------*/
+
+pdu_type peekPDUType(char *buf)
+/* read the next pdu without advancing the buffer, Note that this
+ function is to be used on a buffer that is known to contain an
+ APDU. The pdu_type is written HEADER_LEN bytes into the buffer
+ */
+{
+ pdu_type pdu;
+
+ readPDUType(&pdu, buf + HEADER_LEN);
+ return (pdu);
+}
+
+/*----------------------------------------------------------------------*/
+
+#define BINARY_INTEGER_BYTES sizeof(long) /* the number of bytes used by
+ a "binary integer" */
+char *writeBinaryInteger(long num, unsigned long size, char *buf, long *len)
+/* write out first size bytes of num - no type info
+ XXX should this take unsigned longs instead ??? */
+{
+ long i;
+ char byte;
+
+ if (size < 1 || size > BINARY_INTEGER_BYTES)
+ return (NULL); /* error */
+
+ CHECK_FOR_SPACE_LEFT(size, len);
+
+ for (i = size - 1; i >= 0; i--) {
+ byte = (char) (num & 255);
+ buf[i] = byte;
+ num = num >> bitsPerByte; /* don't and here */
+ }
+
+ return (buf + size);
+}
+
+/*----------------------------------------------------------------------*/
+
+char *readBinaryInteger(long *num, unsigned long size, char *buf)
+/* read in first size bytes of num - no type info
+ XXX this should take unsigned longs instead !!! */
+{
+ unsigned long i;
+ unsigned char byte;
+
+ if (size < 1 || size > BINARY_INTEGER_BYTES)
+ return (buf); /* error */
+ *num = 0;
+
+ for (i = 0; i < size; i++) {
+ byte = buf[i];
+ *num = *num << bitsPerByte;
+ *num += byte;
+ }
+
+ return (buf + size);
+}
+
+/*----------------------------------------------------------------------*/
+
+unsigned long writtenCompressedBinIntSize(long num)
+/* return the number of bytes needed to represent the value num.
+ currently limited to max of 4 bytes
+ Only compresses for positive nums - negatives get whole 4 bytes
+ */
+{
+ if (num < 0L)
+ return (4);
+ else if (num < 256L) /* 2**8 */
+ return (1);
+ else if (num < 65536L) /* 2**16 */
+ return (2);
+ else if (num < 16777216L) /* 2**24 */
+ return (3);
+ else
+ return (4);
+}
+
+/*----------------------------------------------------------------------*/
+
+char *writeNum(long num, data_tag tag, char *buffer, long *len)
+/* write a binary integer + size and tag info */
+{
+ char *buf = buffer;
+ long size = writtenCompressedBinIntSize(num);
+
+ if (num == UNUSED)
+ return (buffer);
+
+ buf = writeTag(tag, buf, len);
+ buf = writeCompressedInteger(size, buf, len);
+ buf = writeBinaryInteger(num, (unsigned long) size, buf, len);
+ return (buf);
+}
+
+/*----------------------------------------------------------------------*/
+
+char *readNum(long *num, char *buffer)
+/* read a binary integer + size and tag info */
+{
+ char *buf = buffer;
+ data_tag tag;
+ unsigned long size;
+ unsigned long val;
+
+ buf = readTag(&tag, buf);
+ buf = readCompressedInteger(&val, buf);
+ size = (unsigned long) val;
+ buf = readBinaryInteger(num, size, buf);
+ return (buf);
+}
+
+/*----------------------------------------------------------------------*/
+
+unsigned long writtenNumSize(data_tag tag, long num)
+{
+ long dataSize = writtenCompressedBinIntSize(num);
+ long size;
+
+ size = writtenTagSize(tag); /* space for the tag */
+ size += writtenCompressedIntSize(dataSize); /* space for the size */
+ size += dataSize; /* space for the data */
+
+ return (size);
+}
+
+/*----------------------------------------------------------------------*/
+
+typedef void (voidfunc) (void *);
+
+void doList(void **list, voidfunc * func)
+/* call func on each element of the NULL terminated list of pointers */
+{
+ register long i;
+ register void *ptr = NULL;
+
+ if (list == NULL)
+ return;
+ for (i = 0, ptr = list[i]; ptr != NULL; ptr = list[++i])
+ (*func) (ptr);
+}
+
+/*----------------------------------------------------------------------*/
+
+char *writeProtocolVersion(char *buf, long *len)
+/* write a bitmap describing the protocols available */
+{
+ static bit_map *version = NULL;
+
+ if (version == NULL) {
+ version = makeBitMap((unsigned long) 1, true); /* version 1! */
+ }
+
+ return (writeBitMap(version, DT_ProtocolVersion, buf, len));
+}
+
+/*----------------------------------------------------------------------*/
+
+char *defaultImplementationID(void)
+{
+ static char ImplementationID[] = "TMC";
+
+ return (ImplementationID);
+}
+
+/*----------------------------------------------------------------------*/
+
+char *defaultImplementationName(void)
+{
+ static char ImplementationName[] = "Thinking Machines Corporation Z39.50";
+
+ return (ImplementationName);
+}
+
+/*----------------------------------------------------------------------*/
+
+char *defaultImplementationVersion(void)
+{
+ static char ImplementationVersion[] = "2.0A";
+
+ return (ImplementationVersion);
+}
+
+/*----------------------------------------------------------------------*/
+
+/*
+ * Routines originally from ZType1.c -- FM
+ *
+ *----------------------------------------------------------------------*/
+/* WIDE AREA INFORMATION SERVER SOFTWARE:
+ * No guarantees or restrictions. See the readme file for the full standard
+ * disclaimer.
+ *
+ * 3.26.90 Harry Morris, morris@think.com
+ * 4.11.90 HWM - generalized conditional includes (see c-dialect.h)
+ */
+/*----------------------------------------------------------------------*/
+
+query_term *makeAttributeTerm(char *use,
+ char *relation,
+ char *position,
+ char *structure,
+ char *truncation,
+ char *completeness,
+ any *term)
+{
+ query_term *qt = (query_term *) s_malloc((size_t) sizeof(query_term));
+
+ qt->TermType = TT_Attribute;
+
+ /* copy in the attributes */
+ LYStrNCpy(qt->Use, use, ATTRIBUTE_SIZE);
+ LYStrNCpy(qt->Relation, relation, ATTRIBUTE_SIZE);
+ LYStrNCpy(qt->Position, position, ATTRIBUTE_SIZE);
+ LYStrNCpy(qt->Structure, structure, ATTRIBUTE_SIZE);
+ LYStrNCpy(qt->Truncation, truncation, ATTRIBUTE_SIZE);
+ LYStrNCpy(qt->Completeness, completeness, ATTRIBUTE_SIZE);
+
+ qt->Term = duplicateAny(term);
+
+ qt->ResultSetID = NULL;
+
+ return (qt);
+}
+
+/*----------------------------------------------------------------------*/
+
+query_term *makeResultSetTerm(any *resultSet)
+{
+ query_term *qt = (query_term *) s_malloc((size_t) sizeof(query_term));
+
+ qt->TermType = TT_ResultSetID;
+
+ qt->ResultSetID = duplicateAny(resultSet);
+
+ qt->Term = NULL;
+
+ return (qt);
+}
+
+/*----------------------------------------------------------------------*/
+
+query_term *makeOperatorTerm(char *operatorCode)
+{
+ query_term *qt = (query_term *) s_malloc((size_t) sizeof(query_term));
+
+ qt->TermType = TT_Operator;
+
+ LYStrNCpy(qt->Operator, operatorCode, OPERATOR_SIZE);
+
+ qt->Term = NULL;
+ qt->ResultSetID = NULL;
+
+ return (qt);
+}
+
+/*----------------------------------------------------------------------*/
+
+void freeTerm(void *param)
+{
+ query_term *qt = (query_term *) param;
+
+ switch (qt->TermType) {
+ case TT_Attribute:
+ freeAny(qt->Term);
+ break;
+ case TT_ResultSetID:
+ freeAny(qt->ResultSetID);
+ break;
+ case TT_Operator:
+ /* do nothing */
+ break;
+ default:
+ panic("Implementation error: Unknown term type %ld",
+ qt->TermType);
+ break;
+ }
+ s_free(qt);
+}
+
+/*----------------------------------------------------------------------*/
+
+#define ATTRIBUTE_LIST_SIZE ATTRIBUTE_SIZE * 6
+#define AT_DELIMITER " "
+
+char *writeQueryTerm(query_term *qt, char *buffer, long *len)
+{
+ char *buf = buffer;
+ char attributes[ATTRIBUTE_LIST_SIZE];
+
+ switch (qt->TermType) {
+ case TT_Attribute:
+ LYStrNCpy(attributes, qt->Use, ATTRIBUTE_LIST_SIZE);
+ s_strncat(attributes, AT_DELIMITER, sizeof(AT_DELIMITER) + 1, ATTRIBUTE_LIST_SIZE);
+ s_strncat(attributes, qt->Relation, ATTRIBUTE_SIZE, ATTRIBUTE_LIST_SIZE);
+ s_strncat(attributes, AT_DELIMITER, sizeof(AT_DELIMITER) + 1, ATTRIBUTE_LIST_SIZE);
+ s_strncat(attributes, qt->Position, ATTRIBUTE_SIZE, ATTRIBUTE_LIST_SIZE);
+ s_strncat(attributes, AT_DELIMITER, sizeof(AT_DELIMITER) + 1, ATTRIBUTE_LIST_SIZE);
+ s_strncat(attributes, qt->Structure, ATTRIBUTE_SIZE, ATTRIBUTE_LIST_SIZE);
+ s_strncat(attributes, AT_DELIMITER, sizeof(AT_DELIMITER) + 1, ATTRIBUTE_LIST_SIZE);
+ s_strncat(attributes, qt->Truncation, ATTRIBUTE_SIZE, ATTRIBUTE_LIST_SIZE);
+ s_strncat(attributes, AT_DELIMITER, sizeof(AT_DELIMITER) + 1, ATTRIBUTE_LIST_SIZE);
+ s_strncat(attributes, qt->Completeness, ATTRIBUTE_SIZE, ATTRIBUTE_LIST_SIZE);
+ buf = writeString(attributes, DT_AttributeList, buf, len);
+ buf = writeAny(qt->Term, DT_Term, buf, len);
+ break;
+ case TT_ResultSetID:
+ buf = writeAny(qt->ResultSetID, DT_ResultSetID, buf, len);
+ break;
+ case TT_Operator:
+ buf = writeString(qt->Operator, DT_Operator, buf, len);
+ break;
+ default:
+ panic("Implementation error: Unknown term type %ld",
+ qt->TermType);
+ break;
+ }
+
+ return (buf);
+}
+
+/*----------------------------------------------------------------------*/
+
+char *readQueryTerm(query_term **qt, char *buffer)
+{
+ char *buf = buffer;
+ char *attributeList = NULL;
+ char *operator = NULL;
+ any *term;
+ char *use = NULL;
+ char *relation = NULL;
+ char *position = NULL;
+ char *structure = NULL;
+ char *truncation = NULL;
+ char *completeness;
+ any *resultSetID = NULL;
+ data_tag tag;
+
+ tag = peekTag(buffer);
+
+ switch (tag) {
+ case DT_AttributeList:
+ buf = readString(&attributeList, buf);
+ buf = readAny(&term, buf);
+ use = strtok(attributeList, AT_DELIMITER);
+ relation = strtok(NULL, AT_DELIMITER);
+ position = strtok(NULL, AT_DELIMITER);
+ structure = strtok(NULL, AT_DELIMITER);
+ truncation = strtok(NULL, AT_DELIMITER);
+ completeness = strtok(NULL, AT_DELIMITER);
+ *qt = makeAttributeTerm(use, relation, position, structure,
+ truncation, completeness, term);
+ s_free(attributeList);
+ freeAny(term);
+ break;
+ case DT_ResultSetID:
+ buf = readAny(&resultSetID, buf);
+ *qt = makeResultSetTerm(resultSetID);
+ freeAny(resultSetID);
+ break;
+ case DT_Operator:
+ buf = readString(&operator, buf);
+ *qt = makeOperatorTerm(operator);
+ s_free(operator);
+ break;
+ default:
+ REPORT_READ_ERROR(buf);
+ break;
+ }
+
+ return (buf);
+}
+
+/*----------------------------------------------------------------------*/
+
+static unsigned long getQueryTermSize(query_term *qt);
+
+static unsigned long getQueryTermSize(query_term *qt)
+/* figure out how many bytes it will take to write this query */
+{
+ unsigned long size = 0;
+ static char attributes[] = "11 22 33 44 55 66"; /* we just need this to
+
+ calculate its written
+ size */
+
+ switch (qt->TermType) {
+ case TT_Attribute:
+ size = writtenStringSize(DT_AttributeList, attributes);
+ size += writtenAnySize(DT_Term, qt->Term);
+ break;
+ case TT_ResultSetID:
+ size = writtenAnySize(DT_ResultSetID, qt->ResultSetID);
+ break;
+ case TT_Operator:
+ size = writtenStringSize(DT_Operator, qt->Operator);
+ break;
+ default:
+ panic("Implementation error: Unknown term type %ld",
+ qt->TermType);
+ break;
+ }
+
+ return (size);
+}
+
+/*----------------------------------------------------------------------*/
+
+/* A query is simply a null terminated list of query terms. For
+ transmission, a query is written into an any which is sent as
+ the user information field. */
+
+any *writeQuery(query_term **terms)
+{
+ any *info = NULL;
+ char *writePos = NULL;
+ char *data = NULL;
+ unsigned long size = 0;
+ long remaining = 0;
+ long i;
+ query_term *qt = NULL;
+
+ if (terms == NULL)
+ return (NULL);
+
+ /* calculate the size of write buffer */
+ for (i = 0, qt = terms[i]; qt != NULL; qt = terms[++i])
+ size += getQueryTermSize(qt);
+
+ data = (char *) s_malloc((size_t) size);
+
+ /* write the terms */
+ writePos = data;
+ remaining = size;
+ for (i = 0, qt = terms[i]; qt != NULL; qt = terms[++i])
+ writePos = writeQueryTerm(qt, writePos, &remaining);
+
+ info = makeAny(size, data);
+
+ return (info);
+}
+
+/*----------------------------------------------------------------------*/
+
+query_term **readQuery(any *info)
+{
+ char *readPos = info->bytes;
+ query_term **terms = NULL;
+ query_term *qt = NULL;
+ long numTerms = 0L;
+ char tmp[100];
+
+ sprintf(tmp, "readquery: bytes: %ld", info->size);
+ log_write(tmp);
+
+ while (readPos < info->bytes + info->size) {
+ readPos = readQueryTerm(&qt, readPos);
+
+ if (terms == NULL) {
+ terms = (query_term **) s_malloc((size_t) (sizeof(query_term *) * 2));
+ } else {
+ terms =
+ (query_term **) s_realloc((char *) terms,
+ (size_t) (sizeof(query_term *) *
+ (numTerms + 2)));
+ }
+ if (qt == NULL)
+ log_write("qt = null");
+ terms[numTerms++] = qt;
+ terms[numTerms] = NULL;
+ }
+
+ return (terms);
+}
+
+/*----------------------------------------------------------------------*/
+
+/*
+ * Routines originally from panic.c -- FM
+ *
+ *----------------------------------------------------------------------*/
+/* WIDE AREA INFORMATION SERVER SOFTWARE:
+ * No guarantees or restrictions. See the readme file for the full standard
+ * disclaimer.
+ *
+ * Morris@think.com
+ */
+
+/* panic is an error system interface. On the Mac, it will pop
+ * up a little window to explain the problem.
+ * On a unix box, it will print out the error and call perror()
+ */
+
+/*----------------------------------------------------------------------*/
+
+static void exitAction(long error);
+
+static void exitAction(long error GCC_UNUSED)
+{
+ exit_immediately(EXIT_SUCCESS);
+}
+
+/*----------------------------------------------------------------------*/
+
+#define PANIC_HEADER "Fatal Error: "
+
+void panic(char *format, ...)
+{
+ va_list ap; /* the variable arguments */
+
+ fprintf(stderr, PANIC_HEADER);
+ LYva_start(ap, format); /* init ap */
+ vfprintf(stderr, format, ap); /* print the contents */
+ va_end(ap); /* free ap */
+ fflush(stderr);
+
+ exitAction(0);
+}
+
+/*----------------------------------------------------------------------*/
+
+/*
+ * Routines originally from cutil.c -- FM
+ *
+ *----------------------------------------------------------------------*/
+/* Wide AREA INFORMATION SERVER SOFTWARE
+ * No guarantees or restrictions. See the readme file for the full standard
+ * disclaimer.
+ *
+ * 3.26.90 Harry Morris, morris@think.com
+ * 4.11.90 HWM - generalized conditional includes (see c-dialect.h)
+ */
+
+/*----------------------------------------------------------------------*/
+
+void fs_checkPtr(void *ptr)
+/* If the ptr is NULL, give an error */
+{
+ if (ptr == NULL)
+ panic("checkPtr found a NULL pointer");
+}
+
+/*----------------------------------------------------------------------*/
+
+void *fs_malloc(size_t size)
+/* does safety checks and optional accounting */
+{
+ register void *ptr = NULL;
+
+ ptr = (void *) calloc((size_t) size, (size_t) 1);
+ s_checkPtr(ptr);
+
+ return (ptr);
+}
+
+/*----------------------------------------------------------------------*/
+
+void *fs_realloc(void *ptr, size_t size)
+/* does safety checks and optional accounting
+ note - we don't know how big ptr's memory is, so we can't ensure
+ that any new memory allocated is NULLed!
+ */
+{
+ register void *nptr = NULL;
+
+ if (ptr == NULL) /* this is really a malloc */
+ return (s_malloc(size));
+
+ nptr = (void *) realloc(ptr, size);
+ s_checkPtr(ptr);
+
+ return (nptr);
+}
+
+/*----------------------------------------------------------------------*/
+
+void fs_free(void *ptr)
+/* does safety checks and optional accounting */
+{
+ if (ptr != NULL) /* some non-ansi compilers/os's can't handle freeing null */
+ { /* if we knew the size of this block of memory, we could clear it - oh well */
+ free(ptr);
+ ptr = NULL;
+ }
+}
+
+/*----------------------------------------------------------------------*/
+
+char *s_strdup(char *s)
+
+/* return a copy of s. This is identical to the standard library routine
+ strdup(), except that it is safe. If s == NULL or malloc fails,
+ appropriate action is taken.
+ */
+{
+ unsigned long len;
+ char *copy = NULL;
+
+ if (s == NULL) /* safety check to postpone stupid errors */
+ return (NULL);
+
+ len = strlen(s); /* length of string - terminator */
+ copy = (char *) s_malloc((size_t) (sizeof(char) * (len + 1)));
+
+ StrNCpy(copy, s, len + 1);
+ return (copy);
+}
+
+/*----------------------------------------------------------------------*/
+
+char *fs_strncat(char *dst, char *src, size_t maxToAdd, size_t maxTotal)
+
+/* like strncat, except the fourth argument limits the maximum total
+ length of the resulting string
+ */
+{
+ size_t dstSize = strlen(dst);
+ size_t srcSize = strlen(src);
+
+ if (dstSize + srcSize < maxTotal) /* use regular old strncat */
+ return (StrNCat(dst, src, maxToAdd));
+ else {
+ size_t truncateTo = maxTotal - dstSize - 1;
+ char saveChar = src[truncateTo];
+ char *result = NULL;
+
+ src[truncateTo] = '\0';
+ result = StrNCat(dst, src, maxToAdd);
+ src[truncateTo] = saveChar;
+ return (result);
+ }
+}
+
+/*----------------------------------------------------------------------*/
+
+char char_downcase(unsigned long long_ch)
+{
+ unsigned char ch = long_ch & 0xFF; /* just want one byte */
+
+ /* when ansi is the way of the world, this can be tolower */
+ return (((ch >= 'A') && (ch <= 'Z')) ? (ch + 'a' - 'A') : ch);
+}
+
+char *string_downcase(char *word)
+{
+ long i = 0;
+
+ while (word[i] != '\0') {
+ word[i] = char_downcase((unsigned long) word[i]);
+ i++;
+ }
+ return (word);
+}
+
+/*----------------------------------------------------------------------*/
+#endif /* VMS */
diff --git a/WWW/Library/Implementation/HTVMS_WaisUI.h b/WWW/Library/Implementation/HTVMS_WaisUI.h
new file mode 100644
index 0000000..4b5b06d
--- /dev/null
+++ b/WWW/Library/Implementation/HTVMS_WaisUI.h
@@ -0,0 +1,664 @@
+/* HTVMS_WAISUI.h
+ *
+ * Adaptation for Lynx by F.Macrides (macrides@sci.wfeb.edu)
+ *
+ * 31-May-1994 FM Initial version.
+ */
+
+#ifndef HTVMSWAIS_H
+#define HTVMSWAIS_H
+
+#ifndef HTUTILS_H
+#include <HTUtils.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+ void log_write(char *);
+
+/*
+ * Routines originally from Panic.h -- FM
+ *
+ *----------------------------------------------------------------------*/
+
+ void panic(char *format, ...);
+
+/*----------------------------------------------------------------------*/
+
+/*
+ * Routines originally from CUtil.h -- FM
+ *
+ *----------------------------------------------------------------------*/
+
+/* types and constants */
+
+#ifndef boolean
+#define boolean unsigned long
+#endif /* boolean */
+
+#ifndef true
+#define true (boolean)1L
+#endif /* true */
+
+#ifndef false
+#define false (boolean)0L /* used to be (!true), but broke
+ some compilers */
+#endif /* false */
+
+#ifndef TRUE
+#define TRUE true
+#endif /* TRUE */
+
+#ifndef FALSE
+#define FALSE false
+#endif /* FALSE */
+
+/*----------------------------------------------------------------------*/
+/* functions */
+
+/* enhanced memory handling functions - don't call them directly, use the
+ macros below */
+ void fs_checkPtr(void *ptr);
+ void *fs_malloc(size_t size);
+ void *fs_realloc(void *ptr, size_t size);
+ void fs_free(void *ptr);
+ char *fs_strncat(char *dst, char *src, size_t maxToAdd, size_t maxTotal);
+
+/* macros for memory functions. call these in your program. */
+#define s_checkPtr(ptr) fs_checkPtr(ptr)
+#define s_malloc(size) fs_malloc(size)
+#define s_realloc(ptr,size) fs_realloc((ptr),(size))
+#define s_free(ptr) { fs_free((char*)ptr); ptr = NULL; }
+#define s_strncat(dst,src,maxToAdd,maxTotal) fs_strncat((dst),(src),(maxToAdd),(maxTotal))
+
+ char *s_strdup(char *s);
+
+#define IS_DELIMITER 1
+#define NOT_DELIMITER !IS_DELIMITER
+
+ char char_downcase(unsigned long ch);
+ char *string_downcase(char *word);
+
+/*----------------------------------------------------------------------*/
+
+/*
+ * Routines originally from ZUtil.c -- FM
+ *
+ *----------------------------------------------------------------------*/
+
+/* Data types / constants */
+
+/* bytes to leave for the header size info */
+#define HEADER_LEN (size_t)2
+
+ typedef long pdu_type;
+
+#define initAPDU (pdu_type)20
+#define initResponseAPDU (pdu_type)21
+#define searchAPDU (pdu_type)22
+#define searchResponseAPDU (pdu_type)23
+#define presentAPDU (pdu_type)24
+#define presentResponseAPDU (pdu_type)25
+#define deteteAPDU (pdu_type)26
+#define deleteResponseAPDU (pdu_type)27
+#define accessControlAPDU (pdu_type)28
+#define accessControlResponseAPDU (pdu_type)29
+#define resourceControlAPDU (pdu_type)30
+#define resourceControlResponseAPDU (pdu_type)31
+
+ typedef struct any { /* an any is a non-ascii string of characters */
+ unsigned long size;
+ char *bytes;
+ } any;
+
+ typedef any bit_map; /* a bit_map is a group of packed bits */
+
+ typedef unsigned long data_tag;
+
+#define DT_PDUType (data_tag)1
+#define DT_ReferenceID (data_tag)2
+#define DT_ProtocolVersion (data_tag)3
+#define DT_Options (data_tag)4
+#define DT_PreferredMessageSize (data_tag)5
+#define DT_MaximumRecordSize (data_tag)6
+#define DT_IDAuthentication (data_tag)7
+#define DT_ImplementationID (data_tag)8
+#define DT_ImplementationName (data_tag)9
+#define DT_ImplementationVersion (data_tag)10
+#define DT_UserInformationField (data_tag)11
+#define DT_Result (data_tag)12
+#define DT_SmallSetUpperBound (data_tag)13
+#define DT_LargeSetLowerBound (data_tag)14
+#define DT_MediumSetPresentNumber (data_tag)15
+#define DT_ReplaceIndicator (data_tag)16
+#define DT_ResultSetName (data_tag)17
+#define DT_DatabaseNames (data_tag)18
+#define DT_ElementSetNames (data_tag)19
+#define DT_QueryType (data_tag)20
+#define DT_Query (data_tag)21
+#define DT_SearchStatus (data_tag)22
+#define DT_ResultCount (data_tag)23
+#define DT_NumberOfRecordsReturned (data_tag)24
+#define DT_NextResultSetPosition (data_tag)25
+#define DT_ResultSetStatus (data_tag)26
+#define DT_PresentStatus (data_tag)27
+#define DT_DatabaseDiagnosticRecords (data_tag)28
+#define DT_NumberOfRecordsRequested (data_tag)29
+#define DT_ResultSetStartPosition (data_tag)30
+#define DT_ResultSetID (data_tag)31
+#define DT_DeleteOperation (data_tag)32
+#define DT_DeleteStatus (data_tag)33
+#define DT_NumberNotDeleted (data_tag)34
+#define DT_BulkStatuses (data_tag)35
+#define DT_DeleteMSG (data_tag)36
+#define DT_SecurityChallenge (data_tag)37
+#define DT_SecurityChallengeResponse (data_tag)38
+#define DT_SuspendedFlag (data_tag)39
+#define DT_ResourceReport (data_tag)40
+#define DT_PartialResultsAvailable (data_tag)41
+#define DT_ContinueFlag (data_tag)42
+#define DT_ResultSetWanted (data_tag)43
+
+#define UNUSED -1
+
+/* number of bytes required to represent the following sizes in compressed
+ integer format
+ */
+#define CompressedInt1Byte 128 /* 2 ^ 7 */
+#define CompressedInt2Byte 16384 /* 2 ^ 14 */
+#define CompressedInt3Byte 2097152 /* 2 ^ 21 */
+/* others may follow ... */
+
+/* types of query */
+#define QT_0 "0" /* query whose non-standard format has been agreed upon
+ client and server */
+/* values for InitAPDU option element */
+#define WILL_USE TRUE
+#define WILL_NOT_USE FALSE
+#define WILL_SUPPORT TRUE
+#define WILL_NOT_SUPPORT FALSE
+
+/* values for InitResponseAPDU result element */
+#define ACCEPT TRUE
+#define REJECT FALSE
+
+/* values for SearchResponseAPDU search status element */
+#define SUCCESS 0 /* intuitive huh? */
+#define FAILURE 1
+
+/* values for SearchResponseAPDU result set status element */
+#define SUBSET 1
+#define INTERIM 2
+#define NONE 3
+
+/* values for SearchResponseAPDU present status element */
+/* SUCCESS already defined */
+#define PARTIAL_1 1
+#define PARTIAL_2 2
+#define PARTIAL_3 3
+#define PARTIAL_4 4
+#define PS_NONE 5 /* can't use NONE since it was used by result
+ set status */
+
+#define DIAGNOSTIC_CODE_SIZE (size_t)3
+
+ typedef struct diagnosticRecord {
+ boolean SURROGATE;
+ char DIAG[DIAGNOSTIC_CODE_SIZE];
+ char *ADDINFO;
+ } diagnosticRecord;
+
+#define D_PermanentSystemError "S1"
+#define D_TemporarySystemError "S2"
+#define D_UnsupportedSearch "S3"
+#define D_TermsOnlyStopWords "S5"
+#define D_TooManyArgumentWords "S6"
+#define D_TooManyBooleanOperators "S7"
+#define D_TooManyTruncatedWords "S8"
+#define D_TooMany IncompleteSubfields "S9"
+#define D_TruncatedWordsTooShort "SA"
+#define D_InvalidFormatForRecordNumber "SB"
+#define D_TooManyCharactersInSearch "SC"
+#define D_TooManyRecordsRetrieved "SD"
+#define D_PresentRequestOutOfRange "SF"
+#define D_SystemErrorInPresentRecords "SG"
+#define D_RecordNotAuthorizedToBeSent "SH"
+#define D_RecordExceedsPrefMessageSize "SI"
+#define D_RecordExceedsMaxRecordSize "SJ"
+#define D_ResultSetNotSuppAsSearchTerm "SK"
+#define D_OnlyOneRsltSetAsSrchTermSupp "SL"
+#define D_OnlyANDingOfASnglRsltSetSupp "SM"
+#define D_RsltSetExistsNoReplace "SN"
+#define D_ResultSetNamingNotSupported "SO"
+#define D_CombinationDatabasesNotSupp "SP"
+#define D_ElementSetNamesNotSupported "SQ"
+#define D_ElementSetNameNotValid "SR"
+#define D_OnlyASingleElmntSetNameSupp "SS"
+#define D_ResultSetDeletedByTarget "ST"
+#define D_ResultSetIsInUse "SU"
+#define D_DatabasesIsLocked "SV"
+#define D_TerminatedByNoContinueResp "SW"
+#define D_ResultSetDoesNotExist "SX"
+#define D_ResExNoResultsAvailable "SY"
+#define D_ResExUnpredictableResults "SZ"
+#define D_ResExValidSubsetOfResults "T1"
+#define D_AccessControlFailure "T2"
+#define D_SecurityNotIssuedReqTerm "T3"
+#define D_SecurityNotBeIssuedRecNotInc "T4"
+
+/*----------------------------------------------------------------------*/
+
+/* for internal error handling */
+
+ extern char *readErrorPosition; /* pos where buf stopped making sense */
+
+/* the following are macros so that they can return OUT of the function
+ which calls them
+ */
+
+#define RETURN_ON_NULL(var) \
+ if (var == NULL) \
+ return(NULL); /* jump out of caller */
+
+#define REPORT_READ_ERROR(pos) \
+ { readErrorPosition = (pos); \
+ return(NULL); /* jump out of caller */ \
+ }
+
+#define CHECK_FOR_SPACE_LEFT(spaceNeeded,spaceLeft) \
+ { if (*spaceLeft >= spaceNeeded) \
+ (*spaceLeft) -= spaceNeeded; \
+ else \
+ { *spaceLeft = 0; \
+ return(NULL); /* jump out of the caller */ \
+ } \
+ }
+
+/*----------------------------------------------------------------------*/
+
+ diagnosticRecord *makeDiag(boolean surrogate, char *code, char *addInfo);
+ void freeDiag(diagnosticRecord * diag);
+ char *writeDiag(diagnosticRecord * diag, char *buffer, long *len);
+ char *readDiag(diagnosticRecord ** diag, char *buffer);
+
+ char *writeCompressedInteger(unsigned long num, char *buf, long *len);
+ char *readCompressedInteger(unsigned long *num, char *buf);
+ char *writeCompressedIntWithPadding(unsigned long num, unsigned long size,
+ char *buffer, long *len);
+ unsigned long writtenCompressedIntSize(unsigned long num);
+
+ char *writeTag(data_tag tag, char *buf, long *len);
+ char *readTag(data_tag *tag, char *buf);
+ data_tag peekTag(char *buf);
+ unsigned long writtenTagSize(data_tag tag);
+
+ any *makeAny(unsigned long size, char *data);
+ void freeAny(any *a);
+ any *duplicateAny(any *a);
+ char *writeAny(any *a, data_tag tag, char *buffer, long *len);
+ char *readAny(any **anAny, char *buffer);
+ unsigned long writtenAnySize(data_tag tag, any *a);
+
+ any *stringToAny(char *s);
+ char *anyToString(any *a);
+ unsigned long writtenStringSize(data_tag tag, char *s);
+
+ any *longToAny(long Num);
+ long anyToLong(any *a);
+
+ char *writeString(char *s, data_tag tag, char *buffer, long *len);
+ char *readString(char **s, char *buffer);
+
+ bit_map *makeBitMap(unsigned long numBits, ...);
+
+ void freeBitMap(bit_map *bm);
+ boolean bitAtPos(unsigned long pos, bit_map *bm);
+ char *writeBitMap(bit_map *bm, data_tag tag, char *buffer, long *len);
+ char *readBitMap(bit_map **bm, char *buffer);
+
+ char *writeByte(unsigned long byte, char *buf, long *len);
+ char *readByte(unsigned char *byte, char *buf);
+
+ char *writeBoolean(boolean flag, char *buf, long *len);
+ char *readBoolean(boolean *flag, char *buf);
+
+ char *writePDUType(pdu_type pduType, char *buf, long *len);
+ char *readPDUType(pdu_type *pduType, char *buf);
+ pdu_type peekPDUType(char *buf);
+
+ char *writeBinaryInteger(long num, unsigned long size,
+ char *buf, long *len);
+ char *readBinaryInteger(long *num, unsigned long size, char *buf);
+ unsigned long writtenCompressedBinIntSize(long num);
+
+ char *writeNum(long num, data_tag tag, char *buffer, long *len);
+ char *readNum(long *num, char *buffer);
+ unsigned long writtenNumSize(data_tag tag, long num);
+
+ void doList(void **list, void (*func) (void *));
+
+ char *writeProtocolVersion(char *buf, long *len);
+ char *defaultImplementationID(void);
+ char *defaultImplementationName(void);
+ char *defaultImplementationVersion(void);
+
+/*----------------------------------------------------------------------*/
+
+/*
+ * Routines originally from ZType1.c -- FM
+ *
+ *----------------------------------------------------------------------*/
+
+/* This file implements the type 1 query defined in appendices B & C
+ of the SR 1 spec.
+ */
+
+/*----------------------------------------------------------------------*/
+/* types and constants */
+
+/* new data tags */
+#define DT_AttributeList (data_tag)44
+#define DT_Term (data_tag)45
+#define DT_Operator (data_tag)46
+
+#define QT_BooleanQuery "1" /* standard boolean query */
+
+/* general attribute code - use in place of any attribute */
+#define IGNORE "ig"
+
+/* use value codes */
+#define UV_ISBN "ub"
+#define CORPORATE_NAME "uc"
+#define ISSN "us"
+#define PERSONAL_NAME "up"
+#define SUBJECT "uj"
+#define TITLE "ut"
+#define GEOGRAPHIC_NAME "ug"
+#define CODEN "ud"
+#define SUBJECT_SUBDIVISION "ue"
+#define SERIES_TITLE "uf"
+#define MICROFORM_GENERATION "uh"
+#define PLACE_OF_PUBLICATION "ui"
+#define NUC_CODE "uk"
+#define LANGUAGE "ul"
+#define COMBINATION_OF_USE_VALUES "um"
+#define SYSTEM_CONTROL_NUMBER "un"
+#define DATE "uo"
+#define LC_CONTROL_NUMBER "ur"
+#define MUSIC_PUBLISHERS_NUMBER "uu"
+#define GOVERNMENT_DOCUMENTS_NUMBER "uv"
+#define SUBJECT_CLASSIFICATION "uw"
+#define RECORD_TYPE "uy"
+
+/* relation value codes */
+#define EQUAL "re"
+#define GREATER_THAN "rg"
+#define GREATER_THAN_OR_EQUAL "ro"
+#define LESS_THAN "rl"
+#define LESS_THAN_OR_EQUAL "rp"
+#define NOT_EQUAL "rn"
+
+/* position value codes */
+#define FIRST_IN_FIELD "pf"
+#define FIRST_IN_SUBFIELD "ps"
+#define FIRST_IN_A_SUBFIELD "pa"
+#define FIRST_IN_NOT_A_SUBFIELD "pt"
+#define ANY_POSITION_IN_FIELD "py"
+
+/* structure value codes */
+#define PHRASE "sp"
+#define WORD "sw"
+#define KEY "sk"
+#define WORD_LIST "sl"
+
+/* truncation value codes */
+#define NO_TRUNCATION "tn"
+#define RIGHT_TRUNCATION "tr"
+#define PROC_NUM_INCLUDED_IN_SEARCH_ARG "ti"
+
+/* completeness value codes */
+#define INCOMPLETE_SUBFIELD "ci"
+#define COMPLETE_SUBFIELD "cs"
+#define COMPLETEFIELD "cf"
+
+/* operator codes */
+#define AND "a"
+#define OR "o"
+#define AND_NOT "n"
+
+/* term types */
+#define TT_Attribute 1
+#define TT_ResultSetID 2
+#define TT_Operator 3
+
+#define ATTRIBUTE_SIZE 3
+#define OPERATOR_SIZE 2
+
+ typedef struct query_term {
+ /* type */
+ long TermType;
+ /* for term */
+ char Use[ATTRIBUTE_SIZE + 1];
+ char Relation[ATTRIBUTE_SIZE + 1];
+ char Position[ATTRIBUTE_SIZE + 1];
+ char Structure[ATTRIBUTE_SIZE + 1];
+ char Truncation[ATTRIBUTE_SIZE + 1];
+ char Completeness[ATTRIBUTE_SIZE + 1];
+ any *Term;
+ /* for result set */
+ any *ResultSetID;
+ /* for operator */
+ char Operator[OPERATOR_SIZE];
+ } query_term;
+
+/*----------------------------------------------------------------------*/
+/* functions */
+
+ query_term *makeAttributeTerm(char *use, char *relation, char *position, char *structure,
+ char *truncation, char *completeness, any *term);
+ query_term *makeResultSetTerm(any *resultSet);
+ query_term *makeOperatorTerm(char *operatorCode);
+ void freeTerm(void *qt);
+ char *writeQueryTerm(query_term *qt, char *buffer, long *len);
+ char *readQueryTerm(query_term **qt, char *buffer);
+ any *writeQuery(query_term **terms);
+ query_term **readQuery(any *info);
+
+/*----------------------------------------------------------------------*/
+
+/*
+ * Routines originally from UI.c -- FM
+ *
+ *----------------------------------------------------------------------*/
+
+ char *generate_retrieval_apdu(char *buff,
+ long *buff_len,
+ any *docID,
+ long chunk_type,
+ long start_line, long end_line,
+ char *type,
+ char *database_name);
+
+ long interpret_message(char *request_message,
+ long request_length,
+ char *response_message,
+ long response_buffer_length, /* length of the buffer (modified) */
+ long connection,
+ boolean verbose);
+
+ char *trim_junk(char *headline);
+
+/*
+ * Routines originally from ZProt.c -- FM
+ *
+ *----------------------------------------------------------------------*/
+
+/* APDU types */
+
+ typedef struct InitAPDU {
+ pdu_type PDUType;
+ boolean willSearch, willPresent, willDelete;
+ boolean supportAccessControl, supportResourceControl;
+ long PreferredMessageSize;
+ long MaximumRecordSize;
+ char *IDAuthentication;
+ char *ImplementationID;
+ char *ImplementationName;
+ char *ImplementationVersion;
+ any *ReferenceID;
+ void *UserInformationField;
+ } InitAPDU;
+
+ typedef struct InitResponseAPDU {
+ pdu_type PDUType;
+ boolean Result;
+ boolean willSearch, willPresent, willDelete;
+ boolean supportAccessControl, supportResourceControl;
+ long PreferredMessageSize;
+ long MaximumRecordSize;
+ char *IDAuthentication;
+ char *ImplementationID;
+ char *ImplementationName;
+ char *ImplementationVersion;
+ any *ReferenceID;
+ void *UserInformationField;
+ } InitResponseAPDU;
+
+ typedef struct SearchAPDU {
+ pdu_type PDUType;
+ long SmallSetUpperBound;
+ long LargeSetLowerBound;
+ long MediumSetPresentNumber;
+ boolean ReplaceIndicator;
+ char *ResultSetName;
+ char **DatabaseNames;
+ char *QueryType;
+ char **ElementSetNames;
+ any *ReferenceID;
+ void *Query;
+ } SearchAPDU;
+
+ typedef struct SearchResponseAPDU {
+ pdu_type PDUType;
+ long SearchStatus;
+ long ResultCount;
+ long NumberOfRecordsReturned;
+ long NextResultSetPosition;
+ long ResultSetStatus;
+ long PresentStatus;
+ any *ReferenceID;
+ void *DatabaseDiagnosticRecords;
+ } SearchResponseAPDU;
+
+ typedef struct PresentAPDU {
+ pdu_type PDUType;
+ long NumberOfRecordsRequested;
+ long ResultSetStartPosition;
+ char *ResultSetID;
+ char *ElementSetNames;
+ any *ReferenceID;
+ void *PresentInfo;
+ } PresentAPDU;
+
+ typedef struct PresentResponseAPDU {
+ pdu_type PDUType;
+ boolean PresentStatus;
+ long NumberOfRecordsReturned;
+ long NextResultSetPosition;
+ any *ReferenceID;
+ void *DatabaseDiagnosticRecords;
+ } PresentResponseAPDU;
+
+/*----------------------------------------------------------------------*/
+/* Functions */
+
+ InitAPDU *makeInitAPDU(boolean search, boolean present, boolean deleteIt,
+ boolean accessControl, boolean resourceControl, long prefMsgSize,
+ long maxMsgSize,
+ char *auth,
+ char *id,
+ char *name,
+ char *version,
+ any *refID,
+ void *userInfo);
+ void freeInitAPDU(InitAPDU * init);
+ char *writeInitAPDU(InitAPDU * init, char *buffer, long *len);
+ char *readInitAPDU(InitAPDU ** init, char *buffer);
+
+ InitResponseAPDU *makeInitResponseAPDU(boolean result,
+ boolean search,
+ boolean present,
+ boolean deleteIt,
+ boolean accessControl,
+ boolean resourceControl,
+ long prefMsgSize,
+ long maxMsgSize,
+ char *auth,
+ char *id,
+ char *name,
+ char *version,
+ any *refID,
+ void *userInfo);
+ void freeInitResponseAPDU(InitResponseAPDU *init);
+ char *writeInitResponseAPDU(InitResponseAPDU *init, char *buffer, long *len);
+ char *readInitResponseAPDU(InitResponseAPDU **init, char *buffer);
+ InitResponseAPDU *replyToInitAPDU(InitAPDU * init, boolean result, void *userInfo);
+
+ SearchAPDU *makeSearchAPDU(long small, long large, long medium,
+ boolean replace, char *name, char **databases,
+ char *type, char **elements, any *refID, void *queryInfo);
+ void freeSearchAPDU(SearchAPDU *query);
+ char *writeSearchAPDU(SearchAPDU *query, char *buffer, long *len);
+ char *readSearchAPDU(SearchAPDU **query, char *buffer);
+
+ SearchResponseAPDU *makeSearchResponseAPDU(long result, long count,
+ long recordsReturned, long nextPos,
+ long resultStatus, long presentStatus,
+ any *refID, void *records);
+ void freeSearchResponseAPDU(SearchResponseAPDU *queryResponse);
+ char *writeSearchResponseAPDU(SearchResponseAPDU *queryResponse, char
+ *buffer, long *len);
+ char *readSearchResponseAPDU(SearchResponseAPDU **queryResponse, char *buffer);
+
+ PresentAPDU *makePresentAPDU(long recsReq, long startPos,
+ char *resultID, any *refID, void *info);
+ void freePresentAPDU(PresentAPDU * present);
+ char *writePresentAPDU(PresentAPDU * present, char *buffer, long *len);
+ char *readPresentAPDU(PresentAPDU ** present, char *buffer);
+
+ PresentResponseAPDU *makePresentResponseAPDU(boolean status, long recsRet,
+ long nextPos, any *refID,
+ void *records);
+ void freePresentResponseAPDU(PresentResponseAPDU * present);
+ char *writePresentResponseAPDU(PresentResponseAPDU * present, char
+ *buffer, long *len);
+ char *readPresentResponseAPDU(PresentResponseAPDU ** present, char *buffer);
+
+/*----------------------------------------------------------------------*/
+/* user extension hooks: */
+
+ extern char *writeInitInfo(InitAPDU * init, char *buffer, long *len);
+ extern char *readInitInfo(void **info, char *buffer);
+
+ extern char *writeInitResponseInfo(InitResponseAPDU *init, char *buffer, long *len);
+ extern char *readInitResponseInfo(void **info, char *buffer);
+
+ extern char *writeSearchInfo(SearchAPDU *query, char *buffer, long *len);
+ extern char *readSearchInfo(void **info, char *buffer);
+
+ extern char *writeSearchResponseInfo(SearchResponseAPDU *query, char
+ *buffer, long *len);
+ extern char *readSearchResponseInfo(void **info, char *buffer);
+
+ extern char *writePresentInfo(PresentAPDU * present, char *buffer, long *len);
+ extern char *readPresentInfo(void **info, char *buffer);
+
+ extern char *writePresentResponseInfo(PresentResponseAPDU * present, char
+ *buffer, long *len);
+ extern char *readPresentResponseInfo(void **info, char *buffer);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* HTVMSWAIS_H */
diff --git a/WWW/Library/Implementation/HTWAIS.c b/WWW/Library/Implementation/HTWAIS.c
new file mode 100644
index 0000000..3c9fb0a
--- /dev/null
+++ b/WWW/Library/Implementation/HTWAIS.c
@@ -0,0 +1,1078 @@
+/*
+ * $LynxId: HTWAIS.c,v 1.40 2020/01/21 22:22:15 tom Exp $
+ *
+ * WorldWideWeb - Wide Area Information Server Access HTWAIS.c
+ * ==================================================
+ *
+ * This module allows a WWW server or client to read data from a
+ * remote WAIS
+ * server, and provide that data to a WWW client in hypertext form.
+ * Source files, once retrieved, are stored and used to provide
+ * information about the index when that is acessed.
+ *
+ * Authors
+ * BK Brewster Kahle, Thinking Machines, <Brewster@think.com>
+ * TBL Tim Berners-Lee, CERN <timbl@info.cern.ch>
+ * FM Foteos Macrides, WFEB <macrides@sci.wfeb.edu>
+ *
+ * History
+ * Sep 91 TBL adapted shell-ui.c (BK) with HTRetrieve.c from WWW.
+ * Feb 91 TBL Generated HTML cleaned up a bit (quotes, escaping)
+ * Refers to lists of sources.
+ * Mar 93 TBL Lib 2.0 compatible module made.
+ * May 94 FM Added DIRECT_WAIS support for VMS.
+ *
+ * Bugs
+ * Uses C stream i/o to read and write sockets, which won't work
+ * on VMS TCP systems.
+ *
+ * Should cache connections.
+ *
+ * ANSI C only as written
+ *
+ * Bugs fixed
+ * NT Nathan Torkington (Nathan.Torkington@vuw.ac.nz)
+ *
+ * WAIS comments:
+ *
+ * 1. Separate directories for different system's .o would help
+ * 2. Document ids are rather long!
+ *
+ * W WW Address mapping convention:
+ *
+ * /servername/database/type/length/document-id
+ *
+ * /servername/database?word+word+word
+ */
+/* WIDE AREA INFORMATION SERVER SOFTWARE:
+ No guarantees or restrictions. See the readme file for the full standard
+ disclaimer.
+
+ Brewster@think.com
+*/
+
+#include <HTUtils.h>
+#include <HTParse.h>
+#include <HTAccess.h> /* We implement a protocol */
+#include <HTML.h> /* The object we will generate */
+#include <HTWSRC.h>
+#include <HTTCP.h>
+#include <HTCJK.h>
+#include <HTAlert.h>
+#include <LYStrings.h>
+
+#undef lines /* term.h conflict with wais.h */
+#undef alloca /* alloca.h conflict with wais.h */
+
+/* From WAIS
+ * ---------
+ */
+#ifdef VMS
+#include <HTVMS_WaisUI.h>
+#include <HTVMS_WaisProt.h>
+#elif defined(HAVE_WAIS_H)
+#include <wais.h>
+#else
+#include <ui.h>
+#endif /* VMS */
+
+#define MAX_MESSAGE_LEN 100000
+#define CHARS_PER_PAGE 10000 /* number of chars retrieved in each request */
+
+#define WAISSEARCH_DATE "Fri Jul 19 1991"
+
+/* FROM WWW
+ * --------
+ */
+#include <LYUtils.h>
+#include <LYLeaks.h>
+
+#define DIRECTORY "/cnidr.org:210/directory-of-servers"
+/* #define DIRECTORY "/quake.think.com:210/directory-of-servers" */
+
+#define BIG 1024 /* identifier size limit @@@@@ */
+
+#define BUFFER_SIZE 4096 /* Arbitrary size for efficiency */
+
+#define HEX_ESCAPE '%'
+
+static BOOL as_gate; /* Client is using us as gateway */
+
+static char line[2048]; /* For building strings to display */
+
+ /* Must be able to take id */
+
+#define PUTC(c) (*target->isa->put_character)(target, c)
+#define PUTS(s) (*target->isa->put_string)(target, s)
+#define START(e) (*target->isa->start_element)(target, e, 0, 0, -1, 0)
+#define END(e) (*target->isa->end_element)(target, e, 0)
+#define MAYBE_END(e) if (HTML_dtd.tags[e].contents != SGML_EMPTY) \
+ (*target->isa->end_element)(target, e, 0)
+#define FREE_TARGET (*target->isa->_free)(target)
+
+struct _HTStructured {
+ const HTStructuredClass *isa;
+ /* ... */
+};
+
+/* ------------------------------------------------------------------------ */
+/* ---------------- Local copy of connect_to_server calls ----------------- */
+/* ------------------------------------------------------------------------ */
+/* Returns 1 on success, 0 on fail, -1 on interrupt. */
+static int fd_mosaic_connect_to_server(char *host_name,
+ long port,
+ long *fd)
+{
+ char *dummy = NULL;
+ int status;
+ int result;
+
+ HTSprintf0(&dummy, "%s//%s:%ld/", STR_WAIS_URL, host_name, port);
+
+ status = HTDoConnect(dummy, "WAIS", 210, (int *) fd);
+ if (status == HT_INTERRUPTED) {
+ result = -1;
+ } else if (status < 0) {
+ result = 0;
+ } else {
+ result = 1;
+ }
+ FREE(dummy);
+ return result;
+}
+
+/* Returns 1 on success, 0 on fail, -1 on interrupt. */
+#ifdef VMS
+static int mosaic_connect_to_server(char *host_name,
+ long port,
+ long *fdp)
+#else
+static int mosaic_connect_to_server(char *host_name,
+ long port,
+ FILE **fp)
+#endif /* VMS */
+{
+#ifndef VMS
+ FILE *file;
+#endif /* VMS */
+ long fd;
+ int rv;
+
+ rv = fd_mosaic_connect_to_server(host_name, port, &fd);
+ if (rv == 0) {
+ HTAlert(gettext("Could not connect to WAIS server."));
+ return 0;
+ } else if (rv == -1) {
+ HTAlert(CONNECTION_INTERRUPTED);
+ return -1;
+ }
+#ifndef VMS
+ if ((file = fdopen(fd, "r+")) == NULL) {
+ HTAlert(gettext("Could not open WAIS connection for reading."));
+ return 0;
+ }
+
+ *fp = file;
+#else
+ *fdp = fd;
+#endif /* VMS */
+ return 1;
+}
+/* ------------------------------------------------------------------------ */
+/* ------------------------------------------------------------------------ */
+
+/* showDiags
+*/
+/* modified from Jonny G's version in ui/question.c */
+static void showDiags(HTStream *target, diagnosticRecord ** d)
+{
+ long i;
+
+ for (i = 0; d[i] != NULL; i++) {
+ if (d[i]->ADDINFO != NULL) {
+ PUTS(gettext("Diagnostic code is "));
+ PUTS(d[i]->DIAG);
+ PUTC(' ');
+ PUTS(d[i]->ADDINFO);
+ PUTC('\n');
+ }
+ }
+}
+
+/* Matrix of allowed characters in filenames
+ * -----------------------------------------
+ */
+
+static BOOL acceptable[256];
+static BOOL acceptable_inited = NO;
+
+static void init_acceptable(void)
+{
+ unsigned int i;
+ char *good =
+ "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./-_$";
+
+ for (i = 0; i < 256; i++)
+ acceptable[i] = NO;
+ for (; *good; good++)
+ acceptable[(unsigned int) *good] = YES;
+ acceptable_inited = YES;
+}
+
+/* Transform file identifier into WWW address
+ * ------------------------------------------
+ *
+ *
+ * On exit,
+ * returns nil if error
+ * pointer to malloced string (must be freed) if ok
+ */
+static char *WWW_from_archie(char *file)
+{
+ char *end;
+ char *result;
+ char *colon;
+
+ for (end = file; *end > ' '; end++) ; /* assumes ASCII encoding */
+ result = (char *) malloc(10 + (end - file));
+ if (!result)
+ return result; /* Malloc error */
+ strcpy(result, "file://");
+ StrNCat(result, file, end - file);
+ colon = StrChr(result + 7, ':'); /* Expect colon after host */
+ if (colon) {
+ for (; colon[0]; colon[0] = colon[1], colon++) ; /* move down */
+ }
+ return result;
+} /* WWW_from_archie */
+
+/* Transform document identifier into URL
+ * --------------------------------------
+ *
+ * Bugs: A static buffer of finite size is used!
+ * The format of the docid MUST be good!
+ *
+ * On exit,
+ * returns nil if error
+ * pointer to malloced string (must be freed) if ok
+ */
+static char hex[17] = "0123456789ABCDEF";
+
+static char *WWW_from_WAIS(any *docid)
+{
+ static char buf[BIG];
+ char *q = buf;
+ char *p = (docid->bytes);
+ char *result = NULL;
+ int i, l;
+
+ if (TRACE) {
+ char *p2;
+
+ fprintf(tfp, "WAIS id (%d bytes) is ", (int) docid->size);
+ for (p2 = docid->bytes; p2 < docid->bytes + docid->size; p2++) {
+ if ((*p2 >= ' ') && (*p2 <= '~')) /* Assume ASCII! */
+ fprintf(tfp, "%c", *p2);
+ else
+ fprintf(tfp, "<%x>", (unsigned) *p2);
+ }
+ fprintf(tfp, "\n");
+ }
+ for (p = docid->bytes;
+ (p < docid->bytes + docid->size) && (q < &buf[BIG]);) {
+ CTRACE((tfp, " Record type %d, length %d\n", p[0], p[1]));
+ if (*p > 10) {
+ CTRACE((tfp, "Eh? DOCID record type of %d!\n", *p));
+ return 0;
+ } { /* Bug fix -- allow any byte value 15 Apr 93 */
+ unsigned int i2 = (unsigned) *p++;
+
+ if (i2 > 99) {
+ *q++ = (i2 / 100) + '0';
+ i2 = i2 % 100;
+ }
+ if (i2 > 9) {
+ *q++ = (i2 / 10) + '0';
+ i2 = i2 % 10;
+ }
+ *q++ = i2 + '0'; /* Record type */
+ }
+ *q++ = '='; /* Separate */
+ l = *p++; /* Length */
+ for (i = 0; i < l; i++, p++) {
+ if (!acceptable[UCH(*p)]) {
+ *q++ = HEX_ESCAPE; /* Means hex coming */
+ *q++ = hex[(*p) >> 4];
+ *q++ = hex[(*p) & 15];
+ } else
+ *q++ = *p;
+ }
+ *q++ = ';'; /* Terminate field */
+ }
+ *q++ = 0; /* Terminate string */
+ CTRACE((tfp, "WWW form of id: %s\n", buf));
+ StrAllocCopy(result, buf);
+ return result;
+} /* WWW_from_WAIS */
+
+/* Transform URL into WAIS document identifier
+ * -------------------------------------------
+ *
+ * On entry,
+ * docname points to valid name produced originally by
+ * WWW_from_WAIS
+ * On exit,
+ * docid->size is valid
+ * docid->bytes is malloced and must later be freed.
+ */
+static any *WAIS_from_WWW(any *docid, char *docname)
+{
+ char *z; /* Output pointer */
+ char *sor; /* Start of record - points to size field. */
+ char *p; /* Input pointer */
+ char *q; /* Poisition of "=" */
+ char *s; /* Position of semicolon */
+ int n; /* size */
+
+ CTRACE((tfp, "WWW id (to become WAIS id): %s\n", docname));
+ for (n = 0, p = docname; *p; p++) { /* Count sizes of strings */
+ n++;
+ if (*p == ';')
+ n--; /* Not converted */
+ else if (*p == HEX_ESCAPE)
+ n = n - 2; /* Save two bytes */
+ docid->size = n;
+ }
+
+ if (!(docid->bytes = (char *) malloc(docid->size))) /* result record */
+ outofmem(__FILE__, "WAIS_from_WWW");
+ z = docid->bytes;
+
+ for (p = docname; *p;) { /* Convert of strings */
+ /* Record type */
+
+ *z = 0; /* Initialize record type */
+ while (*p >= '0' && *p <= '9') {
+ *z = *z * 10 + (*p++ - '0'); /* Decode decimal record type */
+ }
+ z++;
+ if (*p != '=')
+ return 0;
+ q = p;
+
+ s = StrChr(q, ';'); /* (Check only) */
+ if (!s)
+ return 0; /* Bad! No ';'; */
+ sor = z; /* Remember where the size field was */
+ z++; /* Skip record size for now */
+ for (p = q + 1; *p != ';';) {
+ if (*p == HEX_ESCAPE) {
+ char c;
+ unsigned int b;
+
+ p++;
+ c = *p++;
+ b = from_hex(c);
+ c = *p++;
+ if (!c)
+ break; /* Odd number of chars! */
+ *z++ = (b << 4) + from_hex(c);
+ } else {
+ *z++ = *p++; /* Record */
+ }
+ }
+ *sor = (z - sor - 1); /* Fill in size -- not counting size itself */
+ p++; /* After semicolon: start of next record */
+ }
+
+ if (TRACE) {
+ char *p2;
+
+ fprintf(tfp, "WAIS id (%d bytes) is ", (int) docid->size);
+ for (p2 = docid->bytes; p2 < docid->bytes + docid->size; p2++) {
+ if ((*p2 >= ' ') && (*p2 <= '~')) /* Assume ASCII! */
+ fprintf(tfp, "%c", *p2);
+ else
+ fprintf(tfp, "<%x>", (unsigned) *p2);
+ }
+ fprintf(tfp, "\n");
+ }
+ return docid; /* Ok */
+
+} /* WAIS_from_WWW */
+
+/* Send a plain text record to the client output_text_record()
+ * --------------------------------------
+ */
+static void output_text_record(HTStream *target,
+ WAISDocumentText *record,
+ boolean binary)
+{
+ unsigned long count;
+
+ /* printf(" Text\n");
+ print_any(" DocumentID: ", record->DocumentID);
+ printf(" VersionNumber: %d\n", record->VersionNumber);
+ */
+
+ if (binary) {
+ (*target->isa->put_block) (target,
+ record->DocumentText->bytes,
+ record->DocumentText->size);
+ return;
+ }
+
+ for (count = 0; count < record->DocumentText->size; count++) {
+ long ch = (unsigned char) record->DocumentText->bytes[count];
+
+ if (ch == 27) { /* What is this in for? Tim */
+ /* then we have an escape code */
+ /* if the next letter is '(' or ')', then ignore two letters */
+ if ('(' == record->DocumentText->bytes[count + 1] ||
+ ')' == record->DocumentText->bytes[count + 1])
+ count += 1; /* it is a term marker */
+ else
+ count += 4; /* it is a paragraph marker */
+ } else if (ch == '\n' || ch == '\r') {
+ PUTC('\n');
+ } else if (IS_CJK_TTY || ch == '\t' || isprint(ch)) {
+ PUTC(ch);
+ }
+ }
+} /* output text record */
+
+/* Format A Search response for the client display_search_response
+ * ---------------------------------------
+ */
+/* modified from tracy shen's version in wutil.c
+ * displays either a text record or a set of headlines.
+ */
+static void display_search_response(HTStructured * target, SearchResponseAPDU *response,
+ char *the_database,
+ char *keywords)
+{
+ WAISSearchResponse *info;
+ long i, k;
+
+ BOOL archie = strstr(the_database, "archie") != 0; /* Special handling */
+
+ CTRACE((tfp, "HTWAIS: Displaying search response\n"));
+ PUTS(gettext("Index "));
+ START(HTML_EM);
+ PUTS(the_database);
+ END(HTML_EM);
+ sprintf(line, gettext(" contains the following %d item%s relevant to \""),
+ (int) (response->NumberOfRecordsReturned),
+ response->NumberOfRecordsReturned == 1 ? "" : "s");
+ PUTS(line);
+ START(HTML_EM);
+ PUTS(keywords);
+ END(HTML_EM);
+ PUTS("\".\n");
+ PUTS(gettext("The first figure after each entry is its relative score, "));
+ PUTS(gettext("the second is the number of lines in the item."));
+ START(HTML_BR);
+ START(HTML_BR);
+ PUTC('\n');
+ START(HTML_OL);
+
+ if (response->DatabaseDiagnosticRecords != 0) {
+ info = (WAISSearchResponse *) response->DatabaseDiagnosticRecords;
+ i = 0;
+
+ if (info->Diagnostics != NULL)
+ showDiags((HTStream *) target, info->Diagnostics);
+
+ if (info->DocHeaders != 0) {
+ for (k = 0; info->DocHeaders[k] != 0; k++) {
+ WAISDocumentHeader *head = info->DocHeaders[k];
+ char *headline = trim_junk(head->Headline);
+ any *docid = head->DocumentID;
+ char *docname; /* printable version of docid */
+
+ i++;
+ /*
+ * Make a printable string out of the document id.
+ */
+ CTRACE((tfp, "HTWAIS: %2ld: Score: %4ld, lines:%4ld '%s'\n",
+ i,
+ (long int) (info->DocHeaders[k]->Score),
+ (long int) (info->DocHeaders[k]->Lines),
+ headline));
+
+ START(HTML_LI);
+
+ if (archie) {
+ char *www_name = WWW_from_archie(headline);
+
+ if (www_name) {
+ HTStartAnchor(target, NULL, www_name);
+ PUTS(headline);
+ END(HTML_A);
+ FREE(www_name);
+ } else {
+ PUTS(headline);
+ PUTS(gettext(" (bad file name)"));
+ }
+ } else { /* Not archie */
+ docname = WWW_from_WAIS(docid);
+ if (docname) {
+ if ((head->Types) &&
+ (!strcmp(head->Types[0], "URL"))) {
+ HTStartAnchor(target, NULL, headline);
+ } else {
+ char *dbname = HTEscape(the_database, URL_XPALPHAS);
+ char *w3_address = NULL;
+
+ HTSprintf0(&w3_address,
+ "/%s/%s/%d/%s",
+ dbname,
+ head->Types ? head->Types[0] : "TEXT",
+ (int) (head->DocumentLength),
+ docname);
+ HTStartAnchor(target, NULL, w3_address);
+ FREE(w3_address);
+ FREE(dbname);
+ }
+ PUTS(headline);
+ END(HTML_A);
+ FREE(docname);
+ } else {
+ PUTS(gettext("(bad doc id)"));
+ }
+ }
+
+ sprintf(line, "%5ld %5ld ",
+ head->Score,
+ head->Lines);
+ PUTS(line);
+ MAYBE_END(HTML_LI);
+ } /* next document header */
+ }
+ /* if there were any document headers */
+ if (info->ShortHeaders != 0) {
+ k = 0;
+ while (info->ShortHeaders[k] != 0) {
+ i++;
+ PUTS(gettext("(Short Header record, can't display)"));
+ }
+ }
+ if (info->LongHeaders != 0) {
+ k = 0;
+ while (info->LongHeaders[k] != 0) {
+ i++;
+ PUTS(gettext("\nLong Header record, can't display\n"));
+ }
+ }
+ if (info->Text != 0) {
+ k = 0;
+ while (info->Text[k] != 0) {
+ i++;
+ PUTS(gettext("\nText record\n"));
+ output_text_record((HTStream *) target,
+ info->Text[k++], false);
+ }
+ }
+ if (info->Headlines != 0) {
+ k = 0;
+ while (info->Headlines[k] != 0) {
+ i++;
+ PUTS(gettext("\nHeadline record, can't display\n"));
+ /* dsply_headline_record( info->Headlines[k++]); */
+ }
+ }
+ if (info->Codes != 0) {
+ k = 0;
+ while (info->Codes[k] != 0) {
+ i++;
+ PUTS(gettext("\nCode record, can't display\n"));
+ /* dsply_code_record( info->Codes[k++]); */
+ }
+ }
+ } /* Loop: display user info */
+ END(HTML_OL);
+ PUTC('\n');
+}
+
+/* Load by name HTLoadWAIS
+ * ============
+ *
+ * This renders any object or search as required.
+ */
+int HTLoadWAIS(const char *arg,
+ HTParentAnchor *anAnchor,
+ HTFormat format_out,
+ HTStream *sink)
+#define MAX_KEYWORDS_LENGTH 1000
+#define MAX_SERVER_LENGTH 1000
+#define MAX_DATABASE_LENGTH 1000
+#define MAX_SERVICE_LENGTH 1000
+#define MAXDOCS 200
+
+{
+ char *key; /* pointer to keywords in URL */
+ char *request_message = NULL; /* arbitrary message limit */
+ char *response_message = NULL; /* arbitrary message limit */
+ long request_buffer_length; /* how of the request is left */
+ SearchResponseAPDU *retrieval_response = 0;
+ char keywords[MAX_KEYWORDS_LENGTH + 1];
+ char *the_server_name;
+ char *wais_database = NULL; /* name of current database */
+ char *www_database; /* Same name escaped */
+ char *service;
+ char *doctype;
+ char *doclength;
+ long document_length = 0;
+ char *docname = 0;
+
+#ifdef VMS
+ long connection = 0;
+
+#else
+ FILE *connection = NULL;
+#endif /* VMS */
+ char *names; /* Copy of arg to be hacked up */
+ BOOL ok = NO;
+ int return_status = HT_LOADED;
+ int rv;
+
+ if (!acceptable_inited)
+ init_acceptable();
+
+ /* Decipher and check syntax of WWW address:
+ * ----------------------------------------
+ *
+ * First we remove the "wais:" if it was specified. 920110
+ */
+ names = HTParse(arg, "", PARSE_HOST | PARSE_PATH | PARSE_PUNCTUATION);
+ key = StrChr(names, '?');
+
+ if (key) {
+ char *p;
+
+ *key++ = 0; /* Split off keywords */
+ for (p = key; *p; p++)
+ if (*p == '+')
+ *p = ' ';
+ HTUnEscape(key);
+ }
+ if (names[0] == '/') {
+ the_server_name = names + 1;
+ if ((as_gate = (*the_server_name == '/')) != 0)
+ the_server_name++; /* Accept one or two */
+ www_database = StrChr(the_server_name, '/');
+ if (www_database) {
+ *www_database++ = 0; /* Separate database name */
+ doctype = StrChr(www_database, '/');
+ if (key)
+ ok = YES; /* Don't need doc details */
+ else if (doctype) { /* If not search parse doc details */
+ *doctype++ = 0; /* Separate rest of doc address */
+ doclength = StrChr(doctype, '/');
+ if (doclength) {
+ *doclength++ = 0;
+ document_length = atol(doclength);
+ if (document_length) {
+ docname = StrChr(doclength, '/');
+ if (docname) {
+ *docname++ = 0;
+ ok = YES; /* To avoid a goto! */
+ } /* if docname */
+ } /* if document_length valid */
+ } /* if doclength */
+ } else { /* no doctype? Assume index required */
+ if (!key)
+ key = "";
+ ok = YES;
+ } /* if doctype */
+ } /* if database */
+ }
+
+ if (!ok)
+ return HTLoadError(sink, 500, gettext("Syntax error in WAIS URL"));
+
+ CTRACE((tfp, "HTWAIS: Parsed OK\n"));
+
+ service = StrChr(names, ':');
+ if (service)
+ *service++ = 0;
+ else
+ service = "210";
+
+ if (the_server_name[0] == 0) {
+#ifdef VMS
+ connection = 0;
+#else
+ connection = NULL;
+#endif /* VMS */
+
+ } else if (!(key && !*key)) {
+ int status;
+
+ CTRACE((tfp, "===WAIS=== calling mosaic_connect_to_server\n"));
+ status = mosaic_connect_to_server(the_server_name,
+ atoi(service),
+ &connection);
+ if (status == 0) {
+ CTRACE((tfp, "===WAIS=== connection failed\n"));
+ FREE(names);
+ return HT_NOT_LOADED;
+ } else if (status == -1) {
+ CTRACE((tfp, "===WAIS=== connection interrupted\n"));
+ FREE(names);
+ return HT_NOT_LOADED;
+ }
+ }
+
+ StrAllocCopy(wais_database, www_database);
+ HTUnEscape(wais_database);
+
+ /*
+ * This below fixed size stuff is terrible.
+ */
+#ifdef VMS
+ if ((request_message = typecallocn(char, MAX_MESSAGE_LEN)) == 0)
+ outofmem(__FILE__, "HTLoadWAIS");
+ if ((response_message = typecallocn(char, MAX_MESSAGE_LEN)) == 0)
+ outofmem(__FILE__, "HTLoadWAIS");
+
+#else
+ request_message = (char *) s_malloc((size_t) MAX_MESSAGE_LEN * sizeof(char));
+ response_message = (char *) s_malloc((size_t) MAX_MESSAGE_LEN * sizeof(char));
+#endif /* VMS */
+
+ /*
+ * If keyword search is performed but there are no keywords, the user has
+ * followed a link to the index itself. It would be appropriate at this
+ * point to send him the .SRC file - how?
+ */
+ if (key && !*key) { /* I N D E X */
+#ifdef CACHE_FILE_PREFIX
+ char *filename = NULL;
+ FILE *fp;
+#endif
+ HTStructured *target = HTML_new(anAnchor, format_out, sink);
+
+ START(HTML_HEAD);
+ PUTC('\n');
+ HTStartIsIndex(target, HTWAIS_SOLICIT_QUERY, NULL);
+ PUTC('\n');
+
+ {
+ START(HTML_TITLE);
+ PUTS(wais_database);
+ PUTS(gettext(" (WAIS Index)"));
+ END(HTML_TITLE);
+ PUTC('\n');
+ END(HTML_HEAD);
+ PUTC('\n');
+
+ START(HTML_H1);
+ PUTS(gettext("WAIS Index: "));
+ START(HTML_EM);
+ PUTS(wais_database);
+ END(HTML_EM);
+ END(HTML_H1);
+ PUTC('\n');
+ PUTS(gettext("This is a link for searching the "));
+ START(HTML_EM);
+ PUTS(wais_database);
+ END(HTML_EM);
+ PUTS(gettext(" WAIS Index.\n"));
+
+ }
+ /*
+ * If we have seen a source file for this database, use that.
+ */
+#ifdef CACHE_FILE_PREFIX
+ HTSprintf0(&filename, "%sWSRC-%s:%s:%.100s.txt",
+ CACHE_FILE_PREFIX,
+ the_server_name, service, www_database);
+
+ fp = fopen(filename, "r"); /* Have we found this already? */
+ CTRACE((tfp, "HTWAIS: Description of server %s %s.\n",
+ filename,
+ fp ? "exists already" : "does NOT exist!"));
+
+ if (fp) {
+ char c;
+
+ START(HTML_PRE); /* Preformatted description */
+ PUTC('\n');
+ while ((c = getc(fp)) != EOF)
+ PUTC(c); /* Transfer file */
+ END(HTML_PRE);
+ fclose(fp);
+ }
+ FREE(filename);
+#endif
+ START(HTML_P);
+ PUTS(gettext("\nEnter the 's'earch command and then specify search words.\n"));
+
+ FREE_TARGET;
+ } else if (key) { /* S E A R C H */
+ char *p;
+ HTStructured *target;
+
+ LYStrNCpy(keywords, key, MAX_KEYWORDS_LENGTH);
+ while ((p = StrChr(keywords, '+')) != 0)
+ *p = ' ';
+
+ /*
+ * Send advance title to get something fast to the other end.
+ */
+ target = HTML_new(anAnchor, format_out, sink);
+
+ START(HTML_HEAD);
+ PUTC('\n');
+ HTStartIsIndex(target, HTWAIS_SOLICIT_QUERY, NULL);
+ PUTC('\n');
+ START(HTML_TITLE);
+ PUTS(keywords);
+ PUTS(gettext(" (in "));
+ PUTS(wais_database);
+ PUTC(')');
+ END(HTML_TITLE);
+ PUTC('\n');
+ END(HTML_HEAD);
+ PUTC('\n');
+
+ START(HTML_H1);
+ PUTS(gettext("WAIS Search of \""));
+ START(HTML_EM);
+ PUTS(keywords);
+ END(HTML_EM);
+ PUTS(gettext("\" in: "));
+ START(HTML_EM);
+ PUTS(wais_database);
+ END(HTML_EM);
+ END(HTML_H1);
+ PUTC('\n');
+
+ request_buffer_length = MAX_MESSAGE_LEN; /* Amount left */
+ CTRACE((tfp, "HTWAIS: Search for `%s' in `%s'\n",
+ keywords, wais_database));
+ if (NULL ==
+ generate_search_apdu(request_message + HEADER_LENGTH,
+ &request_buffer_length,
+ keywords, wais_database, NULL, MAXDOCS)) {
+#ifdef VMS
+ HTAlert(gettext("HTWAIS: Request too large."));
+ return_status = HT_NOT_LOADED;
+ FREE_TARGET;
+ goto CleanUp;
+#else
+ panic("request too large");
+#endif /* VMS */
+ }
+
+ HTProgress(gettext("Searching WAIS database..."));
+ rv = interpret_message(request_message,
+ MAX_MESSAGE_LEN - request_buffer_length,
+ response_message,
+ MAX_MESSAGE_LEN,
+ connection,
+ false /* true verbose */
+ );
+
+ if (rv == HT_INTERRUPTED) {
+ HTAlert(gettext("Search interrupted."));
+ return_status = HT_INTERRUPTED;
+ FREE_TARGET;
+ goto CleanUp;
+ } else if (!rv) {
+#ifdef VMS
+ HTAlert(HTWAIS_MESSAGE_TOO_BIG);
+ return_status = HT_NOT_LOADED;
+ FREE_TARGET;
+ goto CleanUp;
+#else
+ panic("returned message too large");
+#endif /* VMS */
+ } else { /* returned message ok */
+ SearchResponseAPDU *query_response = 0;
+
+ readSearchResponseAPDU(&query_response,
+ response_message + HEADER_LENGTH);
+ display_search_response(target,
+ query_response, wais_database, keywords);
+ if (query_response->DatabaseDiagnosticRecords)
+ freeWAISSearchResponse(query_response->DatabaseDiagnosticRecords);
+ freeSearchResponseAPDU(query_response);
+ } /* returned message not too large */
+ FREE_TARGET;
+ } else { /* D O C U M E N T F E T C H */
+ HTFormat format_in;
+ boolean binary; /* how to transfer stuff coming over */
+ HTStream *target;
+ long count;
+ any doc_chunk;
+ any *docid = &doc_chunk;
+
+ CTRACE((tfp,
+ "HTWAIS: Retrieve document id `%s' type `%s' length %ld\n",
+ NonNull(docname), doctype, document_length));
+
+ format_in =
+ !strcmp(doctype, "WSRC") ? HTAtom_for("application/x-wais-source") :
+ !strcmp(doctype, "TEXT") ? HTAtom_for(STR_PLAINTEXT) :
+ !strcmp(doctype, "HTML") ? HTAtom_for(STR_HTML) :
+ !strcmp(doctype, "GIF") ? HTAtom_for("image/gif") :
+ HTAtom_for(STR_BINARY);
+ binary =
+ 0 != strcmp(doctype, "WSRC") &&
+ 0 != strcmp(doctype, "TEXT") &&
+ 0 != strcmp(doctype, "HTML");
+
+ target = HTStreamStack(format_in, format_out, sink, anAnchor);
+ if (!target)
+ return HTLoadError(sink, 500,
+ gettext("Can't convert format of WAIS document"));
+ /*
+ * Decode hex or literal format for document ID.
+ */
+ WAIS_from_WWW(docid, docname);
+
+ /*
+ * Loop over slices of the document.
+ */
+ for (count = 0;
+ count * CHARS_PER_PAGE < document_length;
+ count++) {
+#ifdef VMS
+ char *type = NULL;
+
+ StrAllocCopy(type, doctype);
+#else
+ char *type = s_strdup(doctype); /* Gets freed I guess */
+#endif /* VMS */
+ request_buffer_length = MAX_MESSAGE_LEN; /* Amount left */
+ CTRACE((tfp, "HTWAIS: Slice number %ld\n", count));
+
+ if (HTCheckForInterrupt()) {
+ HTAlert(TRANSFER_INTERRUPTED);
+ (*target->isa->_abort) (target, NULL);
+#ifdef VMS
+ FREE(type);
+#endif /* VMS */
+ return_status = HT_NOT_LOADED;
+ goto CleanUp;
+ }
+
+ if (0 ==
+ generate_retrieval_apdu(request_message + HEADER_LENGTH,
+ &request_buffer_length,
+ docid,
+ CT_byte,
+ count * CHARS_PER_PAGE,
+ (((count + 1) * CHARS_PER_PAGE <= document_length)
+ ? (count + 1) * CHARS_PER_PAGE
+ : document_length),
+ type,
+ wais_database)) {
+#ifdef VMS
+ HTAlert(gettext("HTWAIS: Request too long."));
+ return_status = HT_NOT_LOADED;
+ FREE_TARGET;
+ FREE(type);
+ FREE(docid->bytes);
+ goto CleanUp;
+#else
+ panic("request too long");
+#endif /* VMS */
+ }
+
+ /*
+ * Actually do the transaction given by request_message.
+ */
+ HTProgress(gettext("Fetching WAIS document..."));
+ rv = interpret_message(request_message,
+ MAX_MESSAGE_LEN - request_buffer_length,
+ response_message,
+ MAX_MESSAGE_LEN,
+ connection,
+ false /* true verbose */
+ );
+ if (rv == HT_INTERRUPTED) {
+ HTAlert(TRANSFER_INTERRUPTED);
+ return_status = HT_INTERRUPTED;
+ FREE_TARGET;
+ FREE(type);
+ FREE(docid->bytes);
+ goto CleanUp;
+ } else if (!rv) {
+#ifdef VMS
+ HTAlert(HTWAIS_MESSAGE_TOO_BIG);
+ return_status = HT_NOT_LOADED;
+ FREE_TARGET;
+ FREE(type);
+ FREE(docid->bytes);
+ goto CleanUp;
+#else
+ panic("Returned message too large");
+#endif /* VMS */
+ }
+
+ /*
+ * Parse the result which came back into memory.
+ */
+ readSearchResponseAPDU(&retrieval_response,
+ response_message + HEADER_LENGTH);
+
+ if (NULL ==
+ ((WAISSearchResponse *)
+ retrieval_response->DatabaseDiagnosticRecords)->Text) {
+ /* display_search_response(target, retrieval_response,
+ wais_database, keywords); */
+ PUTS(gettext("No text was returned!\n"));
+ /* panic("No text was returned"); */
+ } else {
+ output_text_record(target,
+ ((WAISSearchResponse *)
+ retrieval_response->DatabaseDiagnosticRecords)->Text[0],
+ binary);
+ } /* If text existed */
+
+#ifdef VMS
+ FREE(type);
+#endif /* VMS */
+ } /* Loop over slices */
+
+ FREE_TARGET;
+ FREE(docid->bytes);
+
+ freeWAISSearchResponse(retrieval_response->DatabaseDiagnosticRecords);
+ freeSearchResponseAPDU(retrieval_response);
+
+ } /* If document rather than search */
+
+ CleanUp:
+ /*
+ * (This postponed until later, after a timeout:)
+ */
+#ifdef VMS
+ if (connection)
+ NETCLOSE((int) connection);
+#else
+ if (connection)
+ fclose(connection);
+#endif /* VMS */
+ FREE(wais_database);
+#ifdef VMS
+ FREE(request_message);
+ FREE(response_message);
+#else
+ s_free(request_message);
+ s_free(response_message);
+#endif /* VMS */
+ FREE(names);
+ return (return_status);
+}
+
+#ifdef GLOBALDEF_IS_MACRO
+#define _HTWAIS_C_1_INIT { "wais", HTLoadWAIS, NULL }
+GLOBALDEF(HTProtocol, HTWAIS, _HTWAIS_C_1_INIT);
+#else
+GLOBALDEF HTProtocol HTWAIS =
+{"wais", HTLoadWAIS, NULL};
+#endif /* GLOBALDEF_IS_MACRO */
diff --git a/WWW/Library/Implementation/HTWAIS.h b/WWW/Library/Implementation/HTWAIS.h
new file mode 100644
index 0000000..4ba6f67
--- /dev/null
+++ b/WWW/Library/Implementation/HTWAIS.h
@@ -0,0 +1,43 @@
+/* WAIS protocol module for the W3 library
+ WAIS PROTOCOL INTERFACE
+
+ This module does not actually perform the WAIS protocol directly, but it does using one
+ or more libraries of the freeWAIS distribution. The ui.a library came with the old free
+ WAIS from TMC, the client.a and wais.a libraries are needed from the freeWAIS from
+ CNIDR.
+
+ If you include this module in the library, you must also
+
+ Register the HTWAIS protocol at initialisation (e.g., HTInit or HTSInit) by compiling
+ it with -DDIRECT_WAIS
+
+ Link with the WAIS libraries
+
+ The wais source files are parsed by a separate and independent module, HTWSRC . You
+ can include HTWSRC without including direct wais using this module, and your WWW code
+ will be able to read source files, and access WAIS indexes through a gateway.
+
+ A WAIS-WWW gateway is just a normal W3 server with a libwww compiled with this module.
+
+ Anyways, this interface won't change much:
+
+ */
+#ifndef HTWAIS_H
+#define HTWAIS_H
+
+#include <HTAccess.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+#ifdef GLOBALREF_IS_MACRO
+ extern GLOBALREF (HTProtocol, HTWAIS);
+
+#else
+ GLOBALREF HTProtocol HTWAIS;
+#endif /* GLOBALDEF_IS_MACRO */
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* HTWAIS_H */
diff --git a/WWW/Library/Implementation/HTWSRC.c b/WWW/Library/Implementation/HTWSRC.c
new file mode 100644
index 0000000..ba12cc7
--- /dev/null
+++ b/WWW/Library/Implementation/HTWSRC.c
@@ -0,0 +1,486 @@
+/*
+ * $LynxId: HTWSRC.c,v 1.31 2020/01/21 22:05:46 tom Exp $
+ *
+ * Parse WAIS Source file HTWSRC.c
+ * ======================
+ *
+ * This module parses a stream with WAIS source file
+ * format information on it and creates a structured stream.
+ * That structured stream is then converted into whatever.
+ *
+ * 3 June 93 Bug fix: Won't crash if no description
+ */
+
+#define HTSTREAM_INTERNAL 1
+
+#include <HTUtils.h>
+
+#include <HTWSRC.h>
+#include <LYUtils.h>
+
+#include <HTML.h>
+#include <HTParse.h>
+
+#include <LYLeaks.h>
+
+#define BIG 10000 /* Arbitrary limit to value length */
+#define PARAM_MAX BIG
+#define CACHE_PERIOD (7*86400) /* Time to keep .src file in seconds */
+
+struct _HTStructured {
+ const HTStructuredClass *isa;
+ /* ... */
+};
+
+#define PUTC(c) (*me->target->isa->put_character)(me->target, c)
+#define PUTS(s) (*me->target->isa->put_string)(me->target, s)
+#define START(e) (*me->target->isa->start_element)(me->target, e, 0, 0, -1, 0)
+#define END(e) (*me->target->isa->end_element)(me->target, e, 0)
+#define MAYBE_END(e) if (HTML_dtd.tags[e].contents != SGML_EMPTY) \
+ (*me->target->isa->end_element)(me->target, e, 0)
+
+/* Here are the parameters which can be specified in a source file
+*/
+static const char *par_name[] =
+{
+ "version",
+ "ip-address",
+#define PAR_IP_NAME 2
+ "ip-name",
+#define PAR_TCP_PORT 3
+ "tcp-port",
+#define PAR_DATABASE_NAME 4
+ "database-name",
+#define PAR_COST 5
+ "cost",
+#define PAR_COST_UNIT 6
+ "cost-unit",
+#define PAR_FREE 7
+ "free",
+#define PAR_MAINTAINER 8
+ "maintainer",
+#define PAR_DESCRIPTION 9
+ "description",
+ "keyword-list",
+ "source",
+ "window-geometry",
+ "configuration",
+ "script",
+ "update-time",
+ "contact-at",
+ "last-contacted",
+ "confidence",
+ "num-docs-to-request",
+ "font",
+ "font-size",
+#define PAR_UNKNOWN 22
+ "unknown",
+ 0, /* Terminate list */
+#define PAR_COUNT 23
+};
+
+enum tokenstate {
+ beginning,
+ before_tag,
+ colon,
+ before_value,
+ value,
+ bracketed_value,
+ quoted_value,
+ escape_in_quoted,
+ done
+};
+
+/* Stream Object
+ * ------------
+ *
+ * The target is the structured stream down which the
+ * parsed results will go.
+ *
+ * all the static stuff below should go in here to make it reentrant
+ */
+
+struct _HTStream {
+ const HTStreamClass *isa;
+ HTStructured *target;
+ char *par_value[PAR_COUNT];
+ enum tokenstate state;
+ char param[BIG + 1];
+ int param_number;
+ int param_count;
+};
+
+/* Decode one hex character
+*/
+char from_hex(char c)
+{
+ return (char) ((c >= '0') && (c <= '9') ? c - '0'
+ : (c >= 'A') && (c <= 'F') ? c - 'A' + 10
+ : (c >= 'a') && (c <= 'f') ? c - 'a' + 10
+ : 0);
+}
+
+/* State machine
+ * -------------
+ *
+ * On entry,
+ * me->state is a valid state (see WSRC_init)
+ * c is the next character
+ * On exit,
+ * returns 1 Done with file
+ * 0 Continue. me->state is updated if necessary.
+ * -1 Syntax error error
+ */
+
+/* Treat One Character
+ * -------------------
+ */
+static void WSRCParser_put_character(HTStream *me, int c)
+{
+ switch (me->state) {
+ case beginning:
+ if (c == '(')
+ me->state = before_tag;
+ break;
+
+ case before_tag:
+ if (c == ')') {
+ me->state = done;
+ return; /* Done with input file */
+ } else if (c == ':') {
+ me->param_count = 0;
+ me->state = colon;
+ } /* Ignore other text */
+ break;
+
+ case colon:
+ if (WHITE(c)) {
+ me->param[me->param_count++] = 0; /* Terminate */
+ for (me->param_number = 0;
+ par_name[me->param_number];
+ me->param_number++) {
+ if (0 == strcmp(par_name[me->param_number], me->param)) {
+ break;
+ }
+ }
+ if (!par_name[me->param_number]) { /* Unknown field */
+ CTRACE((tfp, "HTWSRC: Unknown field `%s' in source file\n",
+ me->param));
+ me->param_number = PAR_UNKNOWN;
+ me->state = before_value; /* Could be better ignore */
+ return;
+ }
+ me->state = before_value;
+ } else {
+ if (me->param_count < PARAM_MAX)
+ me->param[me->param_count++] = (char) c;
+ }
+ break;
+
+ case before_value:
+ if (c == ')') {
+ me->state = done;
+ return; /* Done with input file */
+ }
+ if (WHITE(c))
+ return; /* Skip white space */
+ me->param_count = 0;
+ if (c == '"') {
+ me->state = quoted_value;
+ } else {
+ me->state = ((c == '(')
+ ? bracketed_value
+ : value);
+ me->param[me->param_count++] = (char) c; /* Don't miss first character */
+ }
+ break;
+
+ case value:
+ if (WHITE(c)) {
+ me->param[me->param_count] = 0;
+ StrAllocCopy(me->par_value[me->param_number], me->param);
+ me->state = before_tag;
+ } else {
+ if (me->param_count < PARAM_MAX)
+ me->param[me->param_count++] = (char) c;
+ }
+ break;
+
+ case bracketed_value:
+ if (c == ')') {
+ me->param[me->param_count] = 0;
+ StrAllocCopy(me->par_value[me->param_number], me->param);
+ me->state = before_tag;
+ break;
+ }
+ if (me->param_count < PARAM_MAX)
+ me->param[me->param_count++] = (char) c;
+ break;
+
+ case quoted_value:
+ if (c == '"') {
+ me->param[me->param_count] = 0;
+ StrAllocCopy(me->par_value[me->param_number], me->param);
+ me->state = before_tag;
+ break;
+ }
+
+ if (c == '\\') { /* Ignore escape but switch state */
+ me->state = escape_in_quoted;
+ break;
+ }
+ /* Fall through! */
+
+ case escape_in_quoted:
+ if (me->param_count < PARAM_MAX)
+ me->param[me->param_count++] = (char) c;
+ me->state = quoted_value;
+ break;
+
+ case done: /* Ignore anything after EOF */
+ return;
+
+ } /* switch me->state */
+}
+
+/* Open Cache file
+ * ===============
+ *
+ * Bugs: Maybe for filesystem-challenged platforms (MSDOS for example) we
+ * should make a hash code for the filename.
+ */
+
+#ifdef CACHE_FILE_PREFIX
+static BOOL write_cache(HTStream *me)
+{
+ FILE *fp;
+ char *cache_file_name = NULL;
+ char *www_database;
+ int result = NO;
+
+ if (!me->par_value[PAR_DATABASE_NAME]
+ || !me->par_value[PAR_IP_NAME]
+ )
+ return NO;
+
+ www_database = HTEscape(me->par_value[PAR_DATABASE_NAME], URL_XALPHAS);
+ HTSprintf0(&cache_file_name, "%sWSRC-%s:%s:%.100s.txt",
+ CACHE_FILE_PREFIX,
+ me->par_value[PAR_IP_NAME],
+ (me->par_value[PAR_TCP_PORT]
+ ? me->par_value[PAR_TCP_PORT]
+ : "210"),
+ www_database);
+
+ if ((fp = fopen(cache_file_name, TXT_W)) != 0) {
+ result = YES;
+ if (me->par_value[PAR_DESCRIPTION])
+ fputs(me->par_value[PAR_DESCRIPTION], fp);
+ else
+ fputs("Description not available\n", fp);
+ fclose(fp);
+ }
+ FREE(www_database);
+ FREE(cache_file_name);
+ return result;
+}
+#endif
+
+/* Output equivalent HTML
+ * ----------------------
+ *
+ */
+
+static void give_parameter(HTStream *me, int p)
+{
+ PUTS(par_name[p]);
+ if (me->par_value[p]) {
+ PUTS(": ");
+ PUTS(me->par_value[p]);
+ PUTS("; ");
+ } else {
+ PUTS(gettext(" NOT GIVEN in source file; "));
+ }
+}
+
+/* Generate Output
+ * ===============
+ */
+static void WSRC_gen_html(HTStream *me, int source_file)
+{
+ if (me->par_value[PAR_DATABASE_NAME]) {
+ char *shortname = 0;
+ int l;
+
+ StrAllocCopy(shortname, me->par_value[PAR_DATABASE_NAME]);
+ l = (int) strlen(shortname);
+ if (l > 4 && !strcasecomp(shortname + l - 4, ".src")) {
+ shortname[l - 4] = 0; /* Chop of .src -- boring! */
+ }
+
+ START(HTML_HEAD);
+ PUTC('\n');
+ START(HTML_TITLE);
+ PUTS(shortname);
+ PUTS(source_file ? gettext(" WAIS source file") : INDEX_SEGMENT);
+ END(HTML_TITLE);
+ PUTC('\n');
+ END(HTML_HEAD);
+
+ START(HTML_H1);
+ PUTS(shortname);
+ PUTS(source_file ? gettext(" description") : INDEX_SEGMENT);
+ END(HTML_H1);
+ PUTC('\n');
+ FREE(shortname);
+ }
+
+ START(HTML_DL); /* Definition list of details */
+
+ if (source_file) {
+ START(HTML_DT);
+ PUTS(gettext("Access links"));
+ MAYBE_END(HTML_DT);
+ START(HTML_DD);
+ if (me->par_value[PAR_IP_NAME] &&
+ me->par_value[PAR_DATABASE_NAME]) {
+
+ char *WSRC_address = NULL;
+ char *www_database;
+
+ www_database = HTEscape(me->par_value[PAR_DATABASE_NAME],
+ URL_XALPHAS);
+ HTSprintf0(&WSRC_address, "%s//%s%s%s/%s",
+ STR_WAIS_URL,
+ me->par_value[PAR_IP_NAME],
+ me->par_value[PAR_TCP_PORT] ? ":" : "",
+ (me->par_value[PAR_TCP_PORT]
+ ? me->par_value[PAR_TCP_PORT]
+ : ""),
+ www_database);
+
+ HTStartAnchor(me->target, NULL, WSRC_address);
+ PUTS(gettext("Direct access"));
+ END(HTML_A);
+ /** Proxy will be used if defined, so let user know that - FM **/
+ PUTS(gettext(" (or via proxy server, if defined)"));
+
+ FREE(www_database);
+ FREE(WSRC_address);
+
+ } else {
+ give_parameter(me, PAR_IP_NAME);
+ give_parameter(me, PAR_DATABASE_NAME);
+ }
+ MAYBE_END(HTML_DD);
+
+ }
+ /* end if source_file */
+ if (me->par_value[PAR_MAINTAINER]) {
+ START(HTML_DT);
+ PUTS(gettext("Maintainer"));
+ MAYBE_END(HTML_DT);
+ START(HTML_DD);
+ PUTS(me->par_value[PAR_MAINTAINER]);
+ MAYBE_END(HTML_DD);
+ }
+ if (me->par_value[PAR_IP_NAME]) {
+ START(HTML_DT);
+ PUTS(gettext("Host"));
+ MAYBE_END(HTML_DT);
+ START(HTML_DD);
+ PUTS(me->par_value[PAR_IP_NAME]);
+ MAYBE_END(HTML_DD);
+ }
+
+ END(HTML_DL);
+
+ if (me->par_value[PAR_DESCRIPTION]) {
+ START(HTML_PRE); /* Preformatted description */
+ PUTS(me->par_value[PAR_DESCRIPTION]);
+ END(HTML_PRE);
+ }
+
+ (*me->target->isa->_free) (me->target);
+
+ return;
+} /* generate html */
+
+static void WSRCParser_put_string(HTStream *context, const char *str)
+{
+ const char *p;
+
+ for (p = str; *p; p++)
+ WSRCParser_put_character(context, *p);
+}
+
+static void WSRCParser_write(HTStream *context, const char *str,
+ int l)
+{
+ const char *p;
+ const char *e = str + l;
+
+ for (p = str; p < e; p++)
+ WSRCParser_put_character(context, *p);
+}
+
+static void WSRCParser_free(HTStream *me)
+{
+ WSRC_gen_html(me, YES);
+#ifdef CACHE_FILE_PREFIX
+ write_cache(me);
+#endif
+ {
+ int p;
+
+ for (p = 0; par_name[p]; p++) { /* Clear out old values */
+ FREE(me->par_value[p]);
+ }
+ }
+ FREE(me);
+}
+
+static void WSRCParser_abort(HTStream *me, HTError e GCC_UNUSED)
+{
+ WSRCParser_free(me);
+}
+
+/* Stream subclass -- method routines
+ * ---------------
+ */
+
+static HTStreamClass WSRCParserClass =
+{
+ "WSRCParser",
+ WSRCParser_free,
+ WSRCParser_abort,
+ WSRCParser_put_character,
+ WSRCParser_put_string,
+ WSRCParser_write
+};
+
+/* Converter from WAIS Source to whatever
+ * --------------------------------------
+ */
+HTStream *HTWSRCConvert(HTPresentation *pres, HTParentAnchor *anchor,
+ HTStream *sink)
+{
+ HTStream *me = (HTStream *) malloc(sizeof(*me));
+
+ if (!me)
+ outofmem(__FILE__, "HTWSRCConvert");
+
+ me->isa = &WSRCParserClass;
+ me->target = HTML_new(anchor, pres->rep_out, sink);
+
+ {
+ int p;
+
+ for (p = 0; p < PAR_COUNT; p++) { /* Clear out parameter values */
+ me->par_value[p] = 0;
+ }
+ }
+ me->state = beginning;
+
+ return me;
+}
diff --git a/WWW/Library/Implementation/HTWSRC.h b/WWW/Library/Implementation/HTWSRC.h
new file mode 100644
index 0000000..6c81132
--- /dev/null
+++ b/WWW/Library/Implementation/HTWSRC.h
@@ -0,0 +1,43 @@
+/* A parser for WAIS source files
+ WAIS SOURCE FILE PARSER
+
+ This converter returns a stream object into which a WAIS source file can be
+ written. The result is put via a structured stream into whatever format was
+ required for the output stream.
+
+ See also: HTWAIS protocol interface module
+
+ */
+#ifndef HTWSRC_H
+#define HTWSRC_H
+
+#include <HTFormat.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+ extern char from_hex(char c);
+
+ extern HTStream *HTWSRCConvert(HTPresentation *pres,
+ HTParentAnchor *anchor,
+ HTStream *sink);
+
+/*
+
+Escaping Strings
+
+ HTDeSlash takes out the invalid characters in a URL path ELEMENT by
+ converting them into hex-escaped characters. HTEnSlash does the reverse.
+
+ Each returns a pointer to a newly allocated string which must eventually be
+ freed by the caller.
+
+ */
+ extern char *HTDeSlash(const char *str);
+
+ extern char *HTEnSlash(const char *str);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* HTWSRC_H */
diff --git a/WWW/Library/Implementation/HText.h b/WWW/Library/Implementation/HText.h
new file mode 100644
index 0000000..93ff4a6
--- /dev/null
+++ b/WWW/Library/Implementation/HText.h
@@ -0,0 +1,219 @@
+/*
+ * $LynxId: HText.h,v 1.17 2020/01/21 22:08:07 tom Exp $
+ * Rich Hypertext object for libWWW
+ * RICH HYPERTEXT OBJECT
+ *
+ * This is the C interface to the Objective-C (or whatever) Style-oriented
+ * HyperText class. It is used when a style-oriented text object is available
+ * or craeted in order to display hypertext.
+ */
+#ifndef HTEXT_H
+#define HTEXT_H
+
+#include <HTAnchor.h>
+#include <HTStyle.h>
+#include <HTStream.h>
+#include <SGML.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+#ifndef THINK_C
+#ifndef HyperText /* Objective C version defined HyperText */
+ typedef struct _HText HText; /* Normal Library */
+#endif
+#else
+ class CHyperText; /* Mac Think-C browser hook */
+ typedef CHyperText HText;
+#endif
+
+ extern HText *HTMainText; /* Pointer to current main text */
+ extern HTParentAnchor *HTMainAnchor; /* Pointer to current text's anchor */
+
+ extern const char *HTAppName; /* Application name */
+ extern const char *HTAppVersion; /* Application version */
+
+/*
+
+Creation and deletion
+
+ HTEXT_NEW: CREATE HYPERTEXT OBJECT
+
+ There are several methods depending on how much you want to specify. The
+ output stream is used with objects which need to output the hypertext to a
+ stream. The structure is for objects which need to refer to the structure
+ which is kep by the creating stream.
+
+ */
+ extern HText *HText_new(HTParentAnchor *anchor);
+
+ extern HText *HText_new2(HTParentAnchor *anchor,
+ HTStream *output_stream);
+
+ extern HText *HText_new3(HTParentAnchor *anchor,
+ HTStream *output_stream,
+ HTStructured * structure);
+
+/*
+
+ FREE HYPERTEXT OBJECT
+
+ */
+ extern void HText_free(HText *me);
+
+/*
+
+Object Building methods
+
+ These are used by a parser to build the text in an object HText_beginAppend
+ must be called, then any combination of other append calls, then
+ HText_endAppend. This allows optimised handling using buffers and caches
+ which are flushed at the end.
+
+ */
+ extern void HText_beginAppend(HText *text);
+
+ extern void HText_endAppend(HText *text);
+
+/*
+
+ SET THE STYLE FOR FUTURE TEXT
+
+ */
+
+ extern void HText_setStyle(HText *text, HTStyle *style);
+
+/*
+
+ ADD ONE CHARACTER
+
+ */
+ extern void HText_appendCharacter(HText *text, int ch);
+
+/*
+
+ ADD A ZERO-TERMINATED STRING
+
+ */
+
+ extern void HText_appendText(HText *text, const char *str);
+
+/*
+
+ NEW PARAGRAPH
+
+ and similar things
+
+ */
+ extern void HText_appendParagraph(HText *text);
+
+ extern void HText_appendLineBreak(HText *text);
+
+ extern void HText_appendHorizontalRule(HText *text);
+
+/*
+
+ START/END SENSITIVE TEXT
+
+ */
+
+/*
+
+ The anchor object is created and passed to HText_beginAnchor. The sensitive
+ text is added to the text object, and then HText_endAnchor is called.
+ Anchors may not be nested.
+
+ */
+ extern int HText_beginAnchor(HText *text, int underline,
+ HTChildAnchor *anc);
+ extern void HText_endAnchor(HText *text, int number);
+ extern BOOL HText_isAnchorBlank(HText *text, int number);
+
+/*
+
+ APPEND AN INLINE IMAGE
+
+ The image is handled by the creation of an anchor whose destination is the
+ image document to be included. The semantics is the intended inline display
+ of the image.
+
+ An alternative implementation could be, for example, to begin an anchor,
+ append the alternative text or "IMAGE", then end the anchor. This would
+ simply generate some text linked to the image itself as a separate document.
+
+ */
+ extern void HText_appendImage(HText *text, HTChildAnchor *anc,
+ const char *alternative_text,
+ int alignment,
+ int isMap);
+
+/*
+
+ RETURN THE ANCHOR ASSOCIATED WITH THIS NODE
+
+ */
+ extern HTParentAnchor *HText_nodeAnchor(HText *me);
+
+/*
+
+Browsing functions
+
+ */
+
+/*
+
+ BRING TO FRONT AND HIGHLIGHT IT
+
+ */
+
+ extern BOOL HText_select(HText *text);
+ extern BOOL HText_selectAnchor(HText *text, HTChildAnchor *anchor);
+
+/*
+
+Editing functions
+
+ These are called from the application. There are many more functions not
+ included here from the original text object. These functions NEED NOT BE
+ IMPLEMENTED in a browser which cannot edit.
+
+ */
+/* Style handling:
+*/
+/* Apply this style to the selection
+*/
+ extern void HText_applyStyle(HText *me, HTStyle *style);
+
+/* Update all text with changed style.
+*/
+ extern void HText_updateStyle(HText *me, HTStyle *style);
+
+/* Return style of selection
+*/
+ extern HTStyle *HText_selectionStyle(HText *me, HTStyleSheet *sheet);
+
+/* Paste in styled text
+*/
+ extern void HText_replaceSel(HText *me, const char *aString,
+ HTStyle *aStyle);
+
+/* Apply this style to the selection and all similarly formatted text
+ * (style recovery only)
+ */
+ extern void HTextApplyToSimilar(HText *me, HTStyle *style);
+
+/* Select the first unstyled run.
+ * (style recovery only)
+ */
+ extern void HTextSelectUnstyled(HText *me, HTStyleSheet *sheet);
+
+/* Anchor handling:
+*/
+ extern void HText_unlinkSelection(HText *me);
+ extern HTAnchor *HText_referenceSelected(HText *me);
+ extern HTAnchor *HText_linkSelTo(HText *me, HTAnchor * anchor);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* HTEXT_H */
diff --git a/WWW/Library/Implementation/HTioctl.h b/WWW/Library/Implementation/HTioctl.h
new file mode 100644
index 0000000..99f8632
--- /dev/null
+++ b/WWW/Library/Implementation/HTioctl.h
@@ -0,0 +1,11 @@
+/*
+ * A routine to mimic the ioctl function for UCX.
+ * Bjorn S. Nilsson, 25-Nov-1993. Based on an example in the UCX manual.
+ */
+#include <iodef.h>
+#define IOC_OUT (int)0x40000000
+extern int vaxc$get_sdc(), sys$qiow();
+
+#ifndef UCX$C_IOCTL
+#define UCX$C_IOCTL TCPIP$C_IOCTL
+#endif
diff --git a/WWW/Library/Implementation/LYLeaks.h b/WWW/Library/Implementation/LYLeaks.h
new file mode 100644
index 0000000..a47b962
--- /dev/null
+++ b/WWW/Library/Implementation/LYLeaks.h
@@ -0,0 +1,309 @@
+/*
+ * $LynxId: LYLeaks.h,v 1.19 2020/01/21 22:05:46 tom Exp $
+ */
+#ifndef __LYLEAKS_H
+/*
+ * Avoid include redundancy
+ * Include only if finding memory leaks.
+ */
+#define __LYLEAKS_H
+
+/*
+ * Copyright (c) 1994, University of Kansas, All Rights Reserved
+ *
+ * Include File: LYLeaks.h
+ * Purpose: Header to convert requests for allocation to Lynx
+ * custom functions to track memory leaks.
+ * Remarks/Portability/Dependencies/Restrictions:
+ * For the stdlib.h allocation functions to be overridden by the
+ * Lynx memory tracking functions all modules allocating,
+ * freeing, or resizing memory must have LY_FIND_LEAKS
+ * defined before including this file.
+ * This header file should be included in every source file which
+ * does any memory manipulation through use of the
+ * stdlib.h memory functions.
+ * For proper reporting of memory leaks, the function LYLeaks
+ * should be registered for execution by atexit as the
+ * very first executable statement in main.
+ * This code is slow and should not be used except in debugging
+ * circumstances (don't define LY_FIND_LEAKS).
+ * If you are using LY_FIND_LEAKS and don't want the LYLeak*
+ * memory functions to be used in a certain file,
+ * define NO_MEMORY_TRACKING before including this file.
+ * The only safe way to call the LYLeak* functions is to use
+ * the below macros because they depend on the static
+ * string created by __FILE__ to not be dynamic in
+ * nature (don't free it and assume will exist at all
+ * times during execution).
+ * If you are using LY_FIND_LEAKS and LY_FIND_LEAKS_EXTENDED and
+ * want only normal memory tracking (not extended for
+ * HTSprintf/HTSprintf0) to be used in a certain file,
+ * define NO_EXTENDED_MEMORY_TRACKING and don't define
+ * NO_MEMORY_TRACKING before including this file.
+ * Revision History:
+ * 05-26-94 created for Lynx 2-3-1, Garrett Arch Blythe
+ * 10-30-97 modified to handle StrAllocCopy() and
+ * StrAllocCat(). - KW & FM
+ * 1999-10-17 modified to handle HTSprintf0 and HTSprintf(),
+ * and to provide mark_malloced, if
+ * LY_FIND_LEAKS_EXTENDED is defined. - kw
+ * 2003-01-22 add sequence-id for counting mallocs/frees -TD
+ * 2004-04-27 ANSIfy'd -TD
+ * 2012-02-09 add bstring interfaces -TD
+ */
+
+/* Undefine this to get no improved HTSprintf0/HTSprintf tracking: */
+#define LY_FIND_LEAKS_EXTENDED
+
+/*
+ * Required includes
+ */
+
+#ifndef HTUTILS_H
+#include <HTUtils.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+ * Constant defines
+ */
+#define MAX_CONTENT_LENGTH 50
+#ifdef VMS
+#define LEAKAGE_SINK "sys$login:Lynx.leaks"
+#else
+#define LEAKAGE_SINK "Lynx.leaks"
+#endif /* VMS */
+/*
+ * Data structures
+ */
+ typedef struct SourceLocation_tag {
+ /*
+ * The file name and line number of where an event took place.
+ */
+ const char *cp_FileName;
+ short ssi_LineNumber;
+ } SourceLocation;
+
+ typedef struct AllocationList_tag {
+ /*
+ * A singly linked list.
+ */
+ struct AllocationList_tag *ALp_Next;
+
+ /*
+ * Count the number of mallocs.
+ */
+ long st_Sequence;
+
+ /*
+ * The memory pointer allocated. If set to NULL, then an invalid request
+ * was made. The invalid pointer also.
+ */
+ void *vp_Alloced;
+ void *vp_BadRequest;
+
+ /*
+ * The size in bytes of the allocated memory.
+ */
+ size_t st_Bytes;
+
+ /*
+ * The source location of specific event (calloc, malloc, free). realloc
+ * kept separate since will track last realloc on pointer.
+ */
+ SourceLocation SL_memory;
+ SourceLocation SL_realloc;
+ } AllocationList;
+
+/*
+ * Global variable declarations
+ */
+ extern char LYLeaksPath[];
+
+/*
+ * Macros
+ */
+#if defined(LY_FIND_LEAKS) && !defined(NO_MEMORY_TRACKING)
+/*
+ * Only use these macros if we are to track memory allocations. The reason for
+ * using a macro instead of a define is that we want to track where the initial
+ * allocation took place or where the last reallocation took place. Track
+ * where the allocation took place by the __FILE__ and __LINE__ defines which
+ * are automatic to the compiler.
+ */
+#ifdef malloc
+#undef malloc
+#endif /* malloc */
+#define malloc(st_bytes) LYLeakMalloc(st_bytes, __FILE__, __LINE__)
+
+#ifdef calloc
+#undef calloc
+#endif /* calloc */
+#define calloc(st_number, st_bytes) LYLeakCalloc(st_number, st_bytes, \
+ __FILE__, __LINE__)
+
+#ifdef realloc
+#undef realloc
+#endif /* realloc */
+#define realloc(vp_alloced, st_newbytes) LYLeakRealloc(vp_alloced, \
+ st_newbytes, __FILE__, __LINE__)
+
+#ifdef free
+#undef free
+#endif /* free */
+#define free(vp_alloced) LYLeakFree(vp_alloced, __FILE__, __LINE__)
+
+#ifdef strdup
+#undef strdup
+#endif /* free */
+#define strdup(vp_alloced) LYLeakStrdup(vp_alloced, __FILE__, __LINE__)
+
+/*
+ * Added the following two defines to track Lynx's frequent use of those
+ * macros. - KW 1997-10-12
+ */
+#ifdef StrAllocCopy
+#undef StrAllocCopy
+#endif /* StrAllocCopy */
+#define StrAllocCopy(dest, src) LYLeakSACopy(&(dest), src, __FILE__, __LINE__)
+
+#ifdef StrAllocCat
+#undef StrAllocCat
+#endif /* StrAllocCat */
+#define StrAllocCat(dest, src) LYLeakSACat(&(dest), src, __FILE__, __LINE__)
+
+#ifdef BStrAlloc
+#undef BStrAlloc
+#endif
+#define BStrAlloc(d,n) LYLeakSABAlloc( &(d), n, __FILE__, __LINE__)
+
+#ifdef BStrCopy
+#undef BStrCopy
+#endif
+#define BStrCopy(d,s) LYLeakSABCopy( &(d), BStrData(s), BStrLen(s), __FILE__, __LINE__)
+
+#ifdef BStrCopy0
+#undef BStrCopy0
+#endif
+#define BStrCopy0(d,s) LYLeakSABCopy0( &(d), s, __FILE__, __LINE__)
+
+#ifdef BStrCat
+#undef BStrCat
+#endif
+#define BStrCat(d,s) LYLeakSABCat( &(d), BStrData(s), BStrLen(s), __FILE__, __LINE__)
+
+#ifdef BStrCat0
+#undef BStrCat0
+#endif
+#define BStrCat0(d,s) LYLeakSABCat0( &(d), s, __FILE__, __LINE__)
+
+#define mark_malloced(a,size) LYLeak_mark_malloced(a,size, __FILE__, __LINE__)
+
+#if defined(LY_FIND_LEAKS_EXTENDED) && !defined(NO_EXTENDED_MEMORY_TRACKING)
+
+#ifdef HTSprintf0
+#undef HTSprintf0
+#endif /* HTSprintf0 */
+#define HTSprintf0 (Get_htsprintf0_fn(__FILE__,__LINE__))
+
+#ifdef HTSprintf
+#undef HTSprintf
+#endif /* HTSprintf */
+#define HTSprintf (Get_htsprintf_fn(__FILE__,__LINE__))
+
+#endif /* LY_FIND_LEAKS_EXTENDED and not NO_EXTENDED_MEMORY_TRACKING */
+
+#else /* LY_FIND_LEAKS && !NO_MEMORY_TRACKING */
+
+#define mark_malloced(a,size) /* no-op */
+#define LYLeakSequence() (-1)
+
+#endif /* LY_FIND_LEAKS && !NO_MEMORY_TRACKING */
+
+#if defined(LY_FIND_LEAKS)
+#define PUBLIC_IF_FIND_LEAKS /* nothing */
+#else
+#define PUBLIC_IF_FIND_LEAKS static
+#endif
+
+/*
+ * Function declarations.
+ * See the appropriate source file for usage.
+ */
+#ifndef LYLeakSequence
+ extern long LYLeakSequence(void);
+#endif
+ extern void LYLeaks(void);
+
+#ifdef LY_FIND_LEAKS_EXTENDED
+ extern AllocationList *LYLeak_mark_malloced(void *vp_alloced,
+ size_t st_bytes,
+ const char *cp_File,
+ const short ssi_Line);
+#endif /* LY_FIND_LEAKS_EXTENDED */
+ extern void *LYLeakMalloc(size_t st_bytes, const char *cp_File,
+ const short ssi_Line);
+ extern void *LYLeakCalloc(size_t st_number, size_t st_bytes, const char *cp_File,
+ const short ssi_Line);
+ extern void *LYLeakRealloc(void *vp_alloced,
+ size_t st_newbytes,
+ const char *cp_File,
+ const short ssi_Line);
+ extern void LYLeakFree(void *vp_alloced,
+ const char *cp_File,
+ const short ssi_Line);
+ extern char *LYLeakStrdup(const char *src,
+ const char *cp_File,
+ const short ssi_Line);
+ extern char *LYLeakSACopy(char **dest,
+ const char *src,
+ const char *cp_File,
+ const short ssi_Line);
+ extern char *LYLeakSACat(char **dest,
+ const char *src,
+ const char *cp_File,
+ const short ssi_Line);
+ extern void LYLeakSABAlloc(bstring **dest,
+ int len,
+ const char *cp_File,
+ const short ssi_Line);
+ extern void LYLeakSABCopy(bstring **dest,
+ const char *src,
+ int len,
+ const char *cp_File,
+ const short ssi_Line);
+ extern void LYLeakSABCopy0(bstring **dest,
+ const char *src,
+ const char *cp_File,
+ const short ssi_Line);
+ extern void LYLeakSABCat(bstring **dest,
+ const char *src,
+ int len,
+ const char *cp_File,
+ const short ssi_Line);
+ extern void LYLeakSABCat0(bstring **dest,
+ const char *src,
+ const char *cp_File,
+ const short ssi_Line);
+ extern void LYLeakSABFree(bstring **ptr,
+ const char *cp_File,
+ const short ssi_Line);
+
+#ifdef LY_FIND_LEAKS_EXTENDED
+/*
+ * Trick to get tracking of var arg functions without relying on var arg
+ * preprocessor macros:
+ */
+ typedef char *HTSprintflike(char **, const char *, ...);
+ extern HTSprintflike *Get_htsprintf_fn(const char *cp_File,
+ const short ssi_Line);
+ extern HTSprintflike *Get_htsprintf0_fn(const char *cp_File,
+ const short ssi_Line);
+#endif /* LY_FIND_LEAKS_EXTENDED */
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* __LYLEAKS_H */
diff --git a/WWW/Library/Implementation/LYexit.h b/WWW/Library/Implementation/LYexit.h
new file mode 100644
index 0000000..dd4b0e6
--- /dev/null
+++ b/WWW/Library/Implementation/LYexit.h
@@ -0,0 +1,67 @@
+#ifndef __LYEXIT_H
+/*
+ * Avoid include redundancy
+ */
+#define __LYEXIT_H
+
+/*
+ * Copyright (c) 1994, University of Kansas, All Rights Reserved
+ *
+ * Include File: LYexit.h
+ * Purpose: Provide an atexit function for libraries without such.
+ * Remarks/Portability/Dependencies/Restrictions:
+ * Include this header in every file that you have an exit or
+ * atexit statement.
+ * Revision History:
+ * 06-15-94 created Lynx 2-3-1 Garrett Arch Blythe
+ */
+
+/*
+ * Required includes
+ */
+#ifdef _WINDOWS
+#include <process.h> /* declares exit() */
+#endif
+
+#ifndef HTUTILS_H
+#include <HTUtils.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+ * Constant defines
+ */
+#ifdef exit
+#undef exit
+#endif
+#define exit(code) LYexit(code)
+#define atexit LYatexit
+#define ATEXITSIZE 50
+
+/*
+ * Data structures
+ */
+
+/*
+ * Global variable declarations
+ */
+
+/*
+ * Macros
+ */
+
+/*
+ * Function declarations
+ */
+ extern GCC_NORETURN void outofmem(const char *fname, const char *func);
+ extern void reset_signals(void);
+ extern GCC_NORETURN void exit_immediately(int status);
+ extern GCC_NORETURN void LYexit(int status);
+ extern int LYatexit(void (*function) (void));
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* __LYEXIT_H */
diff --git a/WWW/Library/Implementation/SGML.c b/WWW/Library/Implementation/SGML.c
new file mode 100644
index 0000000..2534606
--- /dev/null
+++ b/WWW/Library/Implementation/SGML.c
@@ -0,0 +1,5159 @@
+/*
+ * $LynxId: SGML.c,v 1.183 2022/06/13 00:20:50 tom Exp $
+ *
+ * General SGML Parser code SGML.c
+ * ========================
+ *
+ * This module implements an HTStream object. To parse an
+ * SGML file, create this object which is a parser. The object
+ * is (currently) created by being passed a DTD structure,
+ * and a target HTStructured object at which to throw the parsed stuff.
+ *
+ * 6 Feb 93 Binary searches used. Interface modified.
+ */
+
+#define HTSTREAM_INTERNAL 1
+
+#include <HTUtils.h>
+
+#include <SGML.h>
+#include <HTMLDTD.h>
+#include <HTAccess.h>
+#include <UCAux.h>
+
+#include <HTChunk.h>
+#include <HTUtils.h>
+
+#include <LYCharSets.h>
+#include <LYCharVals.h> /* S/390 -- gil -- 0635 */
+#include <LYGlobalDefs.h>
+#include <LYStrings.h>
+#include <LYLeaks.h>
+#include <LYUtils.h>
+
+#ifdef USE_COLOR_STYLE
+# include <LYStyle.h>
+#endif
+#ifdef USE_PRETTYSRC
+# include <LYPrettySrc.h>
+#endif
+
+/* a global variable doesn't work with info-stages which convert encoding */
+#if defined(EXP_CHINESEUTF8_SUPPORT)
+#undef IS_CJK_TTY
+#define IS_CJK_TTY me->T.do_cjk
+#endif
+
+#define AssumeCP1252(me) \
+ (((me)->inUCLYhndl == LATIN1 \
+ || (me)->inUCLYhndl == US_ASCII) \
+ && html5_charsets)
+
+#define INVALID (-1)
+
+static int sgml_offset;
+
+#ifdef USE_PRETTYSRC
+
+static char *entity_string; /* this is used for printing entity name.
+
+ Unconditionally added since redundant assignments don't hurt much */
+
+static void fake_put_character(HTStream *p GCC_UNUSED,
+ int c GCC_UNUSED)
+{
+}
+
+#define START TRUE
+#define STOP FALSE
+
+#define PUTS_TR(x) psrc_convert_string = TRUE; PUTS(x)
+
+#endif
+
+/* my_casecomp() - optimized by the first character, NOT_ASCII ok */
+#define my_casecomp(a,b) ((TOUPPER(*a) == TOUPPER(*b)) ? \
+ AS_casecomp(a,b) : \
+ (TOASCII(TOUPPER(*a)) - TOASCII(TOUPPER(*b))))
+
+/* ...used for comments and attributes value like href... */
+#define HTChunkPutUtf8Char(ch,x) \
+ { \
+ if ((TOASCII(x) < 128) && (ch->size < ch->allocated)) \
+ ch->data[ch->size++] = (char)x; \
+ else \
+ (HTChunkPutUtf8Char)(ch,x); \
+ }
+
+#define PUTS(str) ((*me->actions->put_string)(me->target, str))
+#define PUTC(ch) ((*me->actions->put_character)(me->target, (char) ch))
+#define PUTUTF8(code) (UCPutUtf8_charstring((HTStream *)me->target, \
+ (putc_func_t*)(me->actions->put_character), code))
+
+#ifdef USE_PRETTYSRC
+#define PRETTYSRC_PUTC(c) if (psrc_view) PUTC(c)
+#else
+#define PRETTYSRC_PUTC(c) /* nothing */
+#endif
+
+/*the following macros are used for pretty source view. */
+#define IS_C(attr) (attr.type == HTMLA_CLASS)
+
+#if defined(USE_JAPANESEUTF8_SUPPORT)
+# define UTF8_TTY_ISO2022JP (me->T.output_utf8)
+#else
+# define UTF8_TTY_ISO2022JP 0
+#endif
+
+HTCJKlang HTCJK = NOCJK; /* CJK enum value. */
+BOOL HTPassEightBitRaw = FALSE; /* Pass 161-172,174-255 raw. */
+BOOL HTPassEightBitNum = FALSE; /* Pass ^ numeric entities raw. */
+BOOL HTPassHighCtrlRaw = FALSE; /* Pass 127-160,173,&#127; raw. */
+BOOL HTPassHighCtrlNum = FALSE; /* Pass &#128;-&#159; raw. */
+
+/* The State (context) of the parser
+ *
+ * This is passed with each call to make the parser reentrant
+ */
+
+#define MAX_ATTRIBUTES 36 /* Max number of attributes per element */
+
+/* Element Stack
+ * -------------
+ * This allows us to return down the stack reselecting styles.
+ * As we return, attribute values will be garbage in general.
+ */
+typedef struct _HTElement HTElement;
+struct _HTElement {
+ HTElement *next; /* Previously nested element or 0 */
+ HTTag *tag; /* The tag at this level */
+};
+
+typedef enum {
+ S_text = 0
+ ,S_attr
+ ,S_attr_gap
+ ,S_comment
+ ,S_cro
+ ,S_doctype
+ ,S_dollar
+ ,S_dollar_dq
+ ,S_dollar_paren
+ ,S_dollar_paren_dq
+ ,S_dollar_paren_sq
+ ,S_dollar_sq
+ ,S_dquoted
+ ,S_end
+ ,S_entity
+ ,S_equals
+ ,S_ero
+ ,S_esc
+ ,S_esc_dq
+ ,S_esc_sq
+ ,S_exclamation
+ ,S_in_kanji
+ ,S_incro
+ ,S_junk_tag
+ ,S_litteral
+ ,S_marked
+ ,S_nonascii_text
+ ,S_nonascii_text_dq
+ ,S_nonascii_text_sq
+ ,S_paren
+ ,S_paren_dq
+ ,S_paren_sq
+ ,S_pcdata
+ ,S_pi
+ ,S_script
+ ,S_sgmlatt
+ ,S_sgmlele
+ ,S_sgmlent
+ ,S_squoted
+ ,S_tag
+ ,S_tag_gap
+ ,S_tagname_slash
+ ,S_value
+} sgml_state;
+
+/* Internal Context Data Structure
+ * -------------------------------
+ */
+struct _HTStream {
+
+ const HTStreamClass *isa; /* inherited from HTStream */
+
+ const SGML_dtd *dtd;
+ const HTStructuredClass *actions; /* target class */
+ HTStructured *target; /* target object */
+
+ HTTag *current_tag;
+ HTTag *slashedtag;
+ const HTTag *unknown_tag;
+ BOOL extended_html; /* xhtml */
+ BOOL strict_xml; /* xml */
+ BOOL inSELECT;
+ BOOL no_lynx_specialcodes;
+ int current_attribute_number;
+ HTChunk *string;
+ int leading_spaces;
+ int trailing_spaces;
+ HTElement *element_stack;
+ sgml_state state;
+ unsigned char kanji_buf;
+#ifdef CALLERDATA
+ void *callerData;
+#endif /* CALLERDATA */
+ BOOL present[MAX_ATTRIBUTES]; /* Flags: attribute is present? */
+ char *value[MAX_ATTRIBUTES]; /* NULL, or strings alloc'd with StrAllocCopy_extra() */
+
+ BOOL lead_exclamation;
+ BOOL first_dash;
+ BOOL end_comment;
+ BOOL doctype_bracket;
+ BOOL first_bracket;
+ BOOL second_bracket;
+ BOOL isHex;
+
+ HTParentAnchor *node_anchor;
+ LYUCcharset *inUCI; /* pointer to anchor UCInfo */
+ int inUCLYhndl; /* charset we are fed */
+ LYUCcharset *outUCI; /* anchor UCInfo for target */
+ int outUCLYhndl; /* charset for target */
+ UTFDecodeState U;
+ UCTransParams T;
+ int current_tag_charset; /* charset to pass attributes */
+
+ char *recover;
+ int recover_index;
+ char *include;
+ char *active_include;
+ int include_index;
+ char *url;
+ char *csi;
+ int csi_index;
+#ifdef USE_PRETTYSRC
+ BOOL cur_attr_is_href;
+ BOOL cur_attr_is_name;
+#endif
+};
+
+#ifdef NO_LYNX_TRACE
+#define state_name(n) "state"
+#else
+static const char *state_name(sgml_state n)
+{
+ const char *result = "?";
+ /* *INDENT-OFF* */
+ switch (n) {
+ case S_attr: result = "S_attr"; break;
+ case S_attr_gap: result = "S_attr_gap"; break;
+ case S_comment: result = "S_comment"; break;
+ case S_cro: result = "S_cro"; break;
+ case S_doctype: result = "S_doctype"; break;
+ case S_dollar: result = "S_dollar"; break;
+ case S_dollar_dq: result = "S_dollar_dq"; break;
+ case S_dollar_paren: result = "S_dollar_paren"; break;
+ case S_dollar_paren_dq: result = "S_dollar_paren_dq"; break;
+ case S_dollar_paren_sq: result = "S_dollar_paren_sq"; break;
+ case S_dollar_sq: result = "S_dollar_sq"; break;
+ case S_dquoted: result = "S_dquoted"; break;
+ case S_end: result = "S_end"; break;
+ case S_entity: result = "S_entity"; break;
+ case S_equals: result = "S_equals"; break;
+ case S_ero: result = "S_ero"; break;
+ case S_esc: result = "S_esc"; break;
+ case S_esc_dq: result = "S_esc_dq"; break;
+ case S_esc_sq: result = "S_esc_sq"; break;
+ case S_exclamation: result = "S_exclamation"; break;
+ case S_in_kanji: result = "S_in_kanji"; break;
+ case S_incro: result = "S_incro"; break;
+ case S_pi: result = "S_pi"; break;
+ case S_junk_tag: result = "S_junk_tag"; break;
+ case S_litteral: result = "S_litteral"; break;
+ case S_marked: result = "S_marked"; break;
+ case S_nonascii_text: result = "S_nonascii_text"; break;
+ case S_nonascii_text_dq: result = "S_nonascii_text_dq"; break;
+ case S_nonascii_text_sq: result = "S_nonascii_text_sq"; break;
+ case S_paren: result = "S_paren"; break;
+ case S_paren_dq: result = "S_paren_dq"; break;
+ case S_paren_sq: result = "S_paren_sq"; break;
+ case S_pcdata: result = "S_pcdata"; break;
+ case S_script: result = "S_script"; break;
+ case S_sgmlatt: result = "S_sgmlatt"; break;
+ case S_sgmlele: result = "S_sgmlele"; break;
+ case S_sgmlent: result = "S_sgmlent"; break;
+ case S_squoted: result = "S_squoted"; break;
+ case S_tag: result = "S_tag"; break;
+ case S_tag_gap: result = "S_tag_gap"; break;
+ case S_tagname_slash: result = "S_tagname_slash"; break;
+ case S_text: result = "S_text"; break;
+ case S_value: result = "S_value"; break;
+ }
+ /* *INDENT-ON* */
+
+ return result;
+}
+#endif
+
+/* storage for Element Stack */
+#define DEPTH 10
+static HTElement pool[DEPTH];
+static int depth = 0;
+
+static HTElement *pool_alloc(void)
+{
+ depth++;
+ if (depth > DEPTH)
+ return (HTElement *) malloc(sizeof(HTElement));
+ return (pool + depth - 1);
+}
+
+static void pool_free(HTElement * e)
+{
+ if (depth > DEPTH)
+ FREE(e);
+ depth--;
+ return;
+}
+
+#ifdef USE_PRETTYSRC
+
+static void HTMLSRC_apply_markup(HTStream *me,
+ HTlexeme lexeme,
+ int start)
+{
+ HT_tagspec *ts = *((start ? lexeme_start : lexeme_end) + lexeme);
+
+ while (ts) {
+#ifdef USE_COLOR_STYLE
+ if (ts->start) {
+ current_tag_style = ts->style;
+ force_current_tag_style = TRUE;
+ forced_classname = ts->class_name;
+ force_classname = TRUE;
+ }
+#endif
+ CTRACE((tfp, ts->start ? "SRCSTART %d\n" : "SRCSTOP %d\n", (int) lexeme));
+ if (ts->start)
+ (*me->actions->start_element) (me->target,
+ (int) ts->element,
+ ts->present,
+ (STRING2PTR) ts->value,
+ me->current_tag_charset,
+ &me->include);
+ else
+ (*me->actions->end_element) (me->target,
+ (int) ts->element,
+ &me->include);
+ ts = ts->next;
+ }
+}
+
+#define PSRCSTART(x) HTMLSRC_apply_markup(me,HTL_##x,START)
+#define PSRCSTOP(x) HTMLSRC_apply_markup(me,HTL_##x,STOP)
+
+#define attr_is_href me->cur_attr_is_href
+#define attr_is_name me->cur_attr_is_name
+#endif
+
+static void set_chartrans_handling(HTStream *me,
+ HTParentAnchor *anchor,
+ int chndl)
+{
+ if (chndl < 0) {
+ /*
+ * Nothing was set for the parser in earlier stages, so the HTML
+ * parser's UCLYhndl should still be its default. - FM
+ */
+ chndl = HTAnchor_getUCLYhndl(anchor, UCT_STAGE_STRUCTURED);
+ if (chndl < 0)
+ /*
+ * That wasn't set either, so seek the HText default. - FM
+ */
+ chndl = HTAnchor_getUCLYhndl(anchor, UCT_STAGE_HTEXT);
+ if (chndl < 0)
+ /*
+ * That wasn't set either, so assume the current display character
+ * set. - FM
+ */
+ chndl = current_char_set;
+ /*
+ * Try to set the HText and HTML stages' chartrans info with the
+ * default lock level (will not be changed if it was set previously
+ * with a higher lock level). - FM
+ */
+ HTAnchor_setUCInfoStage(anchor, chndl,
+ UCT_STAGE_HTEXT,
+ UCT_SETBY_DEFAULT);
+ HTAnchor_setUCInfoStage(anchor, chndl,
+ UCT_STAGE_STRUCTURED,
+ UCT_SETBY_DEFAULT);
+ /*
+ * Get the chartrans info for output to the HTML parser. - FM
+ */
+ me->outUCI = HTAnchor_getUCInfoStage(anchor,
+ UCT_STAGE_STRUCTURED);
+ me->outUCLYhndl = HTAnchor_getUCLYhndl(me->node_anchor,
+ UCT_STAGE_STRUCTURED);
+ }
+ /*
+ * Set the in->out transformation parameters. - FM
+ */
+ UCSetTransParams(&me->T,
+ me->inUCLYhndl, me->inUCI,
+ me->outUCLYhndl, me->outUCI);
+ /*
+ * This is intended for passing the SGML parser's input charset as an
+ * argument in each call to the HTML parser's start tag function, but it
+ * would be better to call a Lynx_HTML_parser function to set an element in
+ * its HTStructured object, itself, if this were needed. - FM
+ */
+#ifndef USE_JAPANESEUTF8_SUPPORT
+ if (IS_CJK_TTY) {
+ me->current_tag_charset = -1;
+ } else
+#endif
+ if (me->T.transp) {
+ me->current_tag_charset = me->inUCLYhndl;
+ } else if (me->T.decode_utf8) {
+ me->current_tag_charset = me->inUCLYhndl;
+ } else if (me->T.do_8bitraw ||
+ me->T.use_raw_char_in) {
+ me->current_tag_charset = me->inUCLYhndl;
+ } else if (me->T.output_utf8 ||
+ me->T.trans_from_uni) {
+ me->current_tag_charset = UCGetLYhndl_byMIME("utf-8");
+ } else {
+ me->current_tag_charset = LATIN1;
+ }
+}
+
+static void change_chartrans_handling(HTStream *me)
+{
+ int new_LYhndl = HTAnchor_getUCLYhndl(me->node_anchor,
+ UCT_STAGE_PARSER);
+
+ if (new_LYhndl != me->inUCLYhndl &&
+ new_LYhndl >= 0) {
+ /*
+ * Something changed. but ignore if a META wants an unknown charset.
+ */
+ LYUCcharset *new_UCI = HTAnchor_getUCInfoStage(me->node_anchor,
+ UCT_STAGE_PARSER);
+
+ if (new_UCI) {
+ LYUCcharset *next_UCI = HTAnchor_getUCInfoStage(me->node_anchor,
+ UCT_STAGE_STRUCTURED);
+ int next_LYhndl = HTAnchor_getUCLYhndl(me->node_anchor, UCT_STAGE_STRUCTURED);
+
+ me->inUCI = new_UCI;
+ me->inUCLYhndl = new_LYhndl;
+ me->outUCI = next_UCI;
+ me->outUCLYhndl = next_LYhndl;
+ set_chartrans_handling(me,
+ me->node_anchor, next_LYhndl);
+ }
+ }
+}
+
+#ifdef USE_COLOR_STYLE
+#include <AttrList.h>
+static int current_is_class = 0;
+#endif
+
+/* Handle Attribute
+ * ----------------
+ */
+/* PUBLIC const char * SGML_default = ""; ?? */
+
+static void handle_attribute_name(HTStream *me, const char *s)
+{
+ HTTag *tag = me->current_tag;
+ const attr *attributes = tag->attributes;
+ int high, low, i, diff;
+
+#ifdef USE_PRETTYSRC
+ if (psrc_view) {
+ attr_is_href = FALSE;
+ attr_is_name = FALSE;
+ }
+#endif
+ /*
+ * Ignore unknown tag. - KW
+ */
+ if (tag == me->unknown_tag) {
+#ifdef USE_PRETTYSRC
+ if (psrc_view)
+ me->current_attribute_number = 1; /* anything !=INVALID */
+#endif
+ return;
+ }
+
+ /*
+ * Binary search for attribute name.
+ */
+ for (low = 0, high = tag->number_of_attributes;
+ high > low;
+ diff < 0 ? (low = i + 1) : (high = i)) {
+ i = (low + (high - low) / 2);
+ diff = my_casecomp(attributes[i].name, s);
+ if (diff == 0) { /* success: found it */
+ me->current_attribute_number = i;
+#ifdef USE_PRETTYSRC
+ if (psrc_view) {
+ attr_is_name = (BOOL) (attributes[i].type == HTMLA_ANAME);
+ attr_is_href = (BOOL) (attributes[i].type == HTMLA_HREF);
+ } else
+#endif
+ {
+ me->present[i] = YES;
+ Clear_extra(me->value[i]);
+#ifdef USE_COLOR_STYLE
+# ifdef USE_PRETTYSRC
+ current_is_class = IS_C(attributes[i]);
+# else
+ current_is_class = (!strcasecomp("class", s));
+# endif
+ CTRACE((tfp, "SGML: found attribute %s, %d\n", s, current_is_class));
+#endif
+ }
+ return;
+ }
+ /* if */
+ } /* for */
+
+ CTRACE((tfp, "SGML: Unknown attribute %s for tag %s\n",
+ s, NonNull(me->current_tag->name)));
+ me->current_attribute_number = INVALID; /* Invalid */
+}
+
+/* Handle attribute value
+ * ----------------------
+ */
+static void handle_attribute_value(HTStream *me, const char *s)
+{
+ if (me->current_attribute_number != INVALID) {
+ StrAllocCopy_extra(me->value[me->current_attribute_number], s);
+#ifdef USE_COLOR_STYLE
+ if (current_is_class) {
+ StrNCpy(class_string, s, TEMPSTRINGSIZE);
+ CTRACE((tfp, "SGML: class is '%s'\n", s));
+ } else {
+ CTRACE((tfp, "SGML: attribute value is '%s'\n", s));
+ }
+#endif
+ } else {
+ CTRACE((tfp, "SGML: Attribute value %s ***ignored\n", s));
+ }
+ me->current_attribute_number = INVALID; /* can't have two assignments! */
+}
+
+/*
+ * Translate some Unicodes to Lynx special codes and output them.
+ * Special codes - ones those output depend on parsing.
+ *
+ * Additional issue, like handling bidirectional text if necessary
+ * may be called from here: zwnj (8204), zwj (8205), lrm (8206), rlm (8207)
+ * - currently they are ignored in SGML.c and LYCharUtils.c
+ * but also in UCdomap.c because they are non printable...
+ *
+ */
+static BOOL put_special_unicodes(HTStream *me, UCode_t code)
+{
+ /* (Tgf_nolyspcl) */
+ if (me->no_lynx_specialcodes) {
+ /*
+ * We were asked by a "DTD" flag to not generate lynx specials. - kw
+ */
+ return NO;
+ }
+
+ if (code == CH_NBSP) { /* S/390 -- gil -- 0657 */
+ /*
+ * Use Lynx special character for nbsp.
+ */
+#ifdef USE_PRETTYSRC
+ if (!psrc_view)
+#endif
+ PUTC(HT_NON_BREAK_SPACE);
+ } else if (code == CH_SHY) {
+ /*
+ * Use Lynx special character for shy.
+ */
+#ifdef USE_PRETTYSRC
+ if (!psrc_view)
+#endif
+ PUTC(LY_SOFT_HYPHEN);
+ } else if (code == 8194 || code == 8201) {
+ /*
+ * Use Lynx special character for ensp or thinsp.
+ *
+ * Originally, Lynx use space '32' as word delimiter and omits this
+ * space at end of line if word is wrapped to the next line. There are
+ * several other spaces in the Unicode repertoire and we should teach
+ * Lynx to understand them, not only as regular characters but in the
+ * context of line wrapping. Unfortunately, if we use HT_EN_SPACE we
+ * override the chartrans tables for those spaces with a single '32'
+ * for all (but do line wrapping more fancy).
+ *
+ * We may treat emsp as one or two ensp (below).
+ */
+#ifdef USE_PRETTYSRC
+ if (!psrc_view)
+#endif
+ PUTC(HT_EN_SPACE);
+ } else if (code == 8195) {
+ /*
+ * Use Lynx special character for emsp.
+ */
+#ifdef USE_PRETTYSRC
+ if (!psrc_view) {
+#endif
+ /* PUTC(HT_EN_SPACE); let's stay with a single space :) */
+ PUTC(HT_EN_SPACE);
+#ifdef USE_PRETTYSRC
+ }
+#endif
+ } else {
+ /*
+ * Return NO if nothing done.
+ */
+ return NO;
+ }
+ /*
+ * We have handled it.
+ */
+ return YES;
+}
+
+#ifdef USE_PRETTYSRC
+static void put_pretty_entity(HTStream *me, int term)
+{
+ PSRCSTART(entity);
+ PUTC('&');
+ PUTS(entity_string);
+ if (term)
+ PUTC((char) term);
+ PSRCSTOP(entity);
+}
+
+static void put_pretty_number(HTStream *me)
+{
+ PSRCSTART(entity);
+ PUTS((me->isHex ? "&#x" : "&#"));
+ PUTS(entity_string);
+ PUTC(';');
+ PSRCSTOP(entity);
+}
+#endif /* USE_PRETTYSRC */
+
+/* Handle entity
+ * -------------
+ *
+ * On entry,
+ * s contains the entity name zero terminated
+ * Bugs:
+ * If the entity name is unknown, the terminator is treated as
+ * a printable non-special character in all cases, even if it is '<'
+ * Bug-fix:
+ * Modified SGML_character() so we only come here with terminator
+ * as '\0' and check a FoundEntity flag. -- Foteos Macrides
+ *
+ * Modified more (for use with Lynx character translation code):
+ */
+static char replace_buf[64]; /* buffer for replacement strings */
+static BOOL FoundEntity = FALSE;
+
+static void handle_entity(HTStream *me, int term)
+{
+ UCode_t code;
+ long uck = -1;
+ const char *s = me->string->data;
+
+ /*
+ * Handle all entities normally. - FM
+ */
+ FoundEntity = FALSE;
+ if ((code = HTMLGetEntityUCValue(s)) != 0) {
+ /*
+ * We got a Unicode value for the entity name. Check for special
+ * Unicodes. - FM
+ */
+ if (put_special_unicodes(me, code)) {
+#ifdef USE_PRETTYSRC
+ if (psrc_view) {
+ put_pretty_entity(me, term);
+ }
+#endif
+ FoundEntity = TRUE;
+ return;
+ }
+ /*
+ * Seek a translation from the chartrans tables.
+ */
+ if ((uck = UCTransUniChar(code, me->outUCLYhndl)) >= 32 &&
+/* =============== work in ASCII below here =============== S/390 -- gil -- 0672 */
+ uck < 256 &&
+ (uck < 127 ||
+ uck >= LYlowest_eightbit[me->outUCLYhndl])) {
+#ifdef USE_PRETTYSRC
+ if (psrc_view) {
+ put_pretty_entity(me, term);
+ } else
+#endif
+ PUTC(FROMASCII((char) uck));
+ FoundEntity = TRUE;
+ return;
+ } else if ((uck == -4 ||
+ (me->T.repl_translated_C0 &&
+ uck > 0 && uck < 32)) &&
+ /*
+ * Not found; look for replacement string.
+ */
+ (uck = UCTransUniCharStr(replace_buf, 60, code,
+ me->outUCLYhndl, 0) >= 0)) {
+#ifdef USE_PRETTYSRC
+ if (psrc_view) {
+ put_pretty_entity(me, term);
+ } else
+#endif
+ PUTS(replace_buf);
+ FoundEntity = TRUE;
+ return;
+ }
+ /*
+ * If we're displaying UTF-8, try that now. - FM
+ */
+#ifndef USE_PRETTYSRC
+ if (me->T.output_utf8 && PUTUTF8(code)) {
+ FoundEntity = TRUE;
+ return;
+ }
+#else
+ if (me->T.output_utf8 && (psrc_view
+ ? (UCPutUtf8_charstring((HTStream *) me->target,
+ (putc_func_t *) (fake_put_character),
+ code))
+ : PUTUTF8(code))) {
+
+ if (psrc_view) {
+ put_pretty_entity(me, term);
+ }
+
+ FoundEntity = TRUE;
+ return;
+ }
+#endif
+ /*
+ * If it's safe ASCII, use it. - FM
+ */
+ if (code >= 32 && code < 127) {
+#ifdef USE_PRETTYSRC
+ if (psrc_view) {
+ put_pretty_entity(me, term);
+ } else
+#endif
+
+ PUTC(FROMASCII((char) code));
+ FoundEntity = TRUE;
+ return;
+ }
+/* =============== work in ASCII above here =============== S/390 -- gil -- 0682 */
+ /*
+ * Ignore zwnj (8204) and zwj (8205), if we get to here. Note that
+ * zwnj may have been handled as <WBR> by the calling function. - FM
+ */
+ if (!strcmp(s, "zwnj") ||
+ !strcmp(s, "zwj")) {
+ CTRACE((tfp, "handle_entity: Ignoring '%s'.\n", s));
+#ifdef USE_PRETTYSRC
+ if (psrc_view) {
+ put_pretty_entity(me, term);
+ }
+#endif
+ FoundEntity = TRUE;
+ return;
+ }
+ /*
+ * Ignore lrm (8206), and rln (8207), if we get to here. - FM
+ */
+ if (!strcmp(s, "lrm") ||
+ !strcmp(s, "rlm")) {
+ CTRACE((tfp, "handle_entity: Ignoring '%s'.\n", s));
+#ifdef USE_PRETTYSRC
+ if (psrc_view) {
+ put_pretty_entity(me, term);
+ }
+#endif
+ FoundEntity = TRUE;
+ return;
+ }
+ }
+
+ /*
+ * If entity string not found, display as text.
+ */
+#ifdef USE_PRETTYSRC
+ if (psrc_view)
+ PSRCSTART(badseq);
+#endif
+ /* S/390 -- gil -- 0695 */
+ CTRACE((tfp, "SGML: Unknown entity '%s' %" PRI_UCode_t " %ld\n", s, code, uck));
+ PUTC('&');
+ PUTS(s);
+ if (term != '\0')
+ PUTC(term);
+#ifdef USE_PRETTYSRC
+ if (psrc_view)
+ PSRCSTOP(badseq);
+#endif
+}
+
+/* Handle comment
+ * --------------
+ */
+static void handle_comment(HTStream *me)
+{
+ const char *s = me->string->data;
+
+ CTRACE((tfp, "SGML Comment:\n<%s>\n", s));
+
+ if (me->csi == NULL &&
+ StrNCmp(s, "!--#", 4) == 0 &&
+ LYCheckForCSI(me->node_anchor, &me->url) == TRUE) {
+ LYDoCSI(me->url, s, &me->csi);
+ } else {
+ LYCommentHacks(me->node_anchor, me->string->data);
+ }
+
+ return;
+}
+
+/* Handle identifier
+ * -----------------
+ */
+static void handle_identifier(HTStream *me)
+{
+ const char *s = me->string->data;
+
+ CTRACE((tfp, "SGML Identifier:\n<%s>\n", s));
+
+ return;
+}
+
+/* Handle doctype
+ * --------------
+ */
+static void handle_doctype(HTStream *me)
+{
+ const char *s = me->string->data;
+
+ CTRACE((tfp, "SGML Doctype:\n<%s>\n", s));
+ if (strstr(s, "DTD XHTML ") != 0) {
+ CTRACE((tfp, "...processing extended HTML\n"));
+ me->extended_html = TRUE;
+ }
+
+ return;
+}
+
+/* Handle marked
+ * -------------
+ */
+static void handle_marked(HTStream *me)
+{
+ const char *s = me->string->data;
+
+ CTRACE((tfp, "SGML Marked Section:\n<%s>\n", s));
+
+ if (!StrNCmp(me->string->data, "![INCLUDE[", 10)) {
+ me->string->data[me->string->size - 3] = '\0';
+ StrAllocCat(me->include, me->string->data + 10);
+ /* @@@ This needs to take charset into account! @@@
+ the wrong assumptions will be made about the data's
+ charset once it is in include - kw */
+
+ } else if (!StrNCmp(me->string->data, "![CDATA[", 8)) {
+ (*me->actions->put_block) (me->target,
+ me->string->data + 8,
+ me->string->size - 11);
+
+ }
+ return;
+}
+
+/* Handle processing instruction
+ * -----------------------------
+ */
+static void handle_processing_instruction(HTStream *me)
+{
+ const char *s = me->string->data;
+
+ CTRACE((tfp, "SGML Processing instruction:\n<%s>\n", s));
+
+ if (!StrNCmp(s, "?xml ", 5)) {
+ int flag = me->T.decode_utf8;
+
+ me->strict_xml = TRUE;
+ /*
+ * Switch to UTF-8 if the encoding is explicitly "utf-8".
+ */
+ if (!flag) {
+ char *t = strstr(s, "encoding=");
+
+ if (t != 0) {
+ t += 9;
+ if (*t == '"')
+ ++t;
+ flag = !StrNCmp(t, "utf-8", 5);
+ }
+ if (flag) {
+ CTRACE((tfp, "...Use UTF-8 for XML\n"));
+ me->T.decode_utf8 = TRUE;
+ }
+ }
+ }
+
+ return;
+}
+
+/* Handle sgmlent
+ * --------------
+ */
+static void handle_sgmlent(HTStream *me)
+{
+ const char *s = me->string->data;
+
+ CTRACE((tfp, "SGML Entity Declaration:\n<%s>\n", s));
+
+ return;
+}
+
+/* Handle sgmlent
+ * --------------
+ */
+static void handle_sgmlele(HTStream *me)
+{
+ const char *s = me->string->data;
+
+ CTRACE((tfp, "SGML Element Declaration:\n<%s>\n", s));
+
+ return;
+}
+
+/* Handle sgmlatt
+ * --------------
+ */
+static void handle_sgmlatt(HTStream *me)
+{
+ const char *s = me->string->data;
+
+ CTRACE((tfp, "SGML Attribute Declaration:\n<%s>\n", s));
+
+ return;
+}
+
+/*
+ * Convenience macros - tags (elements) are identified sometimes by an int or
+ * enum value ('TAGNUM'), sometimes by a pointer to HTTag ('TAGP'). - kw
+ */
+#define TAGNUM_OF_TAGP(t) (HTMLElement) (t - me->dtd->tags)
+#define TAGP_OF_TAGNUM(e) (me->dtd->tags + e)
+
+/*
+ * The following implement special knowledge about OBJECT. As long as
+ * HTML_OBJECT is the only tag for which an alternative variant exist, they can
+ * be simple macros. - kw
+ */
+/* does 'TAGNUM' e have an alternative (variant) parsing mode? */
+#define HAS_ALT_TAGNUM(e) (e == HTML_OBJECT)
+
+/* return 'TAGNUM' of the alternative mode for 'TAGNUM' e, if any. */
+#define ALT_TAGNUM(e) ((e == HTML_OBJECT) ? HTML_ALT_OBJECT : e)
+
+/* return 'TAGNUM' of the normal mode for 'TAGNUM' e which may be alt. */
+#define NORMAL_TAGNUM(e) (((int)(e) >= HTML_ELEMENTS) ? HTML_OBJECT : (HTMLElement)e)
+
+/* More convenience stuff. - kw */
+#define ALT_TAGP_OF_TAGNUM(e) TAGP_OF_TAGNUM(ALT_TAGNUM(e))
+#define NORMAL_TAGP_OF_TAGNUM(e) TAGP_OF_TAGNUM(NORMAL_TAGNUM(e))
+
+#define ALT_TAGP(t) ALT_TAGP_OF_TAGNUM(TAGNUM_OF_TAGP(t))
+#define NORMAL_TAGP(t) NORMAL_TAGP_OF_TAGNUM(TAGNUM_OF_TAGP(t))
+
+#define IsTagAlias(a,b) (((a) == (b)) || ((a) - (a)->alias == (b) - (b)->alias))
+
+static BOOL element_valid_within(HTTag * new_tag, HTTag * stacked_tag, int direct)
+{
+ BOOL result = YES;
+ TagClass usecontains, usecontained;
+
+ if (stacked_tag && new_tag) {
+ usecontains = (direct ? stacked_tag->contains : stacked_tag->icontains);
+ usecontained = (direct ? new_tag->contained : new_tag->icontained);
+ if (IsTagAlias(new_tag, stacked_tag)) {
+ result = (BOOL) ((Tgc_same & usecontains) &&
+ (Tgc_same & usecontained));
+ } else {
+ result = (BOOL) ((new_tag->tagclass & usecontains) &&
+ (stacked_tag->tagclass & usecontained));
+ }
+ }
+ return result;
+}
+
+static BOOL element_really_within(HTTag * new_tag, HTTag * stacked_tag, int direct)
+{
+ BOOL result = YES;
+ TagClass usecontains, usecontained;
+
+ if (stacked_tag && new_tag) {
+ usecontains = (direct ? stacked_tag->contains : stacked_tag->icontains);
+ usecontained = (direct ? new_tag->contained : new_tag->icontained);
+ if (IsTagAlias(new_tag, stacked_tag)) {
+ result = (BOOL) ((Tgc_same & usecontains) &&
+ (Tgc_same & usecontained));
+ } else {
+ result = (BOOL) ((new_tag->tagclass & usecontains) ==
+ new_tag->tagclass &&
+ (stacked_tag->tagclass & usecontained) == stacked_tag->tagclass);
+ }
+ }
+ return result;
+}
+
+typedef enum {
+ close_NO = 0,
+ close_error = 1,
+ close_valid = 2
+} canclose_t;
+
+static canclose_t can_close(HTTag * new_tag, HTTag * stacked_tag)
+{
+ canclose_t result;
+
+ if (!stacked_tag) {
+ result = close_NO;
+ } else if (stacked_tag->flags & Tgf_endO) {
+ result = close_valid;
+ } else if (IsTagAlias(new_tag, stacked_tag)) {
+ result = ((Tgc_same & new_tag->canclose)
+ ? close_error
+ : close_NO);
+ } else {
+ result = ((stacked_tag->tagclass & new_tag->canclose)
+ ? close_error
+ : close_NO);
+ }
+ return result;
+}
+
+static void do_close_stacked(HTStream *me)
+{
+ HTElement *stacked = me->element_stack;
+ HTMLElement e;
+
+ if (!stacked)
+ return; /* stack was empty */
+ if (me->inSELECT && !strcasecomp(stacked->tag->name, "SELECT")) {
+ me->inSELECT = FALSE;
+ }
+ e = NORMAL_TAGNUM(TAGNUM_OF_TAGP(stacked->tag));
+#ifdef USE_PRETTYSRC
+ if (!psrc_view) /* Don't actually pass call on if viewing psrc - kw */
+#endif
+ (*me->actions->end_element) (me->target,
+ (int) e,
+ &me->include);
+ me->element_stack = stacked->next;
+ pool_free(stacked);
+ me->no_lynx_specialcodes =
+ (BOOL) (me->element_stack
+ ? (me->element_stack->tag->flags & Tgf_nolyspcl)
+ : NO);
+}
+
+static int is_on_stack(HTStream *me, HTTag * old_tag)
+{
+ HTElement *stacked = me->element_stack;
+ int i = 1;
+
+ for (; stacked; stacked = stacked->next, i++) {
+ if (IsTagAlias(stacked->tag, old_tag) ||
+ stacked->tag == ALT_TAGP(old_tag))
+ return i;
+ }
+ return 0;
+}
+
+/* End element
+ * -----------
+ */
+static void end_element(HTStream *me, HTTag * old_tag)
+{
+ BOOL extra_action_taken = NO;
+ canclose_t canclose_check = close_valid;
+ int stackpos = is_on_stack(me, old_tag);
+ BOOL direct_container = YES;
+
+ if (!Old_DTD) {
+ if (old_tag->aliases) {
+ if (me->element_stack) {
+ if (!element_really_within(old_tag,
+ me->element_stack->tag,
+ direct_container) &&
+ element_really_within(old_tag + 1,
+ me->element_stack->tag,
+ direct_container)) {
+ ++old_tag;
+ }
+ }
+ }
+ while (canclose_check != close_NO &&
+ me->element_stack &&
+ (stackpos > 1 || (!extra_action_taken && stackpos == 0))) {
+ if (stackpos == 0 && (old_tag->flags & Tgf_startO) &&
+ element_valid_within(old_tag, me->element_stack->tag, YES)) {
+ CTRACE((tfp, "SGML: </%s> ignored\n", old_tag->name));
+ return;
+ }
+ canclose_check = can_close(old_tag, me->element_stack->tag);
+ if (canclose_check != close_NO) {
+ CTRACE((tfp, "SGML: End </%s> \t<- %s end </%s>\n",
+ me->element_stack->tag->name,
+ ((canclose_check == close_valid)
+ ? "supplied,"
+ : "***forced by"),
+ old_tag->name));
+ do_close_stacked(me);
+ extra_action_taken = YES;
+ stackpos = is_on_stack(me, old_tag);
+ }
+ }
+
+ if (stackpos == 0 && old_tag->contents != SGML_EMPTY) {
+ CTRACE((tfp, "SGML: Still open %s, ***no open %s for </%s>\n",
+ me->element_stack ?
+ me->element_stack->tag->name : "none",
+ old_tag->name,
+ old_tag->name));
+ return;
+ }
+ if (stackpos > 1) {
+ CTRACE((tfp,
+ "SGML: Nesting <%s>...<%s> \t<- ***invalid end </%s>\n",
+ old_tag->name,
+ me->element_stack ?
+ me->element_stack->tag->name : "none",
+ old_tag->name));
+ return;
+ }
+ }
+ /* Now let the non-extended code deal with the rest. - kw */
+
+ /*
+ * If we are in a SELECT block, ignore anything but a SELECT end tag. - FM
+ */
+ if (me->inSELECT) {
+ if (!strcasecomp(old_tag->name, "SELECT")) {
+ /*
+ * Turn off the inSELECT flag and fall through. - FM
+ */
+ me->inSELECT = FALSE;
+ } else {
+ /*
+ * Ignore the end tag. - FM
+ */
+ CTRACE((tfp, "SGML: ***Ignoring end tag </%s> in SELECT block.\n",
+ old_tag->name));
+ return;
+ }
+ }
+ /*
+ * Handle the end tag. - FM
+ */
+ CTRACE((tfp, "SGML: End </%s>\n", old_tag->name));
+ if (old_tag->contents == SGML_EMPTY) {
+ CTRACE((tfp, "SGML: ***Illegal end tag </%s> found.\n",
+ old_tag->name));
+ return;
+ }
+#ifdef WIND_DOWN_STACK
+ while (me->element_stack) /* Loop is error path only */
+#else
+ if (me->element_stack) /* Substitute and remove one stack element */
+#endif /* WIND_DOWN_STACK */
+ {
+ int status = HT_OK;
+ HTMLElement e;
+ HTElement *N = me->element_stack;
+ HTTag *t = (N->tag != old_tag) ? NORMAL_TAGP(N->tag) : N->tag;
+
+ if (old_tag != t) { /* Mismatch: syntax error */
+ if (me->element_stack->next) { /* This is not the last level */
+ CTRACE((tfp,
+ "SGML: Found </%s> when expecting </%s>. </%s> ***assumed.\n",
+ old_tag->name, t->name, t->name));
+ } else { /* last level */
+ CTRACE((tfp,
+ "SGML: Found </%s> when expecting </%s>. </%s> ***Ignored.\n",
+ old_tag->name, t->name, old_tag->name));
+ return; /* Ignore */
+ }
+ }
+
+ e = NORMAL_TAGNUM(TAGNUM_OF_TAGP(t));
+ CTRACE2(TRACE_SGML, (tfp, "tagnum(%p) = %d\n", (void *) t, (int) e));
+#ifdef USE_PRETTYSRC
+ if (!psrc_view) /* Don't actually pass call on if viewing psrc - kw */
+#endif
+ status = (*me->actions->end_element) (me->target,
+ (int) e,
+ &me->include);
+ if (status == HT_PARSER_REOPEN_ELT) {
+ CTRACE((tfp, "SGML: Restart <%s>\n", t->name));
+ (*me->actions->start_element) (me->target,
+ (int) e,
+ NULL,
+ NULL,
+ me->current_tag_charset,
+ &me->include);
+ } else if (status == HT_PARSER_OTHER_CONTENT) {
+ CTRACE((tfp, "SGML: Continue with other content model for <%s>\n", t->name));
+ me->element_stack->tag = ALT_TAGP_OF_TAGNUM(e);
+ } else {
+ me->element_stack = N->next; /* Remove from stack */
+ pool_free(N);
+ }
+ me->no_lynx_specialcodes =
+ (BOOL) (me->element_stack
+ ? (me->element_stack->tag->flags & Tgf_nolyspcl)
+ : NO);
+#ifdef WIND_DOWN_STACK
+ if (IsTagAlias(old_tag, t))
+ return; /* Correct sequence */
+#else
+ return;
+#endif /* WIND_DOWN_STACK */
+
+ /* Syntax error path only */
+
+ }
+ CTRACE((tfp, "SGML: Extra end tag </%s> found and ignored.\n",
+ old_tag->name));
+}
+
+/* Start a element
+*/
+static void start_element(HTStream *me)
+{
+ int status;
+ HTTag *new_tag = me->current_tag;
+ HTMLElement e = TAGNUM_OF_TAGP(new_tag);
+ BOOL ok = FALSE;
+
+ BOOL valid = YES;
+ BOOL direct_container = YES;
+ BOOL extra_action_taken = NO;
+ canclose_t canclose_check = close_valid;
+
+ if (!Old_DTD) {
+ if (new_tag->aliases) {
+ if (me->element_stack) {
+ if (!element_really_within(new_tag,
+ me->element_stack->tag,
+ direct_container) &&
+ element_really_within(new_tag + 1,
+ me->element_stack->tag,
+ direct_container)) {
+ ++new_tag;
+ }
+ }
+ }
+ while (me->element_stack &&
+ (canclose_check == close_valid ||
+ (canclose_check == close_error &&
+ IsTagAlias(new_tag, me->element_stack->tag))) &&
+ !(valid = element_valid_within(new_tag,
+ me->element_stack->tag,
+ direct_container))) {
+ canclose_check = can_close(new_tag, me->element_stack->tag);
+ if (canclose_check != close_NO) {
+ CTRACE((tfp, "SGML: End </%s> \t<- %s start <%s>\n",
+ me->element_stack->tag->name,
+ ((canclose_check == close_valid)
+ ? "supplied,"
+ : "***forced by"),
+ new_tag->name));
+ do_close_stacked(me);
+ extra_action_taken = YES;
+ if (canclose_check == close_error)
+ direct_container = NO;
+ } else {
+ CTRACE((tfp,
+ "SGML: Still open %s \t<- ***invalid start <%s>\n",
+ me->element_stack->tag->name,
+ new_tag->name));
+ }
+ }
+ if (me->element_stack && !valid &&
+ (me->element_stack->tag->flags & Tgf_strict) &&
+ !(valid = element_valid_within(new_tag,
+ me->element_stack->tag,
+ direct_container))) {
+ CTRACE((tfp, "SGML: Still open %s \t<- ***ignoring start <%s>\n",
+ me->element_stack->tag->name,
+ new_tag->name));
+ return;
+ }
+
+ if (me->element_stack &&
+ !extra_action_taken &&
+ (canclose_check == close_NO) &&
+ !valid && (new_tag->flags & Tgf_mafse)) {
+ BOOL has_attributes = NO;
+ int i = 0;
+
+ for (; i < new_tag->number_of_attributes && !has_attributes; i++)
+ has_attributes = me->present[i];
+ if (!has_attributes) {
+ CTRACE((tfp,
+ "SGML: Still open %s, ***converting invalid <%s> to </%s>\n",
+ me->element_stack->tag->name,
+ new_tag->name,
+ new_tag->name));
+ end_element(me, new_tag);
+ return;
+ }
+ }
+
+ if (me->element_stack &&
+ (canclose_check == close_error) &&
+ !element_valid_within(new_tag,
+ me->element_stack->tag,
+ direct_container)) {
+ CTRACE((tfp, "SGML: Still open %s \t<- ***invalid start <%s>\n",
+ me->element_stack->tag->name,
+ new_tag->name));
+ }
+ }
+ /* Fall through to the non-extended code - kw */
+
+ /*
+ * If we are not in a SELECT block, check if this is a SELECT start tag.
+ * Otherwise (i.e., we are in a SELECT block) accept only OPTION as valid,
+ * terminate the SELECT block if it is any other form-related element, and
+ * otherwise ignore it. - FM
+ */
+ if (!me->inSELECT) {
+ /*
+ * We are not in a SELECT block, so check if this starts one. - FM
+ * (frequent case!)
+ */
+ /* my_casecomp() - optimized by the first character */
+ if (!my_casecomp(new_tag->name, "SELECT")) {
+ /*
+ * Set the inSELECT flag and fall through. - FM
+ */
+ me->inSELECT = TRUE;
+ }
+ } else {
+ /*
+ * We are in a SELECT block. - FM
+ */
+ if (strcasecomp(new_tag->name, "OPTION")) {
+ /*
+ * Ugh, it is not an OPTION. - FM
+ */
+ switch (e) {
+ case HTML_INPUT:
+ case HTML_TEXTAREA:
+ case HTML_SELECT:
+ case HTML_BUTTON:
+ case HTML_FIELDSET:
+ case HTML_LABEL:
+ case HTML_LEGEND:
+ case HTML_FORM:
+ ok = TRUE;
+ break;
+ default:
+ break;
+ }
+ if (ok) {
+ /*
+ * It is another form-related start tag, so terminate the
+ * current SELECT block and fall through. - FM
+ */
+ CTRACE((tfp,
+ "SGML: ***Faking SELECT end tag before <%s> start tag.\n",
+ new_tag->name));
+ end_element(me, SGMLFindTag(me->dtd, "SELECT"));
+ } else {
+ /*
+ * Ignore the start tag. - FM
+ */
+ CTRACE((tfp,
+ "SGML: ***Ignoring start tag <%s> in SELECT block.\n",
+ new_tag->name));
+ return;
+ }
+ }
+ }
+ /*
+ * Handle the start tag. - FM
+ */
+ CTRACE((tfp, "SGML: Start <%s>\n", new_tag->name));
+ status = (*me->actions->start_element) (me->target,
+ (int) TAGNUM_OF_TAGP(new_tag),
+ me->present,
+ (STRING2PTR) me->value, /* coerce type for think c */
+ me->current_tag_charset,
+ &me->include);
+ if (status == HT_PARSER_OTHER_CONTENT)
+ new_tag = ALT_TAGP(new_tag); /* this is only returned for OBJECT */
+ if (new_tag->contents != SGML_EMPTY) { /* i.e., tag not empty */
+ HTElement *N = pool_alloc();
+
+ if (N == NULL)
+ outofmem(__FILE__, "start_element");
+
+ N->next = me->element_stack;
+ N->tag = new_tag;
+ me->element_stack = N;
+ me->no_lynx_specialcodes = (BOOLEAN) (new_tag->flags & Tgf_nolyspcl);
+
+ } else if (e == HTML_META) {
+ /*
+ * Check for result of META tag. - KW & FM
+ */
+ change_chartrans_handling(me);
+ }
+}
+
+/* Find Tag in DTD tag list
+ * ------------------------
+ *
+ * On entry,
+ * dtd points to dtd structure including valid tag list
+ * string points to name of tag in question
+ *
+ * On exit,
+ * returns:
+ * NULL tag not found
+ * else address of tag structure in dtd
+ */
+HTTag *SGMLFindTag(const SGML_dtd * dtd,
+ const char *s)
+{
+ int high, low, i, diff;
+ static HTTag *last[64] =
+ {NULL}; /*optimize using the previous results */
+ HTTag **res = last + (UCH(*s) % 64); /*pointer arithmetic */
+
+ if (*res) {
+ if ((*res)->name == NULL)
+ return NULL;
+ if (!strcasecomp((*res)->name, s))
+ return *res;
+ }
+
+ for (low = 0, high = dtd->number_of_tags;
+ high > low;
+ diff < 0 ? (low = i + 1) : (high = i)) { /* Binary search */
+ i = (low + (high - low) / 2);
+ /* my_casecomp() - optimized by the first character, NOT_ASCII ok */
+ diff = my_casecomp(dtd->tags[i].name, s); /* Case insensitive */
+ if (diff == 0) { /* success: found it */
+ i -= dtd->tags[i].alias;
+ *res = &dtd->tags[i];
+ return *res;
+ }
+ }
+ if (IsNmStart(*s)) {
+ /*
+ * Unrecognized, but may be valid. - KW
+ */
+ return &HTTag_unrecognized;
+ }
+ return NULL;
+}
+
+/*________________________________________________________________________
+ * Public Methods
+ */
+
+/* Could check that we are back to bottom of stack! @@ */
+/* Do check! - FM */
+/* */
+static void SGML_free(HTStream *me)
+{
+ int i;
+ HTElement *cur;
+ HTTag *t;
+
+ /*
+ * Free the buffers. - FM
+ */
+ FREE(me->recover);
+ FREE(me->url);
+ FREE(me->csi);
+ FREE(me->include);
+ FREE(me->active_include);
+
+ /*
+ * Wind down stack if any elements are open. - FM
+ */
+ while (me->element_stack) {
+ cur = me->element_stack;
+ t = cur->tag;
+ me->element_stack = cur->next; /* Remove from stack */
+ pool_free(cur);
+#ifdef USE_PRETTYSRC
+ if (!psrc_view) /* Don't actually call on target if viewing psrc - kw */
+#endif
+ (*me->actions->end_element)
+ (me->target,
+ (int) NORMAL_TAGNUM(TAGNUM_OF_TAGP(t)),
+ &me->include);
+ FREE(me->include);
+ }
+
+ /*
+ * Finish off the target. - FM
+ */
+ (*me->actions->_free) (me->target);
+
+ /*
+ * Free the strings and context structure. - FM
+ */
+ HTChunkFree(me->string);
+ for (i = 0; i < MAX_ATTRIBUTES; i++)
+ FREE_extra(me->value[i]);
+ FREE(me);
+
+#ifdef USE_PRETTYSRC
+ sgml_in_psrc_was_initialized = FALSE;
+#endif
+}
+
+static void SGML_abort(HTStream *me, HTError e)
+{
+ int i;
+ HTElement *cur;
+
+ /*
+ * Abort the target. - FM
+ */
+ (*me->actions->_abort) (me->target, e);
+
+ /*
+ * Free the buffers. - FM
+ */
+ FREE(me->recover);
+ FREE(me->include);
+ FREE(me->active_include);
+ FREE(me->url);
+ FREE(me->csi);
+
+ /*
+ * Free stack memory if any elements were left open. - KW
+ */
+ while (me->element_stack) {
+ cur = me->element_stack;
+ me->element_stack = cur->next; /* Remove from stack */
+ pool_free(cur);
+ }
+
+ /*
+ * Free the strings and context structure. - FM
+ */
+ HTChunkFree(me->string);
+ for (i = 0; i < MAX_ATTRIBUTES; i++)
+ FREE_extra(me->value[i]);
+ FREE(me);
+
+#ifdef USE_PRETTYSRC
+ sgml_in_psrc_was_initialized = FALSE;
+#endif
+}
+
+/* Read and write user callback handle
+ * -----------------------------------
+ *
+ * The callbacks from the SGML parser have an SGML context parameter.
+ * These calls allow the caller to associate his own context with a
+ * particular SGML context.
+ */
+
+#ifdef CALLERDATA
+void *SGML_callerData(HTStream *me)
+{
+ return me->callerData;
+}
+
+void SGML_setCallerData(HTStream *me, void *data)
+{
+ me->callerData = data;
+}
+#endif /* CALLERDATA */
+
+#ifdef USE_PRETTYSRC
+static void transform_tag(HTStream *me, HTChunk *string)
+{
+ if (!me->strict_xml) {
+ if (tagname_transform != 1) {
+ if (tagname_transform == 0)
+ LYLowerCase(string->data);
+ else
+ LYUpperCase(string->data);
+ }
+ }
+}
+#endif /* USE_PRETTYSRC */
+
+static BOOL ignore_when_empty(HTTag * tag)
+{
+ BOOL result = FALSE;
+
+ if (!LYPreparsedSource
+ && LYxhtml_parsing
+ && tag->name != 0
+ && !(tag->flags & Tgf_mafse)
+ && tag->contents != SGML_EMPTY
+ && tag->tagclass != Tgc_Plike
+ && (tag->tagclass == Tgc_APPLETlike
+ || tag->tagclass == Tgc_SELECTlike
+ || (tag->contains && tag->icontains))) {
+ result = TRUE;
+ }
+ CTRACE((tfp, "SGML Do%s ignore_when_empty:%s\n",
+ result ? "" : " not",
+ NonNull(tag->name)));
+ return result;
+}
+
+static void discard_empty(HTStream *me)
+{
+ static HTTag empty_tag;
+
+ CTRACE((tfp, "SGML discarding empty %s\n",
+ NonNull(me->current_tag->name)));
+ CTRACE_FLUSH(tfp);
+
+ memset(&empty_tag, 0, sizeof(empty_tag));
+ me->current_tag = &empty_tag;
+ me->string->size = 0;
+
+ /* do not call end_element() if start_element() was not called */
+}
+
+#ifdef USE_PRETTYSRC
+static BOOL end_if_prettysrc(HTStream *me, HTChunk *string, int end_ch)
+{
+ BOOL result = psrc_view;
+
+ if (psrc_view) {
+ if (attr_is_name) {
+ HTStartAnchor(me->target, string->data, NULL);
+ (*me->actions->end_element) (me->target,
+ HTML_A,
+ &me->include);
+ } else if (attr_is_href) {
+ PSRCSTART(href);
+ HTStartAnchor(me->target, NULL, string->data);
+ }
+ PUTS_TR(string->data);
+ if (attr_is_href) {
+ (*me->actions->end_element) (me->target,
+ HTML_A,
+ &me->include);
+ PSRCSTOP(href);
+ }
+ if (end_ch)
+ PUTC(end_ch);
+ PSRCSTOP(attrval);
+ }
+ return result;
+}
+#endif
+
+static void SGML_character(HTStream *me, int c_in)
+{
+ const SGML_dtd *dtd = me->dtd;
+ HTChunk *string = me->string;
+ const char *EntityName;
+ HTTag *testtag = NULL;
+ BOOLEAN chk; /* Helps (?) walk through all the else ifs... */
+ UCode_t clong, uck = 0; /* Enough bits for UCS4 ... */
+ int testlast;
+
+ unsigned char c;
+ unsigned char saved_char_in = '\0';
+
+ ++sgml_offset;
+
+ c = UCH(c_in);
+ clong = UCH(c);
+
+ if (me->T.decode_utf8) {
+ switch (HTDecodeUTF8(&(me->U), &c_in, &clong)) {
+ case dUTF8_ok:
+ if (clong < 256) {
+ c_in = FROMASCII(UCH(clong));
+ }
+ break;
+ case dUTF8_err:
+ clong = UCS_REPL;
+ strcpy(me->U.utf_buf, "\357\277\275");
+ me->U.utf_buf_p = (me->U.utf_buf + 3);
+ break;
+ case dUTF8_more:
+ return;
+ }
+
+ c = UCH(c_in);
+ if ((me->U.utf_buf_p - me->U.utf_buf) > 1) {
+ goto top1;
+ }
+ }
+
+ /*
+ * If we want the raw input converted to Unicode, try that now. - FM
+ */
+#ifdef USE_JAPANESEUTF8_SUPPORT
+ /* Convert ISO-2022-JP to Unicode (charset=iso-2022-jp is unrecognized) */
+#define IS_JIS7_HILO(c) (0x20<(c)&&(c)<0x7F)
+ if (UTF8_TTY_ISO2022JP && (me->state == S_nonascii_text
+ || me->state == S_nonascii_text_sq
+ || me->state == S_nonascii_text_dq)) {
+ /* end of ISO-2022-JP? || not in ISO-2022-JP range */
+ if (TOASCII(c) == '\033' || !IS_JIS7_HILO(c)) {
+ me->kanji_buf = '\0';
+ goto top1;
+ }
+ if (me->kanji_buf == '\t') { /* flag for single byte kana in "ESC(I" */
+ if (conv_jisx0201kana) {
+ JISx0201TO0208_SJIS(c | 0200,
+ (unsigned char *) me->U.utf_buf,
+ (unsigned char *) me->U.utf_buf + 1);
+ clong = UCTransJPToUni(me->U.utf_buf, 2,
+ UCGetLYhndl_byMIME("shift_jis"));
+ } else {
+ clong = UCTransToUni(c | 0200, UCGetLYhndl_byMIME("shift_jis"));
+ }
+ } else if (me->kanji_buf) {
+ me->U.utf_buf[0] = (char) (me->kanji_buf | 0200); /* to EUC-JP */
+ me->U.utf_buf[1] = (char) (c | 0200);
+ clong = UCTransJPToUni(me->U.utf_buf, 2,
+ UCGetLYhndl_byMIME("euc-jp"));
+ me->kanji_buf = '\0';
+ } else {
+ me->kanji_buf = c;
+ clong = ucNeedMore;
+ }
+ goto top1;
+ }
+#endif /* USE_JAPANESEUTF8_SUPPORT */
+#ifdef USE_JAPANESEUTF8_SUPPORT
+ if (me->T.trans_to_uni &&
+ ((strcmp(LYCharSet_UC[me->inUCLYhndl].MIMEname, "euc-jp") == 0) ||
+ (strcmp(LYCharSet_UC[me->inUCLYhndl].MIMEname, "shift_jis") == 0))) {
+ if (strcmp(LYCharSet_UC[me->inUCLYhndl].MIMEname, "shift_jis") == 0) {
+ if (me->U.utf_count == 0) {
+ if (IS_SJIS_HI1(c) ||
+ IS_SJIS_HI2(c)) {
+ me->U.utf_buf[0] = (char) c;
+ me->U.utf_count = 1;
+ clong = ucCannotConvert;
+ } else if (IS_SJIS_X0201KANA(c)) {
+ if (conv_jisx0201kana) {
+ JISx0201TO0208_SJIS(c,
+ (unsigned char *) me->U.utf_buf,
+ (unsigned char *) me->U.utf_buf + 1);
+ clong = UCTransJPToUni(me->U.utf_buf, 2, me->inUCLYhndl);
+ } else {
+ clong = UCTransToUni(c, me->inUCLYhndl);
+ }
+ }
+ } else {
+ if (IS_SJIS_LO(c)) {
+ me->U.utf_buf[1] = (char) c;
+ clong = UCTransJPToUni(me->U.utf_buf, 2, me->inUCLYhndl);
+ }
+ me->U.utf_count = 0;
+ }
+ } else {
+ if (me->U.utf_count == 0) {
+ if (IS_EUC_HI(c) || c == 0x8E) {
+ me->U.utf_buf[0] = (char) c;
+ me->U.utf_count = 1;
+ clong = ucCannotConvert;
+ }
+ } else {
+ if (IS_EUC_LOX(c)) {
+ me->U.utf_buf[1] = (char) c;
+ clong = UCTransJPToUni(me->U.utf_buf, 2, me->inUCLYhndl);
+ }
+ me->U.utf_count = 0;
+ }
+ }
+ goto top1;
+ } else
+#endif /* USE_JAPANESEUTF8_SUPPORT */
+#ifdef EXP_CHINESEUTF8_SUPPORT
+ if (me->T.trans_to_uni &&
+ ((strcmp(LYCharSet_UC[me->inUCLYhndl].MIMEname, "euc-cn") == 0))) {
+ if (me->U.utf_count == 0) {
+ if (IS_GBK_HI(c)) {
+ me->U.utf_buf[0] = (char) c;
+ me->U.utf_count = 1;
+ clong = ucCannotConvert;
+ CTRACE((tfp, "Get EUC-CN: 0x%02X\n", UCH(c)));
+ }
+ } else {
+ if (IS_GBK_LO(c)) {
+ me->U.utf_buf[1] = (char) c;
+ clong = UCTransJPToUni(me->U.utf_buf, 2, me->inUCLYhndl);
+ if (clong > 0) {
+ CTRACE((tfp, "... second: [%02X%02X] U+%04lX\n",
+ UCH(me->U.utf_buf[0]),
+ UCH(me->U.utf_buf[1]),
+ clong));
+ } else {
+ CTRACE((tfp, "... second: [%02X%02X] %ld\n",
+ UCH(me->U.utf_buf[0]),
+ UCH(me->U.utf_buf[1]),
+ clong));
+ }
+ }
+ me->U.utf_count = 0;
+ }
+ goto top1;
+ } else
+#endif /* EXP_CHINESEUTF8_SUPPORT */
+#ifdef EXP_CHINESEUTF8_SUPPORT
+ if (me->T.trans_to_uni &&
+ ((strcmp(LYCharSet_UC[me->inUCLYhndl].MIMEname, "euc-kr") == 0))) {
+ if (me->U.utf_count == 0) {
+ if (IS_EUC_HI(c)) {
+ me->U.utf_buf[0] = (char) c;
+ me->U.utf_count = 1;
+ clong = ucCannotConvert;
+ CTRACE((tfp, "Get EUC-KR: 0x%02X\n", UCH(c)));
+ }
+ } else {
+ if (IS_EUC_LOS(c) ||
+ IS_EUC_LOX(c)) {
+ me->U.utf_buf[1] = (char) c;
+ clong = UCTransJPToUni(me->U.utf_buf, 2, me->inUCLYhndl);
+ if (clong > 0) {
+ CTRACE((tfp, "... second: [%02X%02X] U+%04lX\n",
+ UCH(me->U.utf_buf[0]),
+ UCH(me->U.utf_buf[1]),
+ clong));
+ } else {
+ CTRACE((tfp, "... second: [%02X%02X] %ld\n",
+ UCH(me->U.utf_buf[0]),
+ UCH(me->U.utf_buf[1]),
+ clong));
+ }
+ }
+ me->U.utf_count = 0;
+ }
+ goto top1;
+ } else
+#endif /* EXP_CHINESEUTF8_SUPPORT */
+#ifdef EXP_CHINESEUTF8_SUPPORT
+ if (me->T.trans_to_uni &&
+ ((strcmp(LYCharSet_UC[me->inUCLYhndl].MIMEname, "big5") == 0))) {
+ if (me->U.utf_count == 0) {
+ if (IS_BIG5_HI(c)) {
+ me->U.utf_buf[0] = (char) c;
+ me->U.utf_count = 1;
+ clong = ucCannotConvert;
+ CTRACE((tfp, "Get BIG5: 0x%02X\n", UCH(c)));
+ }
+ } else {
+ if (IS_BIG5_LOS(c) ||
+ IS_BIG5_LOX(c)) {
+ me->U.utf_buf[1] = (char) c;
+ clong = UCTransJPToUni(me->U.utf_buf, 2, me->inUCLYhndl);
+ if (clong > 0) {
+ CTRACE((tfp, "... second: [%02X%02X] U+%04lX\n",
+ UCH(me->U.utf_buf[0]),
+ UCH(me->U.utf_buf[1]),
+ clong));
+ } else {
+ CTRACE((tfp, "... second: [%02X%02X] %ld\n",
+ UCH(me->U.utf_buf[0]),
+ UCH(me->U.utf_buf[1]),
+ clong));
+ }
+ }
+ me->U.utf_count = 0;
+ }
+ goto top1;
+ } else
+#endif /* EXP_CHINESEUTF8_SUPPORT */
+ if (me->T.trans_to_uni &&
+ /* S/390 -- gil -- 0744 */
+ ((TOASCII(clong) >= LYlowest_eightbit[me->inUCLYhndl]) ||
+ (clong < ' ' && clong != 0 &&
+ me->T.trans_C0_to_uni))) {
+ /*
+ * Convert the octet to Unicode. - FM
+ */
+ clong = UCTransToUni((char) c, me->inUCLYhndl);
+ if (clong > 0) {
+ saved_char_in = c;
+ if (clong < 256) {
+ c = FROMASCII(UCH(clong));
+ }
+ }
+ goto top1;
+ } else if (clong < ' ' && clong != 0 && /* S/390 -- gil -- 0768 */
+ me->T.trans_C0_to_uni) {
+ /*
+ * This else if may be too ugly to keep. - KW
+ */
+ if (me->T.trans_from_uni &&
+ (((clong = UCTransToUni((char) c, me->inUCLYhndl)) >= ' ') ||
+ (me->T.transp &&
+ (clong = UCTransToUni((char) c, me->inUCLYhndl)) > 0))) {
+ saved_char_in = c;
+ if (clong < 256) {
+ c = FROMASCII(UCH(clong));
+ }
+ goto top1;
+ } else {
+ uck = -1;
+ if (me->T.transp) {
+ uck = UCTransCharStr(replace_buf, 60, (char) c,
+ me->inUCLYhndl,
+ me->inUCLYhndl, NO);
+ }
+ if (!me->T.transp || uck < 0) {
+ uck = UCTransCharStr(replace_buf, 60, (char) c,
+ me->inUCLYhndl,
+ me->outUCLYhndl, YES);
+ }
+ if (uck == 0) {
+ return;
+ } else if (uck < 0) {
+ goto top0a;
+ }
+ c = UCH(replace_buf[0]);
+ if (c && replace_buf[1]) {
+ if (me->state == S_text) {
+ PUTS(replace_buf);
+ return;
+ }
+ StrAllocCat(me->recover, replace_buf + 1);
+ }
+ goto top0a;
+ } /* Next line end of ugly stuff for C0. - KW */
+ } else { /* end of me->T.trans_to_uni S/390 -- gil -- 0791 */
+ goto top0a;
+ }
+
+ /*
+ * We jump up to here from below if we have
+ * stuff in the recover, insert, or csi buffers
+ * to process. We zero saved_char_in, in effect
+ * as a flag that the octet is not that of the
+ * actual call to this function. This may be OK
+ * for now, for the stuff this function adds to
+ * its recover buffer, but it might not be for
+ * stuff other functions added to the insert or
+ * csi buffer, so bear that in mind. - FM
+ * Stuff from the recover buffer is now handled
+ * as UTF-8 if we can expect that's what it is,
+ * and in that case we don't come back up here. - kw
+ */
+ top:
+ saved_char_in = '\0';
+ /*
+ * We jump to here from above when we don't have
+ * UTF-8 input, haven't converted to Unicode, and
+ * want clong set to the input octet (unsigned)
+ * without zeroing its saved_char_in copy (which
+ * is signed). - FM
+ */
+ top0a:
+ *(me->U.utf_buf) = '\0';
+ clong = UCH(c);
+ /*
+ * We jump to here from above if we have converted
+ * the input, or a multibyte sequence across calls,
+ * to a Unicode value and loaded it into clong (to
+ * which unsign_c has been defined), and from below
+ * when we are recycling a character (e.g., because
+ * it terminated an entity but is not the standard
+ * semi-colon). The character will already have
+ * been put through the Unicode conversions. - FM
+ */
+ top1:
+ /*
+ * Ignore low ISO 646 7-bit control characters if HTCJK is not set. - FM
+ */
+ /*
+ * Works for both ASCII and EBCDIC. -- gil
+ * S/390 -- gil -- 0811
+ */
+ if (TOASCII(clong) < 32 &&
+ c != '\t' && c != '\n' && c != '\r' &&
+ !IS_CJK_TTY &&
+ !(UTF8_TTY_ISO2022JP && (TOASCII(c) == '\033')))
+ goto after_switch;
+
+ /*
+ * Ignore 127 if we don't have HTPassHighCtrlRaw or HTCJK set. - FM
+ */
+#define PASSHICTRL (me->T.transp || \
+ clong >= LYlowest_eightbit[me->inUCLYhndl])
+ if (TOASCII(c) == 127 && /* S/390 -- gil -- 0830 */
+ !(PASSHICTRL || IS_CJK_TTY))
+ goto after_switch;
+
+ /*
+ * Ignore 8-bit control characters 128 - 159 if neither HTPassHighCtrlRaw
+ * nor HTCJK is set. - FM
+ */
+ if (TOASCII(clong) > 127 && TOASCII(clong) < 160 && /* S/390 -- gil -- 0847 */
+ !(PASSHICTRL || IS_CJK_TTY)) {
+ /*
+ * If we happen to be reading from an "ISO-8859-1" or "US-ASCII"
+ * document, allow the cp-1252 codes, to accommodate the HTML5 draft
+ * recommendation for replacement encoding:
+ *
+ * http://www.whatwg.org/specs/web-apps/current-work/multipage/infrastructure.html#character-encodings-0
+ */
+ if (AssumeCP1252(me)) {
+ clong = LYcp1252ToUnicode((UCode_t) c);
+ goto top1;
+ }
+ goto after_switch;
+ }
+
+ /* Almost all CJK characters are double byte but only Japanese
+ * JIS X0201 Kana is single byte. To prevent to fail SGML parsing
+ * we have to take care of them here. -- TH
+ */
+ if ((HTCJK == JAPANESE) && (me->state == S_in_kanji) &&
+ !IS_JAPANESE_2BYTE(me->kanji_buf, UCH(c))
+#ifdef USE_JAPANESEUTF8_SUPPORT
+ && !me->T.decode_utf8
+#endif
+ ) {
+#ifdef CONV_JISX0201KANA_JISX0208KANA
+ if (IS_SJIS_X0201KANA(me->kanji_buf)) {
+ unsigned char sjis_hi, sjis_lo;
+
+ JISx0201TO0208_SJIS(me->kanji_buf, &sjis_hi, &sjis_lo);
+ PUTC(sjis_hi);
+ PUTC(sjis_lo);
+ } else
+#endif
+ PUTC(me->kanji_buf);
+ me->state = S_text;
+ }
+
+ /*
+ * Handle character based on me->state.
+ */
+ CTRACE2(TRACE_SGML, (tfp, "SGML before %s|%.*s|%c|\n",
+ state_name(me->state),
+ string->size,
+ NonNull(string->data),
+ UCH(c)));
+ switch (me->state) {
+
+ case S_in_kanji:
+ /*
+ * Note that if we don't have a CJK input, then this is not the second
+ * byte of a CJK di-byte, and we're trashing the input. That's why
+ * 8-bit characters followed by, for example, '<' can cause the tag to
+ * be treated as text, not markup. We could try to deal with it by
+ * holding each first byte and then checking byte pairs, but that
+ * doesn't seem worth the overhead (see below). - FM
+ */
+ me->state = S_text;
+ PUTC(me->kanji_buf);
+ PUTC(c);
+ break;
+
+ case S_tagname_slash:
+ /*
+ * We had something link "<name/" so far, set state to S_text but keep
+ * me->slashedtag as a flag; except if we get '>' directly
+ * after the "<name/", and really have a tag for that name in
+ * me->slashedtag, in which case keep state as is and let code
+ * below deal with it. - kw
+ */
+ if (!(c == '>' && me->slashedtag && TOASCII(clong) < 127)) {
+ me->state = S_text;
+ }
+ /* FALLTHRU */
+ case S_text:
+#ifdef EXP_CHINESEUTF8_SUPPORT
+ if (IS_CJK_TTY &&
+ (!strcmp(LYCharSet_UC[me->inUCLYhndl].MIMEname, "euc-cn") ||
+ !strcmp(LYCharSet_UC[me->inUCLYhndl].MIMEname, "big5") ||
+ !strcmp(LYCharSet_UC[me->inUCLYhndl].MIMEname, "euc-kr"))) {
+ /*
+ * Leave the case statement if we have not collected both of the
+ * bytes for the EUC-CN character. If we have, then continue on
+ * to convert it to Unicode.
+ */
+ if (clong == ucCannotConvert) {
+ break;
+ }
+ } else
+#endif
+ if (IS_CJK_TTY && ((TOASCII(c) & 0200) != 0)
+#ifdef USE_JAPANESEUTF8_SUPPORT
+ && !me->T.decode_utf8
+#endif
+ ) { /* S/390 -- gil -- 0864 */
+ /*
+ * Setting up for Kanji multibyte handling (based on Takuya ASADA's
+ * (asada@three-a.co.jp) CJK Lynx). Note that if the input is not
+ * in fact CJK, the next byte also will be mishandled, as explained
+ * above. Toggle raw mode off in such cases, or select the "7 bit
+ * approximations" display character set, which is largely
+ * equivalent to having raw mode off with CJK. - FM
+ */
+ me->state = S_in_kanji;
+ me->kanji_buf = c;
+ break;
+ } else if ((IS_CJK_TTY || UTF8_TTY_ISO2022JP) && TOASCII(c) == '\033') {
+ /* S/390 -- gil -- 0881 */
+ /*
+ * Setting up for CJK escape sequence handling (based on Takuya
+ * ASADA's (asada@three-a.co.jp) CJK Lynx). - FM
+ */
+ me->state = S_esc;
+ if (!UTF8_TTY_ISO2022JP)
+ PUTC(c);
+ break;
+ }
+
+ if (c == '&' || c == '<') {
+#ifdef USE_PRETTYSRC
+ if (psrc_view) { /*there is nothing useful in the element_stack */
+ testtag = me->current_tag;
+ } else
+#endif
+ {
+ testtag = me->element_stack ?
+ me->element_stack->tag : NULL;
+ }
+ }
+
+ if (c == '&' && TOASCII(clong) < 127 && /* S/390 -- gil -- 0898 */
+ (!testtag ||
+ (testtag->contents == SGML_MIXED ||
+ testtag->contents == SGML_ELEMENT ||
+ testtag->contents == SGML_PCDATA ||
+#ifdef USE_PRETTYSRC
+ testtag->contents == SGML_EMPTY ||
+#endif
+ testtag->contents == SGML_RCDATA))) {
+ /*
+ * Setting up for possible entity, without the leading '&'. - FM
+ */
+ string->size = 0;
+ me->state = S_ero;
+ } else if (c == '<' && TOASCII(clong) < 127) { /* S/390 -- gil -- 0915 */
+ /*
+ * Setting up for possible tag. - FM
+ */
+ string->size = 0;
+ if (testtag && testtag->contents == SGML_PCDATA) {
+ me->state = S_pcdata;
+ } else if (testtag && (testtag->contents == SGML_LITTERAL
+ || testtag->contents == SGML_CDATA)) {
+ me->state = S_litteral;
+ } else if (testtag && (testtag->contents == SGML_SCRIPT)) {
+ me->state = S_script;
+ } else {
+ me->state = S_tag;
+ }
+ me->slashedtag = NULL;
+ } else if (me->slashedtag &&
+ me->slashedtag->name &&
+ (c == '/' ||
+ (c == '>' && me->state == S_tagname_slash)) &&
+ TOASCII(clong) < 127) {
+ /*
+ * We got either the second slash of a pending "<NAME/blah blah/"
+ * shortref construct, or the '>' of a mere "<NAME/>". In both
+ * cases generate a "</NAME>" end tag in the recover buffer for
+ * reparsing unless NAME is really an empty element. - kw
+ */
+#ifdef USE_PRETTYSRC
+ if (psrc_view) {
+ PSRCSTART(abracket);
+ PUTC(c);
+ PSRCSTOP(abracket);
+ } else
+#endif
+ if (me->slashedtag != me->unknown_tag &&
+ !ReallyEmptyTag(me->slashedtag)) {
+ if (me->recover == NULL) {
+ StrAllocCopy(me->recover, "</");
+ me->recover_index = 0;
+ } else {
+ StrAllocCat(me->recover, "</");
+ }
+ StrAllocCat(me->recover, me->slashedtag->name);
+ StrAllocCat(me->recover, ">");
+ }
+ me->slashedtag = NULL;
+
+ } else if (me->element_stack &&
+ (me->element_stack->tag->flags & Tgf_frecyc)) {
+ /*
+ * The element stack says we are within the contents of an element
+ * that the next stage (HTML.c) may want to feed us back again (via
+ * the *include string). So try to output text in UTF-8 if
+ * possible, using the same logic as for attribute values (which
+ * should be in line with what me->current_tag_charset
+ * indicates). - kw
+ */
+ if (me->T.decode_utf8 &&
+ *me->U.utf_buf) {
+ PUTS(me->U.utf_buf);
+ me->U.utf_buf_p = me->U.utf_buf;
+ *(me->U.utf_buf_p) = '\0';
+ } else if (!IS_CJK_TTY &&
+ (me->T.output_utf8 ||
+ me->T.trans_from_uni)) {
+ if (LYIsASCII(clong)) {
+ PUTC(c);
+ } else if (clong == UCS_REPL && saved_char_in &&
+ HTPassEightBitRaw &&
+ saved_char_in >=
+ LYlowest_eightbit[me->outUCLYhndl]) {
+ PUTUTF8((UCode_t) (0xf000 | saved_char_in));
+ } else {
+ PUTUTF8(clong);
+ }
+ } else if (saved_char_in && me->T.use_raw_char_in) {
+ PUTC(saved_char_in);
+ } else {
+ PUTC(c);
+ }
+
+#define PASS8859SPECL me->T.pass_160_173_raw
+ /*
+ * Convert 160 (nbsp) to Lynx special character if neither
+ * HTPassHighCtrlRaw nor HTCJK is set. - FM
+ */
+ } else if (clong == CH_NBSP && /* S/390 -- gil -- 0932 */
+ !me->no_lynx_specialcodes &&
+ !(PASS8859SPECL || IS_CJK_TTY)) {
+ PUTC(HT_NON_BREAK_SPACE);
+ /*
+ * Convert 173 (shy) to Lynx special character if neither
+ * HTPassHighCtrlRaw nor HTCJK is set. - FM
+ */
+ } else if (clong == CH_SHY && /* S/390 -- gil -- 0949 */
+ !me->no_lynx_specialcodes &&
+ !(PASS8859SPECL || IS_CJK_TTY)) {
+ PUTC(LY_SOFT_HYPHEN);
+ /*
+ * Handle the case in which we think we have a character which
+ * doesn't need further processing (e.g., a koi8-r input for a
+ * koi8-r output). - FM
+ */
+ } else if (me->T.use_raw_char_in && saved_char_in) {
+ /*
+ * Only if the original character is still in saved_char_in,
+ * otherwise we may be iterating from a goto top. - KW
+ */
+ PUTC(saved_char_in);
+ } else if ((chk = (BOOL) (me->T.trans_from_uni &&
+ TOASCII(clong) >= 160)) && /* S/390 -- gil -- 0968 */
+ (uck = UCTransUniChar(clong,
+ me->outUCLYhndl)) >= ' ' &&
+ uck < 256) {
+ CTRACE((tfp, "UCTransUniChar returned 0x%.2" PRI_UCode_t
+ ":'%c'.\n",
+ uck, FROMASCII((char)uck)));
+ /*
+ * We got one octet from the conversions, so use it. - FM
+ */
+ PUTC(FROMASCII((char) uck));
+ } else if ((chk &&
+ (uck == -4 ||
+ (me->T.repl_translated_C0 &&
+ uck > 0 && uck < 32))) &&
+ /*
+ * Not found; look for replacement string. - KW
+ */
+ (uck = UCTransUniCharStr(replace_buf, 60, clong,
+ me->outUCLYhndl,
+ 0) >= 0)) {
+ /*
+ * Got a replacement string. No further tests for validity -
+ * assume that whoever defined replacement strings knew what she
+ * was doing. - KW
+ */
+ PUTS(replace_buf);
+ /*
+ * If we're displaying UTF-8, try that now. - FM
+ */
+ } else if (me->T.output_utf8 && PUTUTF8(clong)) {
+ ; /* do nothing more */
+ /*
+ * If it's any other (> 160) 8-bit character, and we have not set
+ * HTPassEightBitRaw nor HTCJK, nor have the "ISO Latin 1"
+ * character set selected, back translate for our character set. -
+ * FM
+ */
+#define IncludesLatin1Enc \
+ (me->outUCLYhndl == LATIN1 || \
+ (me->outUCI && \
+ (me->outUCI->enc & (UCT_CP_SUPERSETOF_LAT1))))
+
+#define PASSHI8BIT (HTPassEightBitRaw || \
+ (me->T.do_8bitraw && !me->T.trans_from_uni))
+
+ } else if (clong > 160 && clong < 256 &&
+ !(PASSHI8BIT || IS_CJK_TTY) &&
+ !IncludesLatin1Enc) {
+#ifdef USE_PRETTYSRC
+ int psrc_view_backup = 0;
+#endif
+
+ string->size = 0;
+ EntityName = HTMLGetEntityName((UCode_t) (clong - 160));
+ HTChunkPuts(string, EntityName);
+ HTChunkTerminate(string);
+#ifdef USE_PRETTYSRC
+ /* we need to disable it temporarily */
+ if (psrc_view) {
+ psrc_view_backup = 1;
+ psrc_view = 0;
+ }
+#endif
+ handle_entity(me, '\0');
+#ifdef USE_PRETTYSRC
+ /* we need to disable it temporarily */
+ if (psrc_view_backup)
+ psrc_view = TRUE;
+#endif
+
+ string->size = 0;
+ if (!FoundEntity)
+ PUTC(';');
+ /*
+ * If we get to here and have an ASCII char, pass the character. -
+ * KW
+ */
+ } else if (TOASCII(clong) < 127 && clong > 0) { /* S/390 -- gil -- 0987 */
+ PUTC(c);
+ /*
+ * If we get to here, and should have translated, translation has
+ * failed so far. - KW
+ *
+ * We should have sent UTF-8 output to the parser already, but what
+ * the heck, try again. - FM
+ */
+ } else if (me->T.output_utf8 && *me->U.utf_buf) {
+ PUTS(me->U.utf_buf);
+ me->U.utf_buf_p = me->U.utf_buf;
+ *(me->U.utf_buf_p) = '\0';
+ /*
+ * If we don't actually want the character, make it safe and output
+ * that now. - FM
+ */
+ } else if (TOASCII(UCH(c)) < /* S/390 -- gil -- 0997 */
+ LYlowest_eightbit[me->outUCLYhndl] ||
+ (me->T.trans_from_uni && !HTPassEightBitRaw)) {
+ /*
+ * If we get to here, pass the character. - FM
+ */
+ } else {
+ PUTC(c);
+ }
+ break;
+
+ /*
+ * Found '<' in SGML_PCDATA content; treat this mode nearly like
+ * S_litteral, but recognize '<!' and '<?' to filter out comments and
+ * processing instructions. - kw
+ */
+ case S_pcdata:
+ if (!string->size && TOASCII(clong) < 127) { /* first after '<' */
+ if (c == '!') { /* <! */
+ /*
+ * Terminate and set up for possible comment, identifier,
+ * declaration, or marked section as under S_tag. - kw
+ */
+ me->state = S_exclamation;
+ me->lead_exclamation = TRUE;
+ me->doctype_bracket = FALSE;
+ me->first_bracket = FALSE;
+ HTChunkPutc(string, c);
+ break;
+ } else if (c == '?') { /* <? - ignore as a PI until '>' - kw */
+ CTRACE((tfp,
+ "SGML: Found PI in PCDATA, junking it until '>'\n"));
+#ifdef USE_PRETTYSRC
+ if (psrc_view) {
+ PSRCSTART(abracket);
+ PUTS("<?");
+ PSRCSTOP(abracket);
+ }
+#endif
+ me->state = S_pi;
+ break;
+ }
+ }
+ goto case_S_litteral;
+
+ /*
+ * Found '<' in SGML_SCRIPT content; treat this mode nearly like
+ * S_litteral, but recognize '<!' to allow the content to be treated as
+ * a comment by lynx.
+ */
+ case S_script:
+ if (!string->size && TOASCII(clong) < 127) { /* first after '<' */
+ if (c == '!') { /* <! */
+ /*
+ * Terminate and set up for possible comment, identifier,
+ * declaration, or marked section as under S_tag. - kw
+ */
+ me->state = S_exclamation;
+ me->lead_exclamation = TRUE;
+ me->doctype_bracket = FALSE;
+ me->first_bracket = FALSE;
+ HTChunkPutc(string, c);
+ break;
+ }
+ }
+ goto case_S_litteral;
+
+ /*
+ * In literal mode, waits only for specific end tag (for compatibility
+ * with old servers, and for Lynx). - FM
+ */
+ case_S_litteral:
+ case S_litteral:
+ /*PSRC:this case not understood completely by HV, not done */
+ HTChunkPutc(string, c);
+#ifdef USE_PRETTYSRC
+ if (psrc_view) {
+ /* there is nothing useful in the element_stack */
+ testtag = me->current_tag;
+ } else
+#endif
+ testtag = (me->element_stack
+ ? me->element_stack->tag
+ : NULL);
+
+ if (testtag == NULL || testtag->name == NULL) {
+ string->size--;
+ me->state = S_text;
+ goto top1;
+ }
+
+ /*
+ * Normally when we get the closing ">",
+ * testtag contains something like "TITLE"
+ * string contains something like "/title>"
+ * so we decrement by 2 to compare the final character of each.
+ */
+ testlast = string->size - 2 - me->trailing_spaces - me->leading_spaces;
+
+#ifdef USE_COLOR_STYLE
+#define TagSize(p) ((p)->name_len)
+#else
+#define TagSize(p) (strlen((p)->name))
+#endif
+
+ if (TOUPPER(c) != ((testlast < 0)
+ ? '/'
+ : ((testlast < (int) TagSize(testtag))
+ ? testtag->name[testlast]
+ : 0))) {
+ int i;
+
+ /*
+ * If complete match, end literal.
+ */
+ if ((c == '>') &&
+ testlast >= 0 && !testtag->name[testlast]) {
+#ifdef USE_PRETTYSRC
+ if (psrc_view) {
+ char *trailing = NULL;
+
+ if (me->trailing_spaces) {
+ StrAllocCopy(trailing,
+ string->data
+ + string->size
+ - 1
+ - me->trailing_spaces);
+ trailing[me->trailing_spaces] = '\0';
+ }
+
+ PSRCSTART(abracket);
+ PUTS("</");
+ PSRCSTOP(abracket);
+ PSRCSTART(tag);
+
+ strcpy(string->data, me->current_tag->name);
+ transform_tag(me, string);
+ PUTS(string->data);
+
+ if (trailing) {
+ PUTS(trailing);
+ FREE(trailing);
+ }
+
+ PSRCSTOP(tag);
+ PSRCSTART(abracket);
+ PUTC('>');
+ PSRCSTOP(abracket);
+
+ me->current_tag = NULL;
+ } else
+#endif
+ end_element(me, me->element_stack->tag);
+
+ string->size = 0;
+ me->current_attribute_number = INVALID;
+ me->state = S_text;
+ me->leading_spaces = 0;
+ me->trailing_spaces = 0;
+ break;
+ }
+
+ /*
+ * Allow whitespace between the "<" or ">" and the keyword, for
+ * error-recovery.
+ */
+ if (isspace(UCH(c))) {
+ if (testlast == -1) {
+ me->leading_spaces += 1;
+ CTRACE2(TRACE_SGML, (tfp, "leading spaces: %d\n", me->leading_spaces));
+ break;
+ } else if (testlast > 0) {
+ me->trailing_spaces += 1;
+ CTRACE2(TRACE_SGML, (tfp, "trailing spaces: %d\n", me->trailing_spaces));
+ break;
+ }
+ }
+
+ /*
+ * Mismatch - recover.
+ */
+ me->leading_spaces = 0;
+ me->trailing_spaces = 0;
+ if (((testtag->contents != SGML_LITTERAL &&
+ (testtag->flags & Tgf_strict)) ||
+ (me->state == S_pcdata &&
+ (testtag->flags & (Tgf_strict | Tgf_endO)))) &&
+ (testlast > -1 &&
+ (c == '>' || testlast > 0 || IsNmStart(c)))) {
+ me->state = S_end;
+ string->size--;
+ for (i = 0; i < string->size; i++) /* remove '/' */
+ string->data[i] = string->data[i + 1];
+ if ((string->size == 1) ? IsNmStart(c) : IsNmChar(c))
+ break;
+ string->size--;
+ goto top1;
+ }
+ if (me->state == S_pcdata &&
+ (testtag->flags & (Tgf_strict | Tgf_endO)) &&
+ (testlast < 0 && IsNmStart(c))) {
+ me->state = S_tag;
+ break;
+ }
+ /*
+ * If Mismatch: recover string literally.
+ */
+ PUTC('<');
+ for (i = 0; i < string->size - 1; i++) /* recover, except last c */
+ PUTC(string->data[i]);
+ string->size = 0;
+ me->state = S_text;
+ goto top1; /* to recover last c */
+ }
+ break;
+
+ /*
+ * Character reference (numeric entity) or named entity.
+ */
+ case S_ero:
+ if (c == '#') {
+ /*
+ * Setting up for possible numeric entity.
+ */
+ me->state = S_cro; /* &# is Char Ref Open */
+ break;
+ }
+ me->state = S_entity; /* Fall through! */
+
+ /*
+ * Handle possible named entity.
+ */
+ case S_entity:
+ if (TOASCII(clong) < 127 && (string->size ? /* S/390 -- gil -- 1029 */
+ isalnum(UCH(c)) : isalpha(UCH(c)))) {
+ /* Should probably use IsNmStart/IsNmChar above (is that right?),
+ but the world is not ready for that - there's &nbsp: (note
+ colon!) and stuff around. */
+ /*
+ * Accept valid ASCII character. - FM
+ */
+ HTChunkPutc(string, c);
+ } else if (string->size == 0) {
+ /*
+ * It was an ampersand that's just text, so output the ampersand
+ * and recycle this character. - FM
+ */
+#ifdef USE_PRETTYSRC
+ if (psrc_view)
+ PSRCSTART(badseq);
+#endif
+ PUTC('&');
+#ifdef USE_PRETTYSRC
+ if (psrc_view)
+ PSRCSTOP(badseq);
+#endif
+ me->state = S_text;
+ goto top1;
+ } else {
+ /*
+ * Terminate entity name and try to handle it. - FM
+ */
+ HTChunkTerminate(string);
+#ifdef USE_PRETTYSRC
+ entity_string = string->data;
+#endif
+ if (!strcmp(string->data, "zwnj") &&
+ (!me->element_stack ||
+ (me->element_stack->tag &&
+ me->element_stack->tag->contents == SGML_MIXED))) {
+ /*
+ * Handle zwnj (8204) as <WBR>. - FM
+ */
+ char temp[8];
+
+ CTRACE((tfp,
+ "SGML_character: Handling 'zwnj' entity as 'WBR' element.\n"));
+
+ if (c != ';') {
+ sprintf(temp, "<WBR>%c", c);
+ } else {
+ sprintf(temp, "<WBR>");
+ }
+ if (me->recover == NULL) {
+ StrAllocCopy(me->recover, temp);
+ me->recover_index = 0;
+ } else {
+ StrAllocCat(me->recover, temp);
+ }
+ string->size = 0;
+ me->state = S_text;
+ break;
+ } else {
+ handle_entity(me, '\0');
+ }
+ string->size = 0;
+ me->state = S_text;
+ /*
+ * Don't eat the terminator if we didn't find the entity name and
+ * therefore sent the raw string via handle_entity(), or if the
+ * terminator is not the "standard" semi-colon for HTML. - FM
+ */
+#ifdef USE_PRETTYSRC
+ if (psrc_view && FoundEntity && c == ';') {
+ PSRCSTART(entity);
+ PUTC(c);
+ PSRCSTOP(entity);
+ }
+#endif
+ if (!FoundEntity || c != ';')
+ goto top1;
+ }
+ break;
+
+ /*
+ * Check for a numeric entity.
+ */
+ case S_cro:
+ if (TOASCII(clong) < 127 && TOLOWER(UCH(c)) == 'x') { /* S/390 -- gil -- 1060 */
+ me->isHex = TRUE;
+ me->state = S_incro;
+ } else if (TOASCII(clong) < 127 && isdigit(UCH(c))) {
+ /*
+ * Accept only valid ASCII digits. - FM
+ */
+ HTChunkPutc(string, c); /* accumulate a character NUMBER */
+ me->isHex = FALSE;
+ me->state = S_incro;
+ } else if (string->size == 0) {
+ /*
+ * No 'x' or digit following the "&#" so recover them and recycle
+ * the character. - FM
+ */
+#ifdef USE_PRETTYSRC
+ if (psrc_view)
+ PSRCSTART(badseq);
+#endif
+ PUTC('&');
+ PUTC('#');
+#ifdef USE_PRETTYSRC
+ if (psrc_view)
+ PSRCSTOP(badseq);
+#endif
+ me->state = S_text;
+ goto top1;
+ }
+ break;
+
+ /*
+ * Handle a numeric entity.
+ */
+ case S_incro:
+ /* S/390 -- gil -- 1075 */
+ if ((TOASCII(clong) < 127) &&
+ (me->isHex
+ ? isxdigit(UCH(c))
+ : isdigit(UCH(c)))) {
+ /*
+ * Accept only valid hex or ASCII digits. - FM
+ */
+ HTChunkPutc(string, c); /* accumulate a character NUMBER */
+ } else if (string->size == 0) {
+ /*
+ * No hex digit following the "&#x" so recover them and recycle the
+ * character. - FM
+ */
+#ifdef USE_PRETTYSRC
+ if (psrc_view)
+ PSRCSTART(badseq);
+#endif
+ PUTS("&#x");
+#ifdef USE_PRETTYSRC
+ if (psrc_view)
+ PSRCSTOP(badseq);
+#endif
+ me->isHex = FALSE;
+ me->state = S_text;
+ goto top1;
+ } else {
+ /*
+ * Terminate the numeric entity and try to handle it. - FM
+ */
+ UCode_t code;
+ int i;
+
+ HTChunkTerminate(string);
+#ifdef USE_PRETTYSRC
+ entity_string = string->data;
+#endif
+ if (UCScanCode(&code, string->data, me->isHex)) {
+
+/* =============== work in ASCII below here =============== S/390 -- gil -- 1092 */
+ if (AssumeCP1252(me)) {
+ code = LYcp1252ToUnicode(code);
+ }
+ /*
+ * Check for special values. - FM
+ */
+ if ((code == 8204) &&
+ (!me->element_stack ||
+ (me->element_stack->tag &&
+ me->element_stack->tag->contents == SGML_MIXED))) {
+ /*
+ * Handle zwnj (8204) as <WBR>. - FM
+ */
+ char temp[8];
+
+ CTRACE((tfp,
+ "SGML_character: Handling '8204' (zwnj) reference as 'WBR' element.\n"));
+
+ /*
+ * Include the terminator if it is not the standard
+ * semi-colon. - FM
+ */
+ if (c != ';') {
+ sprintf(temp, "<WBR>%c", c);
+ } else {
+ sprintf(temp, "<WBR>");
+ }
+ /*
+ * Add the replacement string to the recover buffer for
+ * processing. - FM
+ */
+ if (me->recover == NULL) {
+ StrAllocCopy(me->recover, temp);
+ me->recover_index = 0;
+ } else {
+ StrAllocCat(me->recover, temp);
+ }
+ string->size = 0;
+ me->isHex = FALSE;
+ me->state = S_text;
+ break;
+ } else if (put_special_unicodes(me, code)) {
+ /*
+ * We handled the value as a special character, so recycle
+ * the terminator or break. - FM
+ */
+#ifdef USE_PRETTYSRC
+ if (psrc_view) {
+ PSRCSTART(entity);
+ PUTS((me->isHex ? "&#x" : "&#"));
+ PUTS(entity_string);
+ if (c == ';')
+ PUTC(';');
+ PSRCSTOP(entity);
+ }
+#endif
+ string->size = 0;
+ me->isHex = FALSE;
+ me->state = S_text;
+ if (c != ';')
+ goto top1;
+ break;
+ }
+ /*
+ * Seek a translation from the chartrans tables.
+ */
+ if ((uck = UCTransUniChar(code,
+ me->outUCLYhndl)) >= 32 &&
+ uck < 256 &&
+ (uck < 127 ||
+ uck >= LYlowest_eightbit[me->outUCLYhndl])) {
+#ifdef USE_PRETTYSRC
+ if (!psrc_view) {
+#endif
+ PUTC(FROMASCII((char) uck));
+#ifdef USE_PRETTYSRC
+ } else {
+ put_pretty_number(me);
+ }
+#endif
+ } else if ((uck == -4 ||
+ (me->T.repl_translated_C0 &&
+ uck > 0 && uck < 32)) &&
+ /*
+ * Not found; look for replacement string.
+ */
+ (uck = UCTransUniCharStr(replace_buf, 60, code,
+ me->outUCLYhndl,
+ 0) >= 0)) {
+#ifdef USE_PRETTYSRC
+ if (psrc_view) {
+ put_pretty_number(me);
+ } else
+#endif
+ PUTS(replace_buf);
+ /*
+ * If we're displaying UTF-8, try that now. - FM
+ */
+ } else if (me->T.output_utf8 && PUTUTF8(code)) {
+ ; /* do nothing more */
+ /*
+ * Ignore 8205 (zwj), 8206 (lrm), and 8207 (rln), if we get
+ * to here. - FM
+ */
+ } else if (code == 8205 ||
+ code == 8206 ||
+ code == 8207) {
+ if (TRACE) {
+ string->size--;
+ LYStrNCpy(replace_buf,
+ string->data,
+ (string->size < 64 ? string->size : 63));
+ fprintf(tfp,
+ "SGML_character: Ignoring '%s%s'.\n",
+ (me->isHex ? "&#x" : "&#"),
+ replace_buf);
+ }
+#ifdef USE_PRETTYSRC
+ if (psrc_view) {
+ PSRCSTART(badseq);
+ PUTS((me->isHex ? "&#x" : "&#"));
+ PUTS(entity_string);
+ if (c == ';')
+ PUTC(';');
+ PSRCSTOP(badseq);
+ }
+#endif
+ string->size = 0;
+ me->isHex = FALSE;
+ me->state = S_text;
+ if (c != ';')
+ goto top1;
+ break;
+ /*
+ * Show the numeric entity if we get to here and the value:
+ * (1) Is greater than 255 (but use ASCII characters for
+ * spaces or dashes).
+ * (2) Is less than 32, and not valid or we don't have
+ * HTCJK set.
+ * (3) Is 127 and we don't have HTPassHighCtrlRaw or HTCJK
+ * set.
+ * (4) Is 128 - 159 and we don't have HTPassHighCtrlNum
+ * set.
+ * - FM
+ */
+ } else if ((code > 255) ||
+ (code < ' ' && /* S/390 -- gil -- 1140 */
+ code != '\t' && code != '\n' && code != '\r' &&
+ !IS_CJK_TTY) ||
+ (TOASCII(code) == 127 &&
+ !(HTPassHighCtrlRaw || IS_CJK_TTY)) ||
+ (TOASCII(code) > 127 && code < 160 &&
+ !HTPassHighCtrlNum)) {
+ /*
+ * Unhandled or illegal value. Recover the "&#" or "&#x"
+ * and digit(s), and recycle the terminator. - FM
+ */
+#ifdef USE_PRETTYSRC
+ if (psrc_view) {
+ PSRCSTART(badseq);
+ }
+#endif
+ if (me->isHex) {
+ PUTS("&#x");
+ me->isHex = FALSE;
+ } else {
+ PUTS("&#");
+ }
+ string->size--;
+ for (i = 0; i < string->size; i++) /* recover */
+ PUTC(string->data[i]);
+#ifdef USE_PRETTYSRC
+ if (psrc_view) {
+ PSRCSTOP(badseq);
+ }
+#endif
+ string->size = 0;
+ me->isHex = FALSE;
+ me->state = S_text;
+ goto top1;
+ } else if (TOASCII(code) < 161 || /* S/390 -- gil -- 1162 */
+ HTPassEightBitNum ||
+ IncludesLatin1Enc) {
+ /*
+ * No conversion needed. - FM
+ */
+#ifdef USE_PRETTYSRC
+ if (psrc_view) {
+ put_pretty_number(me);
+ } else
+#endif
+ PUTC(FROMASCII((char) code));
+ } else {
+ /*
+ * Handle as named entity. - FM
+ */
+ code -= 160;
+ EntityName = HTMLGetEntityName(code);
+ if (EntityName && EntityName[0] != '\0') {
+ string->size = 0;
+ HTChunkPuts(string, EntityName);
+ HTChunkTerminate(string);
+ handle_entity(me, '\0');
+ /*
+ * Add a semi-colon if something went wrong and
+ * handle_entity() sent the string. - FM
+ */
+ if (!FoundEntity) {
+ PUTC(';');
+ }
+ } else {
+ /*
+ * Our conversion failed, so recover the "&#" and
+ * digit(s), and recycle the terminator. - FM
+ */
+#ifdef USE_PRETTYSRC
+ if (psrc_view)
+ PSRCSTART(badseq);
+#endif
+ if (me->isHex) {
+ PUTS("&#x");
+ me->isHex = FALSE;
+ } else {
+ PUTS("&#");
+ }
+ string->size--;
+ for (i = 0; i < string->size; i++) /* recover */
+ PUTC(string->data[i]);
+#ifdef USE_PRETTYSRC
+ if (psrc_view)
+ PSRCSTOP(badseq);
+#endif
+ string->size = 0;
+ me->isHex = FALSE;
+ me->state = S_text;
+ goto top1;
+ }
+ }
+ /*
+ * If we get to here, we succeeded. Hoorah!!! - FM
+ */
+ string->size = 0;
+ me->isHex = FALSE;
+ me->state = S_text;
+ /*
+ * Don't eat the terminator if it's not the "standard"
+ * semi-colon for HTML. - FM
+ */
+ if (c != ';') {
+ goto top1;
+ }
+ } else {
+ /*
+ * Not an entity, and don't know why not, so add the terminator
+ * to the string, output the "&#" or "&#x", and process the
+ * string via the recover element. - FM
+ */
+ string->size--;
+ HTChunkPutc(string, c);
+ HTChunkTerminate(string);
+#ifdef USE_PRETTYSRC
+ if (psrc_view)
+ PSRCSTART(badseq);
+#endif
+ if (me->isHex) {
+ PUTS("&#x");
+ me->isHex = FALSE;
+ } else {
+ PUTS("&#");
+ }
+#ifdef USE_PRETTYSRC
+ if (psrc_view)
+ PSRCSTOP(badseq);
+#endif
+ if (me->recover == NULL) {
+ StrAllocCopy(me->recover, string->data);
+ me->recover_index = 0;
+ } else {
+ StrAllocCat(me->recover, string->data);
+ }
+ string->size = 0;
+ me->isHex = FALSE;
+ me->state = S_text;
+ break;
+ }
+ }
+ break;
+
+ /*
+ * Tag
+ */
+ case S_tag: /* new tag */
+ if (TOASCII(clong) < 127 && (string->size ? /* S/390 -- gil -- 1179 */
+ IsNmChar(c) : IsNmStart(c))) {
+ /*
+ * Add valid ASCII character. - FM
+ */
+ HTChunkPutc(string, c);
+ } else if (c == '!' && !string->size) { /* <! */
+ /*
+ * Terminate and set up for possible comment, identifier,
+ * declaration, or marked section. - FM
+ */
+ me->state = S_exclamation;
+ me->lead_exclamation = TRUE;
+ me->doctype_bracket = FALSE;
+ me->first_bracket = FALSE;
+ HTChunkPutc(string, c);
+ break;
+ } else if (!string->size &&
+ (TOASCII(clong) <= 160 && /* S/390 -- gil -- 1196 */
+ (c != '/' && c != '?' && c != '_' && c != ':'))) {
+ /*
+ * '<' must be followed by an ASCII letter to be a valid start tag.
+ * Here it isn't, nor do we have a '/' for an end tag, nor one of
+ * some other characters with a special meaning for SGML or which
+ * are likely to be legal Name Start characters in XML or some
+ * other extension. So recover the '<' and following character as
+ * data. - FM & KW
+ */
+ me->state = S_text;
+#ifdef USE_PRETTYSRC
+ if (psrc_view)
+ PSRCSTART(badseq);
+#endif
+ PUTC('<');
+#ifdef USE_PRETTYSRC
+ if (psrc_view)
+ PSRCSTOP(badseq);
+#endif
+ goto top1;
+ } else { /* End of tag name */
+ /*
+ * Try to handle tag. - FM
+ */
+ HTTag *t;
+
+ if (c == '/') {
+ if (string->size == 0) {
+ me->state = S_end;
+ break;
+ }
+ CTRACE((tfp, "SGML: `<%.*s/' found!\n", string->size, string->data));
+ }
+ HTChunkTerminate(string);
+
+ t = SGMLFindTag(dtd, string->data);
+ if (t == me->unknown_tag &&
+ ((c == ':' &&
+ string->size == 4 && 0 == strcasecomp(string->data, "URL")) ||
+ (string->size > 4 && 0 == strncasecomp(string->data, "URL:", 4)))) {
+ /*
+ * Treat <URL: as text rather than a junk tag, so we display
+ * it and the URL (Lynxism 8-). - FM
+ */
+#ifdef USE_PRETTYSRC
+ if (psrc_view)
+ PSRCSTART(badseq);
+#endif
+ PUTC('<');
+ PUTS(string->data); /* recover */
+ PUTC(c);
+#ifdef USE_PRETTYSRC
+ if (psrc_view)
+ PSRCSTOP(badseq);
+#endif
+ CTRACE((tfp, "SGML: Treating <%s%c as text\n",
+ string->data, c));
+ string->size = 0;
+ me->state = S_text;
+ break;
+ }
+ if (c == '/' && t) {
+ /*
+ * Element name was ended by '/'. Remember the tag that ended
+ * thusly, we'll interpret this as either an indication of an
+ * empty element (if '>' follows directly) or do some
+ * SGMLshortref-ish treatment. - kw
+ */
+ me->slashedtag = t;
+ }
+ if (!t) {
+ if (c == '?' && string->size <= 1) {
+ CTRACE((tfp, "SGML: Found PI, looking for '>'\n"));
+#ifdef USE_PRETTYSRC
+ if (psrc_view) {
+ PSRCSTART(abracket);
+ PUTS("<?");
+ PSRCSTOP(abracket);
+ }
+#endif
+ string->size = 0;
+ me->state = S_pi;
+ HTChunkPutc(string, c);
+ break;
+ }
+ CTRACE((tfp, "SGML: *** Invalid element %s\n",
+ string->data));
+
+#ifdef USE_PRETTYSRC
+ if (psrc_view) {
+ PSRCSTART(abracket);
+ PUTC('<');
+ PSRCSTOP(abracket);
+ PSRCSTART(badtag);
+ transform_tag(me, string);
+ PUTS(string->data);
+ if (c == '>') {
+ PSRCSTOP(badtag);
+ PSRCSTART(abracket);
+ PUTC('>');
+ PSRCSTOP(abracket);
+ } else {
+ PUTC(c);
+ }
+ }
+#endif
+ me->state = (c == '>') ? S_text : S_junk_tag;
+ break;
+ } else if (t == me->unknown_tag) {
+ CTRACE((tfp, "SGML: *** Unknown element \"%s\"\n",
+ string->data));
+ /*
+ * Fall through and treat like valid tag for attribute parsing.
+ * - KW
+ */
+
+ }
+ me->current_tag = t;
+
+#ifdef USE_PRETTYSRC
+ if (psrc_view) {
+ PSRCSTART(abracket);
+ PUTC('<');
+ PSRCSTOP(abracket);
+ if (t != me->unknown_tag)
+ PSRCSTART(tag);
+ else
+ PSRCSTART(badtag);
+ transform_tag(me, string);
+ PUTS(string->data);
+ if (t != me->unknown_tag)
+ PSRCSTOP(tag);
+ else
+ PSRCSTOP(badtag);
+ }
+ if (!psrc_view) /*don't waste time */
+#endif
+ {
+ /*
+ * Clear out attributes.
+ */
+ memset((void *) me->present, 0, sizeof(BOOL) *
+ (unsigned) (me->current_tag->number_of_attributes));
+ }
+
+ string->size = 0;
+ me->current_attribute_number = INVALID;
+#ifdef USE_PRETTYSRC
+ if (psrc_view) {
+ if (c == '>' || c == '<' || (c == '/' && me->slashedtag)) {
+ if (c != '<') {
+ PSRCSTART(abracket);
+ PUTC(c);
+ PSRCSTOP(abracket);
+ me->state = (c == '>') ? S_text : S_tagname_slash;
+ } else {
+ me->state = S_tag;
+ }
+ } else {
+ if (!WHITE(c))
+ PUTC(c);
+ me->state = S_tag_gap;
+ }
+ } else
+#endif
+ if (c == '>' || c == '<' || (c == '/' && me->slashedtag)) {
+ if (me->current_tag->name)
+ start_element(me);
+ me->state = (c == '>') ? S_text :
+ (c == '<') ? S_tag : S_tagname_slash;
+ } else {
+ me->state = S_tag_gap;
+ }
+ }
+ break;
+
+ case S_exclamation:
+ if (me->lead_exclamation && c == '-') {
+ /*
+ * Set up for possible comment. - FM
+ */
+ me->lead_exclamation = FALSE;
+ me->first_dash = TRUE;
+ HTChunkPutc(string, c);
+ break;
+ }
+ if (me->lead_exclamation && c == '[') {
+ /*
+ * Set up for possible marked section. - FM
+ */
+ me->lead_exclamation = FALSE;
+ me->first_bracket = TRUE;
+ me->second_bracket = FALSE;
+ HTChunkPutc(string, c);
+ me->state = S_marked;
+ break;
+ }
+ if (me->first_dash && c == '-') {
+ /*
+ * Set up to handle comment. - FM
+ */
+ me->lead_exclamation = FALSE;
+ me->first_dash = FALSE;
+ me->end_comment = FALSE;
+ HTChunkPutc(string, c);
+ me->state = S_comment;
+ break;
+ }
+ me->lead_exclamation = FALSE;
+ me->first_dash = FALSE;
+ if (c == '>') {
+ /*
+ * Try to handle identifier. - FM
+ */
+ HTChunkTerminate(string);
+#ifdef USE_PRETTYSRC
+ if (psrc_view) {
+ PSRCSTART(sgmlspecial);
+ PUTC('<');
+ PUTS(string->data);
+ PUTC('>');
+ PSRCSTOP(sgmlspecial);
+ } else
+#endif
+ handle_identifier(me);
+ string->size = 0;
+ me->state = S_text;
+ break;
+ }
+ if (WHITE(c)) {
+ if (string->size == 8 &&
+ !strncasecomp(string->data, "!DOCTYPE", 8)) {
+ /*
+ * Set up for DOCTYPE declaration. - FM
+ */
+ HTChunkPutc(string, c);
+ me->doctype_bracket = FALSE;
+ me->state = S_doctype;
+ break;
+ }
+ if (string->size == 7 &&
+ !strncasecomp(string->data, "!ENTITY", 7)) {
+ /*
+ * Set up for ENTITY declaration. - FM
+ */
+ HTChunkPutc(string, c);
+ me->first_dash = FALSE;
+ me->end_comment = TRUE;
+ me->state = S_sgmlent;
+ break;
+ }
+ if (string->size == 8 &&
+ !strncasecomp(string->data, "!ELEMENT", 8)) {
+ /*
+ * Set up for ELEMENT declaration. - FM
+ */
+ HTChunkPutc(string, c);
+ me->first_dash = FALSE;
+ me->end_comment = TRUE;
+ me->state = S_sgmlele;
+ break;
+ }
+ if (string->size == 8 &&
+ !strncasecomp(string->data, "!ATTLIST", 8)) {
+ /*
+ * Set up for ATTLIST declaration. - FM
+ */
+ HTChunkPutc(string, c);
+ me->first_dash = FALSE;
+ me->end_comment = TRUE;
+ me->state = S_sgmlatt;
+ break;
+ }
+ }
+ HTChunkPutc(string, c);
+ break;
+
+ case S_comment: /* Expecting comment. - FM */
+ if (historical_comments) {
+ /*
+ * Any '>' terminates. - FM
+ */
+ if (c == '>') {
+ HTChunkTerminate(string);
+#ifdef USE_PRETTYSRC
+ if (psrc_view) {
+ PSRCSTART(comm);
+ PUTC('<');
+ PUTS_TR(string->data);
+ PUTC('>');
+ PSRCSTOP(comm);
+ } else
+#endif
+ handle_comment(me);
+ string->size = 0;
+ me->end_comment = FALSE;
+ me->first_dash = FALSE;
+ me->state = S_text;
+ break;
+ }
+ goto S_comment_put_c;
+ }
+ if (!me->first_dash && c == '-') {
+ HTChunkPutc(string, c);
+ me->first_dash = TRUE;
+ break;
+ }
+ if (me->first_dash && c == '-') {
+ HTChunkPutc(string, c);
+ me->first_dash = FALSE;
+ if (!me->end_comment)
+ me->end_comment = TRUE;
+ else if (!minimal_comments)
+ /*
+ * Validly treat '--' pairs as successive comments (for
+ * minimal, any "--WHITE>" terminates). - FM
+ */
+ me->end_comment = FALSE;
+ break;
+ }
+ if (me->end_comment && c == '>') {
+ /*
+ * Terminate and handle the comment. - FM
+ */
+ HTChunkTerminate(string);
+#ifdef USE_PRETTYSRC
+ if (psrc_view) {
+ PSRCSTART(comm);
+ PUTC('<');
+ PUTS_TR(string->data);
+ PUTC('>');
+ PSRCSTOP(comm);
+ } else
+#endif
+ handle_comment(me);
+ string->size = 0;
+ me->end_comment = FALSE;
+ me->first_dash = FALSE;
+ me->state = S_text;
+ break;
+ }
+ me->first_dash = FALSE;
+ if (me->end_comment && !isspace(UCH(c)))
+ me->end_comment = FALSE;
+
+ S_comment_put_c:
+ if (me->T.decode_utf8 &&
+ *me->U.utf_buf) {
+ HTChunkPuts(string, me->U.utf_buf);
+ me->U.utf_buf_p = me->U.utf_buf;
+ *(me->U.utf_buf_p) = '\0';
+ } else if (!IS_CJK_TTY &&
+ (me->T.output_utf8 ||
+ me->T.trans_from_uni)) {
+ if (clong == UCS_REPL && saved_char_in &&
+ HTPassEightBitRaw &&
+ saved_char_in >=
+ LYlowest_eightbit[me->outUCLYhndl]) {
+ (HTChunkPutUtf8Char) (string,
+ (UCode_t) (0xf000 | saved_char_in));
+ } else {
+ HTChunkPutUtf8Char(string, clong);
+ }
+ } else if (saved_char_in && me->T.use_raw_char_in) {
+ HTChunkPutc(string, saved_char_in);
+ } else {
+ HTChunkPutc(string, c);
+ }
+ break;
+
+ case S_doctype: /* Expecting DOCTYPE. - FM */
+ if (me->doctype_bracket) {
+ HTChunkPutc(string, c);
+ if (c == ']')
+ me->doctype_bracket = FALSE;
+ break;
+ }
+ if (c == '[' && WHITE(string->data[string->size - 1])) {
+ HTChunkPutc(string, c);
+ me->doctype_bracket = TRUE;
+ break;
+ }
+ if (c == '>') {
+ HTChunkTerminate(string);
+#ifdef USE_PRETTYSRC
+ if (psrc_view) {
+ PSRCSTART(sgmlspecial);
+ PUTC('<');
+ PUTS(string->data);
+ PUTC('>');
+ PSRCSTOP(sgmlspecial);
+ } else
+#endif
+ handle_doctype(me);
+ string->size = 0;
+ me->state = S_text;
+ break;
+ }
+ HTChunkPutc(string, c);
+ break;
+
+ case S_marked: /* Expecting marked section. - FM */
+ if (me->first_bracket && c == '[') {
+ HTChunkPutc(string, c);
+ me->first_bracket = FALSE;
+ me->second_bracket = TRUE;
+ break;
+ }
+ if (me->second_bracket && c == ']' &&
+ string->data[string->size - 1] == ']') {
+ HTChunkPutc(string, c);
+ me->second_bracket = FALSE;
+ break;
+ }
+ if (!me->second_bracket && c == '>') {
+ HTChunkTerminate(string);
+#ifdef USE_PRETTYSRC
+ if (psrc_view) {
+ PSRCSTART(sgmlspecial);
+ PUTC('<');
+ PUTS(string->data);
+ PUTC('>');
+ PSRCSTOP(sgmlspecial);
+ } else
+#endif
+ handle_marked(me);
+ string->size = 0;
+ me->state = S_text;
+ break;
+ }
+ HTChunkPutc(string, c);
+ break;
+
+ case S_sgmlent: /* Expecting ENTITY. - FM */
+ if (!me->first_dash && c == '-') {
+ HTChunkPutc(string, c);
+ me->first_dash = TRUE;
+ break;
+ }
+ if (me->first_dash && c == '-') {
+ HTChunkPutc(string, c);
+ me->first_dash = FALSE;
+ if (!me->end_comment)
+ me->end_comment = TRUE;
+ else
+ me->end_comment = FALSE;
+ break;
+ }
+ if (me->end_comment && c == '>') {
+ HTChunkTerminate(string);
+#ifdef USE_PRETTYSRC
+ if (psrc_view) {
+ PSRCSTART(sgmlspecial);
+ PUTC('<');
+ PUTS(string->data);
+ PUTC('>');
+ PSRCSTOP(sgmlspecial);
+ } else
+#endif
+ handle_sgmlent(me);
+ string->size = 0;
+ me->end_comment = FALSE;
+ me->first_dash = FALSE;
+ me->state = S_text;
+ break;
+ }
+ me->first_dash = FALSE;
+ HTChunkPutc(string, c);
+ break;
+
+ case S_sgmlele: /* Expecting ELEMENT. - FM */
+ if (!me->first_dash && c == '-') {
+ HTChunkPutc(string, c);
+ me->first_dash = TRUE;
+ break;
+ }
+ if (me->first_dash && c == '-') {
+ HTChunkPutc(string, c);
+ me->first_dash = FALSE;
+ if (!me->end_comment)
+ me->end_comment = TRUE;
+ else
+ me->end_comment = FALSE;
+ break;
+ }
+ if (me->end_comment && c == '>') {
+ HTChunkTerminate(string);
+#ifdef USE_PRETTYSRC
+ if (psrc_view) {
+ PSRCSTART(sgmlspecial);
+ PUTC('<');
+ PUTS(string->data);
+ PUTC('>');
+ PSRCSTOP(sgmlspecial);
+ } else
+#endif
+ handle_sgmlele(me);
+ string->size = 0;
+ me->end_comment = FALSE;
+ me->first_dash = FALSE;
+ me->state = S_text;
+ break;
+ }
+ me->first_dash = FALSE;
+ HTChunkPutc(string, c);
+ break;
+
+ case S_sgmlatt: /* Expecting ATTLIST. - FM */
+ if (!me->first_dash && c == '-') {
+ HTChunkPutc(string, c);
+ me->first_dash = TRUE;
+ break;
+ }
+ if (me->first_dash && c == '-') {
+ HTChunkPutc(string, c);
+ me->first_dash = FALSE;
+ if (!me->end_comment)
+ me->end_comment = TRUE;
+ else
+ me->end_comment = FALSE;
+ break;
+ }
+ if (me->end_comment && c == '>') {
+ HTChunkTerminate(string);
+#ifdef USE_PRETTYSRC
+ if (psrc_view) {
+ PSRCSTART(sgmlspecial);
+ PUTC('<');
+ PUTS(string->data);
+ PUTC('>');
+ PSRCSTOP(sgmlspecial);
+ } else
+#endif
+ handle_sgmlatt(me);
+ string->size = 0;
+ me->end_comment = FALSE;
+ me->first_dash = FALSE;
+ me->state = S_text;
+ break;
+ }
+ me->first_dash = FALSE;
+ HTChunkPutc(string, c);
+ break;
+
+ case S_tag_gap: /* Expecting attribute or '>' */
+ if (WHITE(c)) {
+ /* PUTC(c); - no, done as special case */
+ break; /* Gap between attributes */
+ }
+ if (c == '>') { /* End of tag */
+#ifdef USE_PRETTYSRC
+ if (!psrc_view)
+#endif
+ if (me->current_tag->name)
+ start_element(me);
+#ifdef USE_PRETTYSRC
+ if (psrc_view) {
+ PSRCSTART(abracket);
+ PUTC('>');
+ PSRCSTOP(abracket);
+ }
+#endif
+ me->state = S_text;
+ break;
+ }
+ HTChunkPutc(string, c);
+ me->state = S_attr; /* Get attribute */
+ break;
+
+ /* accumulating value */
+ case S_attr:
+ if (WHITE(c) || (c == '>') || (c == '=')) { /* End of word */
+ if ((c == '>')
+ && (string->size >= 1)
+ && (string->data[string->size - 1] == '/')) {
+ if ((LYxhtml_parsing || me->extended_html)
+ && ignore_when_empty(me->current_tag)) {
+ discard_empty(me);
+ }
+ } else {
+ HTChunkTerminate(string);
+ handle_attribute_name(me, string->data);
+ }
+#ifdef USE_PRETTYSRC
+ if (!psrc_view) {
+#endif
+ string->size = 0;
+ if (c == '>') { /* End of tag */
+ if (me->current_tag->name)
+ start_element(me);
+ me->state = S_text;
+ break;
+ }
+#ifdef USE_PRETTYSRC
+ } else {
+ PUTC(' ');
+ if (me->current_attribute_number == INVALID)
+ PSRCSTART(badattr);
+ else
+ PSRCSTART(attrib);
+ if (attrname_transform != 1) {
+ if (attrname_transform == 0)
+ LYLowerCase(string->data);
+ else
+ LYUpperCase(string->data);
+ }
+ PUTS(string->data);
+ if (c == '=' || WHITE(c))
+ PUTC(c);
+ if (c == '=' || c == '>') {
+ if (me->current_attribute_number == INVALID) {
+ PSRCSTOP(badattr);
+ } else {
+ PSRCSTOP(attrib);
+ }
+ }
+ if (c == '>') {
+ PSRCSTART(abracket);
+ PUTC('>');
+ PSRCSTOP(abracket);
+ me->state = S_text;
+ break;
+ }
+ string->size = 0;
+ }
+#endif
+ me->state = (c == '=' ? S_equals : S_attr_gap);
+ } else {
+ HTChunkPutc(string, c);
+ }
+ break;
+
+ case S_attr_gap: /* Expecting attribute or '=' or '>' */
+ if (WHITE(c)) {
+ PRETTYSRC_PUTC(c);
+ break; /* Gap after attribute */
+ }
+ if (c == '>') { /* End of tag */
+#ifdef USE_PRETTYSRC
+ if (psrc_view) {
+ if (me->current_attribute_number == INVALID) {
+ PSRCSTOP(badattr);
+ } else {
+ PSRCSTOP(attrib);
+ }
+ PSRCSTART(abracket);
+ PUTC('>');
+ PSRCSTOP(abracket);
+ } else
+#endif
+ if (me->current_tag->name)
+ start_element(me);
+ me->state = S_text;
+ break;
+ } else if (c == '=') {
+#ifdef USE_PRETTYSRC
+ if (psrc_view) {
+ PUTC('=');
+ if (me->current_attribute_number == INVALID) {
+ PSRCSTOP(badattr);
+ } else {
+ PSRCSTOP(attrib);
+ }
+ }
+#endif
+ me->state = S_equals;
+ break;
+ }
+ HTChunkPutc(string, c);
+ me->state = S_attr; /* Get next attribute */
+ break;
+
+ case S_equals: /* After attr = */
+ if (WHITE(c)) {
+ PRETTYSRC_PUTC(c);
+ break; /* Before attribute value */
+ }
+ if (c == '>') { /* End of tag */
+ CTRACE((tfp, "SGML: found = but no value\n"));
+#ifdef USE_PRETTYSRC
+ if (psrc_view) {
+ PSRCSTART(abracket);
+ PUTC('>');
+ PSRCSTOP(abracket);
+ } else
+#endif
+ if (me->current_tag->name)
+ start_element(me);
+ me->state = S_text;
+ break;
+
+ } else if (c == '\'') {
+#ifdef USE_PRETTYSRC
+ if (psrc_view) {
+ PSRCSTART(attrval);
+ PUTC(c);
+ }
+#endif
+ me->state = S_squoted;
+ break;
+
+ } else if (c == '"') {
+#ifdef USE_PRETTYSRC
+ if (psrc_view) {
+ PSRCSTART(attrval);
+ PUTC(c);
+ }
+#endif
+ me->state = S_dquoted;
+ break;
+ }
+#ifdef USE_PRETTYSRC
+ if (psrc_view)
+ PSRCSTART(attrval);
+#endif
+ me->state = S_value;
+ /* FALLTHRU */
+
+ case S_value:
+ if (WHITE(c) || (c == '>')) { /* End of word */
+ HTChunkTerminate(string);
+#ifdef USE_PRETTYSRC
+ if (!end_if_prettysrc(me, string, 0))
+#endif
+ {
+#ifdef CJK_EX /* Quick hack. - JH7AYN */
+ if (IS_CJK_TTY) {
+ if (string->data[0] == '$') {
+ if (string->data[1] == 'B' || string->data[1] == '@') {
+ char *jis_buf = 0;
+
+ HTSprintf0(&jis_buf, "\033%s", string->data);
+ TO_EUC((const unsigned char *) jis_buf,
+ (unsigned char *) string->data);
+ FREE(jis_buf);
+ }
+ }
+ }
+#endif
+ handle_attribute_value(me, string->data);
+ }
+ string->size = 0;
+ if (c == '>') { /* End of tag */
+#ifdef USE_PRETTYSRC
+ if (psrc_view) {
+ PSRCSTART(abracket);
+ PUTC('>');
+ PSRCSTOP(abracket);
+ } else
+#endif
+ if (me->current_tag->name)
+ start_element(me);
+ me->state = S_text;
+ break;
+ } else
+ me->state = S_tag_gap;
+ } else if (me->T.decode_utf8 &&
+ *me->U.utf_buf) {
+ HTChunkPuts(string, me->U.utf_buf);
+ me->U.utf_buf_p = me->U.utf_buf;
+ *(me->U.utf_buf_p) = '\0';
+ } else if (!IS_CJK_TTY &&
+ (me->T.output_utf8 ||
+ me->T.trans_from_uni)) {
+ if (clong == UCS_REPL && saved_char_in &&
+ HTPassEightBitRaw &&
+ saved_char_in >=
+ LYlowest_eightbit[me->outUCLYhndl]) {
+ (HTChunkPutUtf8Char) (string,
+ (UCode_t) (0xf000 | saved_char_in));
+ } else {
+ HTChunkPutUtf8Char(string, clong);
+ }
+ } else if (saved_char_in && me->T.use_raw_char_in) {
+ HTChunkPutc(string, saved_char_in);
+ } else {
+ HTChunkPutc(string, c);
+ }
+ break;
+
+ case S_squoted: /* Quoted attribute value */
+ if (c == '\'') { /* End of attribute value */
+ HTChunkTerminate(string);
+#ifdef USE_PRETTYSRC
+ if (!end_if_prettysrc(me, string, '\''))
+#endif
+ handle_attribute_value(me, string->data);
+ string->size = 0;
+ me->state = S_tag_gap;
+ } else if (TOASCII(c) == '\033') { /* S/390 -- gil -- 1213 */
+ /*
+ * Setting up for possible single quotes in CJK escape sequences.
+ * - Takuya ASADA (asada@three-a.co.jp)
+ */
+ me->state = S_esc_sq;
+ if (!UTF8_TTY_ISO2022JP)
+ HTChunkPutc(string, c);
+ } else if (me->T.decode_utf8 &&
+ *me->U.utf_buf) {
+ HTChunkPuts(string, me->U.utf_buf);
+ me->U.utf_buf_p = me->U.utf_buf;
+ *(me->U.utf_buf_p) = '\0';
+ } else if (!IS_CJK_TTY &&
+ (me->T.output_utf8 ||
+ me->T.trans_from_uni)) {
+ if (clong == UCS_REPL && saved_char_in &&
+ HTPassEightBitRaw &&
+ saved_char_in >=
+ LYlowest_eightbit[me->outUCLYhndl]) {
+ (HTChunkPutUtf8Char) (string,
+ (UCode_t) (0xf000 | saved_char_in));
+ } else {
+ HTChunkPutUtf8Char(string, clong);
+ }
+ } else if (saved_char_in && me->T.use_raw_char_in) {
+ HTChunkPutc(string, saved_char_in);
+ } else {
+ HTChunkPutc(string, c);
+ }
+ break;
+
+ case S_dquoted: /* Quoted attribute value */
+ if (c == '"' || /* Valid end of attribute value */
+ (soft_dquotes && /* If emulating old Netscape bug, treat '>' */
+ c == '>')) { /* as a co-terminator of dquoted and tag */
+ HTChunkTerminate(string);
+#ifdef USE_PRETTYSRC
+ if (!end_if_prettysrc(me, string, (char) c))
+#endif
+ handle_attribute_value(me, string->data);
+ string->size = 0;
+ me->state = S_tag_gap;
+ if (c == '>') /* We emulated the Netscape bug, so we go */
+ goto top1; /* back and treat it as the tag terminator */
+ } else if (TOASCII(c) == '\033') { /* S/390 -- gil -- 1230 */
+ /*
+ * Setting up for possible double quotes in CJK escape sequences.
+ * - Takuya ASADA (asada@three-a.co.jp)
+ */
+ me->state = S_esc_dq;
+ if (!UTF8_TTY_ISO2022JP)
+ HTChunkPutc(string, c);
+ } else if (me->T.decode_utf8 &&
+ *me->U.utf_buf) {
+ HTChunkPuts(string, me->U.utf_buf);
+ me->U.utf_buf_p = me->U.utf_buf;
+ *(me->U.utf_buf_p) = '\0';
+ } else if (!IS_CJK_TTY &&
+ (me->T.output_utf8 ||
+ me->T.trans_from_uni)) {
+ if (clong == UCS_REPL && saved_char_in &&
+ HTPassEightBitRaw &&
+ saved_char_in >=
+ LYlowest_eightbit[me->outUCLYhndl]) {
+ (HTChunkPutUtf8Char) (string,
+ (UCode_t) (0xf000 | saved_char_in));
+ } else {
+ HTChunkPutUtf8Char(string, clong);
+ }
+ } else if (saved_char_in && me->T.use_raw_char_in) {
+ HTChunkPutc(string, saved_char_in);
+ } else {
+ HTChunkPutc(string, c);
+ }
+ break;
+
+ case S_end: /* </ */
+ if (TOASCII(clong) < 127 && (string->size ? /* S/390 -- gil -- 1247 */
+ IsNmChar(c) : IsNmStart(c))) {
+ HTChunkPutc(string, c);
+ } else { /* End of end tag name */
+ HTTag *t = 0;
+
+#ifdef USE_PRETTYSRC
+ BOOL psrc_tagname_processed = FALSE;
+#endif
+
+ HTChunkTerminate(string);
+ if (!*string->data) { /* Empty end tag */
+ if (me->element_stack)
+ t = me->element_stack->tag;
+ } else {
+ t = SGMLFindTag(dtd, string->data);
+ }
+ if (!t || t == me->unknown_tag) {
+ CTRACE((tfp, "Unknown end tag </%s>\n", string->data));
+#ifdef USE_PRETTYSRC
+ if (psrc_view) {
+ PSRCSTART(abracket);
+ PUTS("</");
+ PSRCSTOP(abracket);
+ PSRCSTART(badtag);
+ transform_tag(me, string);
+ PUTS(string->data);
+ if (c != '>') {
+ PUTC(c);
+ } else {
+ PSRCSTOP(badtag);
+ PSRCSTART(abracket);
+ PUTC('>');
+ PSRCSTOP(abracket);
+ }
+ psrc_tagname_processed = TRUE;
+ }
+ } else if (psrc_view) {
+#endif
+ } else {
+ BOOL tag_OK = (BOOL) (c == '>' || WHITE(c));
+ HTMLElement e = TAGNUM_OF_TAGP(t);
+ int branch = 2; /* it can be 0,1,2 */
+
+ me->current_tag = t;
+ if (HAS_ALT_TAGNUM(TAGNUM_OF_TAGP(t)) &&
+ me->element_stack &&
+ ALT_TAGP(t) == me->element_stack->tag)
+ me->element_stack->tag = NORMAL_TAGP(me->element_stack->tag);
+
+ if (tag_OK && Old_DTD) {
+ switch (e) {
+ case HTML_DD:
+ case HTML_DT:
+ case HTML_LI:
+ case HTML_LH:
+ case HTML_TD:
+ case HTML_TH:
+ case HTML_TR:
+ case HTML_THEAD:
+ case HTML_TFOOT:
+ case HTML_TBODY:
+ case HTML_COLGROUP:
+ branch = 0;
+ break;
+
+ case HTML_A:
+ case HTML_B:
+ case HTML_BLINK:
+ case HTML_CITE:
+ case HTML_EM:
+ case HTML_FONT:
+ case HTML_FORM:
+ case HTML_I:
+ case HTML_P:
+ case HTML_STRONG:
+ case HTML_TT:
+ case HTML_U:
+ branch = 1;
+ break;
+ default:
+ break;
+ }
+ }
+
+ /*
+ * Just handle ALL end tags normally :-) - kw
+ */
+ if (!Old_DTD) {
+ end_element(me, me->current_tag);
+ } else if (tag_OK && (branch == 0)) {
+ /*
+ * Don't treat these end tags as invalid, nor act on them.
+ * - FM
+ */
+ CTRACE((tfp, "SGML: `</%s%c' found! Ignoring it.\n",
+ string->data, c));
+ string->size = 0;
+ me->current_attribute_number = INVALID;
+ if (c != '>') {
+ me->state = S_junk_tag;
+ } else {
+ me->current_tag = NULL;
+ me->state = S_text;
+ }
+ break;
+ } else if (tag_OK && (branch == 1)) {
+ /*
+ * Handle end tags for container elements declared as
+ * SGML_EMPTY to prevent "expected tag substitution" but
+ * still processed via HTML_end_element() in HTML.c with
+ * checks there to avoid throwing the HTML.c stack out of
+ * whack (Ugh, what a hack! 8-). - FM
+ */
+ if (me->inSELECT) {
+ /*
+ * We are in a SELECT block. - FM
+ */
+ if (strcasecomp(string->data, "FORM")) {
+ /*
+ * It is not at FORM end tag, so ignore it. - FM
+ */
+ CTRACE((tfp,
+ "SGML: ***Ignoring end tag </%s> in SELECT block.\n",
+ string->data));
+ } else {
+ /*
+ * End the SELECT block and then handle the FORM
+ * end tag. - FM
+ */
+ CTRACE((tfp,
+ "SGML: ***Faking SELECT end tag before </%s> end tag.\n",
+ string->data));
+ end_element(me,
+ SGMLFindTag(me->dtd, "SELECT"));
+ CTRACE((tfp, "SGML: End </%s>\n", string->data));
+
+#ifdef USE_PRETTYSRC
+ if (!psrc_view) /* Don't actually call if viewing psrc - kw */
+#endif
+ (*me->actions->end_element)
+ (me->target,
+ (int) TAGNUM_OF_TAGP(me->current_tag),
+ &me->include);
+ }
+ } else if (!strcasecomp(string->data, "P")) {
+ /*
+ * Treat a P end tag like a P start tag (Ugh, what a
+ * hack! 8-). - FM
+ */
+ CTRACE((tfp,
+ "SGML: `</%s%c' found! Treating as '<%s%c'.\n",
+ string->data, c, string->data, c));
+ {
+ int i;
+
+ for (i = 0;
+ i < me->current_tag->number_of_attributes;
+ i++) {
+ me->present[i] = NO;
+ }
+ }
+ if (me->current_tag->name)
+ start_element(me);
+ } else {
+ CTRACE((tfp, "SGML: End </%s>\n", string->data));
+
+#ifdef USE_PRETTYSRC
+ if (!psrc_view) /* Don't actually call if viewing psrc - kw */
+#endif
+ (*me->actions->end_element)
+ (me->target,
+ (int) TAGNUM_OF_TAGP(me->current_tag),
+ &me->include);
+ }
+ string->size = 0;
+ me->current_attribute_number = INVALID;
+ if (c != '>') {
+ me->state = S_junk_tag;
+ } else {
+ me->current_tag = NULL;
+ me->state = S_text;
+ }
+ break;
+ } else {
+ /*
+ * Handle all other end tags normally. - FM
+ */
+ end_element(me, me->current_tag);
+ }
+ }
+
+#ifdef USE_PRETTYSRC
+ if (psrc_view && !psrc_tagname_processed) {
+ PSRCSTART(abracket);
+ PUTS("</");
+ PSRCSTOP(abracket);
+ PSRCSTART(tag);
+ if (tagname_transform != 1) {
+ if (tagname_transform == 0)
+ LYLowerCase(string->data);
+ else
+ LYUpperCase(string->data);
+ }
+ PUTS(string->data);
+ PSRCSTOP(tag);
+ if (c != '>') {
+ PSRCSTART(badtag);
+ PUTC(c);
+ } else {
+ PSRCSTART(abracket);
+ PUTC('>');
+ PSRCSTOP(abracket);
+ }
+ }
+#endif
+
+ string->size = 0;
+ me->current_attribute_number = INVALID;
+ if (c != '>') {
+ if (!WHITE(c))
+ CTRACE((tfp, "SGML: `</%s%c' found!\n", string->data, c));
+ me->state = S_junk_tag;
+ } else {
+ me->current_tag = NULL;
+ me->state = S_text;
+ }
+ }
+ break;
+
+ case S_esc: /* Expecting '$'or '(' following CJK ESC. */
+ if (c == '$') {
+ me->state = S_dollar;
+ } else if (c == '(') {
+ me->state = S_paren;
+ } else {
+ me->state = S_text;
+ if (UTF8_TTY_ISO2022JP)
+ goto top1;
+ }
+ if (!UTF8_TTY_ISO2022JP)
+ PUTC(c);
+ break;
+
+ case S_dollar: /* Expecting '@', 'B', 'A' or '(' after CJK "ESC$". */
+ if (c == '@' || c == 'B' || c == 'A') {
+ me->state = S_nonascii_text;
+ } else if (c == '(') {
+ me->state = S_dollar_paren;
+ }
+ if (!UTF8_TTY_ISO2022JP)
+ PUTC(c);
+ break;
+
+ case S_dollar_paren: /* Expecting 'C' after CJK "ESC$(". */
+ if (c == 'C') {
+ me->state = S_nonascii_text;
+ } else {
+ me->state = S_text;
+ if (UTF8_TTY_ISO2022JP) {
+ PUTS("$(");
+ goto top1;
+ }
+ }
+ if (!UTF8_TTY_ISO2022JP)
+ PUTC(c);
+ break;
+
+ case S_paren: /* Expecting 'B', 'J', 'T' or 'I' after CJK "ESC(". */
+ if (c == 'B' || c == 'J' || c == 'T') {
+ me->state = S_text;
+ } else if (c == 'I') {
+ me->state = S_nonascii_text;
+ if (UTF8_TTY_ISO2022JP)
+ me->kanji_buf = '\t'; /* flag for single byte katakana */
+ } else {
+ me->state = S_text;
+ if (UTF8_TTY_ISO2022JP) {
+ PUTC('(');
+ goto top1;
+ }
+ }
+ if (!UTF8_TTY_ISO2022JP)
+ PUTC(c);
+ break;
+
+ case S_nonascii_text: /* Expecting CJK ESC after non-ASCII text. */
+ if (TOASCII(c) == '\033') { /* S/390 -- gil -- 1264 */
+ me->state = S_esc;
+ } else if (c < 32) {
+ me->state = S_text;
+ }
+ if (UTF8_TTY_ISO2022JP) {
+ if (TOASCII(c) != '\033')
+ PUTUTF8(clong);
+ } else
+ PUTC(c);
+ break;
+
+ case S_esc_sq: /* Expecting '$'or '(' following CJK ESC. */
+ if (c == '$') {
+ me->state = S_dollar_sq;
+ } else if (c == '(') {
+ me->state = S_paren_sq;
+ } else {
+ me->state = S_squoted;
+ if (UTF8_TTY_ISO2022JP)
+ goto top1;
+ }
+ if (!UTF8_TTY_ISO2022JP)
+ HTChunkPutc(string, c);
+ break;
+
+ case S_dollar_sq: /* Expecting '@', 'B', 'A' or '(' after CJK "ESC$". */
+ if (c == '@' || c == 'B' || c == 'A') {
+ me->state = S_nonascii_text_sq;
+ } else if (c == '(') {
+ me->state = S_dollar_paren_sq;
+ }
+ if (!UTF8_TTY_ISO2022JP)
+ HTChunkPutc(string, c);
+ break;
+
+ case S_dollar_paren_sq: /* Expecting 'C' after CJK "ESC$(". */
+ if (c == 'C') {
+ me->state = S_nonascii_text_sq;
+ } else {
+ me->state = S_squoted;
+ if (UTF8_TTY_ISO2022JP) {
+ HTChunkPuts(string, "$(");
+ goto top1;
+ }
+ }
+ if (!UTF8_TTY_ISO2022JP)
+ HTChunkPutc(string, c);
+ break;
+
+ case S_paren_sq: /* Expecting 'B', 'J', 'T' or 'I' after CJK "ESC(". */
+ if (c == 'B' || c == 'J' || c == 'T') {
+ me->state = S_squoted;
+ } else if (c == 'I') {
+ me->state = S_nonascii_text_sq;
+ if (UTF8_TTY_ISO2022JP)
+ me->kanji_buf = '\t'; /* flag for single byte katakana */
+ } else {
+ me->state = S_squoted;
+ if (UTF8_TTY_ISO2022JP) {
+ HTChunkPutc(string, '(');
+ goto top1;
+ }
+ }
+ if (!UTF8_TTY_ISO2022JP)
+ HTChunkPutc(string, c);
+ break;
+
+ case S_nonascii_text_sq: /* Expecting CJK ESC after non-ASCII text. */
+ if (TOASCII(c) == '\033') { /* S/390 -- gil -- 1281 */
+ me->state = S_esc_sq;
+ }
+ if (UTF8_TTY_ISO2022JP) {
+ if (TOASCII(c) != '\033')
+ HTChunkPutUtf8Char(string, clong);
+ } else
+ HTChunkPutc(string, c);
+ break;
+
+ case S_esc_dq: /* Expecting '$'or '(' following CJK ESC. */
+ if (c == '$') {
+ me->state = S_dollar_dq;
+ } else if (c == '(') {
+ me->state = S_paren_dq;
+ } else {
+ me->state = S_dquoted;
+ if (UTF8_TTY_ISO2022JP)
+ goto top1;
+ }
+ if (!UTF8_TTY_ISO2022JP)
+ HTChunkPutc(string, c);
+ break;
+
+ case S_dollar_dq: /* Expecting '@', 'B', 'A' or '(' after CJK "ESC$". */
+ if (c == '@' || c == 'B' || c == 'A') {
+ me->state = S_nonascii_text_dq;
+ } else if (c == '(') {
+ me->state = S_dollar_paren_dq;
+ }
+ if (!UTF8_TTY_ISO2022JP)
+ HTChunkPutc(string, c);
+ break;
+
+ case S_dollar_paren_dq: /* Expecting 'C' after CJK "ESC$(". */
+ if (c == 'C') {
+ me->state = S_nonascii_text_dq;
+ } else {
+ me->state = S_dquoted;
+ if (UTF8_TTY_ISO2022JP) {
+ HTChunkPuts(string, "$(");
+ goto top1;
+ }
+ }
+ if (!UTF8_TTY_ISO2022JP)
+ HTChunkPutc(string, c);
+ break;
+
+ case S_paren_dq: /* Expecting 'B', 'J', 'T' or 'I' after CJK "ESC(". */
+ if (c == 'B' || c == 'J' || c == 'T') {
+ me->state = S_dquoted;
+ } else if (c == 'I') {
+ me->state = S_nonascii_text_dq;
+ if (UTF8_TTY_ISO2022JP)
+ me->kanji_buf = '\t'; /* flag for single byte katakana */
+ } else {
+ me->state = S_dquoted;
+ if (UTF8_TTY_ISO2022JP) {
+ HTChunkPutc(string, '(');
+ goto top1;
+ }
+ }
+ if (!UTF8_TTY_ISO2022JP)
+ HTChunkPutc(string, c);
+ break;
+
+ case S_nonascii_text_dq: /* Expecting CJK ESC after non-ASCII text. */
+ if (TOASCII(c) == '\033') { /* S/390 -- gil -- 1298 */
+ me->state = S_esc_dq;
+ }
+ if (UTF8_TTY_ISO2022JP) {
+ if (TOASCII(c) != '\033')
+ HTChunkPutUtf8Char(string, clong);
+ } else
+ HTChunkPutc(string, c);
+ break;
+
+ case S_junk_tag:
+ case S_pi:
+ if (c == '>') {
+ HTChunkTerminate(string);
+#ifdef USE_PRETTYSRC
+ if (psrc_view) {
+ if (me->state == S_junk_tag) {
+ PSRCSTOP(badtag);
+ }
+ PSRCSTART(abracket);
+ PUTC('>');
+ PSRCSTOP(abracket);
+ }
+#endif
+ if (me->state == S_pi)
+ handle_processing_instruction(me);
+ string->size = 0;
+ me->current_tag = NULL;
+ me->state = S_text;
+ } else {
+ HTChunkPutc(string, c);
+#ifdef USE_PRETTYSRC
+ if (psrc_view) {
+ PUTC(c);
+ }
+#endif
+ }
+
+ } /* switch on me->state */
+ CTRACE2(TRACE_SGML, (tfp, "SGML after %s|%.*s|%c|\n",
+ state_name(me->state),
+ string->size,
+ NonNull(string->data),
+ UCH(c)));
+
+ after_switch:
+ /*
+ * Check whether an external function has added anything to the include
+ * buffer. If so, move the new stuff to the beginning of active_include.
+ * - kw
+ */
+ if (me->include != NULL) {
+ if (me->include[0] == '\0') {
+ FREE(me->include);
+ } else {
+ if (me->active_include &&
+ me->active_include[me->include_index] != '\0')
+ StrAllocCat(me->include,
+ me->active_include + me->include_index);
+ FREE(me->active_include);
+ me->active_include = me->include;
+ me->include_index = 0;
+ me->include = NULL;
+ }
+ }
+
+ /*
+ * Check whether we've added anything to the recover buffer. - FM
+ */
+ if (me->recover != NULL) {
+ if (me->recover[me->recover_index] == '\0') {
+ FREE(me->recover);
+ me->recover_index = 0;
+ } else {
+ c = UCH(me->recover[me->recover_index]);
+ me->recover_index++;
+ goto top;
+ }
+ }
+
+ /*
+ * Check whether an external function had added anything to the include
+ * buffer; it should now be in active_include. - FM / kw
+ */
+ if (me->active_include != NULL) {
+ if (me->active_include[me->include_index] == '\0') {
+ FREE(me->active_include);
+ me->include_index = 0;
+ } else {
+ if (me->current_tag_charset == UTF8_handle ||
+ me->T.trans_from_uni) {
+ /*
+ * If it looks like we would have fed UTF-8 to the next
+ * processing stage, assume that whatever we were fed back is
+ * in UTF-8 form, too. This won't be always true for all uses
+ * of the include buffer, but it's a start. - kw
+ */
+ const char *puni = me->active_include + me->include_index;
+
+ c = UCH(*puni);
+ clong = UCGetUniFromUtf8String(&puni);
+ if (clong < 256 && clong >= 0) {
+ c = UCH((clong & 0xff));
+ }
+ saved_char_in = '\0';
+ me->include_index = (int) (puni
+ - me->active_include
+ + 1);
+ goto top1;
+ } else {
+ /*
+ * Otherwise assume no UTF-8 - do charset-naive processing and
+ * hope for the best. - kw
+ */
+ c = UCH(me->active_include[me->include_index]);
+ me->include_index++;
+ goto top;
+ }
+ }
+ }
+
+ /*
+ * Check whether an external function has added anything to the csi buffer.
+ * - FM
+ */
+ if (me->csi != NULL) {
+ if (me->csi[me->csi_index] == '\0') {
+ FREE(me->csi);
+ me->csi_index = 0;
+ } else {
+ c = UCH(me->csi[me->csi_index]);
+ me->csi_index++;
+ goto top;
+ }
+ }
+} /* SGML_character */
+
+static void InferUtfFromBom(HTStream *me, int chndl)
+{
+ HTAnchor_setUCInfoStage(me->node_anchor, chndl,
+ UCT_STAGE_PARSER,
+ UCT_SETBY_PARSER);
+ change_chartrans_handling(me);
+}
+
+/*
+ * Avoid rewrite of SGML_character() to handle hypothetical case of UTF-16
+ * webpages, by pretending that the data is UTF-8.
+ */
+static void SGML_widechar(HTStream *me, int ch)
+{
+ if (!UCPutUtf8_charstring(me, SGML_character, (UCode_t) ch)) {
+ SGML_character(me, ch);
+ }
+}
+
+static void SGML_write(HTStream *me, const char *str, int l)
+{
+ const char *p;
+ const char *e = str + l;
+
+ if (sgml_offset == 0) {
+ if (l > 3
+ && !MemCmp(str, "\357\273\277", 3)) {
+ CTRACE((tfp, "SGML_write found UTF-8 BOM\n"));
+ InferUtfFromBom(me, UTF8_handle);
+ str += 3;
+ } else if (l > 2) {
+ if (!MemCmp(str, "\377\376", 2)) {
+ CTRACE((tfp, "SGML_write found UCS-2 LE BOM\n"));
+ InferUtfFromBom(me, UTF8_handle);
+ str += 2;
+ me->T.ucs_mode = -1;
+ } else if (!MemCmp(str, "\376\377", 2)) {
+ CTRACE((tfp, "SGML_write found UCS-2 BE BOM\n"));
+ InferUtfFromBom(me, UTF8_handle);
+ str += 2;
+ me->T.ucs_mode = 1;
+ }
+ }
+ }
+ switch (me->T.ucs_mode) {
+ case -1:
+ for (p = str; p < e; p += 2)
+ SGML_widechar(me, (UCH(p[1]) << 8) | UCH(p[0]));
+ break;
+ case 1:
+ for (p = str; p < e; p += 2)
+ SGML_widechar(me, (UCH(p[0]) << 8) | UCH(p[1]));
+ break;
+ default:
+ for (p = str; p < e; p++)
+ SGML_character(me, *p);
+ break;
+ }
+}
+
+static void SGML_string(HTStream *me, const char *str)
+{
+ SGML_write(me, str, (int) strlen(str));
+}
+
+/*_______________________________________________________________________
+*/
+
+/* Structured Object Class
+ * -----------------------
+ */
+const HTStreamClass SGMLParser =
+{
+ "SGMLParser",
+ SGML_free,
+ SGML_abort,
+ SGML_character,
+ SGML_string,
+ SGML_write,
+};
+
+/* Create SGML Engine
+ * ------------------
+ *
+ * On entry,
+ * dtd represents the DTD, along with
+ * actions is the sink for the data as a set of routines.
+ *
+ */
+
+HTStream *SGML_new(const SGML_dtd * dtd,
+ HTParentAnchor *anchor,
+ HTStructured * target,
+ int extended_html)
+{
+ HTStream *me = typecalloc(struct _HTStream);
+
+ if (!me)
+ outofmem(__FILE__, "SGML_begin");
+
+ me->isa = &SGMLParser;
+ me->string = HTChunkCreate(128); /* Grow by this much */
+ me->dtd = dtd;
+ me->target = target;
+ me->actions = (const HTStructuredClass *) (((HTStream *) target)->isa);
+ /* Ugh: no OO */
+ me->unknown_tag = &HTTag_unrecognized;
+ me->current_tag = me->slashedtag = NULL;
+ me->state = S_text;
+#ifdef CALLERDATA
+ me->callerData = (void *) callerData;
+#endif /* CALLERDATA */
+
+ me->node_anchor = anchor; /* Could be NULL? */
+ me->U.utf_buf_p = me->U.utf_buf;
+ UCTransParams_clear(&me->T);
+ me->inUCLYhndl = HTAnchor_getUCLYhndl(anchor,
+ UCT_STAGE_PARSER);
+ if (me->inUCLYhndl < 0) {
+ HTAnchor_copyUCInfoStage(anchor,
+ UCT_STAGE_PARSER,
+ UCT_STAGE_MIME,
+ -1);
+ me->inUCLYhndl = HTAnchor_getUCLYhndl(anchor,
+ UCT_STAGE_PARSER);
+ }
+#ifdef CAN_SWITCH_DISPLAY_CHARSET /* Allow a switch to a more suitable display charset */
+ else if (anchor->UCStages
+ && anchor->UCStages->s[UCT_STAGE_PARSER].LYhndl >= 0
+ && anchor->UCStages->s[UCT_STAGE_PARSER].LYhndl != current_char_set) {
+ int o = anchor->UCStages->s[UCT_STAGE_PARSER].LYhndl;
+
+ anchor->UCStages->s[UCT_STAGE_PARSER].LYhndl = -1; /* Force reset */
+ HTAnchor_resetUCInfoStage(anchor, o, UCT_STAGE_PARSER,
+ /* Preserve change this: */
+ anchor->UCStages->s[UCT_STAGE_PARSER].lock);
+ }
+#endif
+
+ me->inUCI = HTAnchor_getUCInfoStage(anchor,
+ UCT_STAGE_PARSER);
+ set_chartrans_handling(me, anchor, -1);
+
+ me->recover = NULL;
+ me->recover_index = 0;
+ me->include = NULL;
+ me->active_include = NULL;
+ me->include_index = 0;
+ me->url = NULL;
+ me->csi = NULL;
+ me->csi_index = 0;
+
+#ifdef USE_PRETTYSRC
+ if (psrc_view) {
+ psrc_view = FALSE;
+ mark_htext_as_source = TRUE;
+ SGML_string(me,
+ "<HTML><HEAD><TITLE>source</TITLE></HEAD><BODY><PRE>");
+ psrc_view = TRUE;
+ psrc_convert_string = FALSE;
+ sgml_in_psrc_was_initialized = TRUE;
+ }
+#endif
+ if (extended_html)
+ {
+ me->extended_html = TRUE;
+ }
+
+ sgml_offset = 0;
+ return me;
+}
+
+/*
+ * Return the offset within the document where we're parsing. This is used
+ * to help identify anchors which shift around while reparsing.
+ */
+int SGML_offset(void)
+{
+ int result = sgml_offset;
+
+#ifdef USE_PRETTYSRC
+ result += psrc_view;
+#endif
+ return result;
+}
+
+/* Asian character conversion functions
+ * ====================================
+ *
+ * Added 24-Mar-96 by FM, based on:
+ *
+ ////////////////////////////////////////////////////////////////////////
+Copyright (c) 1993 Electrotechnical Laboratory (ETL)
+
+Permission to use, copy, modify, and distribute this material
+for any purpose and without fee is hereby granted, provided
+that the above copyright notice and this permission notice
+appear in all copies, and that the name of ETL not be
+used in advertising or publicity pertaining to this
+material without the specific, prior written permission
+of an authorized representative of ETL.
+ETL MAKES NO REPRESENTATIONS ABOUT THE ACCURACY OR SUITABILITY
+OF THIS MATERIAL FOR ANY PURPOSE. IT IS PROVIDED "AS IS",
+WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES.
+/////////////////////////////////////////////////////////////////////////
+Content-Type: program/C; charset=US-ASCII
+Program: SJIS.c
+Author: Yutaka Sato <ysato@etl.go.jp>
+Description:
+History:
+ 930923 extracted from codeconv.c of cosmos
+///////////////////////////////////////////////////////////////////////
+*/
+
+static int TREAT_SJIS = 1;
+
+void JISx0201TO0208_EUC(unsigned IHI,
+ unsigned ILO,
+ unsigned char *OHI,
+ unsigned char *OLO)
+{
+ static const char *table[] =
+ {
+ "\241\243", /* A1,A3 */
+ "\241\326", /* A1,D6 */
+ "\241\327", /* A1,D7 */
+ "\241\242", /* A1,A2 */
+ "\241\246", /* A1,A6 */
+ "\245\362", /* A5,F2 */
+ "\245\241", /* A5,A1 */
+ "\245\243", /* A5,A3 */
+ "\245\245", /* A5,A5 */
+ "\245\247", /* A5,A7 */
+ "\245\251", /* A5,A9 */
+ "\245\343", /* A5,E3 */
+ "\245\345", /* A5,E5 */
+ "\245\347", /* A5,E7 */
+ "\245\303", /* A5,C3 */
+ "\241\274", /* A1,BC */
+ "\245\242", /* A5,A2 */
+ "\245\244", /* A5,A4 */
+ "\245\246", /* A5,A6 */
+ "\245\250", /* A5,A8 */
+ "\245\252", /* A5,AA */
+ "\245\253", /* A5,AB */
+ "\245\255", /* A5,AD */
+ "\245\257", /* A5,AF */
+ "\245\261", /* A5,B1 */
+ "\245\263", /* A5,B3 */
+ "\245\265", /* A5,B5 */
+ "\245\267", /* A5,B7 */
+ "\245\271", /* A5,B9 */
+ "\245\273", /* A5,BB */
+ "\245\275", /* A5,BD */
+ "\245\277", /* A5,BF */
+ "\245\301", /* A5,C1 */
+ "\245\304", /* A5,C4 */
+ "\245\306", /* A5,C6 */
+ "\245\310", /* A5,C8 */
+ "\245\312", /* A5,CA */
+ "\245\313", /* A5,CB */
+ "\245\314", /* A5,CC */
+ "\245\315", /* A5,CD */
+ "\245\316", /* A5,CE */
+ "\245\317", /* A5,CF */
+ "\245\322", /* A5,D2 */
+ "\245\325", /* A5,D5 */
+ "\245\330", /* A5,D8 */
+ "\245\333", /* A5,DB */
+ "\245\336", /* A5,DE */
+ "\245\337", /* A5,DF */
+ "\245\340", /* A5,E0 */
+ "\245\341", /* A5,E1 */
+ "\245\342", /* A5,E2 */
+ "\245\344", /* A5,E4 */
+ "\245\346", /* A5,E6 */
+ "\245\350", /* A5,E8 */
+ "\245\351", /* A5,E9 */
+ "\245\352", /* A5,EA */
+ "\245\353", /* A5,EB */
+ "\245\354", /* A5,EC */
+ "\245\355", /* A5,ED */
+ "\245\357", /* A5,EF */
+ "\245\363", /* A5,F3 */
+ "\241\253", /* A1,AB */
+ "\241\254" /* A1,AC */
+ };
+
+ if ((IHI == 0x8E) && (ILO >= 0xA1) && (ILO <= 0xDF)) {
+ *OHI = UCH(table[ILO - 0xA1][0]);
+ *OLO = UCH(table[ILO - 0xA1][1]);
+ } else {
+ *OHI = UCH(IHI);
+ *OLO = UCH(ILO);
+ }
+}
+
+static int IS_SJIS_STR(const unsigned char *str)
+{
+ const unsigned char *s;
+ unsigned char ch;
+ int is_sjis = 0;
+
+ s = str;
+ while ((ch = *s++) != '\0') {
+ if (ch & 0x80)
+ if (IS_SJIS(ch, *s, is_sjis))
+ return 1;
+ }
+ return 0;
+}
+
+unsigned char *SJIS_TO_JIS1(unsigned HI,
+ unsigned LO,
+ unsigned char *JCODE)
+{
+ HI = UCH(HI - (unsigned) UCH((HI <= 0x9F) ? 0x71 : 0xB1));
+ HI = UCH((HI << 1) + 1);
+ if (0x7F < LO)
+ LO--;
+ if (0x9E <= LO) {
+ LO = UCH(LO - UCH(0x7D));
+ HI++;
+ } else {
+ LO = UCH(LO - UCH(0x1F));
+ }
+ JCODE[0] = UCH(HI);
+ JCODE[1] = UCH(LO);
+ return JCODE;
+}
+
+unsigned char *JIS_TO_SJIS1(unsigned HI,
+ unsigned LO,
+ unsigned char *SJCODE)
+{
+ if (HI & 1)
+ LO = UCH(LO + UCH(0x1F));
+ else
+ LO = UCH(LO + UCH(0x7D));
+ if (0x7F <= LO)
+ LO++;
+
+ HI = UCH(((HI - 0x21) >> 1) + 0x81);
+ if (0x9F < HI)
+ HI = UCH(HI + UCH(0x40));
+ SJCODE[0] = UCH(HI);
+ SJCODE[1] = UCH(LO);
+ return SJCODE;
+}
+
+unsigned char *EUC_TO_SJIS1(unsigned HI,
+ unsigned LO,
+ unsigned char *SJCODE)
+{
+ unsigned char HI_data[2];
+ unsigned char LO_data[2];
+
+ HI_data[0] = UCH(HI);
+ LO_data[0] = UCH(LO);
+ if (HI == 0x8E) {
+ JISx0201TO0208_EUC(HI, LO, HI_data, LO_data);
+ }
+ JIS_TO_SJIS1(UCH(HI_data[0] & 0x7F), UCH(LO_data[0] & 0x7F), SJCODE);
+ return SJCODE;
+}
+
+void JISx0201TO0208_SJIS(unsigned I,
+ unsigned char *OHI,
+ unsigned char *OLO)
+{
+ unsigned char SJCODE[2];
+
+ JISx0201TO0208_EUC(0x8E, I, OHI, OLO);
+ JIS_TO_SJIS1(UCH(*OHI & 0x7F), UCH(*OLO & 0x7F), SJCODE);
+ *OHI = SJCODE[0];
+ *OLO = SJCODE[1];
+}
+
+unsigned char *SJIS_TO_EUC1(unsigned HI,
+ unsigned LO,
+ unsigned char *data)
+{
+ SJIS_TO_JIS1(HI, LO, data);
+ data[0] |= 0x80;
+ data[1] |= 0x80;
+ return data;
+}
+
+unsigned char *SJIS_TO_EUC(unsigned char *src,
+ unsigned char *dst)
+{
+ unsigned char hi, lo, *sp, *dp;
+ int in_sjis = 0;
+
+ in_sjis = IS_SJIS_STR(src);
+ for (sp = src, dp = dst; (hi = sp[0]) != '\0';) {
+ lo = sp[1];
+ if (TREAT_SJIS && IS_SJIS(hi, lo, in_sjis)) {
+ SJIS_TO_JIS1(hi, lo, dp);
+ dp[0] |= 0x80;
+ dp[1] |= 0x80;
+ dp += 2;
+ sp += 2;
+ } else
+ *dp++ = *sp++;
+ }
+ *dp = 0;
+ return dst;
+}
+
+unsigned char *EUC_TO_SJIS(unsigned char *src,
+ unsigned char *dst)
+{
+ unsigned char *sp, *dp;
+
+ for (sp = src, dp = dst; *sp;) {
+ if (*sp & 0x80) {
+ if (sp[1] && (sp[1] & 0x80)) {
+ JIS_TO_SJIS1(UCH(sp[0] & 0x7F), UCH(sp[1] & 0x7F), dp);
+ dp += 2;
+ sp += 2;
+ } else {
+ sp++;
+ }
+ } else {
+ *dp++ = *sp++;
+ }
+ }
+ *dp = 0;
+ return dst;
+}
+
+#define Strcpy(a,b) (strcpy((char*)a,(const char*)b),&a[strlen((const char*)a)])
+
+unsigned char *EUC_TO_JIS(unsigned char *src,
+ unsigned char *dst,
+ const char *toK,
+ const char *toA)
+{
+ unsigned char kana_mode = 0;
+ unsigned char cch;
+ unsigned char *sp = src;
+ unsigned char *dp = dst;
+ int is_JIS = 0;
+
+ while ((cch = *sp++) != '\0') {
+ if (cch & 0x80) {
+ if (!IS_EUC(cch, *sp)) {
+ if (cch == 0xA0 && is_JIS) /* ignore NBSP */
+ continue;
+ is_JIS++;
+ *dp++ = cch;
+ continue;
+ }
+ if (!kana_mode) {
+ kana_mode = UCH(~kana_mode);
+ dp = Strcpy(dp, toK);
+ }
+ if (*sp & 0x80) {
+ *dp++ = UCH(cch & ~0x80);
+ *dp++ = UCH(*sp++ & ~0x80);
+ }
+ } else {
+ if (kana_mode) {
+ kana_mode = UCH(~kana_mode);
+ dp = Strcpy(dp, toA);
+ }
+ *dp++ = cch;
+ }
+ }
+ if (kana_mode)
+ dp = Strcpy(dp, toA);
+
+ if (dp)
+ *dp = 0;
+ return dst;
+}
+
+#define IS_JIS7(c1,c2) (0x20<(c1)&&(c1)<0x7F && 0x20<(c2)&&(c2)<0x7F)
+#define SO ('N'-0x40)
+#define SI ('O'-0x40)
+
+static int repair_JIS = 0;
+
+static const unsigned char *repairJIStoEUC(const unsigned char *src,
+ unsigned char **dstp)
+{
+ const unsigned char *s;
+ unsigned char *d, ch1, ch2;
+
+ d = *dstp;
+ s = src;
+ while ((ch1 = s[0]) && (ch2 = s[1])) {
+ s += 2;
+ if (ch1 == '(')
+ if (ch2 == 'B' || ch2 == 'J') {
+ *dstp = d;
+ return s;
+ }
+ if (!IS_JIS7(ch1, ch2))
+ return 0;
+
+ *d++ = UCH(0x80 | ch1);
+ *d++ = UCH(0x80 | ch2);
+ }
+ return 0;
+}
+
+unsigned char *TO_EUC(const unsigned char *jis,
+ unsigned char *euc)
+{
+ const unsigned char *s;
+ unsigned char c, jis_stat;
+ unsigned char *d;
+ int to1B, to2B;
+ int in_sjis = 0;
+ static int nje;
+ int n8bits;
+ int is_JIS;
+
+ nje++;
+ n8bits = 0;
+ s = jis;
+ d = euc;
+ jis_stat = 0;
+ to2B = TO_2BCODE;
+ to1B = TO_1BCODE;
+ in_sjis = IS_SJIS_STR(jis);
+ is_JIS = 0;
+
+ while ((c = *s++) != '\0') {
+ if (c == 0x80)
+ continue; /* ignore it */
+ if (c == 0xA0 && is_JIS)
+ continue; /* ignore Non-breaking space */
+
+ if (c == to2B && jis_stat == 0 && repair_JIS) {
+ if (*s == 'B' || *s == '@') {
+ const unsigned char *ts;
+
+ if ((ts = repairJIStoEUC(s + 1, &d)) != NULL) {
+ s = ts;
+ continue;
+ }
+ }
+ }
+ if (c == CH_ESC) {
+ if (*s == to2B) {
+ if ((s[1] == 'B') || (s[1] == '@')) {
+ jis_stat = 0x80;
+ s += 2;
+ is_JIS++;
+ continue;
+ }
+ jis_stat = 0;
+ } else if (*s == to1B) {
+ jis_stat = 0;
+ if ((s[1] == 'B') || (s[1] == 'J') || (s[1] == 'H')) {
+ s += 2;
+ continue;
+ }
+ } else if (*s == ',') { /* MULE */
+ jis_stat = 0;
+ }
+ }
+ if (c & 0x80)
+ n8bits++;
+
+ if (IS_SJIS(c, *s, in_sjis)) {
+ SJIS_TO_EUC1(c, *s, d);
+ d += 2;
+ s++;
+ is_JIS++;
+ } else if (jis_stat) {
+ if (c <= 0x20 || 0x7F <= c) {
+ *d++ = c;
+ if (c == '\n')
+ jis_stat = 0;
+ } else {
+ if (IS_JIS7(c, *s)) {
+ *d++ = jis_stat | c;
+ *d++ = jis_stat | *s++;
+ } else
+ *d++ = c;
+ }
+ } else {
+ if (n8bits == 0 && (c == SI || c == SO)) {
+ } else {
+ *d++ = c;
+ }
+ }
+ }
+ *d = 0;
+ return euc;
+}
+
+#define non94(ch) ((ch) <= 0x20 || (ch) == 0x7F)
+
+static int is_EUC_JP(unsigned char *euc)
+{
+ unsigned char *cp;
+ int ch1, ch2;
+
+ for (cp = euc; (ch1 = *cp) != '\0'; cp++) {
+ if (ch1 & 0x80) {
+ ch2 = cp[1] & 0xFF;
+ if ((ch2 & 0x80) == 0) {
+ /* sv1log("NOT_EUC1[%x][%x]\n",ch1,ch2); */
+ return 0;
+ }
+ if (non94(ch1 & 0x7F) || non94(ch2 & 0x7F)) {
+ /* sv1log("NOT_EUC2[%x][%x]\n",ch1,ch2); */
+ return 0;
+ }
+ cp++;
+ }
+ }
+ return 1;
+}
+
+void TO_SJIS(const unsigned char *arg,
+ unsigned char *sjis)
+{
+ unsigned char *euc;
+
+ euc = typeMallocn(unsigned char, strlen((const char *) arg) + 1);
+
+#ifdef CJK_EX
+ if (!euc)
+ outofmem(__FILE__, "TO_SJIS");
+#endif
+ TO_EUC(arg, euc);
+ if (is_EUC_JP(euc))
+ EUC_TO_SJIS(euc, sjis);
+ else
+ strcpy((char *) sjis, (const char *) arg);
+ free(euc);
+}
+
+void TO_JIS(const unsigned char *arg,
+ unsigned char *jis)
+{
+ unsigned char *euc;
+
+ if (arg[0] == 0) {
+ jis[0] = 0;
+ return;
+ }
+ euc = typeMallocn(unsigned char, strlen((const char *)arg) + 1);
+#ifdef CJK_EX
+ if (!euc)
+ outofmem(__FILE__, "TO_JIS");
+#endif
+ TO_EUC(arg, euc);
+ EUC_TO_JIS(euc, jis, TO_KANJI, TO_ASCII);
+
+ free(euc);
+}
diff --git a/WWW/Library/Implementation/SGML.h b/WWW/Library/Implementation/SGML.h
new file mode 100644
index 0000000..4525fda
--- /dev/null
+++ b/WWW/Library/Implementation/SGML.h
@@ -0,0 +1,287 @@
+/*
+ * $LynxId: SGML.h,v 1.50 2023/01/05 22:23:44 tom Exp $
+ * SGML parse and stream definition for libwww
+ * SGML AND STRUCTURED STREAMS
+ *
+ * The SGML parser is a state machine. It is called for every character
+ * of the input stream. The DTD data structure contains pointers
+ * to functions which are called to implement the actual effect of the
+ * text read. When these functions are called, the attribute structures pointed to by the
+ * DTD are valid, and the function is passed a pointer to the current tag structure, and an
+ * "element stack" which represents the state of nesting within SGML elements.
+ *
+ * The following aspects are from Dan Connolly's suggestions: Binary search,
+ * Structured object scheme basically, SGML content enum type.
+ *
+ * (c) Copyright CERN 1991 - See Copyright.html
+ *
+ */
+#ifndef SGML_H
+#define SGML_H
+
+#include <HTStream.h>
+#include <HTAnchor.h>
+#include <LYJustify.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+ *
+ * SGML content types
+ *
+ */ typedef enum {
+ SGML_EMPTY, /* No content. */
+ SGML_LITTERAL, /* Literal character data. Recognize exact close tag only.
+ Old www server compatibility only! Not SGML */
+ SGML_CDATA, /* Character data. Recognize </ only.
+ (But we treat it just as SGML_LITTERAL.) */
+ SGML_SCRIPT, /* Like CDATA, but allow it to be a comment */
+ SGML_RCDATA, /* Replaceable character data. Should recognize </ and &ref;
+ (but we treat it like SGML_MIXED for old times' sake). */
+ SGML_MIXED, /* Elements and parsed character data.
+ Recognize all markup. */
+ SGML_ELEMENT, /* Any data found should be regarded as an error.
+ (But we treat it just like SGML_MIXED.) */
+ SGML_PCDATA /* Should contain no elements but &ref; is parsed.
+ (We treat it like SGML_CDATA wrt. contained tags
+ i.e. pass them on literally, i.e. like we should
+ treat SGML_RCDATA) (added by KW). */
+ } SGMLContent;
+
+ typedef struct {
+ const char *name; /* The name of the attribute */
+#ifdef USE_PRETTYSRC
+ char type; /* code of the type of the attribute. Code
+ values are in HTMLDTD.h */
+#endif
+ } attr;
+
+ typedef const attr *AttrList;
+
+ typedef struct {
+ const char *name;
+ AttrList list;
+ } AttrType;
+
+ typedef int TagClass;
+
+ /* textflow */
+#define Tgc_FONTlike 0x00001 /* S,STRIKE,I,B,TT,U,BIG,SMALL,STYLE,BLINK;BR,TAB */
+#define Tgc_EMlike 0x00002 /* EM,STRONG,DFN,CODE,SAMP,KBD,VAR,CITE,Q,INS,DEL,SPAN,.. */
+#define Tgc_MATHlike 0x00004 /* SUB,SUP,MATH,COMMENT */
+#define Tgc_Alike 0x00008 /* A */
+#define Tgc_formula 0x00010 /* not used until math is supported better... */
+ /* used for special structures: forms, tables,... */
+#define Tgc_TRlike 0x00020 /* TR and similar */
+#define Tgc_SELECTlike 0x00040 /* SELECT,INPUT,TEXTAREA(,...) */
+ /* structure */
+#define Tgc_FORMlike 0x00080 /* FORM itself */
+#define Tgc_Plike 0x00100 /* P,H1..H6,... structures containing text or
+ insertion but not other structures */
+#define Tgc_DIVlike 0x00200 /* ADDRESS,FIG,BDO,NOTE,FN,DIV,CENTER;FIG
+ structures which can contain other structures */
+#define Tgc_LIlike 0x00400 /* LH,LI,DT,DD;TH,TD structure-like, only valid
+ within certain other structures */
+#define Tgc_ULlike 0x00800 /* UL,OL,DL,DIR,MENU;TABLE;XMP,LISTING
+ special in some way, cannot contain (parsed)
+ text directly */
+ /* insertions */
+#define Tgc_BRlike 0x01000 /* BR,IMG,TAB allowed in any text */
+#define Tgc_APPLETlike 0x02000 /* APPLET,OBJECT,EMBED,SCRIPT;BUTTON */
+#define Tgc_HRlike 0x04000 /* HR,MARQUEE can contain all kinds of things
+ and/or are not allowed (?) in running text */
+#define Tgc_MAPlike 0x08000 /* MAP,AREA some specials that never contain
+ (directly or indirectly) other things than
+ special insertions */
+#define Tgc_outer 0x10000 /* HTML,FRAMESET,FRAME,PLAINTEXT; */
+#define Tgc_BODYlike 0x20000 /* BODY,BODYTEXT,NOFRAMES,TEXTFLOW; */
+#define Tgc_HEADstuff 0x40000 /* HEAD,BASE,STYLE,TITLE; */
+ /* special relations */
+#define Tgc_same 0x80000
+#define Tgc_DELlike 0x100000
+ /* DELlike is a class of aliases for inline DEL/INS */
+ typedef unsigned char TagAlias;
+
+/*
+ * Groups for contains-data.
+ */
+#define Tgc_INLINElike (Tgc_Alike | Tgc_APPLETlike | Tgc_BRlike | Tgc_EMlike | Tgc_FONTlike | Tgc_SELECTlike)
+#define Tgc_LISTlike (Tgc_LIlike | Tgc_ULlike)
+#define Tgc_BLOCKlike (Tgc_DIVlike | Tgc_LISTlike)
+
+/* Some more properties of tags (or rather, elements) and rules how
+ to deal with them. - kw */
+ typedef int TagFlags;
+
+#define Tgf_endO 0x00001 /* end tag can be Omitted */
+#define Tgf_startO 0x00002 /* start tag can be Omitted */
+#define Tgf_mafse 0x00004 /* Make Attribute-Free Start-tag End instead
+ (if found invalid) */
+#define Tgf_strict 0x00008 /* Ignore contained invalid elements,
+ don't pass them on; or other variant
+ handling for some content types */
+#define Tgf_nreie 0x00010 /* Not Really Empty If Empty,
+ used by color style code */
+#define Tgf_frecyc 0x00020 /* Pass element content on in a form that
+ allows recycling, i.e. don't translate to
+ output (display) character set yet (treat
+ content similar to attribute values) */
+#define Tgf_nolyspcl 0x00040 /* Don't generate lynx special characters
+ for soft hyphen and various spaces (nbsp,
+ ensp,..) */
+
+/* A tag structure describes an SGML element.
+ * -----------------------------------------
+ *
+ *
+ * name is the string which comes after the tag opener "<".
+ *
+ * attributes points to a zero-terminated array
+ * of attribute names.
+ */
+ typedef struct _tag HTTag;
+ struct _tag {
+ const char *name; /* The name of the tag */
+#ifdef USE_COLOR_STYLE
+ unsigned name_len; /* The length of the name */
+#endif
+#ifdef USE_JUSTIFY_ELTS
+ BOOL can_justify; /* justification allowed? */
+#endif
+ AttrList attributes; /* The list of acceptable attributes */
+ int number_of_attributes; /* Number of possible attributes */
+ const AttrType *attr_types;
+ SGMLContent contents; /* End only on end tag @@ */
+ TagClass tagclass;
+ TagClass contains; /* which classes of elements this one can contain directly */
+ TagClass icontains; /* which classes of elements this one can contain indirectly */
+ TagClass contained; /* in which classes can this tag be contained ? */
+ TagClass icontained; /* in which classes can this tag be indirectly contained ? */
+ TagClass canclose; /* which classes of elements can this one close
+ if something looks wrong ? */
+ TagFlags flags;
+ TagAlias alias; /* extra levels, e.g, DEL/INS */
+ TagAlias aliases; /* number of extra levels, e.g, DEL/INS */
+ };
+
+/* DTD Information
+ * ---------------
+ *
+ * Not the whole DTD, but all this parser uses of it.
+ */
+ typedef struct {
+ HTTag *tags; /* Must be in strcmp order by name */
+ int number_of_tags;
+ STRING2PTR entity_names; /* Must be in strcmp order by name */
+ size_t number_of_entities;
+ /* "entity_names" table probably unused,
+ * see comments in HTMLDTD.c near the top
+ */
+ } SGML_dtd;
+
+/* SGML context passed to parsers
+*/
+ typedef struct _HTSGMLContext *HTSGMLContext; /* Hidden */
+
+/*__________________________________________________________________________
+*/
+
+/*
+
+Structured Object definition
+
+ A structured object is something which can reasonably be represented
+ in SGML. I'll rephrase that. A structured object is an ordered
+ tree-structured arrangement of data which is representable as text.
+ The SGML parser outputs to a Structured object. A Structured object
+ can output its contents to another Structured Object. It's a kind of
+ typed stream. The architecture is largely Dan Conolly's. Elements and
+ entities are passed to the sob by number, implying a knowledge of the
+ DTD. Knowledge of the SGML syntax is not here, though.
+
+ Superclass: HTStream
+
+ The creation methods will vary on the type of Structured Object.
+ Maybe the callerData is enough info to pass along.
+
+ */
+ typedef struct _HTStructured HTStructured;
+
+ typedef struct _HTStructuredClass {
+
+ const char *name; /* Just for diagnostics */
+
+ void (*_free) (HTStructured * me);
+
+ void (*_abort) (HTStructured * me, HTError e);
+
+ void (*put_character) (HTStructured * me, int ch);
+
+ void (*put_string) (HTStructured * me, const char *str);
+
+ void (*put_block) (HTStructured * me, const char *str, int len);
+
+ /* HTStreamClass ends here */
+
+ int (*start_element) (HTStructured * me, int element_number,
+ const BOOL *attribute_present,
+ STRING2PTR attribute_value,
+ int charset,
+ char **include);
+
+ int (*end_element) (HTStructured * me, int element_number,
+ char **include);
+
+ int (*put_entity) (HTStructured * me, int entity_number);
+
+ } HTStructuredClass;
+
+/*
+ Equivalents to the following functions possibly could be generalised
+ into additional HTStructuredClass members. For now they don't do
+ anything target-specific. - kw
+ */
+ extern BOOLEAN LYCheckForCSI(HTParentAnchor *anchor, char **url);
+ extern void LYDoCSI(char *url, const char *comment, char **csi);
+ extern BOOLEAN LYCommentHacks(HTParentAnchor *anchor, const char *comment);
+
+/*
+
+Find a Tag by Name
+
+ Returns a pointer to the tag within the DTD.
+
+ */
+ extern HTTag *SGMLFindTag(const SGML_dtd * dtd,
+ const char *string);
+
+/*
+ * Return the current offset within the file that SGML is parsing
+ */
+ extern int SGML_offset(void);
+
+/*
+
+Create an SGML parser
+
+ */
+/*
+ * On entry,
+ * dtd must point to a DTD structure as defined above
+ * callbacks must point to user routines.
+ * callData is returned in callbacks transparently.
+ * On exit,
+ * The default tag starter has been processed.
+ */
+ extern HTStream *SGML_new(const SGML_dtd * dtd,
+ HTParentAnchor *anchor,
+ HTStructured * target,
+ int extended_html);
+
+ extern const HTStreamClass SGMLParser;
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* SGML_H */
diff --git a/WWW/Library/Implementation/UCAux.h b/WWW/Library/Implementation/UCAux.h
new file mode 100644
index 0000000..3e574a1
--- /dev/null
+++ b/WWW/Library/Implementation/UCAux.h
@@ -0,0 +1,92 @@
+/*
+ * $LynxId: UCAux.h,v 1.22 2014/12/10 09:48:41 tom Exp $
+ */
+#ifndef UCAUX_H
+#define UCAUX_H
+
+#ifndef HTUTILS_H
+#include <HTUtils.h>
+#endif
+
+#ifndef UCDEFS_H
+#include <UCDefs.h>
+#endif /* UCDEFS_H */
+
+#ifndef HTSTREAM_H
+#include <HTStream.h>
+#endif /* HTSTREAM_H */
+
+#ifndef UCMAP_H
+#include <UCMap.h>
+#endif /* UCMAP_H */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+ extern BOOL UCCanUniTranslateFrom(int from);
+ extern BOOL UCCanTranslateUniTo(int to);
+ extern BOOL UCCanTranslateFromTo(int from, int to);
+ extern BOOL UCNeedNotTranslate(int from,
+ int to);
+
+ struct _UCTransParams {
+ BOOL transp;
+ BOOL do_cjk;
+ BOOL decode_utf8;
+ BOOL output_utf8;
+ BOOL use_raw_char_in;
+ BOOL strip_raw_char_in;
+ BOOL pass_160_173_raw;
+ BOOL do_8bitraw;
+ BOOL trans_to_uni;
+ BOOL trans_C0_to_uni;
+ BOOL repl_translated_C0;
+ BOOL trans_from_uni;
+ int ucs_mode;
+ };
+ typedef struct _UCTransParams UCTransParams;
+
+ typedef struct {
+ char utf_count;
+ UCode_t utf_char;
+ char utf_buf[8];
+ char *utf_buf_p;
+ } UTFDecodeState;
+
+ typedef enum {
+ dUTF8_ok,
+ dUTF8_err,
+ dUTF8_more
+ } dUTF8;
+
+ extern dUTF8 HTDecodeUTF8(UTFDecodeState * me, int *c_in_out, UCode_t *result);
+
+ extern void UCSetTransParams(UCTransParams * pT, int cs_in,
+ const LYUCcharset *p_in,
+ int cs_out,
+ const LYUCcharset *p_out);
+
+ extern void UCTransParams_clear(UCTransParams * pT);
+
+ extern void UCSetBoxChars(int cset,
+ int *pvert_out,
+ int *phori_out,
+ int vert_in,
+ int hori_in);
+
+ typedef void putc_func_t (HTStream *me,
+ int ch);
+
+ extern BOOL UCPutUtf8_charstring(HTStream *target,
+ putc_func_t *actions,
+ UCode_t code);
+
+ extern BOOL UCConvertUniToUtf8(UCode_t code,
+ char *buffer);
+
+ extern UCode_t UCGetUniFromUtf8String(const char **ppuni);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* UCAUX_H */
diff --git a/WWW/Library/Implementation/UCDefs.h b/WWW/Library/Implementation/UCDefs.h
new file mode 100644
index 0000000..4eb7c56
--- /dev/null
+++ b/WWW/Library/Implementation/UCDefs.h
@@ -0,0 +1,106 @@
+/*
+ * $LynxId: UCDefs.h,v 1.18 2021/06/29 00:21:51 tom Exp $
+ *
+ * Definitions for Unicode character-translations
+ */
+
+#ifndef UCDEFS_H
+#define UCDEFS_H
+
+#ifndef HTUTILS_H
+#include <HTUtils.h>
+#endif
+
+typedef struct _LYUCcharset {
+ int UChndl; /* -1 for "old" charsets, >= 0 for chartrans tables */
+
+ const char *MIMEname;
+ int enc;
+ int codepage; /* if positive, an IBM OS/2 specific number;
+ if negative, flag for no table translation */
+
+ /* parameters below are not used by chartrans mechanism, */
+ /* they describe some relationships against built-in Latin1 charset... */
+ int repertoire; /* unused */
+ int codepoints; /* subset/superset of Latin1 ? */
+ int cpranges; /* unused, obsolete by LYlowest_eightbit;
+ "which ranges have valid displayable chars
+ (including nbsp and shy)" */
+ int like8859; /* currently used for nbsp and shy only
+ (but UCT_R_8859SPECL assumed for any UCT_R_8BIT...);
+ "for which ranges is it like 8859-1" */
+} LYUCcharset;
+
+typedef enum {
+ UCT_ENC_7BIT,
+ UCT_ENC_8BIT,
+ UCT_ENC_8859, /* no displayable chars in 0x80-0x9F */
+ UCT_ENC_8BIT_C0, /* 8-bit + some chars in C0 control area */
+ UCT_ENC_MAYBE2022,
+ UCT_ENC_CJK,
+ UCT_ENC_16BIT,
+ UCT_ENC_UTF8
+} eUCT_ENC;
+
+#define UCT_REP_SUBSETOF_LAT1 0x01
+#define UCT_REP_SUPERSETOF_LAT1 0x02
+#define UCT_REP_IS_LAT1 UCT_REP_SUBSETOF_LAT1 | UCT_REP_SUPERSETOF_LAT1
+/*
+ * Assume everything we deal with is included in the UCS2 repertoire,
+ * so a flag for _REP_SUBSETOF_UCS2 would be redundant.
+ */
+
+/*
+ * More general description how the code points relate to 8859-1 and UCS:
+ */
+#define UCT_CP_SUBSETOF_LAT1 0x01 /* implies UCT_CP_SUBSETOF_UCS2 */
+#define UCT_CP_SUPERSETOF_LAT1 0x02
+#define UCT_CP_SUBSETOF_UCS2 0x04
+
+#define UCT_CP_IS_LAT1 UCT_CP_SUBSETOF_LAT1 | UCT_CP_SUPERSETOF_LAT1
+
+/*
+ * More specific bitflags for practically important code point ranges:
+ */
+#define UCT_R_LOWCTRL 0x08 /* 0x00-0x1F, for completeness */
+#define UCT_R_7BITINV 0x10 /* invariant???, displayable 7bit chars */
+#define UCT_R_7BITNAT 0x20 /* displayable 7bit, national??? */
+#define UCT_R_HIGHCTRL 0x40 /* chars in 0x80-0x9F range */
+#define UCT_R_8859SPECL 0x80 /* special chars in 8859-x sets: nbsp and shy */
+#define UCT_R_HIGH8BIT 0x100 /* rest of 0xA0-0xFF range */
+
+#define UCT_R_ASCII UCT_R_7BITINV | UCT_R_7BITNAT /* displayable US-ASCII */
+#define UCT_R_LAT1 UCT_R_ASCII | UCT_R_8859SPECL | UCT_R_HIGH8BIT
+#define UCT_R_8BIT UCT_R_LAT1 | UCT_R_HIGHCTRL /* full 8bit range */
+
+/*
+ * For the following some comments are in HTAnchor.c.
+ */
+typedef enum {
+ UCT_STAGE_MIME,
+ UCT_STAGE_PARSER, /* What the parser (SGML.c) gets to see */
+ UCT_STAGE_STRUCTURED, /* What the structured stream (HTML) gets fed */
+ UCT_STAGE_HTEXT, /* What gets fed to the HText_* functions */
+ UCT_STAGEMAX
+} eUCT_STAGE;
+
+typedef enum {
+ UCT_SETBY_NONE,
+ UCT_SETBY_DEFAULT,
+ UCT_SETBY_LINK, /* set by A or LINK CHARSET= hint */
+ UCT_SETBY_STRUCTURED, /* structured stream stage (HTML.c) */
+ UCT_SETBY_PARSER, /* set by SGML parser or similar */
+ UCT_SETBY_MIME /* set explicitly by MIME charset parameter */
+} eUCT_SETBY;
+
+typedef struct _UCStageInfo {
+ int lock; /* by what it has been set */
+ int LYhndl;
+ LYUCcharset C;
+} UCStageInfo;
+
+typedef struct _UCAnchorInfo {
+ struct _UCStageInfo s[UCT_STAGEMAX];
+} UCAnchorInfo;
+
+#endif /* UCDEFS_H */
diff --git a/WWW/Library/Implementation/UCMap.h b/WWW/Library/Implementation/UCMap.h
new file mode 100644
index 0000000..0c88969
--- /dev/null
+++ b/WWW/Library/Implementation/UCMap.h
@@ -0,0 +1,114 @@
+/*
+ * $LynxId: UCMap.h,v 1.30 2023/01/05 09:17:15 tom Exp $
+ */
+#ifndef UCMAP_H
+#define UCMAP_H
+
+#ifndef HTUTILS_H
+#include <HTUtils.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define UCS_HIDE 0xffff
+#define UCS_REPL 0xfffd
+
+ typedef enum {
+ ucError = -1,
+ ucZeroWidth = -2,
+ ucInvalidHash = -3,
+ ucNotFound = -4,
+ ucNeedMore = -10,
+ ucCannotConvert = -11,
+ ucCannotOutput = -12,
+ ucBufferTooSmall = -13,
+ ucUnknown = -14
+ } UCStatus;
+
+ typedef long UCode_t;
+
+ extern BOOL UCScanCode(UCode_t *, const char *, BOOL);
+
+ extern int UCTransUniChar(UCode_t unicode,
+ int charset_out);
+ extern int UCTransUniCharStr(char *outbuf,
+ int buflen,
+ UCode_t unicode,
+ int charset_out,
+ int chk_single_flag);
+ extern int UCTransChar(int ch_in,
+ int charset_in,
+ int charset_out);
+ extern int UCReverseTransChar(int ch_out,
+ int charset_in,
+ int charset_out);
+ extern int UCTransCharStr(char *outbuf,
+ int buflen,
+ int ch_in,
+ int charset_in,
+ int charset_out,
+ int chk_single_flag);
+#ifdef EXP_CHINESEUTF8_SUPPORT
+ extern UCode_t UCTransJPToUni(char *inbuf,
+ int buflen,
+ int charset_in);
+#endif
+ extern UCode_t UCTransToUni(int ch_in,
+ int charset_in);
+ extern int UCGetRawUniMode_byLYhndl(int i);
+ extern int UCGetLYhndl_byMIME(const char *p); /* returns -1 if name not recognized */
+ extern int safeUCGetLYhndl_byMIME(const char *p); /* returns LATIN1 if name not recognized */
+
+#ifdef USE_LOCALE_CHARSET
+ extern void LYFindLocaleCharset(void);
+#endif
+
+ extern int UCLYhndl_for_unspec;
+ extern int UCLYhndl_for_unrec;
+ extern int UCLYhndl_HTFile_for_unspec;
+ extern int UCLYhndl_HTFile_for_unrec;
+
+/* easy to type: */
+ extern int LATIN1; /* UCGetLYhndl_byMIME("iso-8859-1") */
+ extern int US_ASCII; /* UCGetLYhndl_byMIME("us-ascii") */
+ extern int UTF8_handle; /* UCGetLYhndl_byMIME("utf-8") */
+
+#undef TRANSPARENT /* defined on Solaris in <sys/stream.h> */
+ extern int TRANSPARENT; /* UCGetLYhndl_byMIME("x-transparent") */
+
+/*
+In general, Lynx translates letters from document charset to display charset.
+If document charset is not specified or not recognized by Lynx, we fall back
+to different assumptions below, read also lynx.cfg for info.
+
+UCLYhndl_for_unspec - assume this as charset for documents that don't
+ specify a charset parameter in HTTP headers or via META
+ this corresponds to "assume_charset"
+
+UCLYhndl_HTFile_for_unspec - assume this as charset of local file
+ this corresponds to "assume_local_charset"
+
+UCLYhndl_for_unrec - in case a charset parameter is not recognized;
+ this corresponds to "assume_unrec_charset"
+
+UCLYhndl_HTFile_for_unrec - the same but only for local files,
+ currently not used.
+
+current_char_set - this corresponds to "display charset",
+ declared in LYCharSets.c and really important.
+
+All external charset information is available in so called MIME format.
+For internal needs Lynx uses charset handlers as integers
+from UCGetLYhndl_byMIME(). However, there is no way to recover
+from user's error in configuration file lynx.cfg or command line switches,
+those unrecognized MIME names are assumed as LATIN1 (via safeUCGetLYhndl...).
+*/
+
+#define UCTRANS_NOTFOUND (-4)
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* UCMAP_H */
diff --git a/WWW/Library/Implementation/Version.make b/WWW/Library/Implementation/Version.make
new file mode 100644
index 0000000..4b4b380
--- /dev/null
+++ b/WWW/Library/Implementation/Version.make
@@ -0,0 +1 @@
+VC = 2.14
diff --git a/WWW/Library/Implementation/dtd_util.c b/WWW/Library/Implementation/dtd_util.c
new file mode 100644
index 0000000..cf88998
--- /dev/null
+++ b/WWW/Library/Implementation/dtd_util.c
@@ -0,0 +1,1722 @@
+/*
+ * $LynxId: dtd_util.c,v 1.89 2022/09/29 23:51:16 tom Exp $
+ *
+ * Given a SGML_dtd structure, write a corresponding flat file, or "C" source.
+ * Given the flat-file, write the "C" source.
+ *
+ * TODO: use symbols for HTMLA_NORMAL, etc.
+ */
+
+#include <HTUtils.h>
+#include <HTMLDTD.h>
+#include <string.h>
+#include <stdarg.h>
+
+/*
+ * Tweaks to build standalone.
+ */
+#undef exit
+
+BOOLEAN WWW_TraceFlag = FALSE;
+FILE *TraceFP(void)
+{
+ return stderr;
+}
+
+/*
+ * Begin the actual utility.
+ */
+#define GETOPT "chl:o:tsx"
+
+#define NOTE(message) fprintf(output, message "\n");
+/* *INDENT-OFF* */
+#ifdef USE_PRETTYSRC
+# define N HTMLA_NORMAL
+# define i HTMLA_ANAME
+# define h HTMLA_HREF
+# define c HTMLA_CLASS
+# define x HTMLA_AUXCLASS
+# define T(t) , t
+#else
+# define T(t) /*nothing */
+#endif
+
+#define ATTR_TYPE(name) { #name, name##_attr_list }
+
+#define MY_LIMIT 1024
+#define FMT_WS "%[ \t\n]"
+#define FMT_NUM_ATTR_TYPES "%d attr_types"
+#define FMT_NUM_ATTRS "%d attributes:"
+#define FMT_ONE_ATTR "%d:%d:%s"
+#define NUM_ONE_ATTR 4
+
+static const char alias_codes[] = "!@#$%^&*";
+
+#define DATA(name) { #name, Tgc_##name }
+static const struct {
+ const char *name;
+ TagClass tagclass;
+} class_list[] = {
+ DATA(APPLETlike),
+ DATA(Alike),
+ DATA(BODYlike),
+ DATA(BRlike),
+ DATA(DELlike),
+ DATA(DIVlike),
+ DATA(EMlike),
+ DATA(FONTlike),
+ DATA(FORMlike),
+ DATA(HEADstuff),
+ DATA(HRlike),
+ DATA(LIlike),
+ DATA(MAPlike),
+ DATA(MATHlike),
+ DATA(Plike),
+ DATA(SELECTlike),
+ DATA(TRlike),
+ DATA(ULlike),
+ DATA(formula),
+ DATA(outer),
+ DATA(same)
+};
+
+static const attr core_attr_list[] = {
+ { "CLASS" T(c) },
+ { "ID" T(i) },
+ { "STYLE" T(N) },
+ { "TITLE" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr i18n_attr_list[] = {
+ { "DIR" T(N) },
+ { "LANG" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr events_attr_list[] = {
+ { "ONCLICK" T(N) },
+ { "ONDBLCLICK" T(N) },
+ { "ONKEYDOWN" T(N) },
+ { "ONKEYPRESS" T(N) },
+ { "ONKEYUP" T(N) },
+ { "ONMOUSEDOWN" T(N) },
+ { "ONMOUSEMOVE" T(N) },
+ { "ONMOUSEOUT" T(N) },
+ { "ONMOUSEOVER" T(N) },
+ { "ONMOUSEUP" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr align_attr_list[] = {
+ { "ALIGN" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr cellalign_attr_list[] = {
+ { "ALIGN" T(N) },
+ { "CHAR" T(N) },
+ { "CHAROFF" T(N) },
+ { "VALIGN" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr bgcolor_attr_list[] = {
+ { "BGCOLOR" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+#undef T
+/* *INDENT-ON* */
+
+static const char *input_filename;
+static int input_lineno;
+
+static void failed(const char *s)
+{
+ if (input_lineno) {
+ int save = errno;
+
+ fprintf(stderr, "%s:%d ", input_filename, input_lineno);
+ errno = save;
+ }
+ perror(s);
+ exit(EXIT_FAILURE);
+}
+
+static void warning(const char *, ...) GCC_PRINTFLIKE(1, 2);
+
+static void warning(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ fprintf(stderr, "%s:%d: ", input_filename, input_lineno);
+ vfprintf(stderr, fmt, ap);
+ fputc('\n', stderr);
+ va_end(ap);
+}
+
+static void usage(void)
+{
+ static const char *tbl[] =
+ {
+ "Usage: dtd_util [options]",
+ "",
+ "Options:",
+ " -c generate C-source"
+ " -h generate C-header"
+ " -l load",
+ " -o filename specify output (default: stdout)",
+ " -s strict (HTML DTD 0)",
+ " -t tagsoup (HTML DTD 1)",
+ " -x cross-check contains/contained data (repeat for more)"
+ };
+ unsigned n;
+
+ for (n = 0; n < TABLESIZE(tbl); ++n) {
+ fprintf(stderr, "%s\n", tbl[n]);
+ }
+ exit(EXIT_FAILURE);
+}
+
+static const char *SGMLContent2s(SGMLContent contents)
+{
+ char *value = "?";
+
+ switch (contents) {
+ case SGML_EMPTY:
+ value = "SGML_EMPTY";
+ break;
+ case SGML_LITTERAL:
+ value = "SGML_LITTERAL";
+ break;
+ case SGML_CDATA:
+ value = "SGML_CDATA";
+ break;
+ case SGML_SCRIPT:
+ value = "SGML_SCRIPT";
+ break;
+ case SGML_RCDATA:
+ value = "SGML_RCDATA";
+ break;
+ case SGML_MIXED:
+ value = "SGML_MIXED";
+ break;
+ case SGML_ELEMENT:
+ value = "SGML_ELEMENT";
+ break;
+ case SGML_PCDATA:
+ value = "SGML_PCDATA";
+ break;
+ }
+ return value;
+}
+
+static SGMLContent s2SGMLContent(const char *value)
+{
+ static SGMLContent table[] =
+ {
+ SGML_EMPTY,
+ SGML_LITTERAL,
+ SGML_CDATA,
+ SGML_SCRIPT,
+ SGML_RCDATA,
+ SGML_MIXED,
+ SGML_ELEMENT,
+ SGML_PCDATA
+ };
+ unsigned n;
+ SGMLContent result = SGML_EMPTY;
+
+ for (n = 0; n < TABLESIZE(table); ++n) {
+ if (!strcmp(SGMLContent2s(table[n]), value)) {
+ result = table[n];
+ break;
+ }
+ }
+ return result;
+}
+
+static void PrintF(FILE *, int, const char *, ...) GCC_PRINTFLIKE(3, 4);
+
+static void PrintF(FILE *output, int width, const char *fmt, ...)
+{
+ char buffer[BUFSIZ];
+ va_list ap;
+
+ va_start(ap, fmt);
+ vsprintf(buffer, fmt, ap);
+ va_end(ap);
+
+ fprintf(output, "%-*s", width, buffer);
+}
+
+static char *get_line(FILE *input)
+{
+ char temp[MY_LIMIT];
+ char *result = 0;
+
+ if (fgets(temp, (int) sizeof(temp), input) != 0) {
+ result = strdup(temp);
+ ++input_lineno;
+ }
+ return result;
+}
+
+static int read_num_attr_types(FILE *input)
+{
+ char *next = get_line(input);
+ int count;
+ int code;
+ char trail[MY_LIMIT];
+
+ if (next == 0
+ || (code = sscanf(next, FMT_NUM_ATTR_TYPES FMT_WS, &count, trail)) < 2
+ || !count) {
+ warning("expected attr_types");
+ }
+ return count;
+}
+
+static int same_AttrList(AttrList a, AttrList b)
+{
+ int result = 1;
+
+ if (a && b) {
+ while (a->name && b->name) {
+ if (strcmp(a->name, b->name)) {
+ result = 0;
+ break;
+ }
+ ++a, ++b;
+ }
+ if (a->name || b->name)
+ result = 0;
+ } else {
+ result = 0;
+ }
+ return result;
+}
+
+static int first_attrs(const SGML_dtd * dtd, int which)
+{
+ int check;
+ int result = TRUE;
+
+ for (check = 0; check < which; ++check) {
+ if (dtd->tags[check].attributes == dtd->tags[which].attributes) {
+ result = FALSE;
+ break;
+ } else if (same_AttrList(dtd->tags[check].attributes,
+ dtd->tags[which].attributes)) {
+ result = FALSE;
+ dtd->tags[which].attributes = dtd->tags[check].attributes;
+ break;
+ }
+ }
+ return result;
+}
+
+static char *no_dashes(char *target, const char *source)
+{
+ int j;
+
+ for (j = 0; (target[j] = source[j]) != '\0'; ++j) {
+ if (!isalnum(target[j]))
+ target[j] = '_';
+ }
+ return target;
+}
+
+/* the second "OBJECT" is treated specially */
+static int first_object(const SGML_dtd * dtd, int which)
+{
+ int check;
+
+ for (check = 0; check <= which; ++check) {
+ if (!strcmp(dtd->tags[check].name, "OBJECT"))
+ break;
+ }
+ return (check == which);
+}
+
+static const char *NameOfAttrs(const SGML_dtd * dtd, int which)
+{
+ int check;
+ const char *result = dtd->tags[which].name;
+
+ for (check = 0; check < which; ++check) {
+ if (dtd->tags[check].attributes == dtd->tags[which].attributes) {
+ result = dtd->tags[check].name;
+ break;
+ }
+ }
+ /* special cases to match existing headers */
+ if (!strcmp(result, "ABBR"))
+ result = "GEN";
+ else if (!strcmp(result, "ARTICLE"))
+ result = "GEN5";
+ else if (!strcmp(result, "BLOCKQUOTE"))
+ result = "BQ";
+ else if (!strcmp(result, "BASEFONT"))
+ result = "FONT";
+ else if (!strcmp(result, "CENTER"))
+ result = "DIV";
+ else if (!strcmp(result, "DIR"))
+ result = "UL";
+ else if (!strcmp(result, "H1"))
+ result = "H";
+ else if (!strcmp(result, "TBODY"))
+ result = "TR";
+ return result;
+}
+
+static const char *DEF_name(const SGML_dtd * dtd, int which, unsigned alias)
+{
+ const char *result = dtd->tags[which].name;
+
+ if (!strcmp(result, "OBJECT") && !first_object(dtd, which)) {
+ result = "OBJECT_PCDATA";
+ } else if (alias) {
+ char buffer[MY_LIMIT];
+
+ sprintf(buffer, "%s_%d", result, alias + 1);
+ result = strdup(buffer);
+ }
+ return result;
+}
+
+static const char *EXT_name(HTTag * tag)
+{
+ const char *result = tag->name;
+
+ if (tag->alias) {
+ char buffer[MY_LIMIT];
+
+ sprintf(buffer, "%s%c", result, alias_codes[tag->alias - 1]);
+ result = strdup(buffer);
+ }
+ return result;
+}
+
+typedef struct {
+ const char *name;
+ const attr *attrs;
+ int count;
+ int which;
+} AttrInfo;
+
+static int compare_attr_types(const void *a, const void *b)
+{
+ const AttrType *p = (const AttrType *) a;
+ const AttrType *q = (const AttrType *) b;
+ int result = 0;
+
+ /* keep lowercase AttrType lists before uppercase, since latter are derived */
+ if (isupper(p->name[0]) ^ isupper(q->name[0])) {
+ if (isupper(p->name[0])) {
+ result = 1;
+ } else {
+ result = -1;
+ }
+ } else {
+ result = strcmp(p->name, q->name);
+ }
+ return result;
+}
+
+static int len_AttrTypes(const AttrType * data)
+{
+ int result = 0;
+
+ for (result = 0; data[result].name != 0; ++result) {
+ ;
+ }
+ return result;
+}
+
+static AttrType *sorted_AttrTypes(const AttrType * source)
+{
+ AttrType *result = 0;
+ unsigned number = len_AttrTypes(source);
+
+ if (number != 0) {
+ result = typecallocn(AttrType, number + 1);
+ if (result != 0) {
+ MemCpy(result, source, number * sizeof(*result));
+ qsort(result, number, sizeof(*result), compare_attr_types);
+ }
+ }
+
+ return result;
+}
+
+static int compare_attr(const void *a, const void *b)
+{
+ const AttrInfo *p = (const AttrInfo *) a;
+ const AttrInfo *q = (const AttrInfo *) b;
+
+ return strcmp(p->name, q->name);
+}
+
+static int len_AttrList(AttrList data)
+{
+ int result = 0;
+
+ for (result = 0; data[result].name != 0; ++result) {
+ ;
+ }
+ return result;
+}
+
+static void sort_uniq_AttrList(attr * data)
+{
+ unsigned have = len_AttrList(data);
+ unsigned j, k;
+
+ qsort(data, have, sizeof(*data), compare_attr);
+ /*
+ * Eliminate duplicates
+ */
+ for (j = 0; j < have; ++j) {
+ for (k = j; data[k].name; ++k) {
+ if (data[k + 1].name == 0)
+ break;
+ if (strcmp(data[j].name, data[k + 1].name)) {
+ break;
+ }
+ }
+ data[j] = data[k];
+ }
+ memset(data + j, 0, sizeof(data[0]));
+}
+
+static attr *copy_AttrList(AttrList data)
+{
+ unsigned need = len_AttrList(data);
+ unsigned n;
+
+ attr *result = (attr *) calloc(need + 1, sizeof(attr));
+
+ for (n = 0; n < need; ++n)
+ result[n] = data[n];
+ sort_uniq_AttrList(result);
+ return result;
+}
+
+static attr *merge_AttrLists(const AttrType * data)
+{
+ const AttrType *at;
+ attr *result = 0;
+ unsigned need = 1;
+ unsigned have = 0;
+ unsigned j;
+
+ for (at = data; at->name; ++at) {
+ need += len_AttrList(at->list);
+ }
+ result = (attr *) calloc(need + 1, sizeof(attr));
+ for (at = data; at->name; ++at) {
+ if (!strcmp(at->name, "events")) {
+ ; /* lynx does not use events */
+ } else {
+ for (j = 0; at->list[j].name; ++j) {
+ result[have++] = at->list[j];
+ }
+ }
+ }
+ sort_uniq_AttrList(result);
+ return result;
+}
+
+static int clean_AttrList(attr * target, AttrList source)
+{
+ int result = 0;
+ int j, k;
+
+ for (j = 0; target[j].name != 0; ++j) {
+ for (k = 0; source[k].name != 0; ++k) {
+ if (!strcmp(target[j].name, source[k].name)) {
+ k = j--;
+ for (;;) {
+ target[k] = target[k + 1];
+ if (target[k++].name == 0)
+ break;
+ }
+ ++result;
+ break;
+ }
+ }
+ }
+ return result;
+}
+
+/*
+ * Actually COUNT the number of attributes, to make it possible to edit a
+ * attribute-table in src0_HTMLDTD.h and have all of the files updated by
+ * just doing a "make sources".
+ */
+static int AttrCount(HTTag * tag)
+{
+ return len_AttrList(tag->attributes);
+}
+
+static AttrInfo *sorted_attrs(const SGML_dtd * dtd, unsigned *countp)
+{
+ int j;
+
+ AttrInfo *data = (AttrInfo *) calloc(dtd->number_of_tags, sizeof(AttrInfo));
+ unsigned count = 0;
+
+ /* get the attribute-data */
+ for (j = 0; j < dtd->number_of_tags; ++j) {
+ if (first_attrs(dtd, j)) {
+ data[count].name = NameOfAttrs(dtd, j);
+ data[count].attrs = dtd->tags[j].attributes;
+ data[count].count = AttrCount(&(dtd->tags[j]));
+ data[count].which = j;
+ ++count;
+ }
+ }
+ /* sort the data by the name of their associated tag */
+ qsort(data, count, sizeof(*data), compare_attr);
+ *countp = count;
+ return data;
+}
+
+static void dump_src_HTTag_Defines(FILE *output, const SGML_dtd * dtd, int which)
+{
+ HTTag *tag = &(dtd->tags[which]);
+
+#define myFMT "0x%05X"
+ fprintf(output,
+ "#define T_%-13s "
+ myFMT "," myFMT "," myFMT "," myFMT "," myFMT "," myFMT
+ "," myFMT "\n",
+ DEF_name(dtd, which, tag->alias),
+ tag->tagclass,
+ tag->contains,
+ tag->icontains,
+ tag->contained,
+ tag->icontained,
+ tag->canclose,
+ tag->flags);
+}
+
+static void dump_AttrItem(FILE *output, const attr * data)
+{
+ char buffer[BUFSIZ];
+ char pretty = 'N';
+
+ sprintf(buffer, "\"%s\"", data->name);
+#ifdef USE_PRETTYSRC
+ switch (data->type) {
+ case HTMLA_NORMAL:
+ pretty = 'N';
+ break;
+ case HTMLA_ANAME:
+ pretty = 'i';
+ break;
+ case HTMLA_HREF:
+ pretty = 'h';
+ break;
+ case HTMLA_CLASS:
+ pretty = 'c';
+ break;
+ case HTMLA_AUXCLASS:
+ pretty = 'x';
+ break;
+ }
+#endif
+ fprintf(output, "\t{ %-15s T(%c) },\n", buffer, pretty);
+}
+
+static void dump_AttrItem0(FILE *output)
+{
+ fprintf(output, "\t{ 0 T(N) }\t/* Terminate list */\n");
+}
+
+static void dump_src_AttrType(FILE *output, const char *name, AttrList data, const char **from)
+{
+ int n;
+
+ fprintf(output, "static const attr %s_attr_list[] = {\n", name);
+ if (data != 0) {
+ for (n = 0; data[n].name != 0; ++n) {
+ dump_AttrItem(output, data + n);
+ }
+ }
+ fprintf(output, "\t{ 0 T(N) } /* Terminate list */\n");
+ fprintf(output, "};\n");
+ NOTE("");
+ fprintf(output, "static const AttrType %s_attr_type[] = {\n", name);
+ if (from != 0) {
+ while (*from != 0) {
+ fprintf(output, "\t{ ATTR_TYPE(%s) },\n", *from);
+ ++from;
+ }
+ } else {
+ fprintf(output, "\t{ ATTR_TYPE(%s) },\n", name);
+ }
+ fprintf(output, "\t{ 0, 0 },\n");
+ fprintf(output, "};\n");
+ NOTE("");
+}
+
+static void dump_src_HTTag_Attrs(FILE *output, const SGML_dtd * dtd, int which)
+{
+ HTTag *tag = &(dtd->tags[which]);
+ attr *list = merge_AttrLists(tag->attr_types);
+ char buffer[BUFSIZ];
+ int n;
+ int limit = len_AttrList(list);
+
+ sprintf(buffer, "static const attr %s_attr[] = {", NameOfAttrs(dtd, which));
+ fprintf(output,
+ "%-40s/* %s attributes */\n", buffer, tag->name);
+ for (n = 0; n < limit; ++n) {
+ dump_AttrItem(output, list + n);
+ }
+ dump_AttrItem0(output);
+ fprintf(output, "};\n");
+ NOTE("");
+ free(list);
+}
+
+static void dump_src_HTTag(FILE *output, const SGML_dtd * dtd, int which)
+{
+ HTTag *tag = &(dtd->tags[which]);
+ char *P_macro = "P";
+
+#ifdef USE_JUSTIFY_ELTS
+ if (!tag->can_justify)
+ P_macro = "P0";
+#endif
+ PrintF(output, 19, " { %s(%s),", P_macro, tag->name);
+ PrintF(output, 24, "ATTR_DATA(%s), ", NameOfAttrs(dtd, which));
+ PrintF(output, 14, "%s,", SGMLContent2s(tag->contents));
+ fprintf(output, "T_%s", DEF_name(dtd, which, tag->alias));
+ fprintf(output, ", %d", tag->alias);
+ fprintf(output, ", %d", tag->aliases);
+ fprintf(output, "},\n");
+}
+
+static void dump_source(FILE *output, const SGML_dtd * dtd, int dtd_version)
+{
+ static AttrType generic_types[] =
+ {
+ ATTR_TYPE(core),
+ ATTR_TYPE(i18n),
+ ATTR_TYPE(events),
+ ATTR_TYPE(align),
+ ATTR_TYPE(cellalign),
+ ATTR_TYPE(bgcolor),
+ {0, 0}
+ };
+ AttrType *gt;
+ TagAlias aliases;
+
+ const char *marker = "src_HTMLDTD_H";
+ int j;
+ int inalias;
+
+ unsigned count = 0;
+ AttrInfo *data = sorted_attrs(dtd, &count);
+
+ fprintf(output, "/* %cLynxId%c */\n", '$', '$');
+ fprintf(output, "#ifndef %s%d\n", marker, dtd_version);
+ fprintf(output, "#define %s%d 1\n\n", marker, dtd_version);
+
+ /*
+ * If we ifdef this for once, and make the table names distinct, we can
+ * #include the strict- and tagsoup-output directly in HTMLDTD.c
+ */
+ NOTE("#ifndef once_HTMLDTD");
+ NOTE("#define once_HTMLDTD 1");
+ NOTE("");
+
+ /* construct TagClass-define's */
+ for (j = 0; j <= dtd->number_of_tags; ++j) {
+ dump_src_HTTag_Defines(output, dtd, j);
+ }
+ NOTE("#define T__UNREC_ 0x00000,0x00000,0x00000,0x00000,0x00000,0x00000,0x00000");
+
+ /* construct attribute-tables */
+ NOTE("#ifdef USE_PRETTYSRC");
+ NOTE("# define N HTMLA_NORMAL");
+ NOTE("# define i HTMLA_ANAME");
+ NOTE("# define h HTMLA_HREF");
+ NOTE("# define c HTMLA_CLASS");
+ NOTE("# define x HTMLA_AUXCLASS");
+ NOTE("# define T(t) , t");
+ NOTE("#else");
+ NOTE("# define T(t) /*nothing */");
+ NOTE("#endif");
+ NOTE("/* *INDENT-OFF* */");
+ NOTE("");
+ NOTE("#define ATTR_TYPE(name) #name, name##_attr_list");
+ NOTE("");
+ NOTE("/* generic attributes, used in different tags */");
+ for (gt = generic_types; gt->name != 0; ++gt) {
+ dump_src_AttrType(output, gt->name, gt->list, 0);
+ }
+ NOTE("");
+ NOTE("/* tables defining attributes per-tag in terms of generic attributes (editable) */");
+ for (j = 0; j < (int) count; ++j) {
+ int which = data[j].which;
+
+ if (first_attrs(dtd, which)) {
+ HTTag *tag = &(dtd->tags[which]);
+ const AttrType *types = tag->attr_types;
+ const char *name = NameOfAttrs(dtd, which);
+ attr *list = 0;
+ const char *from_attr[10];
+ int from_size = 0;
+
+ while (types->name != 0) {
+ from_attr[from_size++] = types->name;
+ if (!strcmp(types->name, name)) {
+ list = copy_AttrList(types->list);
+ for (gt = generic_types; gt->name != 0; ++gt) {
+ if (clean_AttrList(list, gt->list)) {
+ int k;
+ int found = 0;
+
+ for (k = 0; k < from_size; ++k) {
+ if (!strcmp(from_attr[k], gt->name)) {
+ found = 1;
+ break;
+ }
+ }
+ if (!found)
+ from_attr[from_size++] = gt->name;
+ break;
+ }
+ }
+ }
+ ++types;
+ }
+ from_attr[from_size] = 0;
+
+ if (list != 0) {
+ /* FIXME check if each remaining attribute is defined in XXX */
+
+ dump_src_AttrType(output, name, list, from_attr);
+ free(list);
+ }
+ }
+ }
+ NOTE("");
+ NOTE("/* attribute lists for the runtime (generated by dtd_util) */");
+ for (j = 0; j < (int) count; ++j) {
+ dump_src_HTTag_Attrs(output, dtd, data[j].which);
+ }
+ NOTE("/* *INDENT-ON* */");
+ NOTE("");
+ NOTE("/* justification-flags */");
+ NOTE("#undef N");
+ NOTE("#undef i");
+ NOTE("#undef h");
+ NOTE("#undef c");
+ NOTE("#undef x");
+ NOTE("");
+ NOTE("#undef T");
+ NOTE("");
+ NOTE("/* tag-names */");
+ /* these may be needed as parameters to macros */
+ for (j = 0; j <= dtd->number_of_tags; ++j) {
+ fprintf(output, "#undef %s\n", DEF_name(dtd, j, 0));
+ }
+ NOTE("");
+ NOTE("/* these definitions are used in the tags-tables */");
+ NOTE("#undef P");
+ NOTE("#undef P_");
+ NOTE("#ifdef USE_COLOR_STYLE");
+ NOTE("#define P_(x) #x, (sizeof #x) -1");
+ NOTE("#define NULL_HTTag_ NULL, 0");
+ NOTE("#else");
+ NOTE("#define P_(x) #x");
+ NOTE("#define NULL_HTTag_ NULL");
+ NOTE("#endif");
+ NOTE("");
+ NOTE("#ifdef USE_JUSTIFY_ELTS");
+ NOTE("#define P(x) P_(x), 1");
+ NOTE("#define P0(x) P_(x), 0");
+ NOTE("#define NULL_HTTag NULL_HTTag_,0");
+ NOTE("#else");
+ NOTE("#define P(x) P_(x)");
+ NOTE("#define P0(x) P_(x)");
+ NOTE("#define NULL_HTTag NULL_HTTag_");
+ NOTE("#endif");
+ NOTE("");
+ NOTE("#define ATTR_DATA(name) name##_attr, HTML_##name##_ATTRIBUTES, name##_attr_type");
+ NOTE("");
+ NOTE("#endif /* once_HTMLDTD */");
+ NOTE("/* *INDENT-OFF* */");
+
+ /* construct the tags table */
+ fprintf(output,
+ "static const HTTag tags_table%d[HTML_ALL_ELEMENTS] = {\n",
+ dtd_version);
+ aliases = 0;
+ inalias = -1;
+ for (j = dtd->number_of_tags - 1; j >= 0; --j) {
+ dtd->tags[j].aliases = aliases;
+ if (aliases != 0) {
+ if ((inalias - aliases) >= j) {
+ aliases = 0;
+ inalias = -1;
+ }
+ } else if ((aliases = dtd->tags[j].alias) != 0) {
+ inalias = j;
+ dtd->tags[j].aliases = aliases;
+ }
+ }
+ for (j = 0; j <= dtd->number_of_tags; ++j) {
+ if (j == dtd->number_of_tags) {
+ NOTE("/* additional (alternative variants), not counted in HTML_ELEMENTS: */");
+ NOTE("/* This one will be used as a temporary substitute within the parser when");
+ NOTE(" it has been signalled to parse OBJECT content as MIXED. - kw */");
+ }
+ dump_src_HTTag(output, dtd, j);
+ }
+ fprintf(output, "};\n");
+
+ NOTE("/* *INDENT-ON* */");
+ NOTE("");
+ fprintf(output, "#endif /* %s%d */\n", marker, dtd_version);
+
+ free(data);
+}
+
+static void dump_hdr_attr(FILE *output, AttrInfo * data)
+{
+ int j;
+ char buffer[BUFSIZ];
+
+ for (j = 0; j < data->count; ++j) {
+ PrintF(output, 33, "#define HTML_%s_%s",
+ data->name,
+ no_dashes(buffer, data->attrs[j].name));
+ fprintf(output, "%2d\n", j);
+ }
+ PrintF(output, 33, "#define HTML_%s_ATTRIBUTES", data->name);
+ fprintf(output, "%2d\n", data->count);
+ fprintf(output, "\n");
+}
+
+static void dump_header(FILE *output, const SGML_dtd * dtd)
+{
+ const char *marker = "hdr_HTMLDTD_H";
+ int j;
+
+ unsigned count = 0;
+ AttrInfo *data = sorted_attrs(dtd, &count);
+
+ fprintf(output, "/* %cLynxId%c */\n", '$', '$');
+ fprintf(output, "#ifndef %s\n", marker);
+ fprintf(output, "#define %s 1\n\n", marker);
+
+ NOTE("#ifdef __cplusplus");
+ NOTE("extern \"C\" {");
+ NOTE("#endif");
+
+ NOTE("/*");
+ NOTE("");
+ NOTE(" Element Numbers");
+ NOTE("");
+ NOTE(" Must Match all tables by element!");
+ NOTE(" These include tables in HTMLDTD.c");
+ NOTE(" and code in HTML.c.");
+ NOTE("");
+ NOTE(" */");
+
+ fprintf(output, " typedef enum {\n");
+ for (j = 0; j < dtd->number_of_tags; ++j) {
+ if (dtd->tags[j].alias) {
+ fprintf(output, "\tHTML_%s_%d,\n", dtd->tags[j].name,
+ dtd->tags[j].alias + 1);
+ } else {
+ fprintf(output, "\tHTML_%s,\n", dtd->tags[j].name);
+ }
+ }
+ NOTE("\tHTML_ALT_OBJECT");
+ NOTE(" } HTMLElement;\n");
+ NOTE("/* Notes: HTML.c uses a different extension of the");
+ NOTE(" HTML_ELEMENTS space privately, see");
+ NOTE(" HTNestedList.h.");
+ NOTE("");
+ NOTE(" Do NOT replace HTML_ELEMENTS with");
+ NOTE(" TABLESIZE(mumble_dtd.tags).");
+ NOTE("");
+ NOTE(" Keep the following defines in synch with");
+ NOTE(" the above enum!");
+ NOTE(" */");
+ NOTE("");
+ NOTE("/* # of elements generally visible to Lynx code */");
+ fprintf(output, "#define HTML_ELEMENTS %d\n", dtd->number_of_tags);
+ NOTE("");
+ NOTE("/* # of elements visible to SGML parser */");
+ fprintf(output, "#define HTML_ALL_ELEMENTS %d\n", dtd->number_of_tags + 1);
+ NOTE("");
+ NOTE("/*");
+ NOTE("");
+ NOTE(" Attribute numbers");
+ NOTE("");
+ NOTE(" Identifier is HTML_<element>_<attribute>.");
+ NOTE(" These must match the tables in HTML.c!");
+ NOTE("");
+ NOTE(" */");
+
+ /* output the sorted list */
+ for (j = 0; j < (int) count; ++j) {
+ dump_hdr_attr(output, data + j);
+ }
+ free(data);
+
+ NOTE("#ifdef __cplusplus");
+ NOTE("}");
+ NOTE("#endif");
+
+ fprintf(output, "#endif\t\t\t\t/* %s */\n", marker);
+}
+
+static void dump_flat_attrs(FILE *output,
+ const attr * attributes,
+ int number_of_attributes)
+{
+ int n;
+
+ fprintf(output, "\t\t" FMT_NUM_ATTRS "\n", number_of_attributes);
+ for (n = 0; n < number_of_attributes; ++n) {
+ fprintf(output, "\t\t\t" FMT_ONE_ATTR "\n", n,
+#ifdef USE_PRETTYSRC
+ attributes[n].type,
+#else
+ 0, /* need placeholder for source-compat */
+#endif
+ attributes[n].name
+ );
+ }
+}
+
+static void dump_flat_attr_types(FILE *output, const AttrType * attr_types)
+{
+ const AttrType *p = sorted_AttrTypes(attr_types);
+ int number = len_AttrTypes(attr_types);
+
+ fprintf(output, "\t\t" FMT_NUM_ATTR_TYPES "\n", number);
+
+ if (p != 0) {
+ while (p->name != 0) {
+ fprintf(output, "\t\t\t%s\n", p->name);
+ ++p;
+ }
+ }
+}
+
+static void dump_flat_SGMLContent(FILE *output, const char *name, SGMLContent contents)
+{
+ fprintf(output, "\t\t%s: %s\n", name, SGMLContent2s(contents));
+}
+
+#define DUMP(name) \
+ if (theClass & Tgc_##name) {\
+ fprintf(output, " " #name); \
+ theClass &= ~(Tgc_##name); \
+ }
+
+static void dump_flat_TagClass(FILE *output, const char *name, TagClass theClass)
+{
+ fprintf(output, "\t\t%s:", name);
+ DUMP(FONTlike);
+ DUMP(EMlike);
+ DUMP(MATHlike);
+ DUMP(Alike);
+ DUMP(formula);
+ DUMP(TRlike);
+ DUMP(SELECTlike);
+ DUMP(FORMlike);
+ DUMP(Plike);
+ DUMP(DIVlike);
+ DUMP(LIlike);
+ DUMP(ULlike);
+ DUMP(BRlike);
+ DUMP(APPLETlike);
+ DUMP(HRlike);
+ DUMP(MAPlike);
+ DUMP(outer);
+ DUMP(BODYlike);
+ DUMP(HEADstuff);
+ DUMP(same);
+ DUMP(DELlike);
+ if (theClass)
+ fprintf(output, " OOPS:%#x", theClass);
+ fprintf(output, "\n");
+}
+
+#undef DUMP
+
+#define DUMP(name) \
+ if (theFlags & Tgf_##name) {\
+ fprintf(output, " " #name); \
+ theFlags &= ~(Tgf_##name); \
+ }
+
+static void dump_flat_TagFlags(FILE *output, const char *name, TagFlags theFlags)
+{
+ fprintf(output, "\t\t%s:", name);
+ DUMP(endO);
+ DUMP(startO);
+ DUMP(mafse);
+ DUMP(strict);
+ DUMP(nreie);
+ DUMP(frecyc);
+ DUMP(nolyspcl);
+ if (theFlags)
+ fprintf(output, " OOPS:%#x", theFlags);
+ fprintf(output, "\n");
+}
+
+#undef DUMP
+
+static void dump_flat_HTTag(FILE *output, unsigned n, HTTag * tag)
+{
+ fprintf(output, "\t%u:%s\n", n, EXT_name(tag));
+#ifdef USE_JUSTIFY_ELTS
+ fprintf(output, "\t\t%s\n", tag->can_justify ? "justify" : "nojustify");
+#endif
+ dump_flat_attrs(output, tag->attributes, AttrCount(tag));
+ dump_flat_attr_types(output, tag->attr_types);
+ dump_flat_SGMLContent(output, "contents", tag->contents);
+ dump_flat_TagClass(output, "tagclass", tag->tagclass);
+ dump_flat_TagClass(output, "contains", tag->contains);
+ dump_flat_TagClass(output, "icontains", tag->icontains);
+ dump_flat_TagClass(output, "contained", tag->contained);
+ dump_flat_TagClass(output, "icontained", tag->icontained);
+ dump_flat_TagClass(output, "canclose", tag->canclose);
+ dump_flat_TagFlags(output, "flags", tag->flags);
+}
+
+static int count_attr_types(AttrType * attr_types, HTTag * tag)
+{
+ int count = 0;
+ const AttrType *p;
+ AttrType *q;
+
+ if ((p = tag->attr_types) != 0) {
+ while (p->name != 0) {
+ if ((q = attr_types) != 0) {
+ while (q->name != 0) {
+ if (!strcmp(q->name, p->name)) {
+ --count;
+ break;
+ }
+ ++q;
+ }
+ *q = *p;
+ }
+ ++count;
+ ++p;
+ }
+ }
+ return count;
+}
+
+static void dump_flatfile(FILE *output, const SGML_dtd * dtd)
+{
+ AttrType *attr_types = 0;
+ int pass;
+ unsigned count = 0;
+ unsigned n;
+
+ /* merge all of the attr_types data */
+ for (pass = 0; pass < 2; ++pass) {
+ for (n = 0; (int) n < dtd->number_of_tags; ++n) {
+ count += count_attr_types(attr_types, &(dtd->tags[n]));
+ }
+ if (pass == 0) {
+ attr_types = typecallocn(AttrType, count + 1);
+ count = 0;
+ } else {
+ count = len_AttrTypes(attr_types);
+ qsort(attr_types, count, sizeof(*attr_types), compare_attr_types);
+ fprintf(output, FMT_NUM_ATTR_TYPES "\n", count);
+ for (n = 0; n < count; ++n) {
+ fprintf(output, "\t%d:%s\n", n, attr_types[n].name);
+ dump_flat_attrs(output, attr_types[n].list,
+ len_AttrList(attr_types[n].list));
+ }
+ }
+ }
+
+ fprintf(output, "%d tags\n", dtd->number_of_tags);
+ for (n = 0; (int) n < dtd->number_of_tags; ++n) {
+ dump_flat_HTTag(output, n, &(dtd->tags[n]));
+ }
+#if 0
+ fprintf(output, "%d entities\n", dtd->number_of_entities);
+ for (n = 0; n < dtd->number_of_entities; ++n) {
+ }
+#endif
+}
+
+#define LOAD(name) \
+ if (!strcmp(data, #name)) {\
+ *theClass |= Tgc_##name; \
+ continue; \
+ }
+
+static int load_flat_TagClass(FILE *input, const char *name, TagClass * theClass)
+{
+ char prefix[80];
+ char *next = get_line(input);
+ char *data;
+ int result = 0;
+
+ *theClass = 0;
+ if (next != 0) {
+ sprintf(prefix, "\t\t%s:", name);
+ data = strtok(next, "\n ");
+
+ if (data != 0 && !strcmp(data, prefix)) {
+ result = 1;
+
+ while ((data = strtok(NULL, "\n ")) != 0) {
+
+ LOAD(FONTlike);
+ LOAD(EMlike);
+ LOAD(MATHlike);
+ LOAD(Alike);
+ LOAD(formula);
+ LOAD(TRlike);
+ LOAD(SELECTlike);
+ LOAD(FORMlike);
+ LOAD(Plike);
+ LOAD(DIVlike);
+ LOAD(LIlike);
+ LOAD(ULlike);
+ LOAD(BRlike);
+ LOAD(APPLETlike);
+ LOAD(HRlike);
+ LOAD(MAPlike);
+ LOAD(outer);
+ LOAD(BODYlike);
+ LOAD(HEADstuff);
+ LOAD(same);
+ LOAD(DELlike);
+
+ warning("Unexpected TagClass '%s'", data);
+ result = 0;
+ break;
+ }
+ } else if (data) {
+ warning("load_flat_TagClass: '%s' vs '%s'", data, prefix);
+ }
+ free(next);
+ } else {
+ warning("Did not find contents");
+ }
+ return result;
+}
+
+#undef LOAD
+
+#define LOAD(name) \
+ if (!strcmp(data, #name)) {\
+ *flags |= Tgf_##name; \
+ continue; \
+ }
+
+static int load_flat_TagFlags(FILE *input, const char *name, TagFlags * flags)
+{
+ char prefix[80];
+ char *next = get_line(input);
+ char *data;
+ int result = 0;
+
+ *flags = 0;
+ if (next != 0) {
+ sprintf(prefix, "\t\t%s:", name);
+ data = strtok(next, "\n ");
+
+ if (data != 0 && !strcmp(data, prefix)) {
+ result = 1;
+
+ while ((data = strtok(NULL, "\n ")) != 0) {
+
+ LOAD(endO);
+ LOAD(startO);
+ LOAD(mafse);
+ LOAD(strict);
+ LOAD(nreie);
+ LOAD(frecyc);
+ LOAD(nolyspcl);
+
+ warning("Unexpected TagFlag '%s'", data);
+ result = 0;
+ break;
+ }
+ } else if (data) {
+ warning("load_flat_TagFlags: '%s' vs '%s'", data, prefix);
+ }
+ free(next);
+ }
+ return result;
+}
+
+#undef LOAD
+
+static int load_flat_AttrList(FILE *input, AttrList * attrs, int *length)
+{
+ attr *attributes;
+ int j, jcmp, code;
+ int result = 1;
+ char name[MY_LIMIT];
+ char *next;
+ char trail[MY_LIMIT];
+
+#ifdef USE_PRETTYSRC
+ int atype;
+#endif
+
+ next = get_line(input);
+ if (sscanf(next, FMT_NUM_ATTRS FMT_WS, length, trail) == 2
+ && *length > 0
+ && (attributes = typecallocn(attr, *length + 1)) != 0) {
+ *attrs = attributes;
+ for (j = 0; j < *length; ++j) {
+ next = get_line(input);
+ code = sscanf(next, FMT_ONE_ATTR FMT_WS, &jcmp, &atype, name, trail);
+ if (code == NUM_ONE_ATTR) {
+ if (j != jcmp)
+ warning("resequence %s from %d to %d", name, jcmp, j);
+ attributes[j].name = strdup(name);
+#ifdef USE_PRETTYSRC
+ attributes[j].type = atype;
+#endif
+ } else {
+ warning("Did not find attributes");
+ result = 0;
+ break;
+ }
+ }
+ if (*length > 1 && result)
+ qsort(attributes, *length, sizeof(attributes[0]), compare_attr);
+ } else {
+ warning("expected attribute count:\n%s", next);
+ }
+ return result;
+}
+
+/* find the given attribute in the list of attr_types */
+static int find_attribute(const char *attribute, const AttrType * attr_types)
+{
+ int j, k;
+ int found = -1;
+
+ for (j = 0; attr_types[j].name; ++j) {
+ AttrList list = attr_types[j].list;
+
+ for (k = 0; list[k].name; ++k) {
+ if (!strcmp(attribute, list[k].name)) {
+ if (found >= 0)
+ warning("attribute %s is in attr_types %s and %s",
+ attribute,
+ attr_types[found].name,
+ attr_types[j].name);
+ found = j;
+ }
+ }
+ }
+ return (found >= 0);
+}
+
+static int load_flat_HTTag(FILE *input, HTTag * tag, AttrType * allTypes)
+{
+ int result = 0;
+ unsigned ncmp = 0;
+ char name[MY_LIMIT];
+ char trail[MY_LIMIT];
+ char *next;
+ int code;
+ int j;
+
+ next = get_line(input);
+ code = sscanf(next, "%d:%s" FMT_WS, &ncmp, name, trail);
+ if (code == 3) {
+ result = 1;
+ tag->alias = 0;
+ tag->aliases = 0;
+ for (j = 0; name[j] != '\0'; ++j) {
+ int ch = UCH(name[j]);
+
+ if (!isalnum(ch)) {
+ if (strchr(alias_codes, ch) != NULL) {
+ tag->alias = (ch - '!') + 1;
+ name[j] = '\0';
+ } else {
+ result = 0;
+ }
+ break;
+ }
+ }
+ tag->name = strdup(name);
+#ifdef USE_COLOR_STYLE
+ tag->name_len = strlen(tag->name);
+#endif
+#ifdef USE_JUSTIFY_ELTS
+ next = get_line(input);
+ if (sscanf(next, "%s" FMT_WS, name, trail) == 2) {
+ tag->can_justify = !strcmp(name, "justify");
+ } else {
+ warning("Did not find can_justify");
+ result = 0;
+ }
+#endif
+ if (result) {
+ result = load_flat_AttrList(input, &(tag->attributes), &(tag->number_of_attributes));
+ }
+ if (result) {
+ AttrType *myTypes;
+ int k, count;
+
+ if ((count = read_num_attr_types(input)) > 0
+ && (myTypes = typecallocn(AttrType, count + 1)) != 0) {
+ tag->attr_types = myTypes;
+ for (k = 0; k < count; ++k) {
+ next = get_line(input);
+ if (next != 0
+ && sscanf(next, "%s" FMT_WS, name, trail)) {
+ int found = -1;
+
+ for (j = 0; allTypes[j].name != 0; ++j) {
+ if (!strcmp(allTypes[j].name, name)) {
+ myTypes[k].name = strdup(name);
+ myTypes[k].list = allTypes[j].list;
+ found = k;
+ break;
+ }
+ }
+ if (found < 0) {
+ warning("not found in attr_types: %s", name);
+ continue;
+ }
+ } else {
+ result = 0;
+ break;
+ }
+ }
+ if (result) {
+ if (count > 1)
+ qsort(myTypes,
+ count,
+ sizeof(myTypes[0]),
+ compare_attr_types);
+ for (k = 0; k < tag->number_of_attributes; ++k) {
+ if (!find_attribute(tag->attributes[k].name, tag->attr_types)) {
+ warning("%s attribute %s not found in attr_types",
+ tag->name, tag->attributes[k].name);
+ }
+ }
+ }
+ }
+ }
+ if (result) {
+
+ next = get_line(input);
+ if (next != 0
+ && sscanf(next, "\t\tcontents: %s" FMT_WS, name, trail)) {
+ tag->contents = s2SGMLContent(name);
+ free(next);
+ } else {
+ warning("Did not find contents");
+ result = 0;
+ }
+ }
+ if (result) {
+ result = load_flat_TagClass(input, "tagclass", &(tag->tagclass));
+ }
+ if (result) {
+ result = load_flat_TagClass(input, "contains", &(tag->contains));
+ }
+ if (result) {
+ result = load_flat_TagClass(input, "icontains", &(tag->icontains));
+ }
+ if (result) {
+ result = load_flat_TagClass(input, "contained", &(tag->contained));
+ }
+ if (result) {
+ result = load_flat_TagClass(input, "icontained", &(tag->icontained));
+ }
+ if (result) {
+ result = load_flat_TagClass(input, "canclose", &(tag->canclose));
+ }
+ if (result) {
+ result = load_flat_TagFlags(input, "flags", &(tag->flags));
+ }
+ } else {
+ warning("load_flat_HTTag error");
+ }
+ return result;
+}
+
+static int load_flat_AttrType(FILE *input, AttrType * types, size_t ncmp)
+{
+ int result = 0;
+ int ntst;
+ char name[MY_LIMIT];
+ char trail[MY_LIMIT];
+ char *next;
+
+ next = get_line(input);
+ if (sscanf(next, "%d:%s" FMT_WS, &ntst, name, trail) == 3
+ && (ntst == (int) ncmp)) {
+ result = 1;
+ types->name = strdup(name);
+ if (!load_flat_AttrList(input, &(types->list), &ntst))
+ result = 0;
+ } else {
+ warning("expected a tag:\n%s", next);
+ }
+ return result;
+}
+
+static int compare_tags(const void *a, const void *b)
+{
+ const HTTag *p = (const HTTag *) a;
+ const HTTag *q = (const HTTag *) b;
+ int result = 0;
+
+ if ((result = strcmp(p->name, q->name)) == 0) {
+ result = p->alias - q->alias;
+ }
+ return result;
+}
+
+static SGML_dtd *load_flatfile(FILE *input)
+{
+ AttrType *attr_types = 0;
+ SGML_dtd *result = 0;
+ char *next;
+ size_t n;
+ int number_of_attrs = 0;
+ int number_of_tags = 0;
+ HTTag *tag;
+ int code;
+ char trail[MY_LIMIT];
+
+ if ((number_of_attrs = read_num_attr_types(input)) <= 0) {
+ ;
+ } else if ((attr_types = typecallocn(AttrType, number_of_attrs + 1)) == 0) {
+ failed("calloc attr_types");
+ }
+
+ for (n = 0; n < (size_t) number_of_attrs; ++n) {
+ if (!load_flat_AttrType(input, attr_types + n, n)) {
+ break;
+ }
+ }
+
+ next = get_line(input);
+ code = sscanf(next, "%d tags" FMT_WS, &number_of_tags, trail);
+ if (code == 2) {
+ if ((result = typecalloc(SGML_dtd)) != 0
+ && (result->tags = typecallocn(HTTag, number_of_tags + 2)) != 0) {
+ for (n = 0; n < (size_t) number_of_tags; ++n) {
+ if (load_flat_HTTag(input, &(result->tags[n]), attr_types)) {
+ result->number_of_tags = (n + 1);
+ } else {
+ break;
+ }
+ }
+ tag = 0;
+ for (n = 0; n < (size_t) number_of_tags; ++n) {
+ if (result->tags[n].name != 0
+ && !strcmp(result->tags[n].name, "OBJECT")) {
+ tag = result->tags + number_of_tags;
+ *tag = result->tags[n];
+ tag->contents = SGML_MIXED;
+ tag->flags = Tgf_strict;
+ break;
+ }
+ }
+ if (tag == 0) {
+ warning("Did not find OBJECT tag");
+ result = 0;
+ } else {
+ qsort(result->tags, number_of_tags, sizeof(HTTag), compare_tags);
+ }
+ }
+ } else {
+ warning("expected tag count:\n%s", next);
+ }
+ return result;
+}
+
+static void cross_check(FILE *output, const SGML_dtd * the_dtd, int level)
+{
+ int ft;
+
+ fprintf(output, "Cross-check HTML DTD:\n");
+ fprintf(output, "\n");
+ /* make a sorted list of tags */
+ /* for each tag in the list, find the classes it might contain */
+ for (ft = 0; ft < the_dtd->number_of_tags; ++ft) {
+ int xc;
+ HTTag *ftag = &(the_dtd->tags[ft]);
+
+ /* for each contained-class, check if it says it can be contained */
+ fprintf(output, "tag %s\n", EXT_name(ftag));
+ for (xc = 0; class_list[xc].name != 0; ++xc) {
+ int rt;
+ int direct;
+ int passes = 2;
+
+ /* most of the tags are (should be) symmetric */
+ if (level <= 1) {
+ BOOL same = TRUE;
+
+ if ((ftag->contains & class_list[xc].tagclass)
+ != (ftag->icontains & class_list[xc].tagclass)) {
+ same = FALSE;
+ } else if ((ftag->contains & class_list[xc].tagclass) == 0) {
+ continue;
+ } else if (0) {
+ HTTag *rtag;
+
+ for (rt = 0; rt < the_dtd->number_of_tags; ++rt) {
+ rtag = &(the_dtd->tags[rt]);
+ if (ftag == rtag)
+ continue;
+ if ((ftag->contains & rtag->tagclass) == 0)
+ continue;
+ if ((rtag->contained & ftag->tagclass)
+ != (rtag->icontained & ftag->tagclass)) {
+ same = FALSE;
+ break;
+ }
+ }
+ }
+ if (same)
+ passes = 1;
+ }
+
+ for (direct = 0; direct < passes; ++direct) {
+ TagClass check = (direct ? ftag->contains : ftag->icontains);
+ BOOL first = TRUE;
+
+ if ((check &= class_list[xc].tagclass) == 0)
+ continue;
+
+ for (rt = 0; rt < the_dtd->number_of_tags; ++rt) {
+ HTTag *rtag = &(the_dtd->tags[rt]);
+ TagClass check2 = (direct ? rtag->contained : rtag->icontained);
+
+ if (rt == ft)
+ continue;
+ if ((check & rtag->tagclass) == 0)
+ continue;
+ if ((check2 & ftag->tagclass) == 0)
+ continue;
+ if (first) {
+ if (passes == 2) {
+ fprintf(output, "\t%s (%s)\n",
+ class_list[xc].name,
+ direct ? "direct" : "indirect");
+ } else {
+ fprintf(output, "\t%s\n",
+ class_list[xc].name);
+ }
+ }
+ fprintf(output, "%s%s",
+ first ? "\t\t" : " ",
+ EXT_name(rtag));
+ first = FALSE;
+ }
+ if (first) {
+ if (level > 1) {
+ if (passes == 2) {
+ fprintf(output, "\t%s (%s)\n",
+ class_list[xc].name,
+ direct ? "direct" : "indirect");
+ } else {
+ fprintf(output, "\t%s\n",
+ class_list[xc].name);
+ }
+ fprintf(output, "\t\t(missing)\n");
+ }
+ } else {
+ fprintf(output, "\n");
+ }
+ }
+ }
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ const SGML_dtd *the_dtd = &HTML_dtd;
+ int ch;
+ int dtd_version = 0;
+ int c_option = FALSE;
+ int h_option = FALSE;
+ int l_option = FALSE;
+ int x_option = 0;
+ FILE *input = stdin;
+ FILE *output = stdout;
+
+ input_filename = "<builtin>";
+ input_lineno = 0;
+
+ while ((ch = getopt(argc, argv, GETOPT)) != -1) {
+ switch (ch) {
+ case 'c':
+ c_option = TRUE;
+ break;
+ case 'h':
+ h_option = TRUE;
+ break;
+ case 'l':
+ l_option = TRUE;
+ input = fopen((input_filename = optarg), "r");
+ if (input == 0)
+ failed(optarg);
+ break;
+ case 'o':
+ output = fopen(optarg, "w");
+ if (output == 0)
+ failed(optarg);
+ break;
+ case 't':
+ dtd_version = 1;
+ break;
+ case 's':
+ dtd_version = 0;
+ break;
+ case 'x':
+ ++x_option;
+ break;
+ default:
+ usage();
+ }
+ }
+
+ HTSwitchDTD(dtd_version);
+ if (l_option)
+ the_dtd = load_flatfile(input);
+
+ if (the_dtd != 0) {
+ if (c_option)
+ dump_source(output, the_dtd, dtd_version);
+ if (h_option)
+ dump_header(output, the_dtd);
+ if (x_option)
+ cross_check(output, the_dtd, x_option);
+ if (!c_option && !h_option && !x_option)
+ dump_flatfile(output, the_dtd);
+ }
+
+ return EXIT_SUCCESS;
+}
diff --git a/WWW/Library/Implementation/hdr_HTMLDTD.h b/WWW/Library/Implementation/hdr_HTMLDTD.h
new file mode 100644
index 0000000..21888d0
--- /dev/null
+++ b/WWW/Library/Implementation/hdr_HTMLDTD.h
@@ -0,0 +1,1000 @@
+/* $LynxId: hdr_HTMLDTD.h,v 1.25 2022/09/28 22:26:32 tom Exp $ */
+#ifndef hdr_HTMLDTD_H
+#define hdr_HTMLDTD_H 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+
+ Element Numbers
+
+ Must Match all tables by element!
+ These include tables in HTMLDTD.c
+ and code in HTML.c.
+
+ */
+ typedef enum {
+ HTML_A,
+ HTML_ABBR,
+ HTML_ACRONYM,
+ HTML_ADDRESS,
+ HTML_APPLET,
+ HTML_AREA,
+ HTML_ARTICLE,
+ HTML_ASIDE,
+ HTML_AU,
+ HTML_AUTHOR,
+ HTML_B,
+ HTML_BANNER,
+ HTML_BASE,
+ HTML_BASEFONT,
+ HTML_BDO,
+ HTML_BGSOUND,
+ HTML_BIG,
+ HTML_BLINK,
+ HTML_BLOCKQUOTE,
+ HTML_BODY,
+ HTML_BODYTEXT,
+ HTML_BQ,
+ HTML_BR,
+ HTML_BUTTON,
+ HTML_CAPTION,
+ HTML_CENTER,
+ HTML_CITE,
+ HTML_CODE,
+ HTML_COL,
+ HTML_COLGROUP,
+ HTML_COMMENT,
+ HTML_CREDIT,
+ HTML_DD,
+ HTML_DEL,
+ HTML_DEL_2,
+ HTML_DFN,
+ HTML_DIR,
+ HTML_DIV,
+ HTML_DL,
+ HTML_DLC,
+ HTML_DT,
+ HTML_EM,
+ HTML_EMBED,
+ HTML_FIELDSET,
+ HTML_FIG,
+ HTML_FIGURE,
+ HTML_FN,
+ HTML_FONT,
+ HTML_FOOTER,
+ HTML_FORM,
+ HTML_FRAME,
+ HTML_FRAMESET,
+ HTML_H1,
+ HTML_H2,
+ HTML_H3,
+ HTML_H4,
+ HTML_H5,
+ HTML_H6,
+ HTML_HEAD,
+ HTML_HEADER,
+ HTML_HR,
+ HTML_HTML,
+ HTML_HY,
+ HTML_I,
+ HTML_IFRAME,
+ HTML_IMG,
+ HTML_INPUT,
+ HTML_INS,
+ HTML_INS_2,
+ HTML_ISINDEX,
+ HTML_KBD,
+ HTML_KEYGEN,
+ HTML_LABEL,
+ HTML_LEGEND,
+ HTML_LH,
+ HTML_LI,
+ HTML_LINK,
+ HTML_LISTING,
+ HTML_MAIN,
+ HTML_MAP,
+ HTML_MARQUEE,
+ HTML_MATH,
+ HTML_MENU,
+ HTML_META,
+ HTML_NAV,
+ HTML_NEXTID,
+ HTML_NOFRAMES,
+ HTML_NOTE,
+ HTML_OBJECT,
+ HTML_OL,
+ HTML_OPTION,
+ HTML_OVERLAY,
+ HTML_P,
+ HTML_PARAM,
+ HTML_PLAINTEXT,
+ HTML_PRE,
+ HTML_Q,
+ HTML_S,
+ HTML_SAMP,
+ HTML_SCRIPT,
+ HTML_SECTION,
+ HTML_SELECT,
+ HTML_SHY,
+ HTML_SMALL,
+ HTML_SPAN,
+ HTML_SPOT,
+ HTML_STRIKE,
+ HTML_STRONG,
+ HTML_STYLE,
+ HTML_SUB,
+ HTML_SUP,
+ HTML_TAB,
+ HTML_TABLE,
+ HTML_TBODY,
+ HTML_TD,
+ HTML_TEXTAREA,
+ HTML_TEXTFLOW,
+ HTML_TFOOT,
+ HTML_TH,
+ HTML_THEAD,
+ HTML_TITLE,
+ HTML_TR,
+ HTML_TT,
+ HTML_U,
+ HTML_UL,
+ HTML_VAR,
+ HTML_WBR,
+ HTML_XMP,
+ HTML_ALT_OBJECT
+ } HTMLElement;
+
+/* Notes: HTML.c uses a different extension of the
+ HTML_ELEMENTS space privately, see
+ HTNestedList.h.
+
+ Do NOT replace HTML_ELEMENTS with
+ TABLESIZE(mumble_dtd.tags).
+
+ Keep the following defines in synch with
+ the above enum!
+ */
+
+/* # of elements generally visible to Lynx code */
+#define HTML_ELEMENTS 128
+
+/* # of elements visible to SGML parser */
+#define HTML_ALL_ELEMENTS 129
+
+/*
+
+ Attribute numbers
+
+ Identifier is HTML_<element>_<attribute>.
+ These must match the tables in HTML.c!
+
+ */
+#define HTML_A_ACCESSKEY 0
+#define HTML_A_CHARSET 1
+#define HTML_A_CLASS 2
+#define HTML_A_CLEAR 3
+#define HTML_A_COORDS 4
+#define HTML_A_DIR 5
+#define HTML_A_HREF 6
+#define HTML_A_HREFLANG 7
+#define HTML_A_ID 8
+#define HTML_A_ISMAP 9
+#define HTML_A_LANG 10
+#define HTML_A_MD 11
+#define HTML_A_NAME 12
+#define HTML_A_NOTAB 13
+#define HTML_A_ONBLUR 14
+#define HTML_A_ONFOCUS 15
+#define HTML_A_REL 16
+#define HTML_A_REV 17
+#define HTML_A_SHAPE 18
+#define HTML_A_STYLE 19
+#define HTML_A_TABINDEX 20
+#define HTML_A_TARGET 21
+#define HTML_A_TITLE 22
+#define HTML_A_TYPE 23
+#define HTML_A_URN 24
+#define HTML_A_ATTRIBUTES 25
+
+#define HTML_ADDRESS_CLASS 0
+#define HTML_ADDRESS_CLEAR 1
+#define HTML_ADDRESS_DIR 2
+#define HTML_ADDRESS_ID 3
+#define HTML_ADDRESS_LANG 4
+#define HTML_ADDRESS_NOWRAP 5
+#define HTML_ADDRESS_STYLE 6
+#define HTML_ADDRESS_TITLE 7
+#define HTML_ADDRESS_ATTRIBUTES 8
+
+#define HTML_APPLET_ALIGN 0
+#define HTML_APPLET_ALT 1
+#define HTML_APPLET_CLASS 2
+#define HTML_APPLET_CLEAR 3
+#define HTML_APPLET_CODE 4
+#define HTML_APPLET_CODEBASE 5
+#define HTML_APPLET_DIR 6
+#define HTML_APPLET_DOWNLOAD 7
+#define HTML_APPLET_HEIGHT 8
+#define HTML_APPLET_HSPACE 9
+#define HTML_APPLET_ID 10
+#define HTML_APPLET_LANG 11
+#define HTML_APPLET_NAME 12
+#define HTML_APPLET_STYLE 13
+#define HTML_APPLET_TITLE 14
+#define HTML_APPLET_VSPACE 15
+#define HTML_APPLET_WIDTH 16
+#define HTML_APPLET_ATTRIBUTES 17
+
+#define HTML_AREA_ACCESSKEY 0
+#define HTML_AREA_ALT 1
+#define HTML_AREA_CLASS 2
+#define HTML_AREA_CLEAR 3
+#define HTML_AREA_COORDS 4
+#define HTML_AREA_DIR 5
+#define HTML_AREA_HREF 6
+#define HTML_AREA_ID 7
+#define HTML_AREA_LANG 8
+#define HTML_AREA_NOHREF 9
+#define HTML_AREA_NOTAB 10
+#define HTML_AREA_ONBLUR 11
+#define HTML_AREA_ONFOCUS 12
+#define HTML_AREA_SHAPE 13
+#define HTML_AREA_STYLE 14
+#define HTML_AREA_TABINDEX 15
+#define HTML_AREA_TARGET 16
+#define HTML_AREA_TITLE 17
+#define HTML_AREA_ATTRIBUTES 18
+
+#define HTML_BASE_CLASS 0
+#define HTML_BASE_HREF 1
+#define HTML_BASE_ID 2
+#define HTML_BASE_STYLE 3
+#define HTML_BASE_TARGET 4
+#define HTML_BASE_TITLE 5
+#define HTML_BASE_ATTRIBUTES 6
+
+#define HTML_BGSOUND_CLASS 0
+#define HTML_BGSOUND_CLEAR 1
+#define HTML_BGSOUND_DIR 2
+#define HTML_BGSOUND_ID 3
+#define HTML_BGSOUND_LANG 4
+#define HTML_BGSOUND_LOOP 5
+#define HTML_BGSOUND_SRC 6
+#define HTML_BGSOUND_STYLE 7
+#define HTML_BGSOUND_TITLE 8
+#define HTML_BGSOUND_ATTRIBUTES 9
+
+#define HTML_BODY_ALINK 0
+#define HTML_BODY_BACKGROUND 1
+#define HTML_BODY_BGCOLOR 2
+#define HTML_BODY_CLASS 3
+#define HTML_BODY_CLEAR 4
+#define HTML_BODY_DIR 5
+#define HTML_BODY_ID 6
+#define HTML_BODY_LANG 7
+#define HTML_BODY_LINK 8
+#define HTML_BODY_ONLOAD 9
+#define HTML_BODY_ONUNLOAD 10
+#define HTML_BODY_STYLE 11
+#define HTML_BODY_TEXT 12
+#define HTML_BODY_TITLE 13
+#define HTML_BODY_VLINK 14
+#define HTML_BODY_ATTRIBUTES 15
+
+#define HTML_BODYTEXT_CLASS 0
+#define HTML_BODYTEXT_CLEAR 1
+#define HTML_BODYTEXT_DATA 2
+#define HTML_BODYTEXT_DIR 3
+#define HTML_BODYTEXT_ID 4
+#define HTML_BODYTEXT_LANG 5
+#define HTML_BODYTEXT_NAME 6
+#define HTML_BODYTEXT_OBJECT 7
+#define HTML_BODYTEXT_REF 8
+#define HTML_BODYTEXT_STYLE 9
+#define HTML_BODYTEXT_TITLE 10
+#define HTML_BODYTEXT_TYPE 11
+#define HTML_BODYTEXT_VALUE 12
+#define HTML_BODYTEXT_VALUETYPE 13
+#define HTML_BODYTEXT_ATTRIBUTES 14
+
+#define HTML_BQ_CITE 0
+#define HTML_BQ_CLASS 1
+#define HTML_BQ_CLEAR 2
+#define HTML_BQ_DIR 3
+#define HTML_BQ_ID 4
+#define HTML_BQ_LANG 5
+#define HTML_BQ_NOWRAP 6
+#define HTML_BQ_STYLE 7
+#define HTML_BQ_TITLE 8
+#define HTML_BQ_ATTRIBUTES 9
+
+#define HTML_BUTTON_ACCESSKEY 0
+#define HTML_BUTTON_CLASS 1
+#define HTML_BUTTON_CLEAR 2
+#define HTML_BUTTON_DIR 3
+#define HTML_BUTTON_DISABLED 4
+#define HTML_BUTTON_FORMACTION 5
+#define HTML_BUTTON_ID 6
+#define HTML_BUTTON_LANG 7
+#define HTML_BUTTON_NAME 8
+#define HTML_BUTTON_ONBLUR 9
+#define HTML_BUTTON_ONFOCUS 10
+#define HTML_BUTTON_READONLY 11
+#define HTML_BUTTON_STYLE 12
+#define HTML_BUTTON_TABINDEX 13
+#define HTML_BUTTON_TITLE 14
+#define HTML_BUTTON_TYPE 15
+#define HTML_BUTTON_VALUE 16
+#define HTML_BUTTON_ATTRIBUTES 17
+
+#define HTML_CAPTION_ACCESSKEY 0
+#define HTML_CAPTION_ALIGN 1
+#define HTML_CAPTION_CLASS 2
+#define HTML_CAPTION_CLEAR 3
+#define HTML_CAPTION_DIR 4
+#define HTML_CAPTION_ID 5
+#define HTML_CAPTION_LANG 6
+#define HTML_CAPTION_STYLE 7
+#define HTML_CAPTION_TITLE 8
+#define HTML_CAPTION_ATTRIBUTES 9
+
+#define HTML_COL_ALIGN 0
+#define HTML_COL_CHAR 1
+#define HTML_COL_CHAROFF 2
+#define HTML_COL_CLASS 3
+#define HTML_COL_CLEAR 4
+#define HTML_COL_DIR 5
+#define HTML_COL_ID 6
+#define HTML_COL_LANG 7
+#define HTML_COL_SPAN 8
+#define HTML_COL_STYLE 9
+#define HTML_COL_TITLE 10
+#define HTML_COL_VALIGN 11
+#define HTML_COL_WIDTH 12
+#define HTML_COL_ATTRIBUTES 13
+
+#define HTML_DEL_CITE 0
+#define HTML_DEL_CLASS 1
+#define HTML_DEL_DATETIME 2
+#define HTML_DEL_DIR 3
+#define HTML_DEL_ID 4
+#define HTML_DEL_LANG 5
+#define HTML_DEL_STYLE 6
+#define HTML_DEL_TITLE 7
+#define HTML_DEL_ATTRIBUTES 8
+
+#define HTML_DIV_ALIGN 0
+#define HTML_DIV_CLASS 1
+#define HTML_DIV_CLEAR 2
+#define HTML_DIV_DIR 3
+#define HTML_DIV_ID 4
+#define HTML_DIV_LANG 5
+#define HTML_DIV_STYLE 6
+#define HTML_DIV_TITLE 7
+#define HTML_DIV_ATTRIBUTES 8
+
+#define HTML_DL_CLASS 0
+#define HTML_DL_CLEAR 1
+#define HTML_DL_COMPACT 2
+#define HTML_DL_DIR 3
+#define HTML_DL_ID 4
+#define HTML_DL_LANG 5
+#define HTML_DL_STYLE 6
+#define HTML_DL_TITLE 7
+#define HTML_DL_ATTRIBUTES 8
+
+#define HTML_EMBED_ALIGN 0
+#define HTML_EMBED_ALT 1
+#define HTML_EMBED_BORDER 2
+#define HTML_EMBED_CLASS 3
+#define HTML_EMBED_CLEAR 4
+#define HTML_EMBED_DIR 5
+#define HTML_EMBED_HEIGHT 6
+#define HTML_EMBED_ID 7
+#define HTML_EMBED_IMAGEMAP 8
+#define HTML_EMBED_ISMAP 9
+#define HTML_EMBED_LANG 10
+#define HTML_EMBED_MD 11
+#define HTML_EMBED_NAME 12
+#define HTML_EMBED_NOFLOW 13
+#define HTML_EMBED_PARAMS 14
+#define HTML_EMBED_SRC 15
+#define HTML_EMBED_STYLE 16
+#define HTML_EMBED_TITLE 17
+#define HTML_EMBED_UNITS 18
+#define HTML_EMBED_USEMAP 19
+#define HTML_EMBED_WIDTH 20
+#define HTML_EMBED_ATTRIBUTES 21
+
+#define HTML_FIG_ALIGN 0
+#define HTML_FIG_BORDER 1
+#define HTML_FIG_CLASS 2
+#define HTML_FIG_CLEAR 3
+#define HTML_FIG_DIR 4
+#define HTML_FIG_HEIGHT 5
+#define HTML_FIG_ID 6
+#define HTML_FIG_IMAGEMAP 7
+#define HTML_FIG_ISOBJECT 8
+#define HTML_FIG_LANG 9
+#define HTML_FIG_MD 10
+#define HTML_FIG_NOFLOW 11
+#define HTML_FIG_SRC 12
+#define HTML_FIG_STYLE 13
+#define HTML_FIG_TITLE 14
+#define HTML_FIG_UNITS 15
+#define HTML_FIG_WIDTH 16
+#define HTML_FIG_ATTRIBUTES 17
+
+#define HTML_FONT_CLASS 0
+#define HTML_FONT_CLEAR 1
+#define HTML_FONT_COLOR 2
+#define HTML_FONT_DIR 3
+#define HTML_FONT_END 4
+#define HTML_FONT_FACE 5
+#define HTML_FONT_ID 6
+#define HTML_FONT_LANG 7
+#define HTML_FONT_SIZE 8
+#define HTML_FONT_STYLE 9
+#define HTML_FONT_TITLE 10
+#define HTML_FONT_ATTRIBUTES 11
+
+#define HTML_FORM_ACCEPT 0
+#define HTML_FORM_ACCEPT_CHARSET 1
+#define HTML_FORM_ACTION 2
+#define HTML_FORM_CLASS 3
+#define HTML_FORM_CLEAR 4
+#define HTML_FORM_DIR 5
+#define HTML_FORM_ENCTYPE 6
+#define HTML_FORM_ID 7
+#define HTML_FORM_LANG 8
+#define HTML_FORM_METHOD 9
+#define HTML_FORM_ONRESET 10
+#define HTML_FORM_ONSUBMIT 11
+#define HTML_FORM_SCRIPT 12
+#define HTML_FORM_STYLE 13
+#define HTML_FORM_SUBJECT 14
+#define HTML_FORM_TARGET 15
+#define HTML_FORM_TITLE 16
+#define HTML_FORM_ATTRIBUTES 17
+
+#define HTML_FRAME_CLASS 0
+#define HTML_FRAME_FRAMEBORDER 1
+#define HTML_FRAME_ID 2
+#define HTML_FRAME_LONGDESC 3
+#define HTML_FRAME_MARGINHEIGHT 4
+#define HTML_FRAME_MARGINWIDTH 5
+#define HTML_FRAME_NAME 6
+#define HTML_FRAME_NORESIZE 7
+#define HTML_FRAME_SCROLLING 8
+#define HTML_FRAME_SRC 9
+#define HTML_FRAME_STYLE 10
+#define HTML_FRAME_TITLE 11
+#define HTML_FRAME_ATTRIBUTES 12
+
+#define HTML_FRAMESET_COLS 0
+#define HTML_FRAMESET_ONLOAD 1
+#define HTML_FRAMESET_ONUNLOAD 2
+#define HTML_FRAMESET_ROWS 3
+#define HTML_FRAMESET_ATTRIBUTES 4
+
+#define HTML_GEN_CLASS 0
+#define HTML_GEN_CLEAR 1
+#define HTML_GEN_DIR 2
+#define HTML_GEN_ID 3
+#define HTML_GEN_LANG 4
+#define HTML_GEN_STYLE 5
+#define HTML_GEN_TITLE 6
+#define HTML_GEN_ATTRIBUTES 7
+
+#define HTML_GEN5_CLASS 0
+#define HTML_GEN5_DIR 1
+#define HTML_GEN5_ID 2
+#define HTML_GEN5_LANG 3
+#define HTML_GEN5_ROLE 4
+#define HTML_GEN5_STYLE 5
+#define HTML_GEN5_TITLE 6
+#define HTML_GEN5_ATTRIBUTES 7
+
+#define HTML_H_ALIGN 0
+#define HTML_H_CLASS 1
+#define HTML_H_CLEAR 2
+#define HTML_H_DINGBAT 3
+#define HTML_H_DIR 4
+#define HTML_H_ID 5
+#define HTML_H_LANG 6
+#define HTML_H_MD 7
+#define HTML_H_NOWRAP 8
+#define HTML_H_SEQNUM 9
+#define HTML_H_SKIP 10
+#define HTML_H_SRC 11
+#define HTML_H_STYLE 12
+#define HTML_H_TITLE 13
+#define HTML_H_ATTRIBUTES 14
+
+#define HTML_HR_ALIGN 0
+#define HTML_HR_CLASS 1
+#define HTML_HR_CLEAR 2
+#define HTML_HR_DIR 3
+#define HTML_HR_ID 4
+#define HTML_HR_LANG 5
+#define HTML_HR_MD 6
+#define HTML_HR_NOSHADE 7
+#define HTML_HR_SIZE 8
+#define HTML_HR_SRC 9
+#define HTML_HR_STYLE 10
+#define HTML_HR_TITLE 11
+#define HTML_HR_WIDTH 12
+#define HTML_HR_ATTRIBUTES 13
+
+#define HTML_IFRAME_ALIGN 0
+#define HTML_IFRAME_CLASS 1
+#define HTML_IFRAME_FRAMEBORDER 2
+#define HTML_IFRAME_HEIGHT 3
+#define HTML_IFRAME_ID 4
+#define HTML_IFRAME_LONGDESC 5
+#define HTML_IFRAME_MARGINHEIGHT 6
+#define HTML_IFRAME_MARGINWIDTH 7
+#define HTML_IFRAME_NAME 8
+#define HTML_IFRAME_SCROLLING 9
+#define HTML_IFRAME_SRC 10
+#define HTML_IFRAME_STYLE 11
+#define HTML_IFRAME_TITLE 12
+#define HTML_IFRAME_WIDTH 13
+#define HTML_IFRAME_ATTRIBUTES 14
+
+#define HTML_IMG_ALIGN 0
+#define HTML_IMG_ALT 1
+#define HTML_IMG_BORDER 2
+#define HTML_IMG_CLASS 3
+#define HTML_IMG_CLEAR 4
+#define HTML_IMG_DIR 5
+#define HTML_IMG_HEIGHT 6
+#define HTML_IMG_HSPACE 7
+#define HTML_IMG_ID 8
+#define HTML_IMG_ISMAP 9
+#define HTML_IMG_ISOBJECT 10
+#define HTML_IMG_LANG 11
+#define HTML_IMG_LONGDESC 12
+#define HTML_IMG_MD 13
+#define HTML_IMG_NAME 14
+#define HTML_IMG_SRC 15
+#define HTML_IMG_STYLE 16
+#define HTML_IMG_TITLE 17
+#define HTML_IMG_UNITS 18
+#define HTML_IMG_USEMAP 19
+#define HTML_IMG_VSPACE 20
+#define HTML_IMG_WIDTH 21
+#define HTML_IMG_ATTRIBUTES 22
+
+#define HTML_INPUT_ACCEPT 0
+#define HTML_INPUT_ACCEPT_CHARSET 1
+#define HTML_INPUT_ACCESSKEY 2
+#define HTML_INPUT_ALIGN 3
+#define HTML_INPUT_ALT 4
+#define HTML_INPUT_CHECKED 5
+#define HTML_INPUT_CLASS 6
+#define HTML_INPUT_CLEAR 7
+#define HTML_INPUT_DIR 8
+#define HTML_INPUT_DISABLED 9
+#define HTML_INPUT_ERROR 10
+#define HTML_INPUT_HEIGHT 11
+#define HTML_INPUT_ID 12
+#define HTML_INPUT_ISMAP 13
+#define HTML_INPUT_LANG 14
+#define HTML_INPUT_MAX 15
+#define HTML_INPUT_MAXLENGTH 16
+#define HTML_INPUT_MD 17
+#define HTML_INPUT_MIN 18
+#define HTML_INPUT_NAME 19
+#define HTML_INPUT_NOTAB 20
+#define HTML_INPUT_ONBLUR 21
+#define HTML_INPUT_ONCHANGE 22
+#define HTML_INPUT_ONFOCUS 23
+#define HTML_INPUT_ONSELECT 24
+#define HTML_INPUT_READONLY 25
+#define HTML_INPUT_SIZE 26
+#define HTML_INPUT_SRC 27
+#define HTML_INPUT_STYLE 28
+#define HTML_INPUT_TABINDEX 29
+#define HTML_INPUT_TITLE 30
+#define HTML_INPUT_TYPE 31
+#define HTML_INPUT_USEMAP 32
+#define HTML_INPUT_VALUE 33
+#define HTML_INPUT_WIDTH 34
+#define HTML_INPUT_ATTRIBUTES 35
+
+#define HTML_ISINDEX_ACTION 0
+#define HTML_ISINDEX_CLASS 1
+#define HTML_ISINDEX_DIR 2
+#define HTML_ISINDEX_HREF 3
+#define HTML_ISINDEX_ID 4
+#define HTML_ISINDEX_LANG 5
+#define HTML_ISINDEX_PROMPT 6
+#define HTML_ISINDEX_STYLE 7
+#define HTML_ISINDEX_TITLE 8
+#define HTML_ISINDEX_ATTRIBUTES 9
+
+#define HTML_KEYGEN_CHALLENGE 0
+#define HTML_KEYGEN_CLASS 1
+#define HTML_KEYGEN_DIR 2
+#define HTML_KEYGEN_ID 3
+#define HTML_KEYGEN_LANG 4
+#define HTML_KEYGEN_NAME 5
+#define HTML_KEYGEN_STYLE 6
+#define HTML_KEYGEN_TITLE 7
+#define HTML_KEYGEN_ATTRIBUTES 8
+
+#define HTML_LABEL_ACCESSKEY 0
+#define HTML_LABEL_CLASS 1
+#define HTML_LABEL_CLEAR 2
+#define HTML_LABEL_DIR 3
+#define HTML_LABEL_FOR 4
+#define HTML_LABEL_ID 5
+#define HTML_LABEL_LANG 6
+#define HTML_LABEL_ONBLUR 7
+#define HTML_LABEL_ONFOCUS 8
+#define HTML_LABEL_STYLE 9
+#define HTML_LABEL_TITLE 10
+#define HTML_LABEL_ATTRIBUTES 11
+
+#define HTML_LI_CLASS 0
+#define HTML_LI_CLEAR 1
+#define HTML_LI_DINGBAT 2
+#define HTML_LI_DIR 3
+#define HTML_LI_ID 4
+#define HTML_LI_LANG 5
+#define HTML_LI_MD 6
+#define HTML_LI_SKIP 7
+#define HTML_LI_SRC 8
+#define HTML_LI_STYLE 9
+#define HTML_LI_TITLE 10
+#define HTML_LI_TYPE 11
+#define HTML_LI_VALUE 12
+#define HTML_LI_ATTRIBUTES 13
+
+#define HTML_LINK_CHARSET 0
+#define HTML_LINK_CLASS 1
+#define HTML_LINK_DIR 2
+#define HTML_LINK_HREF 3
+#define HTML_LINK_HREFLANG 4
+#define HTML_LINK_ID 5
+#define HTML_LINK_LANG 6
+#define HTML_LINK_MEDIA 7
+#define HTML_LINK_REL 8
+#define HTML_LINK_REV 9
+#define HTML_LINK_STYLE 10
+#define HTML_LINK_TARGET 11
+#define HTML_LINK_TITLE 12
+#define HTML_LINK_TYPE 13
+#define HTML_LINK_ATTRIBUTES 14
+
+#define HTML_MAP_CLASS 0
+#define HTML_MAP_CLEAR 1
+#define HTML_MAP_DIR 2
+#define HTML_MAP_ID 3
+#define HTML_MAP_LANG 4
+#define HTML_MAP_NAME 5
+#define HTML_MAP_STYLE 6
+#define HTML_MAP_TITLE 7
+#define HTML_MAP_ATTRIBUTES 8
+
+#define HTML_MATH_BOX 0
+#define HTML_MATH_CLASS 1
+#define HTML_MATH_CLEAR 2
+#define HTML_MATH_DIR 3
+#define HTML_MATH_ID 4
+#define HTML_MATH_LANG 5
+#define HTML_MATH_STYLE 6
+#define HTML_MATH_TITLE 7
+#define HTML_MATH_ATTRIBUTES 8
+
+#define HTML_META_CHARSET 0
+#define HTML_META_CONTENT 1
+#define HTML_META_HTTP_EQUIV 2
+#define HTML_META_NAME 3
+#define HTML_META_SCHEME 4
+#define HTML_META_ATTRIBUTES 5
+
+#define HTML_NEXTID_N 0
+#define HTML_NEXTID_ATTRIBUTES 1
+
+#define HTML_NOTE_CLASS 0
+#define HTML_NOTE_CLEAR 1
+#define HTML_NOTE_DIR 2
+#define HTML_NOTE_ID 3
+#define HTML_NOTE_LANG 4
+#define HTML_NOTE_MD 5
+#define HTML_NOTE_ROLE 6
+#define HTML_NOTE_SRC 7
+#define HTML_NOTE_STYLE 8
+#define HTML_NOTE_TITLE 9
+#define HTML_NOTE_ATTRIBUTES 10
+
+#define HTML_OBJECT_ALIGN 0
+#define HTML_OBJECT_ARCHIVE 1
+#define HTML_OBJECT_BORDER 2
+#define HTML_OBJECT_CLASS 3
+#define HTML_OBJECT_CLASSID 4
+#define HTML_OBJECT_CODEBASE 5
+#define HTML_OBJECT_CODETYPE 6
+#define HTML_OBJECT_DATA 7
+#define HTML_OBJECT_DECLARE 8
+#define HTML_OBJECT_DIR 9
+#define HTML_OBJECT_HEIGHT 10
+#define HTML_OBJECT_HSPACE 11
+#define HTML_OBJECT_ID 12
+#define HTML_OBJECT_ISMAP 13
+#define HTML_OBJECT_LANG 14
+#define HTML_OBJECT_NAME 15
+#define HTML_OBJECT_NOTAB 16
+#define HTML_OBJECT_SHAPES 17
+#define HTML_OBJECT_STANDBY 18
+#define HTML_OBJECT_STYLE 19
+#define HTML_OBJECT_TABINDEX 20
+#define HTML_OBJECT_TITLE 21
+#define HTML_OBJECT_TYPE 22
+#define HTML_OBJECT_USEMAP 23
+#define HTML_OBJECT_VSPACE 24
+#define HTML_OBJECT_WIDTH 25
+#define HTML_OBJECT_ATTRIBUTES 26
+
+#define HTML_OL_CLASS 0
+#define HTML_OL_CLEAR 1
+#define HTML_OL_COMPACT 2
+#define HTML_OL_CONTINUE 3
+#define HTML_OL_DIR 4
+#define HTML_OL_ID 5
+#define HTML_OL_LANG 6
+#define HTML_OL_SEQNUM 7
+#define HTML_OL_START 8
+#define HTML_OL_STYLE 9
+#define HTML_OL_TITLE 10
+#define HTML_OL_TYPE 11
+#define HTML_OL_ATTRIBUTES 12
+
+#define HTML_OPTION_CLASS 0
+#define HTML_OPTION_CLEAR 1
+#define HTML_OPTION_DIR 2
+#define HTML_OPTION_DISABLED 3
+#define HTML_OPTION_ERROR 4
+#define HTML_OPTION_ID 5
+#define HTML_OPTION_LABEL 6
+#define HTML_OPTION_LANG 7
+#define HTML_OPTION_SELECTED 8
+#define HTML_OPTION_SHAPE 9
+#define HTML_OPTION_STYLE 10
+#define HTML_OPTION_TITLE 11
+#define HTML_OPTION_VALUE 12
+#define HTML_OPTION_ATTRIBUTES 13
+
+#define HTML_OVERLAY_CLASS 0
+#define HTML_OVERLAY_HEIGHT 1
+#define HTML_OVERLAY_ID 2
+#define HTML_OVERLAY_IMAGEMAP 3
+#define HTML_OVERLAY_MD 4
+#define HTML_OVERLAY_SRC 5
+#define HTML_OVERLAY_STYLE 6
+#define HTML_OVERLAY_TITLE 7
+#define HTML_OVERLAY_UNITS 8
+#define HTML_OVERLAY_WIDTH 9
+#define HTML_OVERLAY_X 10
+#define HTML_OVERLAY_Y 11
+#define HTML_OVERLAY_ATTRIBUTES 12
+
+#define HTML_P_ALIGN 0
+#define HTML_P_CLASS 1
+#define HTML_P_CLEAR 2
+#define HTML_P_DIR 3
+#define HTML_P_ID 4
+#define HTML_P_LANG 5
+#define HTML_P_NOWRAP 6
+#define HTML_P_STYLE 7
+#define HTML_P_TITLE 8
+#define HTML_P_ATTRIBUTES 9
+
+#define HTML_PARAM_ACCEPT 0
+#define HTML_PARAM_ACCEPT_CHARSET 1
+#define HTML_PARAM_ACCEPT_ENCODING 2
+#define HTML_PARAM_CLASS 3
+#define HTML_PARAM_CLEAR 4
+#define HTML_PARAM_DATA 5
+#define HTML_PARAM_DIR 6
+#define HTML_PARAM_ID 7
+#define HTML_PARAM_LANG 8
+#define HTML_PARAM_NAME 9
+#define HTML_PARAM_OBJECT 10
+#define HTML_PARAM_REF 11
+#define HTML_PARAM_STYLE 12
+#define HTML_PARAM_TITLE 13
+#define HTML_PARAM_TYPE 14
+#define HTML_PARAM_VALUE 15
+#define HTML_PARAM_VALUEREF 16
+#define HTML_PARAM_VALUETYPE 17
+#define HTML_PARAM_ATTRIBUTES 18
+
+#define HTML_Q_CITE 0
+#define HTML_Q_CLASS 1
+#define HTML_Q_CLEAR 2
+#define HTML_Q_DIR 3
+#define HTML_Q_ID 4
+#define HTML_Q_LANG 5
+#define HTML_Q_STYLE 6
+#define HTML_Q_TITLE 7
+#define HTML_Q_ATTRIBUTES 8
+
+#define HTML_SCRIPT_CHARSET 0
+#define HTML_SCRIPT_CLASS 1
+#define HTML_SCRIPT_CLEAR 2
+#define HTML_SCRIPT_DEFER 3
+#define HTML_SCRIPT_DIR 4
+#define HTML_SCRIPT_EVENT 5
+#define HTML_SCRIPT_FOR 6
+#define HTML_SCRIPT_ID 7
+#define HTML_SCRIPT_LANG 8
+#define HTML_SCRIPT_LANGUAGE 9
+#define HTML_SCRIPT_NAME 10
+#define HTML_SCRIPT_SCRIPTENGINE 11
+#define HTML_SCRIPT_SRC 12
+#define HTML_SCRIPT_STYLE 13
+#define HTML_SCRIPT_TITLE 14
+#define HTML_SCRIPT_TYPE 15
+#define HTML_SCRIPT_ATTRIBUTES 16
+
+#define HTML_SELECT_ALIGN 0
+#define HTML_SELECT_CLASS 1
+#define HTML_SELECT_CLEAR 2
+#define HTML_SELECT_DIR 3
+#define HTML_SELECT_DISABLED 4
+#define HTML_SELECT_ERROR 5
+#define HTML_SELECT_HEIGHT 6
+#define HTML_SELECT_ID 7
+#define HTML_SELECT_LANG 8
+#define HTML_SELECT_MD 9
+#define HTML_SELECT_MULTIPLE 10
+#define HTML_SELECT_NAME 11
+#define HTML_SELECT_NOTAB 12
+#define HTML_SELECT_ONBLUR 13
+#define HTML_SELECT_ONCHANGE 14
+#define HTML_SELECT_ONFOCUS 15
+#define HTML_SELECT_SIZE 16
+#define HTML_SELECT_STYLE 17
+#define HTML_SELECT_TABINDEX 18
+#define HTML_SELECT_TITLE 19
+#define HTML_SELECT_UNITS 20
+#define HTML_SELECT_WIDTH 21
+#define HTML_SELECT_ATTRIBUTES 22
+
+#define HTML_STYLE_CLASS 0
+#define HTML_STYLE_DIR 1
+#define HTML_STYLE_ID 2
+#define HTML_STYLE_LANG 3
+#define HTML_STYLE_MEDIA 4
+#define HTML_STYLE_NOTATION 5
+#define HTML_STYLE_STYLE 6
+#define HTML_STYLE_TITLE 7
+#define HTML_STYLE_TYPE 8
+#define HTML_STYLE_ATTRIBUTES 9
+
+#define HTML_TAB_ALIGN 0
+#define HTML_TAB_CLASS 1
+#define HTML_TAB_CLEAR 2
+#define HTML_TAB_DIR 3
+#define HTML_TAB_DP 4
+#define HTML_TAB_ID 5
+#define HTML_TAB_INDENT 6
+#define HTML_TAB_LANG 7
+#define HTML_TAB_STYLE 8
+#define HTML_TAB_TITLE 9
+#define HTML_TAB_TO 10
+#define HTML_TAB_ATTRIBUTES 11
+
+#define HTML_TABLE_ALIGN 0
+#define HTML_TABLE_BACKGROUND 1
+#define HTML_TABLE_BORDER 2
+#define HTML_TABLE_CELLPADDING 3
+#define HTML_TABLE_CELLSPACING 4
+#define HTML_TABLE_CLASS 5
+#define HTML_TABLE_CLEAR 6
+#define HTML_TABLE_COLS 7
+#define HTML_TABLE_COLSPEC 8
+#define HTML_TABLE_DIR 9
+#define HTML_TABLE_DP 10
+#define HTML_TABLE_FRAME 11
+#define HTML_TABLE_ID 12
+#define HTML_TABLE_LANG 13
+#define HTML_TABLE_NOFLOW 14
+#define HTML_TABLE_NOWRAP 15
+#define HTML_TABLE_RULES 16
+#define HTML_TABLE_STYLE 17
+#define HTML_TABLE_SUMMARY 18
+#define HTML_TABLE_TITLE 19
+#define HTML_TABLE_UNITS 20
+#define HTML_TABLE_WIDTH 21
+#define HTML_TABLE_ATTRIBUTES 22
+
+#define HTML_TD_ABBR 0
+#define HTML_TD_ALIGN 1
+#define HTML_TD_AXES 2
+#define HTML_TD_AXIS 3
+#define HTML_TD_BACKGROUND 4
+#define HTML_TD_CHAR 5
+#define HTML_TD_CHAROFF 6
+#define HTML_TD_CLASS 7
+#define HTML_TD_CLEAR 8
+#define HTML_TD_COLSPAN 9
+#define HTML_TD_DIR 10
+#define HTML_TD_DP 11
+#define HTML_TD_HEADERS 12
+#define HTML_TD_HEIGHT 13
+#define HTML_TD_ID 14
+#define HTML_TD_LANG 15
+#define HTML_TD_NOWRAP 16
+#define HTML_TD_ROWSPAN 17
+#define HTML_TD_SCOPE 18
+#define HTML_TD_STYLE 19
+#define HTML_TD_TITLE 20
+#define HTML_TD_VALIGN 21
+#define HTML_TD_WIDTH 22
+#define HTML_TD_ATTRIBUTES 23
+
+#define HTML_TEXTAREA_ACCEPT_CHARSET 0
+#define HTML_TEXTAREA_ACCESSKEY 1
+#define HTML_TEXTAREA_ALIGN 2
+#define HTML_TEXTAREA_CLASS 3
+#define HTML_TEXTAREA_CLEAR 4
+#define HTML_TEXTAREA_COLS 5
+#define HTML_TEXTAREA_DIR 6
+#define HTML_TEXTAREA_DISABLED 7
+#define HTML_TEXTAREA_ERROR 8
+#define HTML_TEXTAREA_ID 9
+#define HTML_TEXTAREA_LANG 10
+#define HTML_TEXTAREA_NAME 11
+#define HTML_TEXTAREA_NOTAB 12
+#define HTML_TEXTAREA_ONBLUR 13
+#define HTML_TEXTAREA_ONCHANGE 14
+#define HTML_TEXTAREA_ONFOCUS 15
+#define HTML_TEXTAREA_ONSELECT 16
+#define HTML_TEXTAREA_READONLY 17
+#define HTML_TEXTAREA_ROWS 18
+#define HTML_TEXTAREA_STYLE 19
+#define HTML_TEXTAREA_TABINDEX 20
+#define HTML_TEXTAREA_TITLE 21
+#define HTML_TEXTAREA_ATTRIBUTES 22
+
+#define HTML_TR_ALIGN 0
+#define HTML_TR_CHAR 1
+#define HTML_TR_CHAROFF 2
+#define HTML_TR_CLASS 3
+#define HTML_TR_CLEAR 4
+#define HTML_TR_DIR 5
+#define HTML_TR_DP 6
+#define HTML_TR_ID 7
+#define HTML_TR_LANG 8
+#define HTML_TR_NOWRAP 9
+#define HTML_TR_STYLE 10
+#define HTML_TR_TITLE 11
+#define HTML_TR_VALIGN 12
+#define HTML_TR_ATTRIBUTES 13
+
+#define HTML_UL_CLASS 0
+#define HTML_UL_CLEAR 1
+#define HTML_UL_COMPACT 2
+#define HTML_UL_DINGBAT 3
+#define HTML_UL_DIR 4
+#define HTML_UL_ID 5
+#define HTML_UL_LANG 6
+#define HTML_UL_MD 7
+#define HTML_UL_PLAIN 8
+#define HTML_UL_SRC 9
+#define HTML_UL_STYLE 10
+#define HTML_UL_TITLE 11
+#define HTML_UL_TYPE 12
+#define HTML_UL_WRAP 13
+#define HTML_UL_ATTRIBUTES 14
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* hdr_HTMLDTD_H */
diff --git a/WWW/Library/Implementation/makefile.in b/WWW/Library/Implementation/makefile.in
new file mode 100644
index 0000000..0b467d7
--- /dev/null
+++ b/WWW/Library/Implementation/makefile.in
@@ -0,0 +1,384 @@
+# $LynxId: makefile.in,v 1.35 2021/07/16 20:01:00 tom Exp $
+# Make WWW under unix for a.n.other unix system (bsd)
+# Use this as a template
+
+# For W3 distribution, machine type for subdirectories
+WWW_MACH = Implementation
+WWWINC = $(top_srcdir)/WWW/Library/Implementation
+
+ECHO = @DONT_ECHO_CC@
+LFLAGS =
+
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+datarootdir = @datarootdir@
+top_srcdir = @top_srcdir@
+srcdir = @srcdir@
+VPATH = $(srcdir)
+
+LYFLAGS = # FIXME: set in parent makefile
+
+CC = @CC@
+DEFS = @DEFS@
+EXTRA_CPPFLAGS = @EXTRA_CPPFLAGS@
+CPPFLAGS = @CPPFLAGS@
+
+AR = @AR@
+ARFLAGS = @ARFLAGS@
+
+RANLIB = @RANLIB@
+
+o = .@OBJEXT@
+x = @EXEEXT@
+
+INTLDIR_CPPFLAGS= @INTLDIR_CPPFLAGS@ -I$(top_srcdir)/intl
+
+CPP = @CPP@
+CPPOPTS = $(DEFS) $(LYFLAGS) \
+ -I../../.. \
+ -I../../../src \
+ -I$(top_srcdir) \
+ -I$(top_srcdir)/src \
+ $(INTLDIR_CPPFLAGS) -I$(WWWINC) $(EXTRA_CPPFLAGS) $(CPPFLAGS)
+
+LY_CFLAGS = @CFLAGS@ @EXTRA_CFLAGS@
+CFLAGS = $(CPPOPTS) $(LY_CFLAGS)
+
+LINT = @LINT@
+LINTOPTS =
+
+CTAGS = @CTAGS@
+
+# Directory for installed binary:
+BINDIR = @bindir@
+
+# Where is the W3 object library to be installed (not normally done)?
+#_________________ OK if normal W3 distribution
+# Where is the WWW source root?
+WWW = $(top_srcdir)/WWW
+
+# Where should temporary (object) files go?
+WTMP = ../..
+
+# (Version.make)
+VC = 2.14
+#______________________________________________________________________
+# (originally CommonMakefile)
+
+# If this env var is set to something else Some makes will use that instead
+SHELL = @CONFIG_SHELL@
+
+# .h files are distributed but originally are made from the
+# self-documenting hypertext files.
+
+.SUFFIXES: .h .html
+.html.h:
+# - chmod +w $*.h
+ www -w90 -na -to text/x-c $*.html > $*.h
+# chmod -w $*.h
+
+# If this is actually run in a subdirectory,
+#
+# WWW = ../../..
+# WWW = ../.. For [cernlib] build in this directory
+
+CMN = $(WWW)/Library/Implementation/
+
+# Where shall we put the objects and built library?
+
+LOB = .
+
+# Only needed if HTWAIS.c is to be compiled. Put into your Makefile.include
+# uncomment these and fill in WAISINC for adding direct wais access
+# to Lynx.
+@MAKE_WAIS@HTWAIS_c = $(CMN)/HTWAIS.c
+@MAKE_WAIS@HTWAIS_o = $(LOB)/HTWAIS$o
+@MAKE_WAIS@WAIS = YES
+#WAISINC = -I../../../../freeWAIS-0.202/ir
+@MAKE_WAIS@WAISCFLAGS = -DDIRECT_WAIS
+#
+
+# add -DNEW_GATEWAY here for the new gateway config stuff
+CFLAGS2 = $(CFLAGS) $(LYFLAGS) $(WAISCFLAGS) -I$(CMN) -DACCESS_AUTH
+
+COMPILE = $(ECHO) $(CC) $(CFLAGS2) -c
+
+COMMON = $(LOB)/HTParse$o $(LOB)/HTAccess$o $(LOB)/HTTP$o \
+ $(LOB)/HTFile$o $(LOB)/HTBTree$o $(LOB)/HTFTP$o $(LOB)/HTTCP$o \
+ $(LOB)/SGML$o $(LOB)/HTMLDTD$o $(LOB)/HTChunk$o \
+ $(LOB)/HTPlain$o \
+ $(LOB)/HTMLGen$o \
+ $(LOB)/HTAtom$o $(LOB)/HTAnchor$o $(LOB)/HTStyle$o \
+ $(LOB)/HTList$o $(LOB)/HTString$o \
+ $(LOB)/HTRules$o $(LOB)/HTFormat$o $(LOB)/HTMIME$o \
+ $(LOB)/HTNews$o $(LOB)/HTGopher$o \
+ $(LOB)/HTTelnet$o $(LOB)/HTFinger$o $(LOB)/HTWSRC$o $(HTWAIS_o) \
+ $(LOB)/HTAAUtil$o $(LOB)/HTAABrow$o \
+ $(LOB)/HTGroup$o \
+ $(LOB)/HTAAProt$o \
+ $(LOB)/HTAssoc$o $(LOB)/HTLex$o $(LOB)/HTUU$o \
+ $(LOB)/HTDOS$o
+
+CFILES = $(CMN)HTParse.c $(CMN)HTAccess.c $(CMN)HTTP.c $(CMN)HTFile.c \
+ $(CMN)HTBTree.c \
+ $(CMN)HTFTP.c $(CMN)HTTCP.c $(CMN)SGML.c \
+ $(CMN)HTMLDTD.c \
+ $(CMN)HTPlain.c \
+ $(CMN)HTMLGen.c \
+ $(CMN)HTChunk.c $(CMN)HTAtom.c $(CMN)HTAnchor.c $(CMN)HTStyle.c \
+ $(CMN)HTList.c $(CMN)HTString.c $(CMN)HTRules.c \
+ $(CMN)HTFormat.c $(CMN)HTMIME.c \
+ $(CMN)HTNews.c $(CMN)HTGopher.c $(CMN)HTTelnet.c \
+ $(CMN)HTFinger.c $(HTWAIS_c) $(CMN)HTWSRC.c \
+ $(CMN)HTAABrow.c \
+ $(CMN)HTGroup.c \
+ $(CMN)HTAAProt.c \
+ $(CMN)HTAssoc.c $(CMN)HTLex.c $(CMN)HTUU.c
+
+HFILES = $(CMN)HTParse.h $(CMN)HTAccess.h $(CMN)HTTP.h $(CMN)HTFile.h \
+ $(CMN)HTBTree.h $(CMN)HTFTP.h $(CMN)HTTCP.h \
+ $(CMN)SGML.h $(CMN)HTML.h $(CMN)HTMLDTD.h $(CMN)HTChunk.h \
+ $(CMN)HTPlain.h \
+ $(CMN)HTFWriter.h $(CMN)HTMLGen.h \
+ $(CMN)HTStream.h \
+ $(CMN)HTAtom.h $(CMN)HTAnchor.h $(CMN)HTStyle.h \
+ $(CMN)HTList.h \
+ $(CMN)HTString.h $(CMN)HTRules.h \
+ $(CMN)HTFormat.h $(CMN)HTInit.h \
+ $(CMN)HTMIME.h $(CMN)HTNews.h \
+ $(CMN)HTGopher.h \
+ $(CMN)HTUtils.h $(CMN)www_tcp.h $(CMN)HText.h \
+ $(CMN)HTTelnet.h $(CMN)HTFinger.h \
+ $(CMN)HTWAIS.h $(CMN)HTWSRC.h \
+ $(CMN)HTAABrow.h \
+ $(CMN)HTGroup.h \
+ $(CMN)HTAAProt.h \
+ $(CMN)HTAssoc.h $(CMN)HTLex.h $(CMN)HTUU.h
+
+C_SRC = $(COMMON:$o=.c)
+
+all : $(LOB)/libwww.a
+
+lint:
+ $(LINT) $(LINTOPTS) $(CPPOPTS) $(C_SRC) 2>&1 |tee ../../../lint.libwww
+
+.SUFFIXES: $o .i .h .html
+
+.c$o:
+ @RULE_CC@
+ @ECHO_CC@$(CC) $(CPPOPTS) $(CFLAGS) -c $(srcdir)/$*.c
+
+.c.i:
+ @RULE_CC@
+ @ECHO_CC@$(CPP) $(CPPOPTS) $(srcdir)/$*.c >$@
+
+depend :
+ makedepend -fmakefile -- $(CFLAGS) -- $(CFILES)
+
+# Library
+#
+# On SGI, ranlib is unnecessary and does not exist so we ignore errors
+# for that step
+$(LOB)/libwww.a : $(COMMON)
+ $(AR) $(ARFLAGS) $(LOB)/libwww.a $(COMMON)
+ -$(RANLIB) $(LOB)/libwww.a
+
+# Clean up everything generatable except final products
+clean :
+ rm -f core *.core *.leaks *.[oi] *.bak tags TAGS
+ rm -f dtd_util$x
+ rm -f $(LOB)/*$o
+
+distclean : clean
+ @echo made $@
+
+tags:
+ $(CTAGS) *.[ch]
+
+# Common code
+# -----------
+
+$(LOB)/HTList$o : $(CMN)HTList.c $(CMN)HTUtils.h $(CMN)HTList.h
+ $(COMPILE) $(CMN)HTList.c
+
+$(LOB)/HTAnchor$o : $(CMN)HTAnchor.c $(CMN)HTUtils.h $(CMN)HTList.h
+ $(COMPILE) $(CMN)HTAnchor.c
+
+$(LOB)/HTFormat$o : $(CMN)HTFormat.c $(CMN)HTUtils.h $(CMN)HTList.h
+ $(COMPILE) $(CMN)HTFormat.c
+
+$(LOB)/HTMIME$o : $(CMN)HTMIME.c $(CMN)HTUtils.h $(CMN)HTList.h
+ $(COMPILE) $(CMN)HTMIME.c
+
+$(LOB)/HTNews$o : $(CMN)HTNews.c $(CMN)HTUtils.h $(CMN)HTList.h\
+ $(CMN)HTMLDTD.h
+ $(COMPILE) $(CMN)HTNews.c
+
+$(LOB)/HTGopher$o : $(CMN)HTGopher.c $(CMN)HTUtils.h $(CMN)HTList.h \
+ $(CMN)HTMLDTD.h
+ $(COMPILE) $(CMN)HTGopher.c
+
+$(LOB)/HTTelnet$o : $(CMN)HTTelnet.c $(CMN)HTUtils.h $(CMN)HTTelnet.h $(CMN)../../../userdefs.h
+ $(COMPILE) $(CMN)HTTelnet.c
+
+$(LOB)/HTFinger$o : $(CMN)HTFinger.c $(CMN)HTUtils.h $(CMN)HTList.h \
+ $(CMN)HTMLDTD.h
+ $(COMPILE) $(CMN)HTFinger.c
+
+$(LOB)/HTStyle$o : $(CMN)HTStyle.c $(CMN)HTUtils.h
+ $(COMPILE) $(CMN)HTStyle.c
+
+$(LOB)/HTAtom$o : $(CMN)HTAtom.c $(CMN)HTUtils.h $(CMN)HTList.h
+ $(COMPILE) $(CMN)HTAtom.c
+
+$(LOB)/HTChunk$o : $(CMN)HTChunk.c $(CMN)HTUtils.h
+ $(COMPILE) $(CMN)HTChunk.c
+
+$(LOB)/HTString$o : $(CMN)HTString.c $(CMN)HTUtils.h $(CMN)Version.make
+ $(COMPILE) -DVC=\"$(VC)\" $(CMN)HTString.c
+
+$(LOB)/HTRules$o : $(CMN)HTRules.c $(CMN)HTUtils.h $(CMN)Version.make \
+ $(CMN)HTAAProt.h
+ $(COMPILE) -DVC=\"$(VC)\" $(CMN)HTRules.c
+
+$(LOB)/SGML$o : $(CMN)SGML.c $(CMN)HTUtils.h $(CMN)UCAux.h
+ $(COMPILE) $(CMN)SGML.c
+
+$(LOB)/HTMLGen$o : $(CMN)HTMLGen.c $(CMN)HTUtils.h $(CMN)HTMLDTD.h
+ $(COMPILE) $(CMN)HTMLGen.c
+
+$(LOB)/HTMLDTD$o : $(CMN)HTMLDTD.c $(CMN)SGML.h
+ $(COMPILE) $(CMN)HTMLDTD.c
+
+$(LOB)/HTPlain$o : $(CMN)HTPlain.c $(CMN)HTPlain.h $(CMN)HTStream.h \
+ $(CMN)UCAux.h
+ $(COMPILE) $(CMN)HTPlain.c
+
+$(LOB)/HTWAIS$o : $(CMN)HTWAIS.c $(CMN)HTUtils.h $(CMN)HTList.h
+ $(COMPILE) $(WAISINC) $(CMN)HTWAIS.c
+
+$(LOB)/HTWSRC$o : $(CMN)HTWSRC.c $(CMN)HTUtils.h $(CMN)HTList.h
+ $(COMPILE) $(CMN)HTWSRC.c
+
+# Access Authorization
+
+$(LOB)/HTAAUtil$o : $(CMN)HTAAUtil.c $(CMN)HTAAUtil.h \
+ $(CMN)HTUtils.h $(CMN)HTString.h
+ $(COMPILE) $(CMN)HTAAUtil.c
+
+$(LOB)/HTGroup$o : $(CMN)HTGroup.c $(CMN)HTGroup.h \
+ $(CMN)HTAAUtil.h \
+ $(CMN)HTAssoc.h $(CMN)HTLex.h
+ $(COMPILE) $(CMN)HTGroup.c
+
+$(LOB)/HTAABrow$o : $(CMN)HTAABrow.c $(CMN)HTAABrow.h \
+ $(CMN)HTAAUtil.h $(CMN)HTUU.h \
+ $(CMN)HTUtils.h $(CMN)HTString.h \
+ $(CMN)HTParse.h $(CMN)HTList.h \
+ $(CMN)HTAssoc.h
+ $(COMPILE) $(CMN)HTAABrow.c
+
+$(LOB)/HTAAProt$o : $(CMN)HTAAProt.c $(CMN)HTAAProt.h \
+ $(CMN)HTUtils.h $(CMN)HTAAUtil.h \
+ $(CMN)HTAssoc.h $(CMN)HTLex.h
+ $(COMPILE) $(CMN)HTAAProt.c
+
+$(LOB)/HTAssoc$o : $(CMN)HTAssoc.c $(CMN)HTAssoc.h \
+ $(CMN)HTUtils.h $(CMN)HTString.h $(CMN)HTList.h
+ $(COMPILE) $(CMN)HTAssoc.c
+
+$(LOB)/HTLex$o : $(CMN)HTLex.c $(CMN)HTLex.h $(CMN)HTUtils.h
+ $(COMPILE) $(CMN)HTLex.c
+
+$(LOB)/HTUU$o : $(CMN)HTUU.c $(CMN)HTUU.h $(CMN)HTUtils.h
+ $(COMPILE) $(CMN)HTUU.c
+
+
+# Communications & Files
+
+$(LOB)/HTTP$o : $(CMN)HTTP.c $(CMN)HTUtils.h $(CMN)HTAABrow.h
+ $(COMPILE) $(CMN)HTTP.c
+
+$(LOB)/HTTCP$o : $(CMN)HTTCP.c $(CMN)HTUtils.h
+ $(COMPILE) $(CMN)HTTCP.c
+
+$(LOB)/HTFile$o : $(CMN)HTFile.c $(CMN)HTUtils.h \
+ $(CMN)HTMLDTD.h
+ $(COMPILE) $(CMN)HTFile.c
+
+$(LOB)/HTBTree$o : $(CMN)HTBTree.c $(CMN)HTUtils.h
+ $(COMPILE) $(CMN)HTBTree.c
+
+$(LOB)/HTFTP$o : $(CMN)HTFTP.c $(CMN)HTUtils.h
+ $(COMPILE) $(CMN)HTFTP.c
+
+$(LOB)/HTAccess$o : $(CMN)HTAccess.c $(CMN)HTUtils.h
+ $(COMPILE) $(CMN)HTAccess.c
+
+$(LOB)/HTParse$o : $(CMN)HTParse.c $(CMN)HTUtils.h
+ $(COMPILE) $(CMN)HTParse.c
+
+$(LOB)/HTVMS_WaisUI$o : $(CMN)HTVMS_WaisUI.c $(CMN)HTUtils.h
+ $(COMPILE) $(CMN)HTVMS_WaisUI.c
+
+$(LOB)/HTDOS$o : $(CMN)HTDOS.c $(CMN)HTUtils.h
+ $(COMPILE) $(CMN)HTDOS.c
+
+# Utilities
+$(LOB)/dtd_util$o : $(CMN)dtd_util.c $(CMN)HTUtils.h
+ $(COMPILE) $(CMN)dtd_util.c
+
+DTD_UTIL = $(LOB)/dtd_util$o $(LOB)/HTMLDTD$o
+
+sources: dtd_util$x src0_HTMLDTD.txt src1_HTMLDTD.txt
+ -rm -f *_HTMLDTD.h
+ ./dtd_util$x -l src0_HTMLDTD.txt -s -c -o src0_HTMLDTD.h
+ ./dtd_util$x -l src1_HTMLDTD.txt -t -c -o src1_HTMLDTD.h
+ ./dtd_util$x -l src0_HTMLDTD.txt -s -h -o hdr_HTMLDTD.h
+
+dtd_util$x: $(DTD_UTIL)
+ $(CC) $(CC_OPTS) $(LDFLAGS) -o $@ $(DTD_UTIL) $(LIBS)
+
+DIFFC = diff -u -I LynxId
+check: dtd_util$x
+ @echo "** comparing builtin src0_HTMLDTD.txt"
+ ./dtd_util$x >HTMLDTD.log
+ -$(DIFFC) src0_HTMLDTD.txt HTMLDTD.log
+
+ @echo "** comparing reloaded src0_HTMLDTD.txt"
+ ./dtd_util$x -l src0_HTMLDTD.txt >HTMLDTD.log
+ -$(DIFFC) src0_HTMLDTD.txt HTMLDTD.log
+
+ @echo "** comparing reloaded src1_HTMLDTD.txt"
+ ./dtd_util$x -t -l src1_HTMLDTD.txt >HTMLDTD.log
+ -$(DIFFC) src1_HTMLDTD.txt HTMLDTD.log
+
+ @echo "** comparing header generated from builtin"
+ ./dtd_util$x -s -h -o HTMLDTD.log
+ -$(DIFFC) hdr_HTMLDTD.h HTMLDTD.log
+ ./dtd_util$x -t -h -o HTMLDTD.log
+ -$(DIFFC) hdr_HTMLDTD.h HTMLDTD.log
+
+ @echo "** comparing header generated by load"
+ ./dtd_util$x -s -h -o HTMLDTD.log -l src0_HTMLDTD.txt
+ -$(DIFFC) hdr_HTMLDTD.h HTMLDTD.log
+ ./dtd_util$x -t -h -o HTMLDTD.log -l src1_HTMLDTD.txt
+ -$(DIFFC) hdr_HTMLDTD.h HTMLDTD.log
+
+ @echo "** comparing strict source generated from builtin"
+ ./dtd_util$x -s -c -o HTMLDTD.log
+ -$(DIFFC) src0_HTMLDTD.h HTMLDTD.log
+
+ @echo "** comparing strict source generated by load"
+ ./dtd_util$x -s -c -o HTMLDTD.log -l src0_HTMLDTD.txt
+ -$(DIFFC) src0_HTMLDTD.h HTMLDTD.log
+
+ @echo "** comparing tagsoup source generated from builtin"
+ ./dtd_util$x -t -c -o HTMLDTD.log
+ -$(DIFFC) src1_HTMLDTD.h HTMLDTD.log
+
+ @echo "** comparing tagsoup source generated by load"
+ ./dtd_util$x -t -c -o HTMLDTD.log -l src1_HTMLDTD.txt
+ -$(DIFFC) src1_HTMLDTD.h HTMLDTD.log
+
+# DO NOT DELETE THIS LINE -- make depend depends on it.
diff --git a/WWW/Library/Implementation/src0_HTMLDTD.h b/WWW/Library/Implementation/src0_HTMLDTD.h
new file mode 100644
index 0000000..a2daa4f
--- /dev/null
+++ b/WWW/Library/Implementation/src0_HTMLDTD.h
@@ -0,0 +1,2478 @@
+/* $LynxId: src0_HTMLDTD.h,v 1.49 2022/09/30 00:04:06 tom Exp $ */
+#ifndef src_HTMLDTD_H0
+#define src_HTMLDTD_H0 1
+
+#ifndef once_HTMLDTD
+#define once_HTMLDTD 1
+
+#define T_A 0x00008,0x0B007,0x0FF17,0x37787,0x77BA7,0x8604F,0x00014
+#define T_ABBR 0x00002,0x8B04F,0x8FFFF,0xA778F,0xF7FBF,0x00003,0x00000
+#define T_ACRONYM 0x00002,0x8B04F,0x8FFFF,0xA778F,0xF7FBF,0x00003,0x00000
+#define T_ADDRESS 0x00200,0x0F14F,0x8FFFF,0x136680,0x1B6FAF,0x80317,0x00000
+#define T_APPLET 0x02000,0x0B0CF,0x8FFFF,0x37F9F,0xB7FBF,0x8300F,0x00000
+#define T_AREA 0x08000,0x00000,0x00000,0x08000,0x3FFFF,0x00F1F,0x00001
+#define T_ARTICLE 0x00200,0x8FBCF,0x8FFFF,0x1B66A0,0x1B7FFF,0x8031F,0x00004
+#define T_ASIDE 0x00200,0x8FBCF,0x8FFFF,0x1B66A0,0x1B7FFF,0x8031F,0x00004
+#define T_AU 0x00002,0x8B04F,0x8FFFF,0xA778F,0xF7FBF,0x00003,0x00000
+#define T_AUTHOR 0x00002,0x8B04F,0x8FFFF,0xA778F,0xF7FBF,0x00003,0x00000
+#define T_B 0x00001,0x8B04F,0xAFFFF,0xA778F,0xF7FBF,0x00001,0x00014
+#define T_BANNER 0x00200,0x0FB8F,0x0FFFF,0x130000,0x130000,0x8031F,0x00000
+#define T_BASE 0x40000,0x00000,0x00000,0x50000,0x50000,0x8000F,0x00001
+#define T_BASEFONT 0x01000,0x00000,0x00000,0x377AF,0x37FAF,0x8F000,0x00001
+#define T_BDO 0x00100,0x0B04F,0x8FFFF,0x136680,0x1B6FAF,0x0033F,0x00000
+#define T_BGSOUND 0x01000,0x00000,0x00000,0x777AF,0x77FAF,0x8730F,0x00001
+#define T_BIG 0x00001,0x8B04F,0x8FFFF,0xA778F,0xF7FBF,0x00001,0x00014
+#define T_BLINK 0x00001,0x8B04F,0x8FFFF,0xA778F,0xF7FAF,0x00001,0x00014
+#define T_BLOCKQUOTE 0x00200,0xAFBCF,0xAFFFF,0x1B6680,0x1B6FAF,0x8031F,0x00000
+#define T_BODY 0x20000,0x12FB8F,0x12FFFF,0x30000,0x30000,0xDFF7F,0x00003
+#define T_BODYTEXT 0x20000,0x10FB8F,0x1AFFFF,0x30200,0xB7FAF,0x8F17F,0x00003
+#define T_BQ 0x00200,0xAFBCF,0xAFFFF,0x1B6680,0x1B6FAF,0x8031F,0x00000
+#define T_BR 0x01000,0x00000,0x00000,0x377BF,0x77FBF,0x8101F,0x00001
+#define T_BUTTON 0x02000,0x0BB07,0x0FF37,0x0378F,0x37FBF,0x8115F,0x00000
+#define T_CAPTION 0x00100,0x0B04F,0x8FFFF,0x106A00,0x1B6FA7,0x8035F,0x00000
+#define T_CENTER 0x00200,0x8FBCF,0x8FFFF,0x1B6680,0x1B6FA7,0x8071F,0x00000
+#define T_CITE 0x00002,0x8B04F,0x8FFFF,0xA778F,0xF7FBF,0x00002,0x00010
+#define T_CODE 0x00002,0x8B04F,0x8FFFF,0xA778F,0xF7FBF,0x00002,0x00000
+#define T_COL 0x04000,0x00000,0x00000,0x00820,0x36FA7,0x88F5F,0x00001
+#define T_COLGROUP 0x00020,0x04000,0x04000,0x00800,0x36FA7,0x8875F,0x00001
+#define T_COMMENT 0x00004,0x00000,0x00000,0xA77AF,0x7FFFF,0x00003,0x00000
+#define T_CREDIT 0x00100,0x0B04F,0x8FFFF,0x106A00,0x1B7FBF,0x8030F,0x00000
+#define T_DD 0x00400,0x0FBCF,0x8FFFF,0x00800,0xB6FFF,0x8071F,0x00001
+#define T_DEL 0x00002,0x8B04F,0x8FEFF,0x87F8F,0xF7FBF,0x100003,0x00000
+#define T_DEL_2 0x100000,0x8BBCF,0x8FFFF,0xA7F8F,0xF7FBF,0x100003,0x00000
+#define T_DFN 0x00002,0x8B0CF,0x8FFFF,0x8778F,0xF7FBF,0x00003,0x00000
+#define T_DIR 0x00800,0x0B400,0x0F75F,0x137680,0x136FB7,0x84F7F,0x00000
+#define T_DIV 0x00200,0x8FBCF,0x8FFFF,0x1B66A0,0x1B7FFF,0x80317,0x00004
+#define T_DL 0x00800,0x0C480,0x8FFFF,0x136680,0x1B7FB7,0x00757,0x00000
+#define T_DLC 0x00800,0x0C480,0x8FFFF,0x136680,0x1B7FB7,0x0075F,0x00000
+#define T_DT 0x00400,0x0B04F,0x0B1FF,0x00800,0x17FFF,0x8071F,0x00001
+#define T_EM 0x00002,0x8B04F,0x8FFFF,0xA778F,0xF7FAF,0x00003,0x00010
+#define T_EMBED 0x02000,0x8F107,0x8FFF7,0xB6FBF,0xB7FBF,0x1FF7F,0x00001
+#define T_FIELDSET 0x00200,0x8FB4F,0x8FF7F,0x186787,0x1B7FF7,0x8805F,0x00000
+#define T_FIG 0x00200,0x0FB00,0x8FFFF,0x136680,0x1B6FBF,0x8834F,0x00000
+#define T_FIGURE 0x00200,0x8FBCF,0x8FFFF,0x1B66A0,0x1B7FFF,0x8031F,0x00004
+#define T_FN 0x00200,0x8FBCF,0x8FFFF,0x1B6680,0x1B7EBF,0x8114F,0x00000
+#define T_FONT 0x00001,0x8B04F,0x8FFFF,0xB778F,0xF7FBF,0x00001,0x00014
+#define T_FOOTER 0x00200,0x8FBCF,0x8FFFF,0x1B66A0,0x1B7FFF,0x8031F,0x00004
+#define T_FORM 0x00080,0x0FF6F,0x0FF7F,0x136E07,0x132F07,0x88DFF,0x00000
+#define T_FRAME 0x10000,0x00000,0x00000,0x10000,0x10000,0x9FFFF,0x00001
+#define T_FRAMESET 0x10000,0x90000,0x90000,0x90000,0x93000,0x9FFFF,0x00000
+#define T_H1 0x00100,0x0B04F,0x0B05F,0x136680,0x137FAF,0x80117,0x00000
+#define T_H2 0x00100,0x0B04F,0x0B05F,0x136680,0x137FAF,0x80117,0x00000
+#define T_H3 0x00100,0x0B04F,0x0B05F,0x136680,0x137FAF,0x80117,0x00000
+#define T_H4 0x00100,0x0B04F,0x0B05F,0x136680,0x137FAF,0x80117,0x00000
+#define T_H5 0x00100,0x0B04F,0x0B05F,0x136680,0x137FAF,0x80117,0x00000
+#define T_H6 0x00100,0x0B04F,0x0B05F,0x136680,0x137FAF,0x80117,0x00000
+#define T_HEAD 0x40000,0x4F000,0x47000,0x10000,0x10000,0x9FF7F,0x00007
+#define T_HEADER 0x00200,0x8FBCF,0x8FFFF,0x1B66A0,0x1B7FFF,0x8031F,0x00004
+#define T_HR 0x04000,0x00000,0x00000,0x3FE80,0x3FFBF,0x87F37,0x00001
+#define T_HTML 0x10000,0x7FB8F,0x7FFFF,0x00000,0x00000,0x1FFFF,0x00003
+#define T_HY 0x01000,0x00000,0x00000,0x3779F,0x77FBF,0x8101F,0x00001
+#define T_I 0x00001,0x8B04F,0x8FFFF,0xA778F,0xF7FBF,0x00001,0x00014
+#define T_IFRAME 0x02000,0x8FBCF,0x8FFFF,0xB679F,0xB6FBF,0xD315F,0x00000
+#define T_IMG 0x01000,0x00000,0x00000,0x3779F,0x37FBF,0x80000,0x00001
+#define T_INPUT 0x00040,0x00000,0x00000,0x03F87,0x37F87,0x8904F,0x00001
+#define T_INS 0x00002,0x8B04F,0x8FEFF,0x87F8F,0xF7FBF,0x100003,0x00000
+#define T_INS_2 0x100000,0x8BBCF,0x8FFFF,0xA7F8F,0xF7FBF,0x100003,0x00000
+#define T_ISINDEX 0x08000,0x00000,0x00000,0x7778F,0x7FFAF,0x80007,0x00001
+#define T_KBD 0x00002,0x00000,0x00000,0x2778F,0x77FBF,0x00003,0x00000
+#define T_KEYGEN 0x00040,0x00000,0x00000,0x07FB7,0x37FB7,0x80070,0x00001
+#define T_LABEL 0x00002,0x0304F,0x0FFFF,0x0679F,0x36FBF,0x00007,0x00000
+#define T_LEGEND 0x00002,0x0B04F,0x8FF7F,0x00200,0xB7FA7,0x00003,0x00000
+#define T_LH 0x00400,0x0BB7F,0x8FFFF,0x00800,0x97FFF,0x8071F,0x00001
+#define T_LI 0x00400,0x0BBFF,0x8FFFF,0x00800,0x97FFF,0x8071F,0x00001
+#define T_LINK 0x08000,0x00000,0x00000,0x50000,0x50000,0x0FF7F,0x00001
+#define T_LISTING 0x00800,0x00000,0x00000,0x136600,0x136F00,0x80F1F,0x00000
+#define T_MAIN 0x00200,0x8FBCF,0x8FFFF,0x1B66A0,0x1B7FFF,0x8031F,0x00004
+#define T_MAP 0x08000,0x08000,0x08000,0x37FCF,0x37FBF,0x0051F,0x00000
+#define T_MARQUEE 0x04000,0x0000F,0x8F01F,0x37787,0xB7FA7,0x8301C,0x00000
+#define T_MATH 0x00004,0x0B05F,0x8FFFF,0x2778F,0xF7FBF,0x0001F,0x00000
+#define T_MENU 0x00800,0x0B400,0x0F75F,0x117680,0x136FB7,0x88F7F,0x00000
+#define T_META 0x08000,0x00000,0x00000,0x50000,0x50000,0x0FF7F,0x00001
+#define T_NAV 0x00200,0x8FBCF,0x8FFFF,0x1B66A0,0x1B7FFF,0x8031F,0x00004
+#define T_NEXTID 0x01000,0x00000,0x00000,0x50000,0x1FFF7,0x00001,0x00001
+#define T_NOFRAMES 0x20000,0x12FB8F,0x10FFFF,0x17000,0x17000,0x0CF5F,0x00000
+#define T_NOTE 0x00200,0x0BBAF,0x8FFFF,0x1376B0,0x1B7FFF,0x8031F,0x00000
+#define T_OBJECT 0x02000,0x8FBCF,0x8FFFF,0xB679F,0xB6FBF,0x83D5F,0x00020
+#define T_OL 0x00800,0x0C400,0x8FFFF,0x137680,0x1B7FB7,0x88F7F,0x00000
+#define T_OPTION 0x08000,0x00000,0x00000,0x00040,0x37FFF,0x8031F,0x00001
+#define T_OVERLAY 0x04000,0x00000,0x00000,0x00200,0x37FBF,0x83F7F,0x00001
+#define T_P 0x00100,0x0B04F,0x8FFFF,0x136680,0x1B6FA7,0x80117,0x00001
+#define T_PARAM 0x01000,0x00000,0x00000,0x33500,0x37FFF,0x81560,0x00001
+#define T_PLAINTEXT 0x10000,0xFFFFF,0xFFFFF,0x90000,0x90000,0x3FFFF,0x00001
+#define T_PRE 0x00200,0x0F04F,0x0F05E,0x136680,0x136FF0,0x8071E,0x00000
+#define T_Q 0x00002,0x8B04F,0x8FFFF,0xA778F,0xF7FAF,0x00003,0x00000
+#define T_S 0x00001,0x8B04F,0x8FFFF,0xA778F,0xF7FBF,0x00001,0x00000
+#define T_SAMP 0x00002,0x8B04F,0x8FFFF,0xA778F,0xF7FBF,0x00002,0x00010
+#define T_SCRIPT 0x02000,0x00000,0x00000,0x77F9F,0x77FFF,0x87D5F,0x00000
+#define T_SECTION 0x00200,0x8FBCF,0x8FFFF,0x1B66A0,0x1B7FFF,0x8031F,0x00004
+#define T_SELECT 0x00040,0x08000,0x08000,0x03FAF,0x33FBF,0x80D5F,0x00008
+#define T_SHY 0x01000,0x00000,0x00000,0x3779F,0x77FBF,0x8101F,0x00001
+#define T_SMALL 0x00001,0x8B04F,0x8FFFF,0xA778F,0xF7FBF,0x00001,0x00014
+#define T_SPAN 0x00002,0x8B04F,0x8FFFF,0xA778F,0xF7FBF,0x80003,0x00000
+#define T_SPOT 0x00008,0x00000,0x00000,0x3FFF7,0x3FFF7,0x00008,0x00001
+#define T_STRIKE 0x00001,0x8B04F,0x8FFFF,0xA778F,0xF7FBF,0x00001,0x00000
+#define T_STRONG 0x00002,0x8B04F,0x8FFFF,0xA778F,0xF7FAF,0x00003,0x00010
+#define T_STYLE 0x40000,0x00000,0x00000,0x7638F,0x76FAF,0x8001F,0x00000
+#define T_SUB 0x00004,0x8B05F,0x8FFFF,0x8779F,0xF7FBF,0x00007,0x00000
+#define T_SUP 0x00004,0x8B05F,0x8FFFF,0x8779F,0xF7FBF,0x00007,0x00000
+#define T_TAB 0x01000,0x00000,0x00000,0x3778F,0x57FAF,0x00001,0x00001
+#define T_TABLE 0x00800,0x0F1E0,0x8FFFF,0x136680,0x1B6FA7,0x8C57F,0x00000
+#define T_TBODY 0x00020,0x00020,0x8FFFF,0x00880,0xB7FB7,0x8C75F,0x00003
+#define T_TD 0x00400,0x0FBCF,0x8FFFF,0x00020,0xB7FB7,0x8C75F,0x00001
+#define T_TEXTAREA 0x00040,0x00000,0x00000,0x07F8F,0x33FBF,0x80D5F,0x00040
+#define T_TEXTFLOW 0x20000,0x18FBFF,0x19FFFF,0x977B0,0xB7FB7,0x9B00F,0x00003
+#define T_TFOOT 0x00020,0x00020,0x8FFFF,0x00800,0xB7FB7,0x8CF5F,0x00001
+#define T_TH 0x00400,0x0FBCF,0x0FFFF,0x00020,0xB7FB7,0x8CF5F,0x00001
+#define T_THEAD 0x00020,0x00020,0x8FFFF,0x00800,0xB7FB7,0x8CF5F,0x00001
+#define T_TITLE 0x40000,0x00000,0x00000,0x50000,0x50000,0x0031F,0x0000C
+#define T_TR 0x00020,0x00400,0x8FFFF,0x00820,0xB7FB7,0x8C75F,0x00001
+#define T_TT 0x00001,0x8B04F,0x8FFFF,0xA778F,0xF7FBF,0x00001,0x00010
+#define T_U 0x00001,0x8B04F,0x8FFFF,0xA778F,0xF7FBF,0x00001,0x00014
+#define T_UL 0x00800,0x0C480,0x8FFFF,0x136680,0x1B7FFF,0x8075F,0x00000
+#define T_VAR 0x00002,0x8B04F,0x8FFFF,0xA778F,0xF7FBF,0x00001,0x00000
+#define T_WBR 0x00001,0x00000,0x00000,0x3778F,0x77FBF,0x8101F,0x00001
+#define T_XMP 0x00800,0x00000,0x00000,0x1367E0,0x136FFF,0x0875F,0x00001
+#define T_OBJECT_PCDATA 0x02000,0x8FBCF,0x8FFFF,0xB679F,0xB6FBF,0x83D5F,0x00008
+#define T__UNREC_ 0x00000,0x00000,0x00000,0x00000,0x00000,0x00000,0x00000
+#ifdef USE_PRETTYSRC
+# define N HTMLA_NORMAL
+# define i HTMLA_ANAME
+# define h HTMLA_HREF
+# define c HTMLA_CLASS
+# define x HTMLA_AUXCLASS
+# define T(t) , t
+#else
+# define T(t) /*nothing */
+#endif
+/* *INDENT-OFF* */
+
+#define ATTR_TYPE(name) #name, name##_attr_list
+
+/* generic attributes, used in different tags */
+static const attr core_attr_list[] = {
+ { "CLASS" T(c) },
+ { "ID" T(i) },
+ { "STYLE" T(N) },
+ { "TITLE" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType core_attr_type[] = {
+ { ATTR_TYPE(core) },
+ { 0, 0 },
+};
+
+static const attr i18n_attr_list[] = {
+ { "DIR" T(N) },
+ { "LANG" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType i18n_attr_type[] = {
+ { ATTR_TYPE(i18n) },
+ { 0, 0 },
+};
+
+static const attr events_attr_list[] = {
+ { "ONCLICK" T(N) },
+ { "ONDBLCLICK" T(N) },
+ { "ONKEYDOWN" T(N) },
+ { "ONKEYPRESS" T(N) },
+ { "ONKEYUP" T(N) },
+ { "ONMOUSEDOWN" T(N) },
+ { "ONMOUSEMOVE" T(N) },
+ { "ONMOUSEOUT" T(N) },
+ { "ONMOUSEOVER" T(N) },
+ { "ONMOUSEUP" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType events_attr_type[] = {
+ { ATTR_TYPE(events) },
+ { 0, 0 },
+};
+
+static const attr align_attr_list[] = {
+ { "ALIGN" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType align_attr_type[] = {
+ { ATTR_TYPE(align) },
+ { 0, 0 },
+};
+
+static const attr cellalign_attr_list[] = {
+ { "ALIGN" T(N) },
+ { "CHAR" T(N) },
+ { "CHAROFF" T(N) },
+ { "VALIGN" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType cellalign_attr_type[] = {
+ { ATTR_TYPE(cellalign) },
+ { 0, 0 },
+};
+
+static const attr bgcolor_attr_list[] = {
+ { "BGCOLOR" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType bgcolor_attr_type[] = {
+ { ATTR_TYPE(bgcolor) },
+ { 0, 0 },
+};
+
+
+/* tables defining attributes per-tag in terms of generic attributes (editable) */
+static const attr A_attr_list[] = {
+ { "ACCESSKEY" T(N) },
+ { "CHARSET" T(N) },
+ { "CLEAR" T(N) },
+ { "COORDS" T(N) },
+ { "HREF" T(h) },
+ { "HREFLANG" T(N) },
+ { "ISMAP" T(N) },
+ { "MD" T(N) },
+ { "NAME" T(i) },
+ { "NOTAB" T(N) },
+ { "ONBLUR" T(N) },
+ { "ONFOCUS" T(N) },
+ { "REL" T(N) },
+ { "REV" T(N) },
+ { "SHAPE" T(N) },
+ { "TABINDEX" T(N) },
+ { "TARGET" T(N) },
+ { "TYPE" T(N) },
+ { "URN" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType A_attr_type[] = {
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(events) },
+ { ATTR_TYPE(i18n) },
+ { ATTR_TYPE(A) },
+ { 0, 0 },
+};
+
+static const attr ADDRESS_attr_list[] = {
+ { "CLEAR" T(N) },
+ { "NOWRAP" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType ADDRESS_attr_type[] = {
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(i18n) },
+ { ATTR_TYPE(ADDRESS) },
+ { 0, 0 },
+};
+
+static const attr APPLET_attr_list[] = {
+ { "ALT" T(N) },
+ { "CLEAR" T(N) },
+ { "CODE" T(N) },
+ { "CODEBASE" T(h) },
+ { "DOWNLOAD" T(N) },
+ { "HEIGHT" T(N) },
+ { "HSPACE" T(N) },
+ { "NAME" T(i) },
+ { "VSPACE" T(N) },
+ { "WIDTH" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType APPLET_attr_type[] = {
+ { ATTR_TYPE(align) },
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(i18n) },
+ { ATTR_TYPE(APPLET) },
+ { 0, 0 },
+};
+
+static const attr AREA_attr_list[] = {
+ { "ACCESSKEY" T(N) },
+ { "ALT" T(N) },
+ { "CLEAR" T(N) },
+ { "COORDS" T(N) },
+ { "HREF" T(h) },
+ { "NOHREF" T(N) },
+ { "NOTAB" T(N) },
+ { "ONBLUR" T(N) },
+ { "ONFOCUS" T(N) },
+ { "SHAPE" T(N) },
+ { "TABINDEX" T(N) },
+ { "TARGET" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType AREA_attr_type[] = {
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(events) },
+ { ATTR_TYPE(i18n) },
+ { ATTR_TYPE(AREA) },
+ { 0, 0 },
+};
+
+static const attr BASE_attr_list[] = {
+ { "HREF" T(h) },
+ { "TARGET" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType BASE_attr_type[] = {
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(BASE) },
+ { 0, 0 },
+};
+
+static const attr BGSOUND_attr_list[] = {
+ { "CLEAR" T(N) },
+ { "LOOP" T(N) },
+ { "SRC" T(h) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType BGSOUND_attr_type[] = {
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(i18n) },
+ { ATTR_TYPE(BGSOUND) },
+ { 0, 0 },
+};
+
+static const attr BODY_attr_list[] = {
+ { "ALINK" T(N) },
+ { "BACKGROUND" T(h) },
+ { "CLEAR" T(N) },
+ { "LINK" T(N) },
+ { "ONLOAD" T(N) },
+ { "ONUNLOAD" T(N) },
+ { "TEXT" T(N) },
+ { "VLINK" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType BODY_attr_type[] = {
+ { ATTR_TYPE(bgcolor) },
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(i18n) },
+ { ATTR_TYPE(BODY) },
+ { 0, 0 },
+};
+
+static const attr BODYTEXT_attr_list[] = {
+ { "CLEAR" T(N) },
+ { "DATA" T(N) },
+ { "NAME" T(N) },
+ { "OBJECT" T(N) },
+ { "REF" T(N) },
+ { "TYPE" T(N) },
+ { "VALUE" T(N) },
+ { "VALUETYPE" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType BODYTEXT_attr_type[] = {
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(i18n) },
+ { ATTR_TYPE(BODYTEXT) },
+ { 0, 0 },
+};
+
+static const attr BQ_attr_list[] = {
+ { "CITE" T(h) },
+ { "CLEAR" T(N) },
+ { "NOWRAP" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType BQ_attr_type[] = {
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(i18n) },
+ { ATTR_TYPE(BQ) },
+ { 0, 0 },
+};
+
+static const attr BUTTON_attr_list[] = {
+ { "ACCESSKEY" T(N) },
+ { "CLEAR" T(N) },
+ { "DISABLED" T(N) },
+ { "FORMACTION" T(N) },
+ { "NAME" T(N) },
+ { "ONBLUR" T(N) },
+ { "ONFOCUS" T(N) },
+ { "READONLY" T(N) },
+ { "TABINDEX" T(N) },
+ { "TYPE" T(N) },
+ { "VALUE" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType BUTTON_attr_type[] = {
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(events) },
+ { ATTR_TYPE(i18n) },
+ { ATTR_TYPE(BUTTON) },
+ { 0, 0 },
+};
+
+static const attr CAPTION_attr_list[] = {
+ { "ACCESSKEY" T(N) },
+ { "CLEAR" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType CAPTION_attr_type[] = {
+ { ATTR_TYPE(align) },
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(events) },
+ { ATTR_TYPE(i18n) },
+ { ATTR_TYPE(CAPTION) },
+ { 0, 0 },
+};
+
+static const attr COL_attr_list[] = {
+ { "CLEAR" T(N) },
+ { "SPAN" T(N) },
+ { "WIDTH" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType COL_attr_type[] = {
+ { ATTR_TYPE(cellalign) },
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(events) },
+ { ATTR_TYPE(i18n) },
+ { ATTR_TYPE(COL) },
+ { 0, 0 },
+};
+
+static const attr DEL_attr_list[] = {
+ { "CITE" T(N) },
+ { "DATETIME" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType DEL_attr_type[] = {
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(events) },
+ { ATTR_TYPE(i18n) },
+ { ATTR_TYPE(DEL) },
+ { 0, 0 },
+};
+
+static const attr DIV_attr_list[] = {
+ { "CLEAR" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType DIV_attr_type[] = {
+ { ATTR_TYPE(align) },
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(i18n) },
+ { ATTR_TYPE(DIV) },
+ { 0, 0 },
+};
+
+static const attr DL_attr_list[] = {
+ { "CLEAR" T(N) },
+ { "COMPACT" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType DL_attr_type[] = {
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(i18n) },
+ { ATTR_TYPE(DL) },
+ { 0, 0 },
+};
+
+static const attr EMBED_attr_list[] = {
+ { "ALT" T(N) },
+ { "BORDER" T(N) },
+ { "CLEAR" T(N) },
+ { "HEIGHT" T(N) },
+ { "IMAGEMAP" T(N) },
+ { "ISMAP" T(N) },
+ { "MD" T(N) },
+ { "NAME" T(i) },
+ { "NOFLOW" T(N) },
+ { "PARAMS" T(N) },
+ { "SRC" T(h) },
+ { "UNITS" T(N) },
+ { "USEMAP" T(N) },
+ { "WIDTH" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType EMBED_attr_type[] = {
+ { ATTR_TYPE(align) },
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(i18n) },
+ { ATTR_TYPE(EMBED) },
+ { 0, 0 },
+};
+
+static const attr FIG_attr_list[] = {
+ { "BORDER" T(N) },
+ { "CLEAR" T(N) },
+ { "HEIGHT" T(N) },
+ { "IMAGEMAP" T(N) },
+ { "ISOBJECT" T(N) },
+ { "MD" T(N) },
+ { "NOFLOW" T(N) },
+ { "SRC" T(h) },
+ { "UNITS" T(N) },
+ { "WIDTH" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType FIG_attr_type[] = {
+ { ATTR_TYPE(align) },
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(i18n) },
+ { ATTR_TYPE(FIG) },
+ { 0, 0 },
+};
+
+static const attr FONT_attr_list[] = {
+ { "CLEAR" T(N) },
+ { "COLOR" T(N) },
+ { "END" T(N) },
+ { "FACE" T(N) },
+ { "SIZE" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType FONT_attr_type[] = {
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(i18n) },
+ { ATTR_TYPE(FONT) },
+ { 0, 0 },
+};
+
+static const attr FORM_attr_list[] = {
+ { "ACCEPT" T(N) },
+ { "ACCEPT-CHARSET" T(N) },
+ { "ACTION" T(h) },
+ { "CLEAR" T(N) },
+ { "ENCTYPE" T(N) },
+ { "METHOD" T(N) },
+ { "ONRESET" T(N) },
+ { "ONSUBMIT" T(N) },
+ { "SCRIPT" T(N) },
+ { "SUBJECT" T(N) },
+ { "TARGET" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType FORM_attr_type[] = {
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(i18n) },
+ { ATTR_TYPE(FORM) },
+ { 0, 0 },
+};
+
+static const attr FRAME_attr_list[] = {
+ { "FRAMEBORDER" T(N) },
+ { "LONGDESC" T(h) },
+ { "MARGINHEIGHT" T(N) },
+ { "MARGINWIDTH" T(N) },
+ { "NAME" T(N) },
+ { "NORESIZE" T(N) },
+ { "SCROLLING" T(N) },
+ { "SRC" T(h) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType FRAME_attr_type[] = {
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(FRAME) },
+ { 0, 0 },
+};
+
+static const attr FRAMESET_attr_list[] = {
+ { "COLS" T(N) },
+ { "ONLOAD" T(N) },
+ { "ONUNLOAD" T(N) },
+ { "ROWS" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType FRAMESET_attr_type[] = {
+ { ATTR_TYPE(FRAMESET) },
+ { 0, 0 },
+};
+
+static const attr GEN_attr_list[] = {
+ { "CLEAR" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType GEN_attr_type[] = {
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(events) },
+ { ATTR_TYPE(i18n) },
+ { ATTR_TYPE(GEN) },
+ { 0, 0 },
+};
+
+static const attr GEN5_attr_list[] = {
+ { "ROLE" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType GEN5_attr_type[] = {
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(events) },
+ { ATTR_TYPE(i18n) },
+ { ATTR_TYPE(GEN5) },
+ { 0, 0 },
+};
+
+static const attr H_attr_list[] = {
+ { "CLEAR" T(N) },
+ { "DINGBAT" T(N) },
+ { "MD" T(N) },
+ { "NOWRAP" T(N) },
+ { "SEQNUM" T(N) },
+ { "SKIP" T(N) },
+ { "SRC" T(h) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType H_attr_type[] = {
+ { ATTR_TYPE(align) },
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(events) },
+ { ATTR_TYPE(i18n) },
+ { ATTR_TYPE(H) },
+ { 0, 0 },
+};
+
+static const attr HR_attr_list[] = {
+ { "CLEAR" T(N) },
+ { "MD" T(N) },
+ { "NOSHADE" T(N) },
+ { "SIZE" T(N) },
+ { "SRC" T(h) },
+ { "WIDTH" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType HR_attr_type[] = {
+ { ATTR_TYPE(align) },
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(i18n) },
+ { ATTR_TYPE(HR) },
+ { 0, 0 },
+};
+
+static const attr IFRAME_attr_list[] = {
+ { "FRAMEBORDER" T(N) },
+ { "HEIGHT" T(N) },
+ { "LONGDESC" T(h) },
+ { "MARGINHEIGHT" T(N) },
+ { "MARGINWIDTH" T(N) },
+ { "NAME" T(N) },
+ { "SCROLLING" T(N) },
+ { "SRC" T(h) },
+ { "WIDTH" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType IFRAME_attr_type[] = {
+ { ATTR_TYPE(align) },
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(IFRAME) },
+ { 0, 0 },
+};
+
+static const attr IMG_attr_list[] = {
+ { "ALT" T(N) },
+ { "BORDER" T(N) },
+ { "CLEAR" T(N) },
+ { "HEIGHT" T(N) },
+ { "HSPACE" T(N) },
+ { "ISMAP" T(N) },
+ { "ISOBJECT" T(N) },
+ { "LONGDESC" T(h) },
+ { "MD" T(N) },
+ { "NAME" T(N) },
+ { "SRC" T(h) },
+ { "UNITS" T(N) },
+ { "USEMAP" T(h) },
+ { "VSPACE" T(N) },
+ { "WIDTH" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType IMG_attr_type[] = {
+ { ATTR_TYPE(align) },
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(events) },
+ { ATTR_TYPE(i18n) },
+ { ATTR_TYPE(IMG) },
+ { 0, 0 },
+};
+
+static const attr INPUT_attr_list[] = {
+ { "ACCEPT" T(N) },
+ { "ACCEPT-CHARSET" T(N) },
+ { "ACCESSKEY" T(N) },
+ { "ALT" T(N) },
+ { "CHECKED" T(N) },
+ { "CLEAR" T(N) },
+ { "DISABLED" T(N) },
+ { "ERROR" T(N) },
+ { "HEIGHT" T(N) },
+ { "ISMAP" T(N) },
+ { "MAX" T(N) },
+ { "MAXLENGTH" T(N) },
+ { "MD" T(N) },
+ { "MIN" T(N) },
+ { "NAME" T(N) },
+ { "NOTAB" T(N) },
+ { "ONBLUR" T(N) },
+ { "ONCHANGE" T(N) },
+ { "ONFOCUS" T(N) },
+ { "ONSELECT" T(N) },
+ { "READONLY" T(N) },
+ { "SIZE" T(N) },
+ { "SRC" T(h) },
+ { "TABINDEX" T(N) },
+ { "TYPE" T(N) },
+ { "USEMAP" T(N) },
+ { "VALUE" T(N) },
+ { "WIDTH" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType INPUT_attr_type[] = {
+ { ATTR_TYPE(align) },
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(events) },
+ { ATTR_TYPE(i18n) },
+ { ATTR_TYPE(INPUT) },
+ { 0, 0 },
+};
+
+static const attr ISINDEX_attr_list[] = {
+ { "ACTION" T(h) },
+ { "HREF" T(h) },
+ { "PROMPT" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType ISINDEX_attr_type[] = {
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(i18n) },
+ { ATTR_TYPE(ISINDEX) },
+ { 0, 0 },
+};
+
+static const attr KEYGEN_attr_list[] = {
+ { "CHALLENGE" T(N) },
+ { "NAME" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType KEYGEN_attr_type[] = {
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(i18n) },
+ { ATTR_TYPE(KEYGEN) },
+ { 0, 0 },
+};
+
+static const attr LABEL_attr_list[] = {
+ { "ACCESSKEY" T(N) },
+ { "CLEAR" T(N) },
+ { "FOR" T(N) },
+ { "ONBLUR" T(N) },
+ { "ONFOCUS" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType LABEL_attr_type[] = {
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(events) },
+ { ATTR_TYPE(i18n) },
+ { ATTR_TYPE(LABEL) },
+ { 0, 0 },
+};
+
+static const attr LI_attr_list[] = {
+ { "CLEAR" T(N) },
+ { "DINGBAT" T(N) },
+ { "MD" T(N) },
+ { "SKIP" T(N) },
+ { "SRC" T(h) },
+ { "TYPE" T(N) },
+ { "VALUE" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType LI_attr_type[] = {
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(events) },
+ { ATTR_TYPE(i18n) },
+ { ATTR_TYPE(LI) },
+ { 0, 0 },
+};
+
+static const attr LINK_attr_list[] = {
+ { "CHARSET" T(N) },
+ { "HREF" T(h) },
+ { "HREFLANG" T(N) },
+ { "MEDIA" T(N) },
+ { "REL" T(N) },
+ { "REV" T(N) },
+ { "TARGET" T(N) },
+ { "TYPE" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType LINK_attr_type[] = {
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(events) },
+ { ATTR_TYPE(i18n) },
+ { ATTR_TYPE(LINK) },
+ { 0, 0 },
+};
+
+static const attr MAP_attr_list[] = {
+ { "CLEAR" T(N) },
+ { "NAME" T(i) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType MAP_attr_type[] = {
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(i18n) },
+ { ATTR_TYPE(MAP) },
+ { 0, 0 },
+};
+
+static const attr MATH_attr_list[] = {
+ { "BOX" T(N) },
+ { "CLEAR" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType MATH_attr_type[] = {
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(i18n) },
+ { ATTR_TYPE(MATH) },
+ { 0, 0 },
+};
+
+static const attr META_attr_list[] = {
+ { "CHARSET" T(N) },
+ { "CONTENT" T(N) },
+ { "HTTP-EQUIV" T(N) },
+ { "NAME" T(N) },
+ { "SCHEME" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType META_attr_type[] = {
+ { ATTR_TYPE(META) },
+ { 0, 0 },
+};
+
+static const attr NEXTID_attr_list[] = {
+ { "N" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType NEXTID_attr_type[] = {
+ { ATTR_TYPE(NEXTID) },
+ { 0, 0 },
+};
+
+static const attr NOTE_attr_list[] = {
+ { "CLEAR" T(N) },
+ { "MD" T(N) },
+ { "ROLE" T(x) },
+ { "SRC" T(h) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType NOTE_attr_type[] = {
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(i18n) },
+ { ATTR_TYPE(NOTE) },
+ { 0, 0 },
+};
+
+static const attr OBJECT_attr_list[] = {
+ { "ARCHIVE" T(N) },
+ { "BORDER" T(N) },
+ { "CLASSID" T(h) },
+ { "CODEBASE" T(h) },
+ { "CODETYPE" T(N) },
+ { "DATA" T(h) },
+ { "DECLARE" T(N) },
+ { "HEIGHT" T(N) },
+ { "HSPACE" T(N) },
+ { "ISMAP" T(N) },
+ { "NAME" T(N) },
+ { "NOTAB" T(N) },
+ { "SHAPES" T(N) },
+ { "STANDBY" T(N) },
+ { "TABINDEX" T(N) },
+ { "TYPE" T(N) },
+ { "USEMAP" T(h) },
+ { "VSPACE" T(N) },
+ { "WIDTH" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType OBJECT_attr_type[] = {
+ { ATTR_TYPE(align) },
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(events) },
+ { ATTR_TYPE(i18n) },
+ { ATTR_TYPE(OBJECT) },
+ { 0, 0 },
+};
+
+static const attr OL_attr_list[] = {
+ { "CLEAR" T(N) },
+ { "COMPACT" T(N) },
+ { "CONTINUE" T(N) },
+ { "SEQNUM" T(N) },
+ { "START" T(N) },
+ { "TYPE" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType OL_attr_type[] = {
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(i18n) },
+ { ATTR_TYPE(OL) },
+ { 0, 0 },
+};
+
+static const attr OPTION_attr_list[] = {
+ { "CLEAR" T(N) },
+ { "DISABLED" T(N) },
+ { "ERROR" T(N) },
+ { "LABEL" T(N) },
+ { "SELECTED" T(N) },
+ { "SHAPE" T(N) },
+ { "VALUE" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType OPTION_attr_type[] = {
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(events) },
+ { ATTR_TYPE(i18n) },
+ { ATTR_TYPE(OPTION) },
+ { 0, 0 },
+};
+
+static const attr OVERLAY_attr_list[] = {
+ { "HEIGHT" T(N) },
+ { "IMAGEMAP" T(N) },
+ { "MD" T(N) },
+ { "SRC" T(h) },
+ { "UNITS" T(N) },
+ { "WIDTH" T(N) },
+ { "X" T(N) },
+ { "Y" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType OVERLAY_attr_type[] = {
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(OVERLAY) },
+ { 0, 0 },
+};
+
+static const attr P_attr_list[] = {
+ { "CLEAR" T(N) },
+ { "NOWRAP" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType P_attr_type[] = {
+ { ATTR_TYPE(align) },
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(i18n) },
+ { ATTR_TYPE(P) },
+ { 0, 0 },
+};
+
+static const attr PARAM_attr_list[] = {
+ { "ACCEPT" T(N) },
+ { "ACCEPT-CHARSET" T(N) },
+ { "ACCEPT-ENCODING" T(N) },
+ { "CLEAR" T(N) },
+ { "DATA" T(N) },
+ { "NAME" T(N) },
+ { "OBJECT" T(N) },
+ { "REF" T(N) },
+ { "TYPE" T(N) },
+ { "VALUE" T(N) },
+ { "VALUEREF" T(N) },
+ { "VALUETYPE" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType PARAM_attr_type[] = {
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(i18n) },
+ { ATTR_TYPE(PARAM) },
+ { 0, 0 },
+};
+
+static const attr Q_attr_list[] = {
+ { "CITE" T(h) },
+ { "CLEAR" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType Q_attr_type[] = {
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(i18n) },
+ { ATTR_TYPE(Q) },
+ { 0, 0 },
+};
+
+static const attr SCRIPT_attr_list[] = {
+ { "CHARSET" T(N) },
+ { "CLEAR" T(N) },
+ { "DEFER" T(N) },
+ { "EVENT" T(N) },
+ { "FOR" T(N) },
+ { "LANGUAGE" T(N) },
+ { "NAME" T(N) },
+ { "SCRIPTENGINE" T(N) },
+ { "SRC" T(h) },
+ { "TYPE" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType SCRIPT_attr_type[] = {
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(i18n) },
+ { ATTR_TYPE(SCRIPT) },
+ { 0, 0 },
+};
+
+static const attr SELECT_attr_list[] = {
+ { "CLEAR" T(N) },
+ { "DISABLED" T(N) },
+ { "ERROR" T(N) },
+ { "HEIGHT" T(N) },
+ { "MD" T(N) },
+ { "MULTIPLE" T(N) },
+ { "NAME" T(N) },
+ { "NOTAB" T(N) },
+ { "ONBLUR" T(N) },
+ { "ONCHANGE" T(N) },
+ { "ONFOCUS" T(N) },
+ { "SIZE" T(N) },
+ { "TABINDEX" T(N) },
+ { "UNITS" T(N) },
+ { "WIDTH" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType SELECT_attr_type[] = {
+ { ATTR_TYPE(align) },
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(i18n) },
+ { ATTR_TYPE(SELECT) },
+ { 0, 0 },
+};
+
+static const attr STYLE_attr_list[] = {
+ { "MEDIA" T(N) },
+ { "NOTATION" T(N) },
+ { "TYPE" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType STYLE_attr_type[] = {
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(i18n) },
+ { ATTR_TYPE(STYLE) },
+ { 0, 0 },
+};
+
+static const attr TAB_attr_list[] = {
+ { "CLEAR" T(N) },
+ { "DP" T(N) },
+ { "INDENT" T(N) },
+ { "TO" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType TAB_attr_type[] = {
+ { ATTR_TYPE(align) },
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(i18n) },
+ { ATTR_TYPE(TAB) },
+ { 0, 0 },
+};
+
+static const attr TABLE_attr_list[] = {
+ { "BACKGROUND" T(h) },
+ { "BORDER" T(N) },
+ { "CELLPADDING" T(N) },
+ { "CELLSPACING" T(N) },
+ { "CLEAR" T(N) },
+ { "COLS" T(N) },
+ { "COLSPEC" T(N) },
+ { "DP" T(N) },
+ { "FRAME" T(N) },
+ { "NOFLOW" T(N) },
+ { "NOWRAP" T(N) },
+ { "RULES" T(N) },
+ { "SUMMARY" T(N) },
+ { "UNITS" T(N) },
+ { "WIDTH" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType TABLE_attr_type[] = {
+ { ATTR_TYPE(align) },
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(events) },
+ { ATTR_TYPE(i18n) },
+ { ATTR_TYPE(TABLE) },
+ { 0, 0 },
+};
+
+static const attr TD_attr_list[] = {
+ { "ABBR" T(N) },
+ { "AXES" T(N) },
+ { "AXIS" T(N) },
+ { "BACKGROUND" T(h) },
+ { "CLEAR" T(N) },
+ { "COLSPAN" T(N) },
+ { "DP" T(N) },
+ { "HEADERS" T(N) },
+ { "HEIGHT" T(N) },
+ { "NOWRAP" T(N) },
+ { "ROWSPAN" T(N) },
+ { "SCOPE" T(N) },
+ { "WIDTH" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType TD_attr_type[] = {
+ { ATTR_TYPE(cellalign) },
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(i18n) },
+ { ATTR_TYPE(TD) },
+ { 0, 0 },
+};
+
+static const attr TEXTAREA_attr_list[] = {
+ { "ACCEPT-CHARSET" T(N) },
+ { "ACCESSKEY" T(N) },
+ { "CLEAR" T(N) },
+ { "COLS" T(N) },
+ { "DISABLED" T(N) },
+ { "ERROR" T(N) },
+ { "NAME" T(N) },
+ { "NOTAB" T(N) },
+ { "ONBLUR" T(N) },
+ { "ONCHANGE" T(N) },
+ { "ONFOCUS" T(N) },
+ { "ONSELECT" T(N) },
+ { "READONLY" T(N) },
+ { "ROWS" T(N) },
+ { "TABINDEX" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType TEXTAREA_attr_type[] = {
+ { ATTR_TYPE(align) },
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(events) },
+ { ATTR_TYPE(i18n) },
+ { ATTR_TYPE(TEXTAREA) },
+ { 0, 0 },
+};
+
+static const attr TR_attr_list[] = {
+ { "CLEAR" T(N) },
+ { "DP" T(N) },
+ { "NOWRAP" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType TR_attr_type[] = {
+ { ATTR_TYPE(cellalign) },
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(events) },
+ { ATTR_TYPE(i18n) },
+ { ATTR_TYPE(TR) },
+ { 0, 0 },
+};
+
+static const attr UL_attr_list[] = {
+ { "CLEAR" T(N) },
+ { "COMPACT" T(N) },
+ { "DINGBAT" T(N) },
+ { "MD" T(N) },
+ { "PLAIN" T(N) },
+ { "SRC" T(h) },
+ { "TYPE" T(N) },
+ { "WRAP" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType UL_attr_type[] = {
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(i18n) },
+ { ATTR_TYPE(UL) },
+ { 0, 0 },
+};
+
+
+/* attribute lists for the runtime (generated by dtd_util) */
+static const attr A_attr[] = { /* A attributes */
+ { "ACCESSKEY" T(N) },
+ { "CHARSET" T(N) },
+ { "CLASS" T(c) },
+ { "CLEAR" T(N) },
+ { "COORDS" T(N) },
+ { "DIR" T(N) },
+ { "HREF" T(h) },
+ { "HREFLANG" T(N) },
+ { "ID" T(i) },
+ { "ISMAP" T(N) },
+ { "LANG" T(N) },
+ { "MD" T(N) },
+ { "NAME" T(i) },
+ { "NOTAB" T(N) },
+ { "ONBLUR" T(N) },
+ { "ONFOCUS" T(N) },
+ { "REL" T(N) },
+ { "REV" T(N) },
+ { "SHAPE" T(N) },
+ { "STYLE" T(N) },
+ { "TABINDEX" T(N) },
+ { "TARGET" T(N) },
+ { "TITLE" T(N) },
+ { "TYPE" T(N) },
+ { "URN" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr ADDRESS_attr[] = { /* ADDRESS attributes */
+ { "CLASS" T(c) },
+ { "CLEAR" T(N) },
+ { "DIR" T(N) },
+ { "ID" T(i) },
+ { "LANG" T(N) },
+ { "NOWRAP" T(N) },
+ { "STYLE" T(N) },
+ { "TITLE" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr APPLET_attr[] = { /* APPLET attributes */
+ { "ALIGN" T(N) },
+ { "ALT" T(N) },
+ { "CLASS" T(c) },
+ { "CLEAR" T(N) },
+ { "CODE" T(N) },
+ { "CODEBASE" T(h) },
+ { "DIR" T(N) },
+ { "DOWNLOAD" T(N) },
+ { "HEIGHT" T(N) },
+ { "HSPACE" T(N) },
+ { "ID" T(i) },
+ { "LANG" T(N) },
+ { "NAME" T(i) },
+ { "STYLE" T(N) },
+ { "TITLE" T(N) },
+ { "VSPACE" T(N) },
+ { "WIDTH" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr AREA_attr[] = { /* AREA attributes */
+ { "ACCESSKEY" T(N) },
+ { "ALT" T(N) },
+ { "CLASS" T(c) },
+ { "CLEAR" T(N) },
+ { "COORDS" T(N) },
+ { "DIR" T(N) },
+ { "HREF" T(h) },
+ { "ID" T(i) },
+ { "LANG" T(N) },
+ { "NOHREF" T(N) },
+ { "NOTAB" T(N) },
+ { "ONBLUR" T(N) },
+ { "ONFOCUS" T(N) },
+ { "SHAPE" T(N) },
+ { "STYLE" T(N) },
+ { "TABINDEX" T(N) },
+ { "TARGET" T(N) },
+ { "TITLE" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr BASE_attr[] = { /* BASE attributes */
+ { "CLASS" T(c) },
+ { "HREF" T(h) },
+ { "ID" T(i) },
+ { "STYLE" T(N) },
+ { "TARGET" T(N) },
+ { "TITLE" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr BGSOUND_attr[] = { /* BGSOUND attributes */
+ { "CLASS" T(c) },
+ { "CLEAR" T(N) },
+ { "DIR" T(N) },
+ { "ID" T(i) },
+ { "LANG" T(N) },
+ { "LOOP" T(N) },
+ { "SRC" T(h) },
+ { "STYLE" T(N) },
+ { "TITLE" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr BODY_attr[] = { /* BODY attributes */
+ { "ALINK" T(N) },
+ { "BACKGROUND" T(h) },
+ { "BGCOLOR" T(N) },
+ { "CLASS" T(c) },
+ { "CLEAR" T(N) },
+ { "DIR" T(N) },
+ { "ID" T(i) },
+ { "LANG" T(N) },
+ { "LINK" T(N) },
+ { "ONLOAD" T(N) },
+ { "ONUNLOAD" T(N) },
+ { "STYLE" T(N) },
+ { "TEXT" T(N) },
+ { "TITLE" T(N) },
+ { "VLINK" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr BODYTEXT_attr[] = { /* BODYTEXT attributes */
+ { "CLASS" T(c) },
+ { "CLEAR" T(N) },
+ { "DATA" T(N) },
+ { "DIR" T(N) },
+ { "ID" T(i) },
+ { "LANG" T(N) },
+ { "NAME" T(N) },
+ { "OBJECT" T(N) },
+ { "REF" T(N) },
+ { "STYLE" T(N) },
+ { "TITLE" T(N) },
+ { "TYPE" T(N) },
+ { "VALUE" T(N) },
+ { "VALUETYPE" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr BQ_attr[] = { /* BLOCKQUOTE attributes */
+ { "CITE" T(h) },
+ { "CLASS" T(c) },
+ { "CLEAR" T(N) },
+ { "DIR" T(N) },
+ { "ID" T(i) },
+ { "LANG" T(N) },
+ { "NOWRAP" T(N) },
+ { "STYLE" T(N) },
+ { "TITLE" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr BUTTON_attr[] = { /* BUTTON attributes */
+ { "ACCESSKEY" T(N) },
+ { "CLASS" T(c) },
+ { "CLEAR" T(N) },
+ { "DIR" T(N) },
+ { "DISABLED" T(N) },
+ { "FORMACTION" T(N) },
+ { "ID" T(i) },
+ { "LANG" T(N) },
+ { "NAME" T(N) },
+ { "ONBLUR" T(N) },
+ { "ONFOCUS" T(N) },
+ { "READONLY" T(N) },
+ { "STYLE" T(N) },
+ { "TABINDEX" T(N) },
+ { "TITLE" T(N) },
+ { "TYPE" T(N) },
+ { "VALUE" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr CAPTION_attr[] = { /* CAPTION attributes */
+ { "ACCESSKEY" T(N) },
+ { "ALIGN" T(N) },
+ { "CLASS" T(c) },
+ { "CLEAR" T(N) },
+ { "DIR" T(N) },
+ { "ID" T(i) },
+ { "LANG" T(N) },
+ { "STYLE" T(N) },
+ { "TITLE" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr COL_attr[] = { /* COL attributes */
+ { "ALIGN" T(N) },
+ { "CHAR" T(N) },
+ { "CHAROFF" T(N) },
+ { "CLASS" T(c) },
+ { "CLEAR" T(N) },
+ { "DIR" T(N) },
+ { "ID" T(i) },
+ { "LANG" T(N) },
+ { "SPAN" T(N) },
+ { "STYLE" T(N) },
+ { "TITLE" T(N) },
+ { "VALIGN" T(N) },
+ { "WIDTH" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr DEL_attr[] = { /* DEL attributes */
+ { "CITE" T(N) },
+ { "CLASS" T(c) },
+ { "DATETIME" T(N) },
+ { "DIR" T(N) },
+ { "ID" T(i) },
+ { "LANG" T(N) },
+ { "STYLE" T(N) },
+ { "TITLE" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr DIV_attr[] = { /* CENTER attributes */
+ { "ALIGN" T(N) },
+ { "CLASS" T(c) },
+ { "CLEAR" T(N) },
+ { "DIR" T(N) },
+ { "ID" T(i) },
+ { "LANG" T(N) },
+ { "STYLE" T(N) },
+ { "TITLE" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr DL_attr[] = { /* DL attributes */
+ { "CLASS" T(c) },
+ { "CLEAR" T(N) },
+ { "COMPACT" T(N) },
+ { "DIR" T(N) },
+ { "ID" T(i) },
+ { "LANG" T(N) },
+ { "STYLE" T(N) },
+ { "TITLE" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr EMBED_attr[] = { /* EMBED attributes */
+ { "ALIGN" T(N) },
+ { "ALT" T(N) },
+ { "BORDER" T(N) },
+ { "CLASS" T(c) },
+ { "CLEAR" T(N) },
+ { "DIR" T(N) },
+ { "HEIGHT" T(N) },
+ { "ID" T(i) },
+ { "IMAGEMAP" T(N) },
+ { "ISMAP" T(N) },
+ { "LANG" T(N) },
+ { "MD" T(N) },
+ { "NAME" T(i) },
+ { "NOFLOW" T(N) },
+ { "PARAMS" T(N) },
+ { "SRC" T(h) },
+ { "STYLE" T(N) },
+ { "TITLE" T(N) },
+ { "UNITS" T(N) },
+ { "USEMAP" T(N) },
+ { "WIDTH" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr FIG_attr[] = { /* FIG attributes */
+ { "ALIGN" T(N) },
+ { "BORDER" T(N) },
+ { "CLASS" T(c) },
+ { "CLEAR" T(N) },
+ { "DIR" T(N) },
+ { "HEIGHT" T(N) },
+ { "ID" T(i) },
+ { "IMAGEMAP" T(N) },
+ { "ISOBJECT" T(N) },
+ { "LANG" T(N) },
+ { "MD" T(N) },
+ { "NOFLOW" T(N) },
+ { "SRC" T(h) },
+ { "STYLE" T(N) },
+ { "TITLE" T(N) },
+ { "UNITS" T(N) },
+ { "WIDTH" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr FONT_attr[] = { /* BASEFONT attributes */
+ { "CLASS" T(c) },
+ { "CLEAR" T(N) },
+ { "COLOR" T(N) },
+ { "DIR" T(N) },
+ { "END" T(N) },
+ { "FACE" T(N) },
+ { "ID" T(i) },
+ { "LANG" T(N) },
+ { "SIZE" T(N) },
+ { "STYLE" T(N) },
+ { "TITLE" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr FORM_attr[] = { /* FORM attributes */
+ { "ACCEPT" T(N) },
+ { "ACCEPT-CHARSET" T(N) },
+ { "ACTION" T(h) },
+ { "CLASS" T(c) },
+ { "CLEAR" T(N) },
+ { "DIR" T(N) },
+ { "ENCTYPE" T(N) },
+ { "ID" T(i) },
+ { "LANG" T(N) },
+ { "METHOD" T(N) },
+ { "ONRESET" T(N) },
+ { "ONSUBMIT" T(N) },
+ { "SCRIPT" T(N) },
+ { "STYLE" T(N) },
+ { "SUBJECT" T(N) },
+ { "TARGET" T(N) },
+ { "TITLE" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr FRAME_attr[] = { /* FRAME attributes */
+ { "CLASS" T(c) },
+ { "FRAMEBORDER" T(N) },
+ { "ID" T(i) },
+ { "LONGDESC" T(h) },
+ { "MARGINHEIGHT" T(N) },
+ { "MARGINWIDTH" T(N) },
+ { "NAME" T(N) },
+ { "NORESIZE" T(N) },
+ { "SCROLLING" T(N) },
+ { "SRC" T(h) },
+ { "STYLE" T(N) },
+ { "TITLE" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr FRAMESET_attr[] = { /* FRAMESET attributes */
+ { "COLS" T(N) },
+ { "ONLOAD" T(N) },
+ { "ONUNLOAD" T(N) },
+ { "ROWS" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr GEN_attr[] = { /* ABBR attributes */
+ { "CLASS" T(c) },
+ { "CLEAR" T(N) },
+ { "DIR" T(N) },
+ { "ID" T(i) },
+ { "LANG" T(N) },
+ { "STYLE" T(N) },
+ { "TITLE" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr GEN5_attr[] = { /* ARTICLE attributes */
+ { "CLASS" T(c) },
+ { "DIR" T(N) },
+ { "ID" T(i) },
+ { "LANG" T(N) },
+ { "ROLE" T(N) },
+ { "STYLE" T(N) },
+ { "TITLE" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr H_attr[] = { /* H1 attributes */
+ { "ALIGN" T(N) },
+ { "CLASS" T(c) },
+ { "CLEAR" T(N) },
+ { "DINGBAT" T(N) },
+ { "DIR" T(N) },
+ { "ID" T(i) },
+ { "LANG" T(N) },
+ { "MD" T(N) },
+ { "NOWRAP" T(N) },
+ { "SEQNUM" T(N) },
+ { "SKIP" T(N) },
+ { "SRC" T(h) },
+ { "STYLE" T(N) },
+ { "TITLE" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr HR_attr[] = { /* HR attributes */
+ { "ALIGN" T(N) },
+ { "CLASS" T(c) },
+ { "CLEAR" T(N) },
+ { "DIR" T(N) },
+ { "ID" T(i) },
+ { "LANG" T(N) },
+ { "MD" T(N) },
+ { "NOSHADE" T(N) },
+ { "SIZE" T(N) },
+ { "SRC" T(h) },
+ { "STYLE" T(N) },
+ { "TITLE" T(N) },
+ { "WIDTH" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr IFRAME_attr[] = { /* IFRAME attributes */
+ { "ALIGN" T(N) },
+ { "CLASS" T(c) },
+ { "FRAMEBORDER" T(N) },
+ { "HEIGHT" T(N) },
+ { "ID" T(i) },
+ { "LONGDESC" T(h) },
+ { "MARGINHEIGHT" T(N) },
+ { "MARGINWIDTH" T(N) },
+ { "NAME" T(N) },
+ { "SCROLLING" T(N) },
+ { "SRC" T(h) },
+ { "STYLE" T(N) },
+ { "TITLE" T(N) },
+ { "WIDTH" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr IMG_attr[] = { /* IMG attributes */
+ { "ALIGN" T(N) },
+ { "ALT" T(N) },
+ { "BORDER" T(N) },
+ { "CLASS" T(c) },
+ { "CLEAR" T(N) },
+ { "DIR" T(N) },
+ { "HEIGHT" T(N) },
+ { "HSPACE" T(N) },
+ { "ID" T(i) },
+ { "ISMAP" T(N) },
+ { "ISOBJECT" T(N) },
+ { "LANG" T(N) },
+ { "LONGDESC" T(h) },
+ { "MD" T(N) },
+ { "NAME" T(N) },
+ { "SRC" T(h) },
+ { "STYLE" T(N) },
+ { "TITLE" T(N) },
+ { "UNITS" T(N) },
+ { "USEMAP" T(h) },
+ { "VSPACE" T(N) },
+ { "WIDTH" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr INPUT_attr[] = { /* INPUT attributes */
+ { "ACCEPT" T(N) },
+ { "ACCEPT-CHARSET" T(N) },
+ { "ACCESSKEY" T(N) },
+ { "ALIGN" T(N) },
+ { "ALT" T(N) },
+ { "CHECKED" T(N) },
+ { "CLASS" T(c) },
+ { "CLEAR" T(N) },
+ { "DIR" T(N) },
+ { "DISABLED" T(N) },
+ { "ERROR" T(N) },
+ { "HEIGHT" T(N) },
+ { "ID" T(i) },
+ { "ISMAP" T(N) },
+ { "LANG" T(N) },
+ { "MAX" T(N) },
+ { "MAXLENGTH" T(N) },
+ { "MD" T(N) },
+ { "MIN" T(N) },
+ { "NAME" T(N) },
+ { "NOTAB" T(N) },
+ { "ONBLUR" T(N) },
+ { "ONCHANGE" T(N) },
+ { "ONFOCUS" T(N) },
+ { "ONSELECT" T(N) },
+ { "READONLY" T(N) },
+ { "SIZE" T(N) },
+ { "SRC" T(h) },
+ { "STYLE" T(N) },
+ { "TABINDEX" T(N) },
+ { "TITLE" T(N) },
+ { "TYPE" T(N) },
+ { "USEMAP" T(N) },
+ { "VALUE" T(N) },
+ { "WIDTH" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr ISINDEX_attr[] = { /* ISINDEX attributes */
+ { "ACTION" T(h) },
+ { "CLASS" T(c) },
+ { "DIR" T(N) },
+ { "HREF" T(h) },
+ { "ID" T(i) },
+ { "LANG" T(N) },
+ { "PROMPT" T(N) },
+ { "STYLE" T(N) },
+ { "TITLE" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr KEYGEN_attr[] = { /* KEYGEN attributes */
+ { "CHALLENGE" T(N) },
+ { "CLASS" T(c) },
+ { "DIR" T(N) },
+ { "ID" T(i) },
+ { "LANG" T(N) },
+ { "NAME" T(N) },
+ { "STYLE" T(N) },
+ { "TITLE" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr LABEL_attr[] = { /* LABEL attributes */
+ { "ACCESSKEY" T(N) },
+ { "CLASS" T(c) },
+ { "CLEAR" T(N) },
+ { "DIR" T(N) },
+ { "FOR" T(N) },
+ { "ID" T(i) },
+ { "LANG" T(N) },
+ { "ONBLUR" T(N) },
+ { "ONFOCUS" T(N) },
+ { "STYLE" T(N) },
+ { "TITLE" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr LI_attr[] = { /* LI attributes */
+ { "CLASS" T(c) },
+ { "CLEAR" T(N) },
+ { "DINGBAT" T(N) },
+ { "DIR" T(N) },
+ { "ID" T(i) },
+ { "LANG" T(N) },
+ { "MD" T(N) },
+ { "SKIP" T(N) },
+ { "SRC" T(h) },
+ { "STYLE" T(N) },
+ { "TITLE" T(N) },
+ { "TYPE" T(N) },
+ { "VALUE" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr LINK_attr[] = { /* LINK attributes */
+ { "CHARSET" T(N) },
+ { "CLASS" T(c) },
+ { "DIR" T(N) },
+ { "HREF" T(h) },
+ { "HREFLANG" T(N) },
+ { "ID" T(i) },
+ { "LANG" T(N) },
+ { "MEDIA" T(N) },
+ { "REL" T(N) },
+ { "REV" T(N) },
+ { "STYLE" T(N) },
+ { "TARGET" T(N) },
+ { "TITLE" T(N) },
+ { "TYPE" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr MAP_attr[] = { /* MAP attributes */
+ { "CLASS" T(c) },
+ { "CLEAR" T(N) },
+ { "DIR" T(N) },
+ { "ID" T(i) },
+ { "LANG" T(N) },
+ { "NAME" T(i) },
+ { "STYLE" T(N) },
+ { "TITLE" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr MATH_attr[] = { /* MATH attributes */
+ { "BOX" T(N) },
+ { "CLASS" T(c) },
+ { "CLEAR" T(N) },
+ { "DIR" T(N) },
+ { "ID" T(i) },
+ { "LANG" T(N) },
+ { "STYLE" T(N) },
+ { "TITLE" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr META_attr[] = { /* META attributes */
+ { "CHARSET" T(N) },
+ { "CONTENT" T(N) },
+ { "HTTP-EQUIV" T(N) },
+ { "NAME" T(N) },
+ { "SCHEME" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr NEXTID_attr[] = { /* NEXTID attributes */
+ { "N" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr NOTE_attr[] = { /* NOTE attributes */
+ { "CLASS" T(c) },
+ { "CLEAR" T(N) },
+ { "DIR" T(N) },
+ { "ID" T(i) },
+ { "LANG" T(N) },
+ { "MD" T(N) },
+ { "ROLE" T(x) },
+ { "SRC" T(h) },
+ { "STYLE" T(N) },
+ { "TITLE" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr OBJECT_attr[] = { /* OBJECT attributes */
+ { "ALIGN" T(N) },
+ { "ARCHIVE" T(N) },
+ { "BORDER" T(N) },
+ { "CLASS" T(c) },
+ { "CLASSID" T(h) },
+ { "CODEBASE" T(h) },
+ { "CODETYPE" T(N) },
+ { "DATA" T(h) },
+ { "DECLARE" T(N) },
+ { "DIR" T(N) },
+ { "HEIGHT" T(N) },
+ { "HSPACE" T(N) },
+ { "ID" T(i) },
+ { "ISMAP" T(N) },
+ { "LANG" T(N) },
+ { "NAME" T(N) },
+ { "NOTAB" T(N) },
+ { "SHAPES" T(N) },
+ { "STANDBY" T(N) },
+ { "STYLE" T(N) },
+ { "TABINDEX" T(N) },
+ { "TITLE" T(N) },
+ { "TYPE" T(N) },
+ { "USEMAP" T(h) },
+ { "VSPACE" T(N) },
+ { "WIDTH" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr OL_attr[] = { /* OL attributes */
+ { "CLASS" T(c) },
+ { "CLEAR" T(N) },
+ { "COMPACT" T(N) },
+ { "CONTINUE" T(N) },
+ { "DIR" T(N) },
+ { "ID" T(i) },
+ { "LANG" T(N) },
+ { "SEQNUM" T(N) },
+ { "START" T(N) },
+ { "STYLE" T(N) },
+ { "TITLE" T(N) },
+ { "TYPE" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr OPTION_attr[] = { /* OPTION attributes */
+ { "CLASS" T(c) },
+ { "CLEAR" T(N) },
+ { "DIR" T(N) },
+ { "DISABLED" T(N) },
+ { "ERROR" T(N) },
+ { "ID" T(i) },
+ { "LABEL" T(N) },
+ { "LANG" T(N) },
+ { "SELECTED" T(N) },
+ { "SHAPE" T(N) },
+ { "STYLE" T(N) },
+ { "TITLE" T(N) },
+ { "VALUE" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr OVERLAY_attr[] = { /* OVERLAY attributes */
+ { "CLASS" T(c) },
+ { "HEIGHT" T(N) },
+ { "ID" T(i) },
+ { "IMAGEMAP" T(N) },
+ { "MD" T(N) },
+ { "SRC" T(h) },
+ { "STYLE" T(N) },
+ { "TITLE" T(N) },
+ { "UNITS" T(N) },
+ { "WIDTH" T(N) },
+ { "X" T(N) },
+ { "Y" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr P_attr[] = { /* P attributes */
+ { "ALIGN" T(N) },
+ { "CLASS" T(c) },
+ { "CLEAR" T(N) },
+ { "DIR" T(N) },
+ { "ID" T(i) },
+ { "LANG" T(N) },
+ { "NOWRAP" T(N) },
+ { "STYLE" T(N) },
+ { "TITLE" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr PARAM_attr[] = { /* PARAM attributes */
+ { "ACCEPT" T(N) },
+ { "ACCEPT-CHARSET" T(N) },
+ { "ACCEPT-ENCODING" T(N) },
+ { "CLASS" T(c) },
+ { "CLEAR" T(N) },
+ { "DATA" T(N) },
+ { "DIR" T(N) },
+ { "ID" T(i) },
+ { "LANG" T(N) },
+ { "NAME" T(N) },
+ { "OBJECT" T(N) },
+ { "REF" T(N) },
+ { "STYLE" T(N) },
+ { "TITLE" T(N) },
+ { "TYPE" T(N) },
+ { "VALUE" T(N) },
+ { "VALUEREF" T(N) },
+ { "VALUETYPE" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr Q_attr[] = { /* Q attributes */
+ { "CITE" T(h) },
+ { "CLASS" T(c) },
+ { "CLEAR" T(N) },
+ { "DIR" T(N) },
+ { "ID" T(i) },
+ { "LANG" T(N) },
+ { "STYLE" T(N) },
+ { "TITLE" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr SCRIPT_attr[] = { /* SCRIPT attributes */
+ { "CHARSET" T(N) },
+ { "CLASS" T(c) },
+ { "CLEAR" T(N) },
+ { "DEFER" T(N) },
+ { "DIR" T(N) },
+ { "EVENT" T(N) },
+ { "FOR" T(N) },
+ { "ID" T(i) },
+ { "LANG" T(N) },
+ { "LANGUAGE" T(N) },
+ { "NAME" T(N) },
+ { "SCRIPTENGINE" T(N) },
+ { "SRC" T(h) },
+ { "STYLE" T(N) },
+ { "TITLE" T(N) },
+ { "TYPE" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr SELECT_attr[] = { /* SELECT attributes */
+ { "ALIGN" T(N) },
+ { "CLASS" T(c) },
+ { "CLEAR" T(N) },
+ { "DIR" T(N) },
+ { "DISABLED" T(N) },
+ { "ERROR" T(N) },
+ { "HEIGHT" T(N) },
+ { "ID" T(i) },
+ { "LANG" T(N) },
+ { "MD" T(N) },
+ { "MULTIPLE" T(N) },
+ { "NAME" T(N) },
+ { "NOTAB" T(N) },
+ { "ONBLUR" T(N) },
+ { "ONCHANGE" T(N) },
+ { "ONFOCUS" T(N) },
+ { "SIZE" T(N) },
+ { "STYLE" T(N) },
+ { "TABINDEX" T(N) },
+ { "TITLE" T(N) },
+ { "UNITS" T(N) },
+ { "WIDTH" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr STYLE_attr[] = { /* STYLE attributes */
+ { "CLASS" T(c) },
+ { "DIR" T(N) },
+ { "ID" T(i) },
+ { "LANG" T(N) },
+ { "MEDIA" T(N) },
+ { "NOTATION" T(N) },
+ { "STYLE" T(N) },
+ { "TITLE" T(N) },
+ { "TYPE" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr TAB_attr[] = { /* TAB attributes */
+ { "ALIGN" T(N) },
+ { "CLASS" T(c) },
+ { "CLEAR" T(N) },
+ { "DIR" T(N) },
+ { "DP" T(N) },
+ { "ID" T(i) },
+ { "INDENT" T(N) },
+ { "LANG" T(N) },
+ { "STYLE" T(N) },
+ { "TITLE" T(N) },
+ { "TO" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr TABLE_attr[] = { /* TABLE attributes */
+ { "ALIGN" T(N) },
+ { "BACKGROUND" T(h) },
+ { "BORDER" T(N) },
+ { "CELLPADDING" T(N) },
+ { "CELLSPACING" T(N) },
+ { "CLASS" T(c) },
+ { "CLEAR" T(N) },
+ { "COLS" T(N) },
+ { "COLSPEC" T(N) },
+ { "DIR" T(N) },
+ { "DP" T(N) },
+ { "FRAME" T(N) },
+ { "ID" T(i) },
+ { "LANG" T(N) },
+ { "NOFLOW" T(N) },
+ { "NOWRAP" T(N) },
+ { "RULES" T(N) },
+ { "STYLE" T(N) },
+ { "SUMMARY" T(N) },
+ { "TITLE" T(N) },
+ { "UNITS" T(N) },
+ { "WIDTH" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr TD_attr[] = { /* TD attributes */
+ { "ABBR" T(N) },
+ { "ALIGN" T(N) },
+ { "AXES" T(N) },
+ { "AXIS" T(N) },
+ { "BACKGROUND" T(h) },
+ { "CHAR" T(N) },
+ { "CHAROFF" T(N) },
+ { "CLASS" T(c) },
+ { "CLEAR" T(N) },
+ { "COLSPAN" T(N) },
+ { "DIR" T(N) },
+ { "DP" T(N) },
+ { "HEADERS" T(N) },
+ { "HEIGHT" T(N) },
+ { "ID" T(i) },
+ { "LANG" T(N) },
+ { "NOWRAP" T(N) },
+ { "ROWSPAN" T(N) },
+ { "SCOPE" T(N) },
+ { "STYLE" T(N) },
+ { "TITLE" T(N) },
+ { "VALIGN" T(N) },
+ { "WIDTH" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr TEXTAREA_attr[] = { /* TEXTAREA attributes */
+ { "ACCEPT-CHARSET" T(N) },
+ { "ACCESSKEY" T(N) },
+ { "ALIGN" T(N) },
+ { "CLASS" T(c) },
+ { "CLEAR" T(N) },
+ { "COLS" T(N) },
+ { "DIR" T(N) },
+ { "DISABLED" T(N) },
+ { "ERROR" T(N) },
+ { "ID" T(i) },
+ { "LANG" T(N) },
+ { "NAME" T(N) },
+ { "NOTAB" T(N) },
+ { "ONBLUR" T(N) },
+ { "ONCHANGE" T(N) },
+ { "ONFOCUS" T(N) },
+ { "ONSELECT" T(N) },
+ { "READONLY" T(N) },
+ { "ROWS" T(N) },
+ { "STYLE" T(N) },
+ { "TABINDEX" T(N) },
+ { "TITLE" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr TR_attr[] = { /* TBODY attributes */
+ { "ALIGN" T(N) },
+ { "CHAR" T(N) },
+ { "CHAROFF" T(N) },
+ { "CLASS" T(c) },
+ { "CLEAR" T(N) },
+ { "DIR" T(N) },
+ { "DP" T(N) },
+ { "ID" T(i) },
+ { "LANG" T(N) },
+ { "NOWRAP" T(N) },
+ { "STYLE" T(N) },
+ { "TITLE" T(N) },
+ { "VALIGN" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr UL_attr[] = { /* DIR attributes */
+ { "CLASS" T(c) },
+ { "CLEAR" T(N) },
+ { "COMPACT" T(N) },
+ { "DINGBAT" T(N) },
+ { "DIR" T(N) },
+ { "ID" T(i) },
+ { "LANG" T(N) },
+ { "MD" T(N) },
+ { "PLAIN" T(N) },
+ { "SRC" T(h) },
+ { "STYLE" T(N) },
+ { "TITLE" T(N) },
+ { "TYPE" T(N) },
+ { "WRAP" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+/* *INDENT-ON* */
+
+/* justification-flags */
+#undef N
+#undef i
+#undef h
+#undef c
+#undef x
+
+#undef T
+
+/* tag-names */
+#undef A
+#undef ABBR
+#undef ACRONYM
+#undef ADDRESS
+#undef APPLET
+#undef AREA
+#undef ARTICLE
+#undef ASIDE
+#undef AU
+#undef AUTHOR
+#undef B
+#undef BANNER
+#undef BASE
+#undef BASEFONT
+#undef BDO
+#undef BGSOUND
+#undef BIG
+#undef BLINK
+#undef BLOCKQUOTE
+#undef BODY
+#undef BODYTEXT
+#undef BQ
+#undef BR
+#undef BUTTON
+#undef CAPTION
+#undef CENTER
+#undef CITE
+#undef CODE
+#undef COL
+#undef COLGROUP
+#undef COMMENT
+#undef CREDIT
+#undef DD
+#undef DEL
+#undef DEL
+#undef DFN
+#undef DIR
+#undef DIV
+#undef DL
+#undef DLC
+#undef DT
+#undef EM
+#undef EMBED
+#undef FIELDSET
+#undef FIG
+#undef FIGURE
+#undef FN
+#undef FONT
+#undef FOOTER
+#undef FORM
+#undef FRAME
+#undef FRAMESET
+#undef H1
+#undef H2
+#undef H3
+#undef H4
+#undef H5
+#undef H6
+#undef HEAD
+#undef HEADER
+#undef HR
+#undef HTML
+#undef HY
+#undef I
+#undef IFRAME
+#undef IMG
+#undef INPUT
+#undef INS
+#undef INS
+#undef ISINDEX
+#undef KBD
+#undef KEYGEN
+#undef LABEL
+#undef LEGEND
+#undef LH
+#undef LI
+#undef LINK
+#undef LISTING
+#undef MAIN
+#undef MAP
+#undef MARQUEE
+#undef MATH
+#undef MENU
+#undef META
+#undef NAV
+#undef NEXTID
+#undef NOFRAMES
+#undef NOTE
+#undef OBJECT
+#undef OL
+#undef OPTION
+#undef OVERLAY
+#undef P
+#undef PARAM
+#undef PLAINTEXT
+#undef PRE
+#undef Q
+#undef S
+#undef SAMP
+#undef SCRIPT
+#undef SECTION
+#undef SELECT
+#undef SHY
+#undef SMALL
+#undef SPAN
+#undef SPOT
+#undef STRIKE
+#undef STRONG
+#undef STYLE
+#undef SUB
+#undef SUP
+#undef TAB
+#undef TABLE
+#undef TBODY
+#undef TD
+#undef TEXTAREA
+#undef TEXTFLOW
+#undef TFOOT
+#undef TH
+#undef THEAD
+#undef TITLE
+#undef TR
+#undef TT
+#undef U
+#undef UL
+#undef VAR
+#undef WBR
+#undef XMP
+#undef OBJECT_PCDATA
+
+/* these definitions are used in the tags-tables */
+#undef P
+#undef P_
+#ifdef USE_COLOR_STYLE
+#define P_(x) #x, (sizeof #x) -1
+#define NULL_HTTag_ NULL, 0
+#else
+#define P_(x) #x
+#define NULL_HTTag_ NULL
+#endif
+
+#ifdef USE_JUSTIFY_ELTS
+#define P(x) P_(x), 1
+#define P0(x) P_(x), 0
+#define NULL_HTTag NULL_HTTag_,0
+#else
+#define P(x) P_(x)
+#define P0(x) P_(x)
+#define NULL_HTTag NULL_HTTag_
+#endif
+
+#define ATTR_DATA(name) name##_attr, HTML_##name##_ATTRIBUTES, name##_attr_type
+
+#endif /* once_HTMLDTD */
+/* *INDENT-OFF* */
+static const HTTag tags_table0[HTML_ALL_ELEMENTS] = {
+ { P(A), ATTR_DATA(A), SGML_EMPTY, T_A, 0, 0},
+ { P(ABBR), ATTR_DATA(GEN), SGML_MIXED, T_ABBR, 0, 0},
+ { P(ACRONYM), ATTR_DATA(GEN), SGML_MIXED, T_ACRONYM, 0, 0},
+ { P(ADDRESS), ATTR_DATA(ADDRESS), SGML_MIXED, T_ADDRESS, 0, 0},
+ { P(APPLET), ATTR_DATA(APPLET), SGML_MIXED, T_APPLET, 0, 0},
+ { P(AREA), ATTR_DATA(AREA), SGML_EMPTY, T_AREA, 0, 0},
+ { P(ARTICLE), ATTR_DATA(GEN5), SGML_MIXED, T_ARTICLE, 0, 0},
+ { P(ASIDE), ATTR_DATA(GEN5), SGML_MIXED, T_ASIDE, 0, 0},
+ { P(AU), ATTR_DATA(GEN), SGML_MIXED, T_AU, 0, 0},
+ { P(AUTHOR), ATTR_DATA(GEN), SGML_MIXED, T_AUTHOR, 0, 0},
+ { P(B), ATTR_DATA(GEN), SGML_EMPTY, T_B, 0, 0},
+ { P0(BANNER), ATTR_DATA(GEN), SGML_MIXED, T_BANNER, 0, 0},
+ { P(BASE), ATTR_DATA(BASE), SGML_EMPTY, T_BASE, 0, 0},
+ { P(BASEFONT), ATTR_DATA(FONT), SGML_EMPTY, T_BASEFONT, 0, 0},
+ { P(BDO), ATTR_DATA(GEN), SGML_MIXED, T_BDO, 0, 0},
+ { P(BGSOUND), ATTR_DATA(BGSOUND), SGML_EMPTY, T_BGSOUND, 0, 0},
+ { P(BIG), ATTR_DATA(GEN), SGML_MIXED, T_BIG, 0, 0},
+ { P(BLINK), ATTR_DATA(GEN), SGML_EMPTY, T_BLINK, 0, 0},
+ { P(BLOCKQUOTE), ATTR_DATA(BQ), SGML_MIXED, T_BLOCKQUOTE, 0, 0},
+ { P(BODY), ATTR_DATA(BODY), SGML_MIXED, T_BODY, 0, 0},
+ { P(BODYTEXT), ATTR_DATA(BODYTEXT), SGML_MIXED, T_BODYTEXT, 0, 0},
+ { P(BQ), ATTR_DATA(BQ), SGML_MIXED, T_BQ, 0, 0},
+ { P(BR), ATTR_DATA(GEN), SGML_EMPTY, T_BR, 0, 0},
+ { P(BUTTON), ATTR_DATA(BUTTON), SGML_MIXED, T_BUTTON, 0, 0},
+ { P(CAPTION), ATTR_DATA(CAPTION), SGML_MIXED, T_CAPTION, 0, 0},
+ { P(CENTER), ATTR_DATA(DIV), SGML_MIXED, T_CENTER, 0, 0},
+ { P(CITE), ATTR_DATA(GEN), SGML_EMPTY, T_CITE, 0, 0},
+ { P(CODE), ATTR_DATA(GEN), SGML_MIXED, T_CODE, 0, 0},
+ { P(COL), ATTR_DATA(COL), SGML_EMPTY, T_COL, 0, 0},
+ { P(COLGROUP), ATTR_DATA(COL), SGML_EMPTY, T_COLGROUP, 0, 0},
+ { P(COMMENT), ATTR_DATA(GEN), SGML_MIXED, T_COMMENT, 0, 0},
+ { P(CREDIT), ATTR_DATA(GEN), SGML_MIXED, T_CREDIT, 0, 0},
+ { P(DD), ATTR_DATA(GEN), SGML_EMPTY, T_DD, 0, 0},
+ { P(DEL), ATTR_DATA(DEL), SGML_MIXED, T_DEL, 0, 1},
+ { P(DEL), ATTR_DATA(DEL), SGML_MIXED, T_DEL_2, 1, 1},
+ { P(DFN), ATTR_DATA(GEN), SGML_MIXED, T_DFN, 0, 0},
+ { P(DIR), ATTR_DATA(UL), SGML_MIXED, T_DIR, 0, 0},
+ { P(DIV), ATTR_DATA(DIV), SGML_MIXED, T_DIV, 0, 0},
+ { P(DL), ATTR_DATA(DL), SGML_MIXED, T_DL, 0, 0},
+ { P(DLC), ATTR_DATA(DL), SGML_MIXED, T_DLC, 0, 0},
+ { P(DT), ATTR_DATA(GEN), SGML_EMPTY, T_DT, 0, 0},
+ { P(EM), ATTR_DATA(GEN), SGML_EMPTY, T_EM, 0, 0},
+ { P(EMBED), ATTR_DATA(EMBED), SGML_EMPTY, T_EMBED, 0, 0},
+ { P(FIELDSET), ATTR_DATA(GEN), SGML_MIXED, T_FIELDSET, 0, 0},
+ { P(FIG), ATTR_DATA(FIG), SGML_MIXED, T_FIG, 0, 0},
+ { P(FIGURE), ATTR_DATA(GEN5), SGML_MIXED, T_FIGURE, 0, 0},
+ { P(FN), ATTR_DATA(GEN), SGML_MIXED, T_FN, 0, 0},
+ { P(FONT), ATTR_DATA(FONT), SGML_EMPTY, T_FONT, 0, 0},
+ { P(FOOTER), ATTR_DATA(GEN5), SGML_MIXED, T_FOOTER, 0, 0},
+ { P(FORM), ATTR_DATA(FORM), SGML_EMPTY, T_FORM, 0, 0},
+ { P(FRAME), ATTR_DATA(FRAME), SGML_EMPTY, T_FRAME, 0, 0},
+ { P(FRAMESET), ATTR_DATA(FRAMESET), SGML_MIXED, T_FRAMESET, 0, 0},
+ { P0(H1), ATTR_DATA(H), SGML_MIXED, T_H1, 0, 0},
+ { P0(H2), ATTR_DATA(H), SGML_MIXED, T_H2, 0, 0},
+ { P0(H3), ATTR_DATA(H), SGML_MIXED, T_H3, 0, 0},
+ { P0(H4), ATTR_DATA(H), SGML_MIXED, T_H4, 0, 0},
+ { P0(H5), ATTR_DATA(H), SGML_MIXED, T_H5, 0, 0},
+ { P0(H6), ATTR_DATA(H), SGML_MIXED, T_H6, 0, 0},
+ { P(HEAD), ATTR_DATA(GEN), SGML_MIXED, T_HEAD, 0, 0},
+ { P(HEADER), ATTR_DATA(GEN5), SGML_MIXED, T_HEADER, 0, 0},
+ { P(HR), ATTR_DATA(HR), SGML_EMPTY, T_HR, 0, 0},
+ { P(HTML), ATTR_DATA(GEN), SGML_MIXED, T_HTML, 0, 0},
+ { P(HY), ATTR_DATA(GEN), SGML_EMPTY, T_HY, 0, 0},
+ { P(I), ATTR_DATA(GEN), SGML_EMPTY, T_I, 0, 0},
+ { P(IFRAME), ATTR_DATA(IFRAME), SGML_MIXED, T_IFRAME, 0, 0},
+ { P(IMG), ATTR_DATA(IMG), SGML_EMPTY, T_IMG, 0, 0},
+ { P(INPUT), ATTR_DATA(INPUT), SGML_EMPTY, T_INPUT, 0, 0},
+ { P(INS), ATTR_DATA(DEL), SGML_MIXED, T_INS, 0, 1},
+ { P(INS), ATTR_DATA(DEL), SGML_MIXED, T_INS_2, 1, 1},
+ { P(ISINDEX), ATTR_DATA(ISINDEX), SGML_EMPTY, T_ISINDEX, 0, 0},
+ { P(KBD), ATTR_DATA(GEN), SGML_MIXED, T_KBD, 0, 0},
+ { P(KEYGEN), ATTR_DATA(KEYGEN), SGML_EMPTY, T_KEYGEN, 0, 0},
+ { P(LABEL), ATTR_DATA(LABEL), SGML_MIXED, T_LABEL, 0, 0},
+ { P(LEGEND), ATTR_DATA(CAPTION), SGML_MIXED, T_LEGEND, 0, 0},
+ { P(LH), ATTR_DATA(GEN), SGML_EMPTY, T_LH, 0, 0},
+ { P(LI), ATTR_DATA(LI), SGML_EMPTY, T_LI, 0, 0},
+ { P(LINK), ATTR_DATA(LINK), SGML_EMPTY, T_LINK, 0, 0},
+ { P(LISTING), ATTR_DATA(GEN), SGML_LITTERAL,T_LISTING, 0, 0},
+ { P(MAIN), ATTR_DATA(GEN5), SGML_MIXED, T_MAIN, 0, 0},
+ { P(MAP), ATTR_DATA(MAP), SGML_MIXED, T_MAP, 0, 0},
+ { P(MARQUEE), ATTR_DATA(GEN), SGML_MIXED, T_MARQUEE, 0, 0},
+ { P(MATH), ATTR_DATA(MATH), SGML_LITTERAL,T_MATH, 0, 0},
+ { P(MENU), ATTR_DATA(UL), SGML_MIXED, T_MENU, 0, 0},
+ { P(META), ATTR_DATA(META), SGML_EMPTY, T_META, 0, 0},
+ { P(NAV), ATTR_DATA(GEN5), SGML_MIXED, T_NAV, 0, 0},
+ { P(NEXTID), ATTR_DATA(NEXTID), SGML_EMPTY, T_NEXTID, 0, 0},
+ { P(NOFRAMES), ATTR_DATA(GEN), SGML_MIXED, T_NOFRAMES, 0, 0},
+ { P(NOTE), ATTR_DATA(NOTE), SGML_MIXED, T_NOTE, 0, 0},
+ { P(OBJECT), ATTR_DATA(OBJECT), SGML_LITTERAL,T_OBJECT, 0, 0},
+ { P(OL), ATTR_DATA(OL), SGML_MIXED, T_OL, 0, 0},
+ { P(OPTION), ATTR_DATA(OPTION), SGML_EMPTY, T_OPTION, 0, 0},
+ { P(OVERLAY), ATTR_DATA(OVERLAY), SGML_EMPTY, T_OVERLAY, 0, 0},
+ { P(P), ATTR_DATA(P), SGML_EMPTY, T_P, 0, 0},
+ { P(PARAM), ATTR_DATA(PARAM), SGML_EMPTY, T_PARAM, 0, 0},
+ { P(PLAINTEXT), ATTR_DATA(GEN), SGML_LITTERAL,T_PLAINTEXT, 0, 0},
+ { P0(PRE), ATTR_DATA(GEN), SGML_MIXED, T_PRE, 0, 0},
+ { P(Q), ATTR_DATA(Q), SGML_MIXED, T_Q, 0, 0},
+ { P(S), ATTR_DATA(GEN), SGML_MIXED, T_S, 0, 0},
+ { P(SAMP), ATTR_DATA(GEN), SGML_MIXED, T_SAMP, 0, 0},
+ { P(SCRIPT), ATTR_DATA(SCRIPT), SGML_LITTERAL,T_SCRIPT, 0, 0},
+ { P(SECTION), ATTR_DATA(GEN5), SGML_MIXED, T_SECTION, 0, 0},
+ { P(SELECT), ATTR_DATA(SELECT), SGML_MIXED, T_SELECT, 0, 0},
+ { P(SHY), ATTR_DATA(GEN), SGML_EMPTY, T_SHY, 0, 0},
+ { P(SMALL), ATTR_DATA(GEN), SGML_MIXED, T_SMALL, 0, 0},
+ { P(SPAN), ATTR_DATA(GEN), SGML_MIXED, T_SPAN, 0, 0},
+ { P(SPOT), ATTR_DATA(GEN), SGML_EMPTY, T_SPOT, 0, 0},
+ { P(STRIKE), ATTR_DATA(GEN), SGML_MIXED, T_STRIKE, 0, 0},
+ { P(STRONG), ATTR_DATA(GEN), SGML_EMPTY, T_STRONG, 0, 0},
+ { P(STYLE), ATTR_DATA(STYLE), SGML_LITTERAL,T_STYLE, 0, 0},
+ { P(SUB), ATTR_DATA(GEN), SGML_MIXED, T_SUB, 0, 0},
+ { P(SUP), ATTR_DATA(GEN), SGML_MIXED, T_SUP, 0, 0},
+ { P(TAB), ATTR_DATA(TAB), SGML_EMPTY, T_TAB, 0, 0},
+ { P(TABLE), ATTR_DATA(TABLE), SGML_MIXED, T_TABLE, 0, 0},
+ { P(TBODY), ATTR_DATA(TR), SGML_EMPTY, T_TBODY, 0, 0},
+ { P(TD), ATTR_DATA(TD), SGML_EMPTY, T_TD, 0, 0},
+ { P(TEXTAREA), ATTR_DATA(TEXTAREA), SGML_LITTERAL,T_TEXTAREA, 0, 0},
+ { P(TEXTFLOW), ATTR_DATA(BODYTEXT), SGML_MIXED, T_TEXTFLOW, 0, 0},
+ { P(TFOOT), ATTR_DATA(TR), SGML_EMPTY, T_TFOOT, 0, 0},
+ { P(TH), ATTR_DATA(TD), SGML_EMPTY, T_TH, 0, 0},
+ { P(THEAD), ATTR_DATA(TR), SGML_EMPTY, T_THEAD, 0, 0},
+ { P(TITLE), ATTR_DATA(GEN), SGML_RCDATA, T_TITLE, 0, 0},
+ { P(TR), ATTR_DATA(TR), SGML_EMPTY, T_TR, 0, 0},
+ { P(TT), ATTR_DATA(GEN), SGML_EMPTY, T_TT, 0, 0},
+ { P(U), ATTR_DATA(GEN), SGML_EMPTY, T_U, 0, 0},
+ { P(UL), ATTR_DATA(UL), SGML_MIXED, T_UL, 0, 0},
+ { P(VAR), ATTR_DATA(GEN), SGML_MIXED, T_VAR, 0, 0},
+ { P(WBR), ATTR_DATA(GEN), SGML_EMPTY, T_WBR, 0, 0},
+ { P0(XMP), ATTR_DATA(GEN), SGML_LITTERAL,T_XMP, 0, 0},
+/* additional (alternative variants), not counted in HTML_ELEMENTS: */
+/* This one will be used as a temporary substitute within the parser when
+ it has been signalled to parse OBJECT content as MIXED. - kw */
+ { P(OBJECT), ATTR_DATA(OBJECT), SGML_MIXED, T_OBJECT_PCDATA, 0, 0},
+};
+/* *INDENT-ON* */
+
+#endif /* src_HTMLDTD_H0 */
diff --git a/WWW/Library/Implementation/src0_HTMLDTD.txt b/WWW/Library/Implementation/src0_HTMLDTD.txt
new file mode 100644
index 0000000..a3c51d9
--- /dev/null
+++ b/WWW/Library/Implementation/src0_HTMLDTD.txt
@@ -0,0 +1,3901 @@
+60 attr_types
+ 0:align
+ 1 attributes:
+ 0:0:ALIGN
+ 1:bgcolor
+ 1 attributes:
+ 0:0:BGCOLOR
+ 2:cellalign
+ 4 attributes:
+ 0:0:ALIGN
+ 1:0:CHAR
+ 2:0:CHAROFF
+ 3:0:VALIGN
+ 3:core
+ 4 attributes:
+ 0:4:CLASS
+ 1:1:ID
+ 2:0:STYLE
+ 3:0:TITLE
+ 4:events
+ 10 attributes:
+ 0:0:ONCLICK
+ 1:0:ONDBLCLICK
+ 2:0:ONKEYDOWN
+ 3:0:ONKEYPRESS
+ 4:0:ONKEYUP
+ 5:0:ONMOUSEDOWN
+ 6:0:ONMOUSEMOVE
+ 7:0:ONMOUSEOUT
+ 8:0:ONMOUSEOVER
+ 9:0:ONMOUSEUP
+ 5:i18n
+ 2 attributes:
+ 0:0:DIR
+ 1:0:LANG
+ 6:A
+ 19 attributes:
+ 0:0:ACCESSKEY
+ 1:0:CHARSET
+ 2:0:CLEAR
+ 3:0:COORDS
+ 4:2:HREF
+ 5:0:HREFLANG
+ 6:0:ISMAP
+ 7:0:MD
+ 8:1:NAME
+ 9:0:NOTAB
+ 10:0:ONBLUR
+ 11:0:ONFOCUS
+ 12:0:REL
+ 13:0:REV
+ 14:0:SHAPE
+ 15:0:TABINDEX
+ 16:0:TARGET
+ 17:0:TYPE
+ 18:0:URN
+ 7:ADDRESS
+ 2 attributes:
+ 0:0:CLEAR
+ 1:0:NOWRAP
+ 8:APPLET
+ 10 attributes:
+ 0:0:ALT
+ 1:0:CLEAR
+ 2:0:CODE
+ 3:2:CODEBASE
+ 4:0:DOWNLOAD
+ 5:0:HEIGHT
+ 6:0:HSPACE
+ 7:1:NAME
+ 8:0:VSPACE
+ 9:0:WIDTH
+ 9:AREA
+ 12 attributes:
+ 0:0:ACCESSKEY
+ 1:0:ALT
+ 2:0:CLEAR
+ 3:0:COORDS
+ 4:2:HREF
+ 5:0:NOHREF
+ 6:0:NOTAB
+ 7:0:ONBLUR
+ 8:0:ONFOCUS
+ 9:0:SHAPE
+ 10:0:TABINDEX
+ 11:0:TARGET
+ 10:BASE
+ 2 attributes:
+ 0:2:HREF
+ 1:0:TARGET
+ 11:BGSOUND
+ 3 attributes:
+ 0:0:CLEAR
+ 1:0:LOOP
+ 2:2:SRC
+ 12:BODY
+ 8 attributes:
+ 0:0:ALINK
+ 1:2:BACKGROUND
+ 2:0:CLEAR
+ 3:0:LINK
+ 4:0:ONLOAD
+ 5:0:ONUNLOAD
+ 6:0:TEXT
+ 7:0:VLINK
+ 13:BODYTEXT
+ 8 attributes:
+ 0:0:CLEAR
+ 1:0:DATA
+ 2:0:NAME
+ 3:0:OBJECT
+ 4:0:REF
+ 5:0:TYPE
+ 6:0:VALUE
+ 7:0:VALUETYPE
+ 14:BQ
+ 3 attributes:
+ 0:2:CITE
+ 1:0:CLEAR
+ 2:0:NOWRAP
+ 15:BUTTON
+ 11 attributes:
+ 0:0:ACCESSKEY
+ 1:0:CLEAR
+ 2:0:DISABLED
+ 3:0:FORMACTION
+ 4:0:NAME
+ 5:0:ONBLUR
+ 6:0:ONFOCUS
+ 7:0:READONLY
+ 8:0:TABINDEX
+ 9:0:TYPE
+ 10:0:VALUE
+ 16:CAPTION
+ 2 attributes:
+ 0:0:ACCESSKEY
+ 1:0:CLEAR
+ 17:COL
+ 3 attributes:
+ 0:0:CLEAR
+ 1:0:SPAN
+ 2:0:WIDTH
+ 18:DEL
+ 2 attributes:
+ 0:0:CITE
+ 1:0:DATETIME
+ 19:DIV
+ 1 attributes:
+ 0:0:CLEAR
+ 20:DL
+ 2 attributes:
+ 0:0:CLEAR
+ 1:0:COMPACT
+ 21:EMBED
+ 14 attributes:
+ 0:0:ALT
+ 1:0:BORDER
+ 2:0:CLEAR
+ 3:0:HEIGHT
+ 4:0:IMAGEMAP
+ 5:0:ISMAP
+ 6:0:MD
+ 7:1:NAME
+ 8:0:NOFLOW
+ 9:0:PARAMS
+ 10:2:SRC
+ 11:0:UNITS
+ 12:0:USEMAP
+ 13:0:WIDTH
+ 22:FIG
+ 10 attributes:
+ 0:0:BORDER
+ 1:0:CLEAR
+ 2:0:HEIGHT
+ 3:0:IMAGEMAP
+ 4:0:ISOBJECT
+ 5:0:MD
+ 6:0:NOFLOW
+ 7:2:SRC
+ 8:0:UNITS
+ 9:0:WIDTH
+ 23:FONT
+ 5 attributes:
+ 0:0:CLEAR
+ 1:0:COLOR
+ 2:0:END
+ 3:0:FACE
+ 4:0:SIZE
+ 24:FORM
+ 11 attributes:
+ 0:0:ACCEPT
+ 1:0:ACCEPT-CHARSET
+ 2:2:ACTION
+ 3:0:CLEAR
+ 4:0:ENCTYPE
+ 5:0:METHOD
+ 6:0:ONRESET
+ 7:0:ONSUBMIT
+ 8:0:SCRIPT
+ 9:0:SUBJECT
+ 10:0:TARGET
+ 25:FRAME
+ 8 attributes:
+ 0:0:FRAMEBORDER
+ 1:2:LONGDESC
+ 2:0:MARGINHEIGHT
+ 3:0:MARGINWIDTH
+ 4:0:NAME
+ 5:0:NORESIZE
+ 6:0:SCROLLING
+ 7:2:SRC
+ 26:FRAMESET
+ 4 attributes:
+ 0:0:COLS
+ 1:0:ONLOAD
+ 2:0:ONUNLOAD
+ 3:0:ROWS
+ 27:GEN
+ 1 attributes:
+ 0:0:CLEAR
+ 28:GEN5
+ 1 attributes:
+ 0:0:ROLE
+ 29:H
+ 7 attributes:
+ 0:0:CLEAR
+ 1:0:DINGBAT
+ 2:0:MD
+ 3:0:NOWRAP
+ 4:0:SEQNUM
+ 5:0:SKIP
+ 6:2:SRC
+ 30:HR
+ 6 attributes:
+ 0:0:CLEAR
+ 1:0:MD
+ 2:0:NOSHADE
+ 3:0:SIZE
+ 4:2:SRC
+ 5:0:WIDTH
+ 31:IFRAME
+ 9 attributes:
+ 0:0:FRAMEBORDER
+ 1:0:HEIGHT
+ 2:2:LONGDESC
+ 3:0:MARGINHEIGHT
+ 4:0:MARGINWIDTH
+ 5:0:NAME
+ 6:0:SCROLLING
+ 7:2:SRC
+ 8:0:WIDTH
+ 32:IMG
+ 15 attributes:
+ 0:0:ALT
+ 1:0:BORDER
+ 2:0:CLEAR
+ 3:0:HEIGHT
+ 4:0:HSPACE
+ 5:0:ISMAP
+ 6:0:ISOBJECT
+ 7:2:LONGDESC
+ 8:0:MD
+ 9:0:NAME
+ 10:2:SRC
+ 11:0:UNITS
+ 12:2:USEMAP
+ 13:0:VSPACE
+ 14:0:WIDTH
+ 33:INPUT
+ 28 attributes:
+ 0:0:ACCEPT
+ 1:0:ACCEPT-CHARSET
+ 2:0:ACCESSKEY
+ 3:0:ALT
+ 4:0:CHECKED
+ 5:0:CLEAR
+ 6:0:DISABLED
+ 7:0:ERROR
+ 8:0:HEIGHT
+ 9:0:ISMAP
+ 10:0:MAX
+ 11:0:MAXLENGTH
+ 12:0:MD
+ 13:0:MIN
+ 14:0:NAME
+ 15:0:NOTAB
+ 16:0:ONBLUR
+ 17:0:ONCHANGE
+ 18:0:ONFOCUS
+ 19:0:ONSELECT
+ 20:0:READONLY
+ 21:0:SIZE
+ 22:2:SRC
+ 23:0:TABINDEX
+ 24:0:TYPE
+ 25:0:USEMAP
+ 26:0:VALUE
+ 27:0:WIDTH
+ 34:ISINDEX
+ 3 attributes:
+ 0:2:ACTION
+ 1:2:HREF
+ 2:0:PROMPT
+ 35:KEYGEN
+ 2 attributes:
+ 0:0:CHALLENGE
+ 1:0:NAME
+ 36:LABEL
+ 5 attributes:
+ 0:0:ACCESSKEY
+ 1:0:CLEAR
+ 2:0:FOR
+ 3:0:ONBLUR
+ 4:0:ONFOCUS
+ 37:LI
+ 7 attributes:
+ 0:0:CLEAR
+ 1:0:DINGBAT
+ 2:0:MD
+ 3:0:SKIP
+ 4:2:SRC
+ 5:0:TYPE
+ 6:0:VALUE
+ 38:LINK
+ 8 attributes:
+ 0:0:CHARSET
+ 1:2:HREF
+ 2:0:HREFLANG
+ 3:0:MEDIA
+ 4:0:REL
+ 5:0:REV
+ 6:0:TARGET
+ 7:0:TYPE
+ 39:MAP
+ 2 attributes:
+ 0:0:CLEAR
+ 1:1:NAME
+ 40:MATH
+ 2 attributes:
+ 0:0:BOX
+ 1:0:CLEAR
+ 41:META
+ 5 attributes:
+ 0:0:CHARSET
+ 1:0:CONTENT
+ 2:0:HTTP-EQUIV
+ 3:0:NAME
+ 4:0:SCHEME
+ 42:NEXTID
+ 1 attributes:
+ 0:0:N
+ 43:NOTE
+ 4 attributes:
+ 0:0:CLEAR
+ 1:0:MD
+ 2:8:ROLE
+ 3:2:SRC
+ 44:OBJECT
+ 19 attributes:
+ 0:0:ARCHIVE
+ 1:0:BORDER
+ 2:2:CLASSID
+ 3:2:CODEBASE
+ 4:0:CODETYPE
+ 5:2:DATA
+ 6:0:DECLARE
+ 7:0:HEIGHT
+ 8:0:HSPACE
+ 9:0:ISMAP
+ 10:0:NAME
+ 11:0:NOTAB
+ 12:0:SHAPES
+ 13:0:STANDBY
+ 14:0:TABINDEX
+ 15:0:TYPE
+ 16:2:USEMAP
+ 17:0:VSPACE
+ 18:0:WIDTH
+ 45:OL
+ 6 attributes:
+ 0:0:CLEAR
+ 1:0:COMPACT
+ 2:0:CONTINUE
+ 3:0:SEQNUM
+ 4:0:START
+ 5:0:TYPE
+ 46:OPTION
+ 7 attributes:
+ 0:0:CLEAR
+ 1:0:DISABLED
+ 2:0:ERROR
+ 3:0:LABEL
+ 4:0:SELECTED
+ 5:0:SHAPE
+ 6:0:VALUE
+ 47:OVERLAY
+ 8 attributes:
+ 0:0:HEIGHT
+ 1:0:IMAGEMAP
+ 2:0:MD
+ 3:2:SRC
+ 4:0:UNITS
+ 5:0:WIDTH
+ 6:0:X
+ 7:0:Y
+ 48:P
+ 2 attributes:
+ 0:0:CLEAR
+ 1:0:NOWRAP
+ 49:PARAM
+ 12 attributes:
+ 0:0:ACCEPT
+ 1:0:ACCEPT-CHARSET
+ 2:0:ACCEPT-ENCODING
+ 3:0:CLEAR
+ 4:0:DATA
+ 5:0:NAME
+ 6:0:OBJECT
+ 7:0:REF
+ 8:0:TYPE
+ 9:0:VALUE
+ 10:0:VALUEREF
+ 11:0:VALUETYPE
+ 50:Q
+ 2 attributes:
+ 0:2:CITE
+ 1:0:CLEAR
+ 51:SCRIPT
+ 10 attributes:
+ 0:0:CHARSET
+ 1:0:CLEAR
+ 2:0:DEFER
+ 3:0:EVENT
+ 4:0:FOR
+ 5:0:LANGUAGE
+ 6:0:NAME
+ 7:0:SCRIPTENGINE
+ 8:2:SRC
+ 9:0:TYPE
+ 52:SELECT
+ 15 attributes:
+ 0:0:CLEAR
+ 1:0:DISABLED
+ 2:0:ERROR
+ 3:0:HEIGHT
+ 4:0:MD
+ 5:0:MULTIPLE
+ 6:0:NAME
+ 7:0:NOTAB
+ 8:0:ONBLUR
+ 9:0:ONCHANGE
+ 10:0:ONFOCUS
+ 11:0:SIZE
+ 12:0:TABINDEX
+ 13:0:UNITS
+ 14:0:WIDTH
+ 53:STYLE
+ 3 attributes:
+ 0:0:MEDIA
+ 1:0:NOTATION
+ 2:0:TYPE
+ 54:TAB
+ 4 attributes:
+ 0:0:CLEAR
+ 1:0:DP
+ 2:0:INDENT
+ 3:0:TO
+ 55:TABLE
+ 15 attributes:
+ 0:2:BACKGROUND
+ 1:0:BORDER
+ 2:0:CELLPADDING
+ 3:0:CELLSPACING
+ 4:0:CLEAR
+ 5:0:COLS
+ 6:0:COLSPEC
+ 7:0:DP
+ 8:0:FRAME
+ 9:0:NOFLOW
+ 10:0:NOWRAP
+ 11:0:RULES
+ 12:0:SUMMARY
+ 13:0:UNITS
+ 14:0:WIDTH
+ 56:TD
+ 13 attributes:
+ 0:0:ABBR
+ 1:0:AXES
+ 2:0:AXIS
+ 3:2:BACKGROUND
+ 4:0:CLEAR
+ 5:0:COLSPAN
+ 6:0:DP
+ 7:0:HEADERS
+ 8:0:HEIGHT
+ 9:0:NOWRAP
+ 10:0:ROWSPAN
+ 11:0:SCOPE
+ 12:0:WIDTH
+ 57:TEXTAREA
+ 15 attributes:
+ 0:0:ACCEPT-CHARSET
+ 1:0:ACCESSKEY
+ 2:0:CLEAR
+ 3:0:COLS
+ 4:0:DISABLED
+ 5:0:ERROR
+ 6:0:NAME
+ 7:0:NOTAB
+ 8:0:ONBLUR
+ 9:0:ONCHANGE
+ 10:0:ONFOCUS
+ 11:0:ONSELECT
+ 12:0:READONLY
+ 13:0:ROWS
+ 14:0:TABINDEX
+ 58:TR
+ 3 attributes:
+ 0:0:CLEAR
+ 1:0:DP
+ 2:0:NOWRAP
+ 59:UL
+ 8 attributes:
+ 0:0:CLEAR
+ 1:0:COMPACT
+ 2:0:DINGBAT
+ 3:0:MD
+ 4:0:PLAIN
+ 5:2:SRC
+ 6:0:TYPE
+ 7:0:WRAP
+128 tags
+ 0:A
+ justify
+ 25 attributes:
+ 0:0:ACCESSKEY
+ 1:0:CHARSET
+ 2:4:CLASS
+ 3:0:CLEAR
+ 4:0:COORDS
+ 5:0:DIR
+ 6:2:HREF
+ 7:0:HREFLANG
+ 8:1:ID
+ 9:0:ISMAP
+ 10:0:LANG
+ 11:0:MD
+ 12:1:NAME
+ 13:0:NOTAB
+ 14:0:ONBLUR
+ 15:0:ONFOCUS
+ 16:0:REL
+ 17:0:REV
+ 18:0:SHAPE
+ 19:0:STYLE
+ 20:0:TABINDEX
+ 21:0:TARGET
+ 22:0:TITLE
+ 23:0:TYPE
+ 24:0:URN
+ 4 attr_types
+ core
+ events
+ i18n
+ A
+ contents: SGML_EMPTY
+ tagclass: Alike
+ contains: FONTlike EMlike MATHlike BRlike APPLETlike MAPlike
+ icontains: FONTlike EMlike MATHlike formula Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike
+ contained: FONTlike EMlike MATHlike FORMlike Plike DIVlike LIlike BRlike APPLETlike HRlike outer BODYlike
+ icontained: FONTlike EMlike MATHlike TRlike FORMlike Plike DIVlike ULlike BRlike APPLETlike HRlike outer BODYlike HEADstuff
+ canclose: FONTlike EMlike MATHlike Alike SELECTlike APPLETlike HRlike same
+ flags: mafse nreie
+ 1:ABBR
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN
+ contents: SGML_MIXED
+ tagclass: EMlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike BRlike APPLETlike MAPlike same
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: FONTlike EMlike MATHlike Alike FORMlike Plike DIVlike LIlike BRlike APPLETlike HRlike BODYlike same
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike HEADstuff same
+ canclose: FONTlike EMlike
+ flags:
+ 2:ACRONYM
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN
+ contents: SGML_MIXED
+ tagclass: EMlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike BRlike APPLETlike MAPlike same
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: FONTlike EMlike MATHlike Alike FORMlike Plike DIVlike LIlike BRlike APPLETlike HRlike BODYlike same
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike HEADstuff same
+ canclose: FONTlike EMlike
+ flags:
+ 3:ADDRESS
+ justify
+ 8 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:NOWRAP
+ 6:0:STYLE
+ 7:0:TITLE
+ 3 attr_types
+ core
+ i18n
+ ADDRESS
+ contents: SGML_MIXED
+ tagclass: DIVlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike Plike BRlike APPLETlike HRlike MAPlike
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: FORMlike DIVlike LIlike APPLETlike HRlike outer BODYlike DELlike
+ icontained: FONTlike EMlike MATHlike Alike TRlike FORMlike Plike DIVlike LIlike ULlike APPLETlike HRlike outer BODYlike same DELlike
+ canclose: FONTlike EMlike MATHlike formula Plike DIVlike same
+ flags:
+ 4:APPLET
+ justify
+ 17 attributes:
+ 0:0:ALIGN
+ 1:0:ALT
+ 2:4:CLASS
+ 3:0:CLEAR
+ 4:0:CODE
+ 5:2:CODEBASE
+ 6:0:DIR
+ 7:0:DOWNLOAD
+ 8:0:HEIGHT
+ 9:0:HSPACE
+ 10:1:ID
+ 11:0:LANG
+ 12:1:NAME
+ 13:0:STYLE
+ 14:0:TITLE
+ 15:0:VSPACE
+ 16:0:WIDTH
+ 4 attr_types
+ align
+ core
+ i18n
+ APPLET
+ contents: SGML_MIXED
+ tagclass: APPLETlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike FORMlike BRlike APPLETlike MAPlike
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: FONTlike EMlike MATHlike Alike formula FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike same
+ canclose: FONTlike EMlike MATHlike Alike BRlike APPLETlike same
+ flags:
+ 5:AREA
+ justify
+ 18 attributes:
+ 0:0:ACCESSKEY
+ 1:0:ALT
+ 2:4:CLASS
+ 3:0:CLEAR
+ 4:0:COORDS
+ 5:0:DIR
+ 6:2:HREF
+ 7:1:ID
+ 8:0:LANG
+ 9:0:NOHREF
+ 10:0:NOTAB
+ 11:0:ONBLUR
+ 12:0:ONFOCUS
+ 13:0:SHAPE
+ 14:0:STYLE
+ 15:0:TABINDEX
+ 16:0:TARGET
+ 17:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ AREA
+ contents: SGML_EMPTY
+ tagclass: MAPlike
+ contains:
+ icontains:
+ contained: MAPlike
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike outer BODYlike
+ canclose: FONTlike EMlike MATHlike Alike formula Plike DIVlike LIlike ULlike
+ flags: endO
+ 6:ARTICLE
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:DIR
+ 2:1:ID
+ 3:0:LANG
+ 4:0:ROLE
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN5
+ contents: SGML_MIXED
+ tagclass: DIVlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike FORMlike Plike DIVlike ULlike BRlike APPLETlike HRlike MAPlike same
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: TRlike FORMlike DIVlike LIlike APPLETlike HRlike outer BODYlike same DELlike
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike same DELlike
+ canclose: FONTlike EMlike MATHlike Alike formula Plike DIVlike same
+ flags: mafse
+ 7:ASIDE
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:DIR
+ 2:1:ID
+ 3:0:LANG
+ 4:0:ROLE
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN5
+ contents: SGML_MIXED
+ tagclass: DIVlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike FORMlike Plike DIVlike ULlike BRlike APPLETlike HRlike MAPlike same
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: TRlike FORMlike DIVlike LIlike APPLETlike HRlike outer BODYlike same DELlike
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike same DELlike
+ canclose: FONTlike EMlike MATHlike Alike formula Plike DIVlike same
+ flags: mafse
+ 8:AU
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN
+ contents: SGML_MIXED
+ tagclass: EMlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike BRlike APPLETlike MAPlike same
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: FONTlike EMlike MATHlike Alike FORMlike Plike DIVlike LIlike BRlike APPLETlike HRlike BODYlike same
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike HEADstuff same
+ canclose: FONTlike EMlike
+ flags:
+ 9:AUTHOR
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN
+ contents: SGML_MIXED
+ tagclass: EMlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike BRlike APPLETlike MAPlike same
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: FONTlike EMlike MATHlike Alike FORMlike Plike DIVlike LIlike BRlike APPLETlike HRlike BODYlike same
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike HEADstuff same
+ canclose: FONTlike EMlike
+ flags:
+ 10:B
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN
+ contents: SGML_EMPTY
+ tagclass: FONTlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike BRlike APPLETlike MAPlike same
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike BODYlike same
+ contained: FONTlike EMlike MATHlike Alike FORMlike Plike DIVlike LIlike BRlike APPLETlike HRlike BODYlike same
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike HEADstuff same
+ canclose: FONTlike
+ flags: mafse nreie
+ 11:BANNER
+ nojustify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN
+ contents: SGML_MIXED
+ tagclass: DIVlike
+ contains: FONTlike EMlike MATHlike Alike FORMlike Plike DIVlike ULlike BRlike APPLETlike HRlike MAPlike
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike
+ contained: outer BODYlike DELlike
+ icontained: outer BODYlike DELlike
+ canclose: FONTlike EMlike MATHlike Alike formula Plike DIVlike same
+ flags:
+ 12:BASE
+ justify
+ 6 attributes:
+ 0:4:CLASS
+ 1:2:HREF
+ 2:1:ID
+ 3:0:STYLE
+ 4:0:TARGET
+ 5:0:TITLE
+ 2 attr_types
+ core
+ BASE
+ contents: SGML_EMPTY
+ tagclass: HEADstuff
+ contains:
+ icontains:
+ contained: outer HEADstuff
+ icontained: outer HEADstuff
+ canclose: FONTlike EMlike MATHlike Alike same
+ flags: endO
+ 13:BASEFONT
+ justify
+ 11 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:COLOR
+ 3:0:DIR
+ 4:0:END
+ 5:0:FACE
+ 6:1:ID
+ 7:0:LANG
+ 8:0:SIZE
+ 9:0:STYLE
+ 10:0:TITLE
+ 3 attr_types
+ core
+ i18n
+ FONT
+ contents: SGML_EMPTY
+ tagclass: BRlike
+ contains:
+ icontains:
+ contained: FONTlike EMlike MATHlike Alike TRlike FORMlike Plike DIVlike LIlike BRlike APPLETlike HRlike outer BODYlike
+ icontained: FONTlike EMlike MATHlike Alike TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike
+ canclose: BRlike APPLETlike HRlike MAPlike same
+ flags: endO
+ 14:BDO
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN
+ contents: SGML_MIXED
+ tagclass: Plike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike BRlike APPLETlike MAPlike
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: FORMlike DIVlike LIlike APPLETlike HRlike outer BODYlike DELlike
+ icontained: FONTlike EMlike MATHlike Alike TRlike FORMlike Plike DIVlike LIlike ULlike APPLETlike HRlike outer BODYlike same DELlike
+ canclose: FONTlike EMlike MATHlike Alike formula TRlike Plike DIVlike
+ flags:
+ 15:BGSOUND
+ justify
+ 9 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:LOOP
+ 6:2:SRC
+ 7:0:STYLE
+ 8:0:TITLE
+ 3 attr_types
+ core
+ i18n
+ BGSOUND
+ contents: SGML_EMPTY
+ tagclass: BRlike
+ contains:
+ icontains:
+ contained: FONTlike EMlike MATHlike Alike TRlike FORMlike Plike DIVlike LIlike BRlike APPLETlike HRlike outer BODYlike HEADstuff
+ icontained: FONTlike EMlike MATHlike Alike TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike HEADstuff
+ canclose: FONTlike EMlike MATHlike Alike Plike DIVlike BRlike APPLETlike HRlike same
+ flags: endO
+ 16:BIG
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN
+ contents: SGML_MIXED
+ tagclass: FONTlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike BRlike APPLETlike MAPlike same
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: FONTlike EMlike MATHlike Alike FORMlike Plike DIVlike LIlike BRlike APPLETlike HRlike BODYlike same
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike HEADstuff same
+ canclose: FONTlike
+ flags: mafse nreie
+ 17:BLINK
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN
+ contents: SGML_EMPTY
+ tagclass: FONTlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike BRlike APPLETlike MAPlike same
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: FONTlike EMlike MATHlike Alike FORMlike Plike DIVlike LIlike BRlike APPLETlike HRlike BODYlike same
+ icontained: FONTlike EMlike MATHlike Alike TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike HEADstuff same
+ canclose: FONTlike
+ flags: mafse nreie
+ 18:BLOCKQUOTE
+ justify
+ 9 attributes:
+ 0:2:CITE
+ 1:4:CLASS
+ 2:0:CLEAR
+ 3:0:DIR
+ 4:1:ID
+ 5:0:LANG
+ 6:0:NOWRAP
+ 7:0:STYLE
+ 8:0:TITLE
+ 3 attr_types
+ core
+ i18n
+ BQ
+ contents: SGML_MIXED
+ tagclass: DIVlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike FORMlike Plike DIVlike ULlike BRlike APPLETlike HRlike MAPlike BODYlike same
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike BODYlike same
+ contained: FORMlike DIVlike LIlike APPLETlike HRlike outer BODYlike same DELlike
+ icontained: FONTlike EMlike MATHlike Alike TRlike FORMlike Plike DIVlike LIlike ULlike APPLETlike HRlike outer BODYlike same DELlike
+ canclose: FONTlike EMlike MATHlike Alike formula Plike DIVlike same
+ flags:
+ 19:BODY
+ justify
+ 15 attributes:
+ 0:0:ALINK
+ 1:2:BACKGROUND
+ 2:0:BGCOLOR
+ 3:4:CLASS
+ 4:0:CLEAR
+ 5:0:DIR
+ 6:1:ID
+ 7:0:LANG
+ 8:0:LINK
+ 9:0:ONLOAD
+ 10:0:ONUNLOAD
+ 11:0:STYLE
+ 12:0:TEXT
+ 13:0:TITLE
+ 14:0:VLINK
+ 4 attr_types
+ bgcolor
+ core
+ i18n
+ BODY
+ contents: SGML_MIXED
+ tagclass: BODYlike
+ contains: FONTlike EMlike MATHlike Alike FORMlike Plike DIVlike ULlike BRlike APPLETlike HRlike MAPlike BODYlike DELlike
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike BODYlike DELlike
+ contained: outer BODYlike
+ icontained: outer BODYlike
+ canclose: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike outer HEADstuff same
+ flags: endO startO
+ 20:BODYTEXT
+ justify
+ 14 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DATA
+ 3:0:DIR
+ 4:1:ID
+ 5:0:LANG
+ 6:0:NAME
+ 7:0:OBJECT
+ 8:0:REF
+ 9:0:STYLE
+ 10:0:TITLE
+ 11:0:TYPE
+ 12:0:VALUE
+ 13:0:VALUETYPE
+ 3 attr_types
+ core
+ i18n
+ BODYTEXT
+ contents: SGML_MIXED
+ tagclass: BODYlike
+ contains: FONTlike EMlike MATHlike Alike FORMlike Plike DIVlike ULlike BRlike APPLETlike HRlike MAPlike DELlike
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike BODYlike same DELlike
+ contained: DIVlike outer BODYlike
+ icontained: FONTlike EMlike MATHlike Alike TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike same
+ canclose: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike Plike BRlike APPLETlike HRlike MAPlike same
+ flags: endO startO
+ 21:BQ
+ justify
+ 9 attributes:
+ 0:2:CITE
+ 1:4:CLASS
+ 2:0:CLEAR
+ 3:0:DIR
+ 4:1:ID
+ 5:0:LANG
+ 6:0:NOWRAP
+ 7:0:STYLE
+ 8:0:TITLE
+ 3 attr_types
+ core
+ i18n
+ BQ
+ contents: SGML_MIXED
+ tagclass: DIVlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike FORMlike Plike DIVlike ULlike BRlike APPLETlike HRlike MAPlike BODYlike same
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike BODYlike same
+ contained: FORMlike DIVlike LIlike APPLETlike HRlike outer BODYlike same DELlike
+ icontained: FONTlike EMlike MATHlike Alike TRlike FORMlike Plike DIVlike LIlike ULlike APPLETlike HRlike outer BODYlike same DELlike
+ canclose: FONTlike EMlike MATHlike Alike formula Plike DIVlike same
+ flags:
+ 22:BR
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN
+ contents: SGML_EMPTY
+ tagclass: BRlike
+ contains:
+ icontains:
+ contained: FONTlike EMlike MATHlike Alike formula TRlike FORMlike Plike DIVlike LIlike BRlike APPLETlike HRlike outer BODYlike
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike HEADstuff
+ canclose: FONTlike EMlike MATHlike Alike formula BRlike same
+ flags: endO
+ 23:BUTTON
+ justify
+ 17 attributes:
+ 0:0:ACCESSKEY
+ 1:4:CLASS
+ 2:0:CLEAR
+ 3:0:DIR
+ 4:0:DISABLED
+ 5:0:FORMACTION
+ 6:1:ID
+ 7:0:LANG
+ 8:0:NAME
+ 9:0:ONBLUR
+ 10:0:ONFOCUS
+ 11:0:READONLY
+ 12:0:STYLE
+ 13:0:TABINDEX
+ 14:0:TITLE
+ 15:0:TYPE
+ 16:0:VALUE
+ 4 attr_types
+ core
+ events
+ i18n
+ BUTTON
+ contents: SGML_MIXED
+ tagclass: APPLETlike
+ contains: FONTlike EMlike MATHlike Plike DIVlike ULlike BRlike APPLETlike MAPlike
+ icontains: FONTlike EMlike MATHlike formula TRlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike
+ contained: FONTlike EMlike MATHlike Alike FORMlike Plike DIVlike LIlike BRlike APPLETlike
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike
+ canclose: FONTlike EMlike MATHlike Alike formula SELECTlike Plike BRlike same
+ flags:
+ 24:CAPTION
+ justify
+ 9 attributes:
+ 0:0:ACCESSKEY
+ 1:0:ALIGN
+ 2:4:CLASS
+ 3:0:CLEAR
+ 4:0:DIR
+ 5:1:ID
+ 6:0:LANG
+ 7:0:STYLE
+ 8:0:TITLE
+ 5 attr_types
+ align
+ core
+ events
+ i18n
+ CAPTION
+ contents: SGML_MIXED
+ tagclass: Plike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike BRlike APPLETlike MAPlike
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: DIVlike ULlike APPLETlike HRlike DELlike
+ icontained: FONTlike EMlike MATHlike TRlike FORMlike Plike DIVlike LIlike ULlike APPLETlike HRlike outer BODYlike same DELlike
+ canclose: FONTlike EMlike MATHlike Alike formula SELECTlike Plike DIVlike same
+ flags:
+ 25:CENTER
+ justify
+ 8 attributes:
+ 0:0:ALIGN
+ 1:4:CLASS
+ 2:0:CLEAR
+ 3:0:DIR
+ 4:1:ID
+ 5:0:LANG
+ 6:0:STYLE
+ 7:0:TITLE
+ 4 attr_types
+ align
+ core
+ i18n
+ DIV
+ contents: SGML_MIXED
+ tagclass: DIVlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike FORMlike Plike DIVlike ULlike BRlike APPLETlike HRlike MAPlike same
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: FORMlike DIVlike LIlike APPLETlike HRlike outer BODYlike same DELlike
+ icontained: FONTlike EMlike MATHlike TRlike FORMlike Plike DIVlike LIlike ULlike APPLETlike HRlike outer BODYlike same DELlike
+ canclose: FONTlike EMlike MATHlike Alike formula Plike DIVlike LIlike same
+ flags:
+ 26:CITE
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN
+ contents: SGML_EMPTY
+ tagclass: EMlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike BRlike APPLETlike MAPlike same
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: FONTlike EMlike MATHlike Alike FORMlike Plike DIVlike LIlike BRlike APPLETlike HRlike BODYlike same
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike HEADstuff same
+ canclose: EMlike
+ flags: nreie
+ 27:CODE
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN
+ contents: SGML_MIXED
+ tagclass: EMlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike BRlike APPLETlike MAPlike same
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: FONTlike EMlike MATHlike Alike FORMlike Plike DIVlike LIlike BRlike APPLETlike HRlike BODYlike same
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike HEADstuff same
+ canclose: EMlike
+ flags:
+ 28:COL
+ justify
+ 13 attributes:
+ 0:0:ALIGN
+ 1:0:CHAR
+ 2:0:CHAROFF
+ 3:4:CLASS
+ 4:0:CLEAR
+ 5:0:DIR
+ 6:1:ID
+ 7:0:LANG
+ 8:0:SPAN
+ 9:0:STYLE
+ 10:0:TITLE
+ 11:0:VALIGN
+ 12:0:WIDTH
+ 5 attr_types
+ cellalign
+ core
+ events
+ i18n
+ COL
+ contents: SGML_EMPTY
+ tagclass: HRlike
+ contains:
+ icontains:
+ contained: TRlike ULlike
+ icontained: FONTlike EMlike MATHlike TRlike FORMlike Plike DIVlike LIlike ULlike APPLETlike HRlike outer BODYlike
+ canclose: FONTlike EMlike MATHlike Alike formula SELECTlike Plike DIVlike LIlike ULlike MAPlike same
+ flags: endO
+ 29:COLGROUP
+ justify
+ 13 attributes:
+ 0:0:ALIGN
+ 1:0:CHAR
+ 2:0:CHAROFF
+ 3:4:CLASS
+ 4:0:CLEAR
+ 5:0:DIR
+ 6:1:ID
+ 7:0:LANG
+ 8:0:SPAN
+ 9:0:STYLE
+ 10:0:TITLE
+ 11:0:VALIGN
+ 12:0:WIDTH
+ 5 attr_types
+ cellalign
+ core
+ events
+ i18n
+ COL
+ contents: SGML_EMPTY
+ tagclass: TRlike
+ contains: HRlike
+ icontains: HRlike
+ contained: ULlike
+ icontained: FONTlike EMlike MATHlike TRlike FORMlike Plike DIVlike LIlike ULlike APPLETlike HRlike outer BODYlike
+ canclose: FONTlike EMlike MATHlike Alike formula SELECTlike Plike DIVlike LIlike MAPlike same
+ flags: endO
+ 30:COMMENT
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN
+ contents: SGML_MIXED
+ tagclass: MATHlike
+ contains:
+ icontains:
+ contained: FONTlike EMlike MATHlike Alike TRlike FORMlike Plike DIVlike LIlike BRlike APPLETlike HRlike BODYlike same
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike outer BODYlike HEADstuff
+ canclose: FONTlike EMlike
+ flags:
+ 31:CREDIT
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN
+ contents: SGML_MIXED
+ tagclass: Plike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike BRlike APPLETlike MAPlike
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: DIVlike ULlike APPLETlike HRlike DELlike
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike same DELlike
+ canclose: FONTlike EMlike MATHlike Alike Plike DIVlike same
+ flags:
+ 32:DD
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN
+ contents: SGML_EMPTY
+ tagclass: LIlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike FORMlike Plike DIVlike ULlike BRlike APPLETlike HRlike MAPlike
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: ULlike
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike APPLETlike HRlike outer BODYlike same
+ canclose: FONTlike EMlike MATHlike Alike formula Plike DIVlike LIlike same
+ flags: endO
+ 33:DEL
+ justify
+ 8 attributes:
+ 0:0:CITE
+ 1:4:CLASS
+ 2:0:DATETIME
+ 3:0:DIR
+ 4:1:ID
+ 5:0:LANG
+ 6:0:STYLE
+ 7:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ DEL
+ contents: SGML_MIXED
+ tagclass: EMlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike BRlike APPLETlike MAPlike same
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: FONTlike EMlike MATHlike Alike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike same
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike HEADstuff same
+ canclose: FONTlike EMlike DELlike
+ flags:
+ 34:DEL!
+ justify
+ 8 attributes:
+ 0:0:CITE
+ 1:4:CLASS
+ 2:0:DATETIME
+ 3:0:DIR
+ 4:1:ID
+ 5:0:LANG
+ 6:0:STYLE
+ 7:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ DEL
+ contents: SGML_MIXED
+ tagclass: DELlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike FORMlike Plike DIVlike ULlike BRlike APPLETlike MAPlike same
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: FONTlike EMlike MATHlike Alike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike BODYlike same
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike HEADstuff same
+ canclose: FONTlike EMlike DELlike
+ flags:
+ 35:DFN
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN
+ contents: SGML_MIXED
+ tagclass: EMlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike FORMlike BRlike APPLETlike MAPlike same
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: FONTlike EMlike MATHlike Alike FORMlike Plike DIVlike LIlike BRlike APPLETlike HRlike same
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike HEADstuff same
+ canclose: FONTlike EMlike
+ flags:
+ 36:DIR
+ justify
+ 14 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:COMPACT
+ 3:0:DINGBAT
+ 4:0:DIR
+ 5:1:ID
+ 6:0:LANG
+ 7:0:MD
+ 8:0:PLAIN
+ 9:2:SRC
+ 10:0:STYLE
+ 11:0:TITLE
+ 12:0:TYPE
+ 13:0:WRAP
+ 3 attr_types
+ core
+ i18n
+ UL
+ contents: SGML_MIXED
+ tagclass: ULlike
+ contains: LIlike BRlike APPLETlike MAPlike
+ icontains: FONTlike EMlike MATHlike Alike formula SELECTlike Plike DIVlike LIlike BRlike APPLETlike HRlike MAPlike
+ contained: FORMlike DIVlike LIlike BRlike APPLETlike HRlike outer BODYlike DELlike
+ icontained: FONTlike EMlike MATHlike formula TRlike FORMlike Plike DIVlike LIlike ULlike APPLETlike HRlike outer BODYlike DELlike
+ canclose: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike Plike DIVlike LIlike ULlike HRlike same
+ flags:
+ 37:DIV
+ justify
+ 8 attributes:
+ 0:0:ALIGN
+ 1:4:CLASS
+ 2:0:CLEAR
+ 3:0:DIR
+ 4:1:ID
+ 5:0:LANG
+ 6:0:STYLE
+ 7:0:TITLE
+ 4 attr_types
+ align
+ core
+ i18n
+ DIV
+ contents: SGML_MIXED
+ tagclass: DIVlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike FORMlike Plike DIVlike ULlike BRlike APPLETlike HRlike MAPlike same
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: TRlike FORMlike DIVlike LIlike APPLETlike HRlike outer BODYlike same DELlike
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike same DELlike
+ canclose: FONTlike EMlike MATHlike formula Plike DIVlike same
+ flags: mafse
+ 38:DL
+ justify
+ 8 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:COMPACT
+ 3:0:DIR
+ 4:1:ID
+ 5:0:LANG
+ 6:0:STYLE
+ 7:0:TITLE
+ 3 attr_types
+ core
+ i18n
+ DL
+ contents: SGML_MIXED
+ tagclass: ULlike
+ contains: FORMlike LIlike HRlike MAPlike
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: FORMlike DIVlike LIlike APPLETlike HRlike outer BODYlike DELlike
+ icontained: FONTlike EMlike MATHlike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike same DELlike
+ canclose: FONTlike EMlike MATHlike formula SELECTlike Plike DIVlike LIlike
+ flags:
+ 39:DLC
+ justify
+ 8 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:COMPACT
+ 3:0:DIR
+ 4:1:ID
+ 5:0:LANG
+ 6:0:STYLE
+ 7:0:TITLE
+ 3 attr_types
+ core
+ i18n
+ DL
+ contents: SGML_MIXED
+ tagclass: ULlike
+ contains: FORMlike LIlike HRlike MAPlike
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: FORMlike DIVlike LIlike APPLETlike HRlike outer BODYlike DELlike
+ icontained: FONTlike EMlike MATHlike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike same DELlike
+ canclose: FONTlike EMlike MATHlike Alike formula SELECTlike Plike DIVlike LIlike
+ flags:
+ 40:DT
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN
+ contents: SGML_EMPTY
+ tagclass: LIlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike BRlike APPLETlike MAPlike
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike BRlike APPLETlike MAPlike
+ contained: ULlike
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer
+ canclose: FONTlike EMlike MATHlike Alike formula Plike DIVlike LIlike same
+ flags: endO
+ 41:EM
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN
+ contents: SGML_EMPTY
+ tagclass: EMlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike BRlike APPLETlike MAPlike same
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: FONTlike EMlike MATHlike Alike FORMlike Plike DIVlike LIlike BRlike APPLETlike HRlike BODYlike same
+ icontained: FONTlike EMlike MATHlike Alike TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike HEADstuff same
+ canclose: FONTlike EMlike
+ flags: nreie
+ 42:EMBED
+ justify
+ 21 attributes:
+ 0:0:ALIGN
+ 1:0:ALT
+ 2:0:BORDER
+ 3:4:CLASS
+ 4:0:CLEAR
+ 5:0:DIR
+ 6:0:HEIGHT
+ 7:1:ID
+ 8:0:IMAGEMAP
+ 9:0:ISMAP
+ 10:0:LANG
+ 11:0:MD
+ 12:1:NAME
+ 13:0:NOFLOW
+ 14:0:PARAMS
+ 15:2:SRC
+ 16:0:STYLE
+ 17:0:TITLE
+ 18:0:UNITS
+ 19:0:USEMAP
+ 20:0:WIDTH
+ 4 attr_types
+ align
+ core
+ i18n
+ EMBED
+ contents: SGML_EMPTY
+ tagclass: APPLETlike
+ contains: FONTlike EMlike MATHlike Plike BRlike APPLETlike HRlike MAPlike same
+ icontains: FONTlike EMlike MATHlike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: FONTlike EMlike MATHlike Alike formula TRlike FORMlike Plike DIVlike LIlike ULlike APPLETlike HRlike outer BODYlike same
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike same
+ canclose: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike outer
+ flags: endO
+ 43:FIELDSET
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN
+ contents: SGML_MIXED
+ tagclass: DIVlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike Plike DIVlike ULlike BRlike APPLETlike HRlike MAPlike same
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: FONTlike EMlike MATHlike FORMlike Plike DIVlike LIlike APPLETlike HRlike same DELlike
+ icontained: FONTlike EMlike MATHlike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike same DELlike
+ canclose: FONTlike EMlike MATHlike Alike formula SELECTlike MAPlike same
+ flags:
+ 44:FIG
+ justify
+ 17 attributes:
+ 0:0:ALIGN
+ 1:0:BORDER
+ 2:4:CLASS
+ 3:0:CLEAR
+ 4:0:DIR
+ 5:0:HEIGHT
+ 6:1:ID
+ 7:0:IMAGEMAP
+ 8:0:ISOBJECT
+ 9:0:LANG
+ 10:0:MD
+ 11:0:NOFLOW
+ 12:2:SRC
+ 13:0:STYLE
+ 14:0:TITLE
+ 15:0:UNITS
+ 16:0:WIDTH
+ 4 attr_types
+ align
+ core
+ i18n
+ FIG
+ contents: SGML_MIXED
+ tagclass: DIVlike
+ contains: Plike DIVlike ULlike BRlike APPLETlike HRlike MAPlike
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: FORMlike DIVlike LIlike APPLETlike HRlike outer BODYlike DELlike
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike FORMlike Plike DIVlike LIlike ULlike APPLETlike HRlike outer BODYlike same DELlike
+ canclose: FONTlike EMlike MATHlike Alike SELECTlike Plike DIVlike MAPlike same
+ flags:
+ 45:FIGURE
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:DIR
+ 2:1:ID
+ 3:0:LANG
+ 4:0:ROLE
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN5
+ contents: SGML_MIXED
+ tagclass: DIVlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike FORMlike Plike DIVlike ULlike BRlike APPLETlike HRlike MAPlike same
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: TRlike FORMlike DIVlike LIlike APPLETlike HRlike outer BODYlike same DELlike
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike same DELlike
+ canclose: FONTlike EMlike MATHlike Alike formula Plike DIVlike same
+ flags: mafse
+ 46:FN
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN
+ contents: SGML_MIXED
+ tagclass: DIVlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike FORMlike Plike DIVlike ULlike BRlike APPLETlike HRlike MAPlike same
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: FORMlike DIVlike LIlike APPLETlike HRlike outer BODYlike same DELlike
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike FORMlike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike same DELlike
+ canclose: FONTlike EMlike MATHlike Alike SELECTlike Plike BRlike same
+ flags:
+ 47:FONT
+ justify
+ 11 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:COLOR
+ 3:0:DIR
+ 4:0:END
+ 5:0:FACE
+ 6:1:ID
+ 7:0:LANG
+ 8:0:SIZE
+ 9:0:STYLE
+ 10:0:TITLE
+ 3 attr_types
+ core
+ i18n
+ FONT
+ contents: SGML_EMPTY
+ tagclass: FONTlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike BRlike APPLETlike MAPlike same
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: FONTlike EMlike MATHlike Alike FORMlike Plike DIVlike LIlike BRlike APPLETlike HRlike outer BODYlike same
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike HEADstuff same
+ canclose: FONTlike
+ flags: mafse nreie
+ 48:FOOTER
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:DIR
+ 2:1:ID
+ 3:0:LANG
+ 4:0:ROLE
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN5
+ contents: SGML_MIXED
+ tagclass: DIVlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike FORMlike Plike DIVlike ULlike BRlike APPLETlike HRlike MAPlike same
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: TRlike FORMlike DIVlike LIlike APPLETlike HRlike outer BODYlike same DELlike
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike same DELlike
+ canclose: FONTlike EMlike MATHlike Alike formula Plike DIVlike same
+ flags: mafse
+ 49:FORM
+ justify
+ 17 attributes:
+ 0:0:ACCEPT
+ 1:0:ACCEPT-CHARSET
+ 2:2:ACTION
+ 3:4:CLASS
+ 4:0:CLEAR
+ 5:0:DIR
+ 6:0:ENCTYPE
+ 7:1:ID
+ 8:0:LANG
+ 9:0:METHOD
+ 10:0:ONRESET
+ 11:0:ONSUBMIT
+ 12:0:SCRIPT
+ 13:0:STYLE
+ 14:0:SUBJECT
+ 15:0:TARGET
+ 16:0:TITLE
+ 3 attr_types
+ core
+ i18n
+ FORM
+ contents: SGML_EMPTY
+ tagclass: FORMlike
+ contains: FONTlike EMlike MATHlike Alike TRlike SELECTlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike
+ contained: FONTlike EMlike MATHlike DIVlike LIlike ULlike APPLETlike HRlike outer BODYlike DELlike
+ icontained: FONTlike EMlike MATHlike Plike DIVlike LIlike ULlike APPLETlike outer BODYlike DELlike
+ canclose: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike LIlike ULlike MAPlike same
+ flags:
+ 50:FRAME
+ justify
+ 12 attributes:
+ 0:4:CLASS
+ 1:0:FRAMEBORDER
+ 2:1:ID
+ 3:2:LONGDESC
+ 4:0:MARGINHEIGHT
+ 5:0:MARGINWIDTH
+ 6:0:NAME
+ 7:0:NORESIZE
+ 8:0:SCROLLING
+ 9:2:SRC
+ 10:0:STYLE
+ 11:0:TITLE
+ 2 attr_types
+ core
+ FRAME
+ contents: SGML_EMPTY
+ tagclass: outer
+ contains:
+ icontains:
+ contained: outer
+ icontained: outer
+ canclose: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike outer same
+ flags: endO
+ 51:FRAMESET
+ justify
+ 4 attributes:
+ 0:0:COLS
+ 1:0:ONLOAD
+ 2:0:ONUNLOAD
+ 3:0:ROWS
+ 1 attr_types
+ FRAMESET
+ contents: SGML_MIXED
+ tagclass: outer
+ contains: outer same
+ icontains: outer same
+ contained: outer same
+ icontained: BRlike APPLETlike outer same
+ canclose: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike outer same
+ flags:
+ 52:H1
+ nojustify
+ 14 attributes:
+ 0:0:ALIGN
+ 1:4:CLASS
+ 2:0:CLEAR
+ 3:0:DINGBAT
+ 4:0:DIR
+ 5:1:ID
+ 6:0:LANG
+ 7:0:MD
+ 8:0:NOWRAP
+ 9:0:SEQNUM
+ 10:0:SKIP
+ 11:2:SRC
+ 12:0:STYLE
+ 13:0:TITLE
+ 5 attr_types
+ align
+ core
+ events
+ i18n
+ H
+ contents: SGML_MIXED
+ tagclass: Plike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike BRlike APPLETlike MAPlike
+ icontains: FONTlike EMlike MATHlike Alike formula SELECTlike BRlike APPLETlike MAPlike
+ contained: FORMlike DIVlike LIlike APPLETlike HRlike outer BODYlike DELlike
+ icontained: FONTlike EMlike MATHlike Alike TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike DELlike
+ canclose: FONTlike EMlike MATHlike formula Plike same
+ flags:
+ 53:H2
+ nojustify
+ 14 attributes:
+ 0:0:ALIGN
+ 1:4:CLASS
+ 2:0:CLEAR
+ 3:0:DINGBAT
+ 4:0:DIR
+ 5:1:ID
+ 6:0:LANG
+ 7:0:MD
+ 8:0:NOWRAP
+ 9:0:SEQNUM
+ 10:0:SKIP
+ 11:2:SRC
+ 12:0:STYLE
+ 13:0:TITLE
+ 5 attr_types
+ align
+ core
+ events
+ i18n
+ H
+ contents: SGML_MIXED
+ tagclass: Plike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike BRlike APPLETlike MAPlike
+ icontains: FONTlike EMlike MATHlike Alike formula SELECTlike BRlike APPLETlike MAPlike
+ contained: FORMlike DIVlike LIlike APPLETlike HRlike outer BODYlike DELlike
+ icontained: FONTlike EMlike MATHlike Alike TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike DELlike
+ canclose: FONTlike EMlike MATHlike formula Plike same
+ flags:
+ 54:H3
+ nojustify
+ 14 attributes:
+ 0:0:ALIGN
+ 1:4:CLASS
+ 2:0:CLEAR
+ 3:0:DINGBAT
+ 4:0:DIR
+ 5:1:ID
+ 6:0:LANG
+ 7:0:MD
+ 8:0:NOWRAP
+ 9:0:SEQNUM
+ 10:0:SKIP
+ 11:2:SRC
+ 12:0:STYLE
+ 13:0:TITLE
+ 5 attr_types
+ align
+ core
+ events
+ i18n
+ H
+ contents: SGML_MIXED
+ tagclass: Plike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike BRlike APPLETlike MAPlike
+ icontains: FONTlike EMlike MATHlike Alike formula SELECTlike BRlike APPLETlike MAPlike
+ contained: FORMlike DIVlike LIlike APPLETlike HRlike outer BODYlike DELlike
+ icontained: FONTlike EMlike MATHlike Alike TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike DELlike
+ canclose: FONTlike EMlike MATHlike formula Plike same
+ flags:
+ 55:H4
+ nojustify
+ 14 attributes:
+ 0:0:ALIGN
+ 1:4:CLASS
+ 2:0:CLEAR
+ 3:0:DINGBAT
+ 4:0:DIR
+ 5:1:ID
+ 6:0:LANG
+ 7:0:MD
+ 8:0:NOWRAP
+ 9:0:SEQNUM
+ 10:0:SKIP
+ 11:2:SRC
+ 12:0:STYLE
+ 13:0:TITLE
+ 5 attr_types
+ align
+ core
+ events
+ i18n
+ H
+ contents: SGML_MIXED
+ tagclass: Plike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike BRlike APPLETlike MAPlike
+ icontains: FONTlike EMlike MATHlike Alike formula SELECTlike BRlike APPLETlike MAPlike
+ contained: FORMlike DIVlike LIlike APPLETlike HRlike outer BODYlike DELlike
+ icontained: FONTlike EMlike MATHlike Alike TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike DELlike
+ canclose: FONTlike EMlike MATHlike formula Plike same
+ flags:
+ 56:H5
+ nojustify
+ 14 attributes:
+ 0:0:ALIGN
+ 1:4:CLASS
+ 2:0:CLEAR
+ 3:0:DINGBAT
+ 4:0:DIR
+ 5:1:ID
+ 6:0:LANG
+ 7:0:MD
+ 8:0:NOWRAP
+ 9:0:SEQNUM
+ 10:0:SKIP
+ 11:2:SRC
+ 12:0:STYLE
+ 13:0:TITLE
+ 5 attr_types
+ align
+ core
+ events
+ i18n
+ H
+ contents: SGML_MIXED
+ tagclass: Plike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike BRlike APPLETlike MAPlike
+ icontains: FONTlike EMlike MATHlike Alike formula SELECTlike BRlike APPLETlike MAPlike
+ contained: FORMlike DIVlike LIlike APPLETlike HRlike outer BODYlike DELlike
+ icontained: FONTlike EMlike MATHlike Alike TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike DELlike
+ canclose: FONTlike EMlike MATHlike formula Plike same
+ flags:
+ 57:H6
+ nojustify
+ 14 attributes:
+ 0:0:ALIGN
+ 1:4:CLASS
+ 2:0:CLEAR
+ 3:0:DINGBAT
+ 4:0:DIR
+ 5:1:ID
+ 6:0:LANG
+ 7:0:MD
+ 8:0:NOWRAP
+ 9:0:SEQNUM
+ 10:0:SKIP
+ 11:2:SRC
+ 12:0:STYLE
+ 13:0:TITLE
+ 5 attr_types
+ align
+ core
+ events
+ i18n
+ H
+ contents: SGML_MIXED
+ tagclass: Plike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike BRlike APPLETlike MAPlike
+ icontains: FONTlike EMlike MATHlike Alike formula SELECTlike BRlike APPLETlike MAPlike
+ contained: FORMlike DIVlike LIlike APPLETlike HRlike outer BODYlike DELlike
+ icontained: FONTlike EMlike MATHlike Alike TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike DELlike
+ canclose: FONTlike EMlike MATHlike formula Plike same
+ flags:
+ 58:HEAD
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN
+ contents: SGML_MIXED
+ tagclass: HEADstuff
+ contains: BRlike APPLETlike HRlike MAPlike HEADstuff
+ icontains: BRlike APPLETlike HRlike HEADstuff
+ contained: outer
+ icontained: outer
+ canclose: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike outer same
+ flags: endO startO mafse
+ 59:HEADER
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:DIR
+ 2:1:ID
+ 3:0:LANG
+ 4:0:ROLE
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN5
+ contents: SGML_MIXED
+ tagclass: DIVlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike FORMlike Plike DIVlike ULlike BRlike APPLETlike HRlike MAPlike same
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: TRlike FORMlike DIVlike LIlike APPLETlike HRlike outer BODYlike same DELlike
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike same DELlike
+ canclose: FONTlike EMlike MATHlike Alike formula Plike DIVlike same
+ flags: mafse
+ 60:HR
+ justify
+ 13 attributes:
+ 0:0:ALIGN
+ 1:4:CLASS
+ 2:0:CLEAR
+ 3:0:DIR
+ 4:1:ID
+ 5:0:LANG
+ 6:0:MD
+ 7:0:NOSHADE
+ 8:0:SIZE
+ 9:2:SRC
+ 10:0:STYLE
+ 11:0:TITLE
+ 12:0:WIDTH
+ 4 attr_types
+ align
+ core
+ i18n
+ HR
+ contents: SGML_EMPTY
+ tagclass: HRlike
+ contains:
+ icontains:
+ contained: FORMlike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike outer BODYlike
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike outer BODYlike
+ canclose: FONTlike EMlike MATHlike formula TRlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike same
+ flags: endO
+ 61:HTML
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN
+ contents: SGML_MIXED
+ tagclass: outer
+ contains: FONTlike EMlike MATHlike Alike FORMlike Plike DIVlike ULlike BRlike APPLETlike HRlike MAPlike outer BODYlike HEADstuff
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike outer BODYlike HEADstuff
+ contained:
+ icontained:
+ canclose: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike outer
+ flags: endO startO
+ 62:HY
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN
+ contents: SGML_EMPTY
+ tagclass: BRlike
+ contains:
+ icontains:
+ contained: FONTlike EMlike MATHlike Alike formula FORMlike Plike DIVlike LIlike BRlike APPLETlike HRlike outer BODYlike
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike HEADstuff
+ canclose: FONTlike EMlike MATHlike Alike formula BRlike same
+ flags: endO
+ 63:I
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN
+ contents: SGML_EMPTY
+ tagclass: FONTlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike BRlike APPLETlike MAPlike same
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: FONTlike EMlike MATHlike Alike FORMlike Plike DIVlike LIlike BRlike APPLETlike HRlike BODYlike same
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike HEADstuff same
+ canclose: FONTlike
+ flags: mafse nreie
+ 64:IFRAME
+ justify
+ 14 attributes:
+ 0:0:ALIGN
+ 1:4:CLASS
+ 2:0:FRAMEBORDER
+ 3:0:HEIGHT
+ 4:1:ID
+ 5:2:LONGDESC
+ 6:0:MARGINHEIGHT
+ 7:0:MARGINWIDTH
+ 8:0:NAME
+ 9:0:SCROLLING
+ 10:2:SRC
+ 11:0:STYLE
+ 12:0:TITLE
+ 13:0:WIDTH
+ 3 attr_types
+ align
+ core
+ IFRAME
+ contents: SGML_MIXED
+ tagclass: APPLETlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike FORMlike Plike DIVlike ULlike BRlike APPLETlike HRlike MAPlike same
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: FONTlike EMlike MATHlike Alike formula FORMlike Plike DIVlike LIlike APPLETlike HRlike outer BODYlike same
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike FORMlike Plike DIVlike LIlike ULlike APPLETlike HRlike outer BODYlike same
+ canclose: FONTlike EMlike MATHlike Alike formula SELECTlike Plike BRlike APPLETlike outer HEADstuff same
+ flags:
+ 65:IMG
+ justify
+ 22 attributes:
+ 0:0:ALIGN
+ 1:0:ALT
+ 2:0:BORDER
+ 3:4:CLASS
+ 4:0:CLEAR
+ 5:0:DIR
+ 6:0:HEIGHT
+ 7:0:HSPACE
+ 8:1:ID
+ 9:0:ISMAP
+ 10:0:ISOBJECT
+ 11:0:LANG
+ 12:2:LONGDESC
+ 13:0:MD
+ 14:0:NAME
+ 15:2:SRC
+ 16:0:STYLE
+ 17:0:TITLE
+ 18:0:UNITS
+ 19:2:USEMAP
+ 20:0:VSPACE
+ 21:0:WIDTH
+ 5 attr_types
+ align
+ core
+ events
+ i18n
+ IMG
+ contents: SGML_EMPTY
+ tagclass: BRlike
+ contains:
+ icontains:
+ contained: FONTlike EMlike MATHlike Alike formula FORMlike Plike DIVlike LIlike BRlike APPLETlike HRlike outer BODYlike
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike
+ canclose: same
+ flags: endO
+ 66:INPUT
+ justify
+ 35 attributes:
+ 0:0:ACCEPT
+ 1:0:ACCEPT-CHARSET
+ 2:0:ACCESSKEY
+ 3:0:ALIGN
+ 4:0:ALT
+ 5:0:CHECKED
+ 6:4:CLASS
+ 7:0:CLEAR
+ 8:0:DIR
+ 9:0:DISABLED
+ 10:0:ERROR
+ 11:0:HEIGHT
+ 12:1:ID
+ 13:0:ISMAP
+ 14:0:LANG
+ 15:0:MAX
+ 16:0:MAXLENGTH
+ 17:0:MD
+ 18:0:MIN
+ 19:0:NAME
+ 20:0:NOTAB
+ 21:0:ONBLUR
+ 22:0:ONCHANGE
+ 23:0:ONFOCUS
+ 24:0:ONSELECT
+ 25:0:READONLY
+ 26:0:SIZE
+ 27:2:SRC
+ 28:0:STYLE
+ 29:0:TABINDEX
+ 30:0:TITLE
+ 31:0:TYPE
+ 32:0:USEMAP
+ 33:0:VALUE
+ 34:0:WIDTH
+ 5 attr_types
+ align
+ core
+ events
+ i18n
+ INPUT
+ contents: SGML_EMPTY
+ tagclass: SELECTlike
+ contains:
+ icontains:
+ contained: FONTlike EMlike MATHlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike
+ icontained: FONTlike EMlike MATHlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike
+ canclose: FONTlike EMlike MATHlike Alike SELECTlike BRlike MAPlike same
+ flags: endO
+ 67:INS
+ justify
+ 8 attributes:
+ 0:0:CITE
+ 1:4:CLASS
+ 2:0:DATETIME
+ 3:0:DIR
+ 4:1:ID
+ 5:0:LANG
+ 6:0:STYLE
+ 7:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ DEL
+ contents: SGML_MIXED
+ tagclass: EMlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike BRlike APPLETlike MAPlike same
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: FONTlike EMlike MATHlike Alike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike same
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike HEADstuff same
+ canclose: FONTlike EMlike DELlike
+ flags:
+ 68:INS!
+ justify
+ 8 attributes:
+ 0:0:CITE
+ 1:4:CLASS
+ 2:0:DATETIME
+ 3:0:DIR
+ 4:1:ID
+ 5:0:LANG
+ 6:0:STYLE
+ 7:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ DEL
+ contents: SGML_MIXED
+ tagclass: DELlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike FORMlike Plike DIVlike ULlike BRlike APPLETlike MAPlike same
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: FONTlike EMlike MATHlike Alike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike BODYlike same
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike HEADstuff same
+ canclose: FONTlike EMlike DELlike
+ flags:
+ 69:ISINDEX
+ justify
+ 9 attributes:
+ 0:2:ACTION
+ 1:4:CLASS
+ 2:0:DIR
+ 3:2:HREF
+ 4:1:ID
+ 5:0:LANG
+ 6:0:PROMPT
+ 7:0:STYLE
+ 8:0:TITLE
+ 3 attr_types
+ core
+ i18n
+ ISINDEX
+ contents: SGML_EMPTY
+ tagclass: MAPlike
+ contains:
+ icontains:
+ contained: FONTlike EMlike MATHlike Alike FORMlike Plike DIVlike LIlike BRlike APPLETlike HRlike outer BODYlike HEADstuff
+ icontained: FONTlike EMlike MATHlike Alike TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike outer BODYlike HEADstuff
+ canclose: FONTlike EMlike MATHlike same
+ flags: endO
+ 70:KBD
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN
+ contents: SGML_MIXED
+ tagclass: EMlike
+ contains:
+ icontains:
+ contained: FONTlike EMlike MATHlike Alike FORMlike Plike DIVlike LIlike BRlike APPLETlike HRlike BODYlike
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike HEADstuff
+ canclose: FONTlike EMlike
+ flags:
+ 71:KEYGEN
+ justify
+ 8 attributes:
+ 0:0:CHALLENGE
+ 1:4:CLASS
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:NAME
+ 6:0:STYLE
+ 7:0:TITLE
+ 3 attr_types
+ core
+ i18n
+ KEYGEN
+ contents: SGML_EMPTY
+ tagclass: SELECTlike
+ contains:
+ icontains:
+ contained: FONTlike EMlike MATHlike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike
+ icontained: FONTlike EMlike MATHlike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike
+ canclose: formula TRlike SELECTlike same
+ flags: endO
+ 72:LABEL
+ justify
+ 11 attributes:
+ 0:0:ACCESSKEY
+ 1:4:CLASS
+ 2:0:CLEAR
+ 3:0:DIR
+ 4:0:FOR
+ 5:1:ID
+ 6:0:LANG
+ 7:0:ONBLUR
+ 8:0:ONFOCUS
+ 9:0:STYLE
+ 10:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ LABEL
+ contents: SGML_MIXED
+ tagclass: EMlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike BRlike APPLETlike
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike
+ contained: FONTlike EMlike MATHlike Alike formula FORMlike Plike DIVlike LIlike APPLETlike HRlike
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike FORMlike Plike DIVlike LIlike ULlike APPLETlike HRlike outer BODYlike
+ canclose: FONTlike EMlike MATHlike
+ flags:
+ 73:LEGEND
+ justify
+ 9 attributes:
+ 0:0:ACCESSKEY
+ 1:0:ALIGN
+ 2:4:CLASS
+ 3:0:CLEAR
+ 4:0:DIR
+ 5:1:ID
+ 6:0:LANG
+ 7:0:STYLE
+ 8:0:TITLE
+ 5 attr_types
+ align
+ core
+ events
+ i18n
+ CAPTION
+ contents: SGML_MIXED
+ tagclass: EMlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike BRlike APPLETlike MAPlike
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: DIVlike
+ icontained: FONTlike EMlike MATHlike TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike same
+ canclose: FONTlike EMlike
+ flags:
+ 74:LH
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN
+ contents: SGML_EMPTY
+ tagclass: LIlike
+ contains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike Plike DIVlike ULlike BRlike APPLETlike MAPlike
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: ULlike
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer same
+ canclose: FONTlike EMlike MATHlike Alike formula Plike DIVlike LIlike same
+ flags: endO
+ 75:LI
+ justify
+ 13 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DINGBAT
+ 3:0:DIR
+ 4:1:ID
+ 5:0:LANG
+ 6:0:MD
+ 7:0:SKIP
+ 8:2:SRC
+ 9:0:STYLE
+ 10:0:TITLE
+ 11:0:TYPE
+ 12:0:VALUE
+ 4 attr_types
+ core
+ events
+ i18n
+ LI
+ contents: SGML_EMPTY
+ tagclass: LIlike
+ contains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike ULlike BRlike APPLETlike MAPlike
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: ULlike
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer same
+ canclose: FONTlike EMlike MATHlike Alike formula Plike DIVlike LIlike same
+ flags: endO
+ 76:LINK
+ justify
+ 14 attributes:
+ 0:0:CHARSET
+ 1:4:CLASS
+ 2:0:DIR
+ 3:2:HREF
+ 4:0:HREFLANG
+ 5:1:ID
+ 6:0:LANG
+ 7:0:MEDIA
+ 8:0:REL
+ 9:0:REV
+ 10:0:STYLE
+ 11:0:TARGET
+ 12:0:TITLE
+ 13:0:TYPE
+ 4 attr_types
+ core
+ events
+ i18n
+ LINK
+ contents: SGML_EMPTY
+ tagclass: MAPlike
+ contains:
+ icontains:
+ contained: outer HEADstuff
+ icontained: outer HEADstuff
+ canclose: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike
+ flags: endO
+ 77:LISTING
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN
+ contents: SGML_LITTERAL
+ tagclass: ULlike
+ contains:
+ icontains:
+ contained: DIVlike LIlike APPLETlike HRlike outer BODYlike DELlike
+ icontained: Plike DIVlike LIlike ULlike APPLETlike HRlike outer BODYlike DELlike
+ canclose: FONTlike EMlike MATHlike Alike formula Plike DIVlike LIlike ULlike same
+ flags:
+ 78:MAIN
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:DIR
+ 2:1:ID
+ 3:0:LANG
+ 4:0:ROLE
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN5
+ contents: SGML_MIXED
+ tagclass: DIVlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike FORMlike Plike DIVlike ULlike BRlike APPLETlike HRlike MAPlike same
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: TRlike FORMlike DIVlike LIlike APPLETlike HRlike outer BODYlike same DELlike
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike same DELlike
+ canclose: FONTlike EMlike MATHlike Alike formula Plike DIVlike same
+ flags: mafse
+ 79:MAP
+ justify
+ 8 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:1:NAME
+ 6:0:STYLE
+ 7:0:TITLE
+ 3 attr_types
+ core
+ i18n
+ MAP
+ contents: SGML_MIXED
+ tagclass: MAPlike
+ contains: MAPlike
+ icontains: MAPlike
+ contained: FONTlike EMlike MATHlike Alike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike
+ canclose: FONTlike EMlike MATHlike Alike formula Plike LIlike
+ flags:
+ 80:MARQUEE
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN
+ contents: SGML_MIXED
+ tagclass: HRlike
+ contains: FONTlike EMlike MATHlike Alike
+ icontains: FONTlike EMlike MATHlike Alike formula BRlike APPLETlike HRlike MAPlike same
+ contained: FONTlike EMlike MATHlike FORMlike Plike DIVlike LIlike BRlike APPLETlike HRlike outer BODYlike
+ icontained: FONTlike EMlike MATHlike TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike same
+ canclose: MATHlike Alike formula BRlike APPLETlike same
+ flags:
+ 81:MATH
+ justify
+ 8 attributes:
+ 0:0:BOX
+ 1:4:CLASS
+ 2:0:CLEAR
+ 3:0:DIR
+ 4:1:ID
+ 5:0:LANG
+ 6:0:STYLE
+ 7:0:TITLE
+ 3 attr_types
+ core
+ i18n
+ MATH
+ contents: SGML_LITTERAL
+ tagclass: MATHlike
+ contains: FONTlike EMlike MATHlike Alike formula SELECTlike BRlike APPLETlike MAPlike
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: FONTlike EMlike MATHlike Alike FORMlike Plike DIVlike LIlike BRlike APPLETlike HRlike BODYlike
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike HEADstuff same
+ canclose: FONTlike EMlike MATHlike Alike formula
+ flags:
+ 82:MENU
+ justify
+ 14 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:COMPACT
+ 3:0:DINGBAT
+ 4:0:DIR
+ 5:1:ID
+ 6:0:LANG
+ 7:0:MD
+ 8:0:PLAIN
+ 9:2:SRC
+ 10:0:STYLE
+ 11:0:TITLE
+ 12:0:TYPE
+ 13:0:WRAP
+ 3 attr_types
+ core
+ i18n
+ UL
+ contents: SGML_MIXED
+ tagclass: ULlike
+ contains: LIlike BRlike APPLETlike MAPlike
+ icontains: FONTlike EMlike MATHlike Alike formula SELECTlike Plike DIVlike LIlike BRlike APPLETlike HRlike MAPlike
+ contained: FORMlike DIVlike LIlike BRlike APPLETlike HRlike outer DELlike
+ icontained: FONTlike EMlike MATHlike formula TRlike FORMlike Plike DIVlike LIlike ULlike APPLETlike HRlike outer BODYlike DELlike
+ canclose: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike Plike DIVlike LIlike ULlike MAPlike same
+ flags:
+ 83:META
+ justify
+ 5 attributes:
+ 0:0:CHARSET
+ 1:0:CONTENT
+ 2:0:HTTP-EQUIV
+ 3:0:NAME
+ 4:0:SCHEME
+ 1 attr_types
+ META
+ contents: SGML_EMPTY
+ tagclass: MAPlike
+ contains:
+ icontains:
+ contained: outer HEADstuff
+ icontained: outer HEADstuff
+ canclose: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike
+ flags: endO
+ 84:NAV
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:DIR
+ 2:1:ID
+ 3:0:LANG
+ 4:0:ROLE
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN5
+ contents: SGML_MIXED
+ tagclass: DIVlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike FORMlike Plike DIVlike ULlike BRlike APPLETlike HRlike MAPlike same
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: TRlike FORMlike DIVlike LIlike APPLETlike HRlike outer BODYlike same DELlike
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike same DELlike
+ canclose: FONTlike EMlike MATHlike Alike formula Plike DIVlike same
+ flags: mafse
+ 85:NEXTID
+ justify
+ 1 attributes:
+ 0:0:N
+ 1 attr_types
+ NEXTID
+ contents: SGML_EMPTY
+ tagclass: BRlike
+ contains:
+ icontains:
+ contained: outer HEADstuff
+ icontained: FONTlike EMlike MATHlike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike outer
+ canclose: FONTlike
+ flags: endO
+ 86:NOFRAMES
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN
+ contents: SGML_MIXED
+ tagclass: BODYlike
+ contains: FONTlike EMlike MATHlike Alike FORMlike Plike DIVlike ULlike BRlike APPLETlike HRlike MAPlike BODYlike DELlike
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike DELlike
+ contained: BRlike APPLETlike HRlike outer
+ icontained: BRlike APPLETlike HRlike outer
+ canclose: FONTlike EMlike MATHlike Alike formula SELECTlike Plike DIVlike LIlike ULlike HRlike MAPlike
+ flags:
+ 87:NOTE
+ justify
+ 10 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:MD
+ 6:8:ROLE
+ 7:2:SRC
+ 8:0:STYLE
+ 9:0:TITLE
+ 3 attr_types
+ core
+ i18n
+ NOTE
+ contents: SGML_MIXED
+ tagclass: DIVlike
+ contains: FONTlike EMlike MATHlike Alike TRlike FORMlike Plike DIVlike ULlike BRlike APPLETlike MAPlike
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: formula TRlike FORMlike DIVlike LIlike BRlike APPLETlike HRlike outer BODYlike DELlike
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike same DELlike
+ canclose: FONTlike EMlike MATHlike Alike formula Plike DIVlike same
+ flags:
+ 88:OBJECT
+ justify
+ 26 attributes:
+ 0:0:ALIGN
+ 1:0:ARCHIVE
+ 2:0:BORDER
+ 3:4:CLASS
+ 4:2:CLASSID
+ 5:2:CODEBASE
+ 6:0:CODETYPE
+ 7:2:DATA
+ 8:0:DECLARE
+ 9:0:DIR
+ 10:0:HEIGHT
+ 11:0:HSPACE
+ 12:1:ID
+ 13:0:ISMAP
+ 14:0:LANG
+ 15:0:NAME
+ 16:0:NOTAB
+ 17:0:SHAPES
+ 18:0:STANDBY
+ 19:0:STYLE
+ 20:0:TABINDEX
+ 21:0:TITLE
+ 22:0:TYPE
+ 23:2:USEMAP
+ 24:0:VSPACE
+ 25:0:WIDTH
+ 5 attr_types
+ align
+ core
+ events
+ i18n
+ OBJECT
+ contents: SGML_LITTERAL
+ tagclass: APPLETlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike FORMlike Plike DIVlike ULlike BRlike APPLETlike HRlike MAPlike same
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: FONTlike EMlike MATHlike Alike formula FORMlike Plike DIVlike LIlike APPLETlike HRlike outer BODYlike same
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike FORMlike Plike DIVlike LIlike ULlike APPLETlike HRlike outer BODYlike same
+ canclose: FONTlike EMlike MATHlike Alike formula SELECTlike Plike LIlike ULlike BRlike APPLETlike same
+ flags: frecyc
+ 89:OL
+ justify
+ 12 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:COMPACT
+ 3:0:CONTINUE
+ 4:0:DIR
+ 5:1:ID
+ 6:0:LANG
+ 7:0:SEQNUM
+ 8:0:START
+ 9:0:STYLE
+ 10:0:TITLE
+ 11:0:TYPE
+ 3 attr_types
+ core
+ i18n
+ OL
+ contents: SGML_MIXED
+ tagclass: ULlike
+ contains: LIlike HRlike MAPlike
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: FORMlike DIVlike LIlike BRlike APPLETlike HRlike outer BODYlike DELlike
+ icontained: FONTlike EMlike MATHlike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike same DELlike
+ canclose: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike Plike DIVlike LIlike ULlike MAPlike same
+ flags:
+ 90:OPTION
+ justify
+ 13 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:0:DISABLED
+ 4:0:ERROR
+ 5:1:ID
+ 6:0:LABEL
+ 7:0:LANG
+ 8:0:SELECTED
+ 9:0:SHAPE
+ 10:0:STYLE
+ 11:0:TITLE
+ 12:0:VALUE
+ 4 attr_types
+ core
+ events
+ i18n
+ OPTION
+ contents: SGML_EMPTY
+ tagclass: MAPlike
+ contains:
+ icontains:
+ contained: SELECTlike
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike
+ canclose: FONTlike EMlike MATHlike Alike formula Plike DIVlike same
+ flags: endO
+ 91:OVERLAY
+ justify
+ 12 attributes:
+ 0:4:CLASS
+ 1:0:HEIGHT
+ 2:1:ID
+ 3:0:IMAGEMAP
+ 4:0:MD
+ 5:2:SRC
+ 6:0:STYLE
+ 7:0:TITLE
+ 8:0:UNITS
+ 9:0:WIDTH
+ 10:0:X
+ 11:0:Y
+ 2 attr_types
+ core
+ OVERLAY
+ contents: SGML_EMPTY
+ tagclass: HRlike
+ contains:
+ icontains:
+ contained: DIVlike
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike
+ canclose: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike Plike DIVlike LIlike ULlike BRlike APPLETlike same
+ flags: endO
+ 92:P
+ justify
+ 9 attributes:
+ 0:0:ALIGN
+ 1:4:CLASS
+ 2:0:CLEAR
+ 3:0:DIR
+ 4:1:ID
+ 5:0:LANG
+ 6:0:NOWRAP
+ 7:0:STYLE
+ 8:0:TITLE
+ 4 attr_types
+ align
+ core
+ i18n
+ P
+ contents: SGML_EMPTY
+ tagclass: Plike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike BRlike APPLETlike MAPlike
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: FORMlike DIVlike LIlike APPLETlike HRlike outer BODYlike DELlike
+ icontained: FONTlike EMlike MATHlike TRlike FORMlike Plike DIVlike LIlike ULlike APPLETlike HRlike outer BODYlike same DELlike
+ canclose: FONTlike EMlike MATHlike formula Plike same
+ flags: endO
+ 93:PARAM
+ justify
+ 18 attributes:
+ 0:0:ACCEPT
+ 1:0:ACCEPT-CHARSET
+ 2:0:ACCEPT-ENCODING
+ 3:4:CLASS
+ 4:0:CLEAR
+ 5:0:DATA
+ 6:0:DIR
+ 7:1:ID
+ 8:0:LANG
+ 9:0:NAME
+ 10:0:OBJECT
+ 11:0:REF
+ 12:0:STYLE
+ 13:0:TITLE
+ 14:0:TYPE
+ 15:0:VALUE
+ 16:0:VALUEREF
+ 17:0:VALUETYPE
+ 3 attr_types
+ core
+ i18n
+ PARAM
+ contents: SGML_EMPTY
+ tagclass: BRlike
+ contains:
+ icontains:
+ contained: Plike LIlike BRlike APPLETlike outer BODYlike
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike
+ canclose: TRlike SELECTlike Plike LIlike BRlike same
+ flags: endO
+ 94:PLAINTEXT
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN
+ contents: SGML_LITTERAL
+ tagclass: outer
+ contains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike outer BODYlike HEADstuff same
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike outer BODYlike HEADstuff same
+ contained: outer same
+ icontained: outer same
+ canclose: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike outer BODYlike
+ flags: endO
+ 95:PRE
+ nojustify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN
+ contents: SGML_MIXED
+ tagclass: DIVlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike BRlike APPLETlike HRlike MAPlike
+ icontains: EMlike MATHlike Alike formula SELECTlike BRlike APPLETlike HRlike MAPlike
+ contained: FORMlike DIVlike LIlike APPLETlike HRlike outer BODYlike DELlike
+ icontained: formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike APPLETlike HRlike outer BODYlike DELlike
+ canclose: EMlike MATHlike Alike formula Plike DIVlike LIlike same
+ flags:
+ 96:Q
+ justify
+ 8 attributes:
+ 0:2:CITE
+ 1:4:CLASS
+ 2:0:CLEAR
+ 3:0:DIR
+ 4:1:ID
+ 5:0:LANG
+ 6:0:STYLE
+ 7:0:TITLE
+ 3 attr_types
+ core
+ i18n
+ Q
+ contents: SGML_MIXED
+ tagclass: EMlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike BRlike APPLETlike MAPlike same
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: FONTlike EMlike MATHlike Alike FORMlike Plike DIVlike LIlike BRlike APPLETlike HRlike BODYlike same
+ icontained: FONTlike EMlike MATHlike Alike TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike HEADstuff same
+ canclose: FONTlike EMlike
+ flags:
+ 97:S
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN
+ contents: SGML_MIXED
+ tagclass: FONTlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike BRlike APPLETlike MAPlike same
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: FONTlike EMlike MATHlike Alike FORMlike Plike DIVlike LIlike BRlike APPLETlike HRlike BODYlike same
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike HEADstuff same
+ canclose: FONTlike
+ flags:
+ 98:SAMP
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN
+ contents: SGML_MIXED
+ tagclass: EMlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike BRlike APPLETlike MAPlike same
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: FONTlike EMlike MATHlike Alike FORMlike Plike DIVlike LIlike BRlike APPLETlike HRlike BODYlike same
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike HEADstuff same
+ canclose: EMlike
+ flags: nreie
+ 99:SCRIPT
+ justify
+ 16 attributes:
+ 0:0:CHARSET
+ 1:4:CLASS
+ 2:0:CLEAR
+ 3:0:DEFER
+ 4:0:DIR
+ 5:0:EVENT
+ 6:0:FOR
+ 7:1:ID
+ 8:0:LANG
+ 9:0:LANGUAGE
+ 10:0:NAME
+ 11:0:SCRIPTENGINE
+ 12:2:SRC
+ 13:0:STYLE
+ 14:0:TITLE
+ 15:0:TYPE
+ 3 attr_types
+ core
+ i18n
+ SCRIPT
+ contents: SGML_LITTERAL
+ tagclass: APPLETlike
+ contains:
+ icontains:
+ contained: FONTlike EMlike MATHlike Alike formula FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike HEADstuff
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike HEADstuff
+ canclose: FONTlike EMlike MATHlike Alike formula SELECTlike Plike LIlike ULlike BRlike APPLETlike HRlike same
+ flags:
+ 100:SECTION
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:DIR
+ 2:1:ID
+ 3:0:LANG
+ 4:0:ROLE
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN5
+ contents: SGML_MIXED
+ tagclass: DIVlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike FORMlike Plike DIVlike ULlike BRlike APPLETlike HRlike MAPlike same
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: TRlike FORMlike DIVlike LIlike APPLETlike HRlike outer BODYlike same DELlike
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike same DELlike
+ canclose: FONTlike EMlike MATHlike Alike formula Plike DIVlike same
+ flags: mafse
+ 101:SELECT
+ justify
+ 22 attributes:
+ 0:0:ALIGN
+ 1:4:CLASS
+ 2:0:CLEAR
+ 3:0:DIR
+ 4:0:DISABLED
+ 5:0:ERROR
+ 6:0:HEIGHT
+ 7:1:ID
+ 8:0:LANG
+ 9:0:MD
+ 10:0:MULTIPLE
+ 11:0:NAME
+ 12:0:NOTAB
+ 13:0:ONBLUR
+ 14:0:ONCHANGE
+ 15:0:ONFOCUS
+ 16:0:SIZE
+ 17:0:STYLE
+ 18:0:TABINDEX
+ 19:0:TITLE
+ 20:0:UNITS
+ 21:0:WIDTH
+ 4 attr_types
+ align
+ core
+ i18n
+ SELECT
+ contents: SGML_MIXED
+ tagclass: SELECTlike
+ contains: MAPlike
+ icontains: MAPlike
+ contained: FONTlike EMlike MATHlike Alike TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike outer BODYlike
+ canclose: FONTlike EMlike MATHlike Alike formula SELECTlike Plike LIlike ULlike same
+ flags: strict
+ 102:SHY
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN
+ contents: SGML_EMPTY
+ tagclass: BRlike
+ contains:
+ icontains:
+ contained: FONTlike EMlike MATHlike Alike formula FORMlike Plike DIVlike LIlike BRlike APPLETlike HRlike outer BODYlike
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike HEADstuff
+ canclose: FONTlike EMlike MATHlike Alike formula BRlike same
+ flags: endO
+ 103:SMALL
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN
+ contents: SGML_MIXED
+ tagclass: FONTlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike BRlike APPLETlike MAPlike same
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: FONTlike EMlike MATHlike Alike FORMlike Plike DIVlike LIlike BRlike APPLETlike HRlike BODYlike same
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike HEADstuff same
+ canclose: FONTlike
+ flags: mafse nreie
+ 104:SPAN
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN
+ contents: SGML_MIXED
+ tagclass: EMlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike BRlike APPLETlike MAPlike same
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: FONTlike EMlike MATHlike Alike FORMlike Plike DIVlike LIlike BRlike APPLETlike HRlike BODYlike same
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike HEADstuff same
+ canclose: FONTlike EMlike same
+ flags:
+ 105:SPOT
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN
+ contents: SGML_EMPTY
+ tagclass: Alike
+ contains:
+ icontains:
+ contained: FONTlike EMlike MATHlike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike outer BODYlike
+ icontained: FONTlike EMlike MATHlike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike outer BODYlike
+ canclose: Alike
+ flags: endO
+ 106:STRIKE
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN
+ contents: SGML_MIXED
+ tagclass: FONTlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike BRlike APPLETlike MAPlike same
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: FONTlike EMlike MATHlike Alike FORMlike Plike DIVlike LIlike BRlike APPLETlike HRlike BODYlike same
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike HEADstuff same
+ canclose: FONTlike
+ flags:
+ 107:STRONG
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN
+ contents: SGML_EMPTY
+ tagclass: EMlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike BRlike APPLETlike MAPlike same
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: FONTlike EMlike MATHlike Alike FORMlike Plike DIVlike LIlike BRlike APPLETlike HRlike BODYlike same
+ icontained: FONTlike EMlike MATHlike Alike TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike HEADstuff same
+ canclose: FONTlike EMlike
+ flags: nreie
+ 108:STYLE
+ justify
+ 9 attributes:
+ 0:4:CLASS
+ 1:0:DIR
+ 2:1:ID
+ 3:0:LANG
+ 4:0:MEDIA
+ 5:0:NOTATION
+ 6:0:STYLE
+ 7:0:TITLE
+ 8:0:TYPE
+ 3 attr_types
+ core
+ i18n
+ STYLE
+ contents: SGML_LITTERAL
+ tagclass: HEADstuff
+ contains:
+ icontains:
+ contained: FONTlike EMlike MATHlike Alike FORMlike Plike DIVlike APPLETlike HRlike outer BODYlike HEADstuff
+ icontained: FONTlike EMlike MATHlike Alike TRlike FORMlike Plike DIVlike LIlike ULlike APPLETlike HRlike outer BODYlike HEADstuff
+ canclose: FONTlike EMlike MATHlike Alike formula same
+ flags:
+ 109:SUB
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN
+ contents: SGML_MIXED
+ tagclass: MATHlike
+ contains: FONTlike EMlike MATHlike Alike formula SELECTlike BRlike APPLETlike MAPlike same
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: FONTlike EMlike MATHlike Alike formula FORMlike Plike DIVlike LIlike BRlike APPLETlike HRlike same
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike HEADstuff same
+ canclose: FONTlike EMlike MATHlike
+ flags:
+ 110:SUP
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN
+ contents: SGML_MIXED
+ tagclass: MATHlike
+ contains: FONTlike EMlike MATHlike Alike formula SELECTlike BRlike APPLETlike MAPlike same
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: FONTlike EMlike MATHlike Alike formula FORMlike Plike DIVlike LIlike BRlike APPLETlike HRlike same
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike HEADstuff same
+ canclose: FONTlike EMlike MATHlike
+ flags:
+ 111:TAB
+ justify
+ 11 attributes:
+ 0:0:ALIGN
+ 1:4:CLASS
+ 2:0:CLEAR
+ 3:0:DIR
+ 4:0:DP
+ 5:1:ID
+ 6:0:INDENT
+ 7:0:LANG
+ 8:0:STYLE
+ 9:0:TITLE
+ 10:0:TO
+ 4 attr_types
+ align
+ core
+ i18n
+ TAB
+ contents: SGML_EMPTY
+ tagclass: BRlike
+ contains:
+ icontains:
+ contained: FONTlike EMlike MATHlike Alike FORMlike Plike DIVlike LIlike BRlike APPLETlike HRlike outer BODYlike
+ icontained: FONTlike EMlike MATHlike Alike TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer HEADstuff
+ canclose: FONTlike
+ flags: endO
+ 112:TABLE
+ justify
+ 22 attributes:
+ 0:0:ALIGN
+ 1:2:BACKGROUND
+ 2:0:BORDER
+ 3:0:CELLPADDING
+ 4:0:CELLSPACING
+ 5:4:CLASS
+ 6:0:CLEAR
+ 7:0:COLS
+ 8:0:COLSPEC
+ 9:0:DIR
+ 10:0:DP
+ 11:0:FRAME
+ 12:1:ID
+ 13:0:LANG
+ 14:0:NOFLOW
+ 15:0:NOWRAP
+ 16:0:RULES
+ 17:0:STYLE
+ 18:0:SUMMARY
+ 19:0:TITLE
+ 20:0:UNITS
+ 21:0:WIDTH
+ 5 attr_types
+ align
+ core
+ events
+ i18n
+ TABLE
+ contents: SGML_MIXED
+ tagclass: ULlike
+ contains: TRlike SELECTlike FORMlike Plike BRlike APPLETlike HRlike MAPlike
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: FORMlike DIVlike LIlike APPLETlike HRlike outer BODYlike DELlike
+ icontained: FONTlike EMlike MATHlike TRlike FORMlike Plike DIVlike LIlike ULlike APPLETlike HRlike outer BODYlike same DELlike
+ canclose: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike Plike LIlike HRlike MAPlike same
+ flags:
+ 113:TBODY
+ justify
+ 13 attributes:
+ 0:0:ALIGN
+ 1:0:CHAR
+ 2:0:CHAROFF
+ 3:4:CLASS
+ 4:0:CLEAR
+ 5:0:DIR
+ 6:0:DP
+ 7:1:ID
+ 8:0:LANG
+ 9:0:NOWRAP
+ 10:0:STYLE
+ 11:0:TITLE
+ 12:0:VALIGN
+ 5 attr_types
+ cellalign
+ core
+ events
+ i18n
+ TR
+ contents: SGML_EMPTY
+ tagclass: TRlike
+ contains: TRlike
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: FORMlike ULlike
+ icontained: FONTlike EMlike MATHlike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike same
+ canclose: FONTlike EMlike MATHlike Alike formula SELECTlike Plike DIVlike LIlike HRlike MAPlike same
+ flags: endO startO
+ 114:TD
+ justify
+ 23 attributes:
+ 0:0:ABBR
+ 1:0:ALIGN
+ 2:0:AXES
+ 3:0:AXIS
+ 4:2:BACKGROUND
+ 5:0:CHAR
+ 6:0:CHAROFF
+ 7:4:CLASS
+ 8:0:CLEAR
+ 9:0:COLSPAN
+ 10:0:DIR
+ 11:0:DP
+ 12:0:HEADERS
+ 13:0:HEIGHT
+ 14:1:ID
+ 15:0:LANG
+ 16:0:NOWRAP
+ 17:0:ROWSPAN
+ 18:0:SCOPE
+ 19:0:STYLE
+ 20:0:TITLE
+ 21:0:VALIGN
+ 22:0:WIDTH
+ 4 attr_types
+ cellalign
+ core
+ i18n
+ TD
+ contents: SGML_EMPTY
+ tagclass: LIlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike FORMlike Plike DIVlike ULlike BRlike APPLETlike HRlike MAPlike
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: TRlike
+ icontained: FONTlike EMlike MATHlike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike same
+ canclose: FONTlike EMlike MATHlike Alike formula SELECTlike Plike DIVlike LIlike HRlike MAPlike same
+ flags: endO
+ 115:TEXTAREA
+ justify
+ 22 attributes:
+ 0:0:ACCEPT-CHARSET
+ 1:0:ACCESSKEY
+ 2:0:ALIGN
+ 3:4:CLASS
+ 4:0:CLEAR
+ 5:0:COLS
+ 6:0:DIR
+ 7:0:DISABLED
+ 8:0:ERROR
+ 9:1:ID
+ 10:0:LANG
+ 11:0:NAME
+ 12:0:NOTAB
+ 13:0:ONBLUR
+ 14:0:ONCHANGE
+ 15:0:ONFOCUS
+ 16:0:ONSELECT
+ 17:0:READONLY
+ 18:0:ROWS
+ 19:0:STYLE
+ 20:0:TABINDEX
+ 21:0:TITLE
+ 5 attr_types
+ align
+ core
+ events
+ i18n
+ TEXTAREA
+ contents: SGML_LITTERAL
+ tagclass: SELECTlike
+ contains:
+ icontains:
+ contained: FONTlike EMlike MATHlike Alike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike outer BODYlike
+ canclose: FONTlike EMlike MATHlike Alike formula SELECTlike Plike LIlike ULlike same
+ flags: nolyspcl
+ 116:TEXTFLOW
+ justify
+ 14 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DATA
+ 3:0:DIR
+ 4:1:ID
+ 5:0:LANG
+ 6:0:NAME
+ 7:0:OBJECT
+ 8:0:REF
+ 9:0:STYLE
+ 10:0:TITLE
+ 11:0:TYPE
+ 12:0:VALUE
+ 13:0:VALUETYPE
+ 3 attr_types
+ core
+ i18n
+ BODYTEXT
+ contents: SGML_MIXED
+ tagclass: BODYlike
+ contains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike ULlike BRlike APPLETlike HRlike MAPlike same DELlike
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike outer same DELlike
+ contained: formula TRlike FORMlike Plike DIVlike LIlike BRlike APPLETlike HRlike outer same
+ icontained: FONTlike EMlike MATHlike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike same
+ canclose: FONTlike EMlike MATHlike Alike BRlike APPLETlike MAPlike outer same
+ flags: endO startO
+ 117:TFOOT
+ justify
+ 13 attributes:
+ 0:0:ALIGN
+ 1:0:CHAR
+ 2:0:CHAROFF
+ 3:4:CLASS
+ 4:0:CLEAR
+ 5:0:DIR
+ 6:0:DP
+ 7:1:ID
+ 8:0:LANG
+ 9:0:NOWRAP
+ 10:0:STYLE
+ 11:0:TITLE
+ 12:0:VALIGN
+ 5 attr_types
+ cellalign
+ core
+ events
+ i18n
+ TR
+ contents: SGML_EMPTY
+ tagclass: TRlike
+ contains: TRlike
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: ULlike
+ icontained: FONTlike EMlike MATHlike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike same
+ canclose: FONTlike EMlike MATHlike Alike formula SELECTlike Plike DIVlike LIlike ULlike HRlike MAPlike same
+ flags: endO
+ 118:TH
+ justify
+ 23 attributes:
+ 0:0:ABBR
+ 1:0:ALIGN
+ 2:0:AXES
+ 3:0:AXIS
+ 4:2:BACKGROUND
+ 5:0:CHAR
+ 6:0:CHAROFF
+ 7:4:CLASS
+ 8:0:CLEAR
+ 9:0:COLSPAN
+ 10:0:DIR
+ 11:0:DP
+ 12:0:HEADERS
+ 13:0:HEIGHT
+ 14:1:ID
+ 15:0:LANG
+ 16:0:NOWRAP
+ 17:0:ROWSPAN
+ 18:0:SCOPE
+ 19:0:STYLE
+ 20:0:TITLE
+ 21:0:VALIGN
+ 22:0:WIDTH
+ 4 attr_types
+ cellalign
+ core
+ i18n
+ TD
+ contents: SGML_EMPTY
+ tagclass: LIlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike FORMlike Plike DIVlike ULlike BRlike APPLETlike HRlike MAPlike
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike
+ contained: TRlike
+ icontained: FONTlike EMlike MATHlike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike same
+ canclose: FONTlike EMlike MATHlike Alike formula SELECTlike Plike DIVlike LIlike ULlike HRlike MAPlike same
+ flags: endO
+ 119:THEAD
+ justify
+ 13 attributes:
+ 0:0:ALIGN
+ 1:0:CHAR
+ 2:0:CHAROFF
+ 3:4:CLASS
+ 4:0:CLEAR
+ 5:0:DIR
+ 6:0:DP
+ 7:1:ID
+ 8:0:LANG
+ 9:0:NOWRAP
+ 10:0:STYLE
+ 11:0:TITLE
+ 12:0:VALIGN
+ 5 attr_types
+ cellalign
+ core
+ events
+ i18n
+ TR
+ contents: SGML_EMPTY
+ tagclass: TRlike
+ contains: TRlike
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: ULlike
+ icontained: FONTlike EMlike MATHlike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike same
+ canclose: FONTlike EMlike MATHlike Alike formula SELECTlike Plike DIVlike LIlike ULlike HRlike MAPlike same
+ flags: endO
+ 120:TITLE
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN
+ contents: SGML_RCDATA
+ tagclass: HEADstuff
+ contains:
+ icontains:
+ contained: outer HEADstuff
+ icontained: outer HEADstuff
+ canclose: FONTlike EMlike MATHlike Alike formula Plike DIVlike
+ flags: mafse strict
+ 121:TR
+ justify
+ 13 attributes:
+ 0:0:ALIGN
+ 1:0:CHAR
+ 2:0:CHAROFF
+ 3:4:CLASS
+ 4:0:CLEAR
+ 5:0:DIR
+ 6:0:DP
+ 7:1:ID
+ 8:0:LANG
+ 9:0:NOWRAP
+ 10:0:STYLE
+ 11:0:TITLE
+ 12:0:VALIGN
+ 5 attr_types
+ cellalign
+ core
+ events
+ i18n
+ TR
+ contents: SGML_EMPTY
+ tagclass: TRlike
+ contains: LIlike
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: TRlike ULlike
+ icontained: FONTlike EMlike MATHlike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike same
+ canclose: FONTlike EMlike MATHlike Alike formula SELECTlike Plike DIVlike LIlike HRlike MAPlike same
+ flags: endO
+ 122:TT
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN
+ contents: SGML_EMPTY
+ tagclass: FONTlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike BRlike APPLETlike MAPlike same
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: FONTlike EMlike MATHlike Alike FORMlike Plike DIVlike LIlike BRlike APPLETlike HRlike BODYlike same
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike HEADstuff same
+ canclose: FONTlike
+ flags: nreie
+ 123:U
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN
+ contents: SGML_EMPTY
+ tagclass: FONTlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike BRlike APPLETlike MAPlike same
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: FONTlike EMlike MATHlike Alike FORMlike Plike DIVlike LIlike BRlike APPLETlike HRlike BODYlike same
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike HEADstuff same
+ canclose: FONTlike
+ flags: mafse nreie
+ 124:UL
+ justify
+ 14 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:COMPACT
+ 3:0:DINGBAT
+ 4:0:DIR
+ 5:1:ID
+ 6:0:LANG
+ 7:0:MD
+ 8:0:PLAIN
+ 9:2:SRC
+ 10:0:STYLE
+ 11:0:TITLE
+ 12:0:TYPE
+ 13:0:WRAP
+ 3 attr_types
+ core
+ i18n
+ UL
+ contents: SGML_MIXED
+ tagclass: ULlike
+ contains: FORMlike LIlike HRlike MAPlike
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: FORMlike DIVlike LIlike APPLETlike HRlike outer BODYlike DELlike
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike same DELlike
+ canclose: FONTlike EMlike MATHlike Alike formula SELECTlike Plike DIVlike LIlike same
+ flags:
+ 125:VAR
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN
+ contents: SGML_MIXED
+ tagclass: EMlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike BRlike APPLETlike MAPlike same
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: FONTlike EMlike MATHlike Alike FORMlike Plike DIVlike LIlike BRlike APPLETlike HRlike BODYlike same
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike HEADstuff same
+ canclose: FONTlike
+ flags:
+ 126:WBR
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN
+ contents: SGML_EMPTY
+ tagclass: FONTlike
+ contains:
+ icontains:
+ contained: FONTlike EMlike MATHlike Alike FORMlike Plike DIVlike LIlike BRlike APPLETlike HRlike outer BODYlike
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike HEADstuff
+ canclose: FONTlike EMlike MATHlike Alike formula BRlike same
+ flags: endO
+ 127:XMP
+ nojustify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN
+ contents: SGML_LITTERAL
+ tagclass: ULlike
+ contains:
+ icontains:
+ contained: TRlike SELECTlike FORMlike Plike DIVlike LIlike APPLETlike HRlike outer BODYlike DELlike
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike APPLETlike HRlike outer BODYlike DELlike
+ canclose: FONTlike EMlike MATHlike Alike formula SELECTlike Plike DIVlike LIlike MAPlike
+ flags: endO
diff --git a/WWW/Library/Implementation/src1_HTMLDTD.h b/WWW/Library/Implementation/src1_HTMLDTD.h
new file mode 100644
index 0000000..39c9594
--- /dev/null
+++ b/WWW/Library/Implementation/src1_HTMLDTD.h
@@ -0,0 +1,2478 @@
+/* $LynxId: src1_HTMLDTD.h,v 1.49 2022/09/30 00:03:49 tom Exp $ */
+#ifndef src_HTMLDTD_H1
+#define src_HTMLDTD_H1 1
+
+#ifndef once_HTMLDTD
+#define once_HTMLDTD 1
+
+#define T_A 0x00008,0x0B007,0x0FF17,0x37787,0x77BA7,0x8604F,0x00014
+#define T_ABBR 0x00002,0x8B04F,0x8FFFF,0xA778F,0xF7FBF,0x00003,0x00000
+#define T_ACRONYM 0x00002,0x8B04F,0x8FFFF,0xA778F,0xF7FBF,0x00003,0x00000
+#define T_ADDRESS 0x00200,0x0F14F,0x8FFFF,0x136680,0x1B6FAF,0x80317,0x00000
+#define T_APPLET 0x02000,0x0B0CF,0x8FFFF,0x37F9F,0xB7FBF,0x8300F,0x00000
+#define T_AREA 0x08000,0x00000,0x00000,0x08000,0x3FFFF,0x00F1F,0x00001
+#define T_ARTICLE 0x00200,0x8FBCF,0x8FFFF,0x1B66A0,0x1B7FFF,0x8031F,0x00004
+#define T_ASIDE 0x00200,0x8FBCF,0x8FFFF,0x1B66A0,0x1B7FFF,0x8031F,0x00004
+#define T_AU 0x00002,0x8B04F,0x8FFFF,0xA778F,0xF7FBF,0x00003,0x00000
+#define T_AUTHOR 0x00002,0x8B04F,0x8FFFF,0xA778F,0xF7FBF,0x00003,0x00000
+#define T_B 0x00001,0x8B04F,0xAFFFF,0xA778F,0xF7FBF,0x00001,0x00014
+#define T_BANNER 0x00200,0x0FB8F,0x0FFFF,0x130000,0x130000,0x8031F,0x00000
+#define T_BASE 0x40000,0x00000,0x00000,0x50000,0x50000,0x8000F,0x00001
+#define T_BASEFONT 0x01000,0x00000,0x00000,0x377AF,0x37FAF,0x8F000,0x00001
+#define T_BDO 0x00100,0x0B04F,0x8FFFF,0x136680,0x1B6FAF,0x0033F,0x00000
+#define T_BGSOUND 0x01000,0x00000,0x00000,0x777AF,0x77FAF,0x8730F,0x00001
+#define T_BIG 0x00001,0x8B04F,0x8FFFF,0xA778F,0xF7FBF,0x00001,0x00014
+#define T_BLINK 0x00001,0x8B04F,0x8FFFF,0xA778F,0xF7FAF,0x00001,0x00014
+#define T_BLOCKQUOTE 0x00200,0xAFBCF,0xAFFFF,0x1B6680,0x1B6FAF,0x8031F,0x00000
+#define T_BODY 0x20000,0x12FB8F,0x12FFFF,0x30000,0x30000,0xDFF7F,0x00003
+#define T_BODYTEXT 0x20000,0x10FB8F,0x1AFFFF,0x30200,0xB7FAF,0x8F17F,0x00003
+#define T_BQ 0x00200,0xAFBCF,0xAFFFF,0x1B6680,0x1B6FAF,0x8031F,0x00000
+#define T_BR 0x01000,0x00000,0x00000,0x377BF,0x77FBF,0x8101F,0x00001
+#define T_BUTTON 0x02000,0x0BB07,0x0FF37,0x0378F,0x37FBF,0x8115F,0x00000
+#define T_CAPTION 0x00100,0x0B04F,0x8FFFF,0x106A00,0x1B6FA7,0x8035F,0x00000
+#define T_CENTER 0x00200,0x8FBCF,0x8FFFF,0x1B6680,0x1B6FA7,0x8071F,0x00000
+#define T_CITE 0x00002,0x8B04F,0x8FFFF,0xA778F,0xF7FBF,0x00002,0x00010
+#define T_CODE 0x00002,0x8B04F,0x8FFFF,0xA778F,0xF7FBF,0x00002,0x00000
+#define T_COL 0x04000,0x00000,0x00000,0x00820,0x36FA7,0x88F5F,0x00001
+#define T_COLGROUP 0x00020,0x04000,0x04000,0x00800,0x36FA7,0x8875F,0x00001
+#define T_COMMENT 0x00004,0x00000,0x00000,0xA77AF,0x7FFFF,0x00003,0x00000
+#define T_CREDIT 0x00100,0x0B04F,0x8FFFF,0x106A00,0x1B7FBF,0x8030F,0x00000
+#define T_DD 0x00400,0x0FBCF,0x8FFFF,0x00800,0xB6FFF,0x8071F,0x00001
+#define T_DEL 0x00002,0x8B04F,0x8FEFF,0x87F8F,0xF7FBF,0x100003,0x00000
+#define T_DEL_2 0x100000,0x8BBCF,0x8FFFF,0xA7F8F,0xF7FBF,0x100003,0x00000
+#define T_DFN 0x00002,0x8B0CF,0x8FFFF,0x8778F,0xF7FBF,0x00003,0x00000
+#define T_DIR 0x00800,0x0B400,0x0F75F,0x137680,0x136FB7,0x84F7F,0x00000
+#define T_DIV 0x00200,0x8FBCF,0x8FFFF,0x1B66A0,0x1B7FFF,0x80317,0x00004
+#define T_DL 0x00800,0x0C480,0x8FFFF,0x136680,0x1B7FB7,0x00757,0x00000
+#define T_DLC 0x00800,0x0C480,0x8FFFF,0x136680,0x1B7FB7,0x0075F,0x00000
+#define T_DT 0x00400,0x0B04F,0x0B1FF,0x00800,0x17FFF,0x8071F,0x00001
+#define T_EM 0x00002,0x8B04F,0x8FFFF,0xA778F,0xF7FAF,0x00003,0x00010
+#define T_EMBED 0x02000,0x8F107,0x8FFF7,0xB6FBF,0xB7FBF,0x1FF7F,0x00001
+#define T_FIELDSET 0x00200,0x8FB4F,0x8FF7F,0x186787,0x1B7FF7,0x8805F,0x00000
+#define T_FIG 0x00200,0x0FB00,0x8FFFF,0x136680,0x1B6FBF,0x8834F,0x00000
+#define T_FIGURE 0x00200,0x8FBCF,0x8FFFF,0x1B66A0,0x1B7FFF,0x8031F,0x00004
+#define T_FN 0x00200,0x8FBCF,0x8FFFF,0x1B6680,0x1B7EBF,0x8114F,0x00000
+#define T_FONT 0x00001,0x8B04F,0x8FFFF,0xB778F,0xF7FBF,0x00001,0x00014
+#define T_FOOTER 0x00200,0x8FBCF,0x8FFFF,0x1B66A0,0x1B7FFF,0x8031F,0x00004
+#define T_FORM 0x00080,0x0FF6F,0x0FF7F,0x136E07,0x132F07,0x88DFF,0x00000
+#define T_FRAME 0x10000,0x00000,0x00000,0x10000,0x10000,0x9FFFF,0x00001
+#define T_FRAMESET 0x10000,0x90000,0x90000,0x90000,0x93000,0x9FFFF,0x00000
+#define T_H1 0x00100,0x0B04F,0x0B05F,0x136680,0x137FAF,0x80117,0x00000
+#define T_H2 0x00100,0x0B04F,0x0B05F,0x136680,0x137FAF,0x80117,0x00000
+#define T_H3 0x00100,0x0B04F,0x0B05F,0x136680,0x137FAF,0x80117,0x00000
+#define T_H4 0x00100,0x0B04F,0x0B05F,0x136680,0x137FAF,0x80117,0x00000
+#define T_H5 0x00100,0x0B04F,0x0B05F,0x136680,0x137FAF,0x80117,0x00000
+#define T_H6 0x00100,0x0B04F,0x0B05F,0x136680,0x137FAF,0x80117,0x00000
+#define T_HEAD 0x40000,0x4F000,0x47000,0x10000,0x10000,0x9FF7F,0x00007
+#define T_HEADER 0x00200,0x8FBCF,0x8FFFF,0x1B66A0,0x1B7FFF,0x8031F,0x00004
+#define T_HR 0x04000,0x00000,0x00000,0x3FE80,0x3FFBF,0x87F37,0x00001
+#define T_HTML 0x10000,0x7FB8F,0x7FFFF,0x00000,0x00000,0x1FFFF,0x00003
+#define T_HY 0x01000,0x00000,0x00000,0x3779F,0x77FBF,0x8101F,0x00001
+#define T_I 0x00001,0x8B04F,0x8FFFF,0xA778F,0xF7FBF,0x00001,0x00014
+#define T_IFRAME 0x02000,0x8FBCF,0x8FFFF,0xB679F,0xB6FBF,0xD315F,0x00000
+#define T_IMG 0x01000,0x00000,0x00000,0x3779F,0x37FBF,0x80000,0x00001
+#define T_INPUT 0x00040,0x00000,0x00000,0x03F87,0x37F87,0x8904F,0x00001
+#define T_INS 0x00002,0x8B04F,0x8FEFF,0x87F8F,0xF7FBF,0x100003,0x00000
+#define T_INS_2 0x100000,0x8BBCF,0x8FFFF,0xA7F8F,0xF7FBF,0x100003,0x00000
+#define T_ISINDEX 0x08000,0x00000,0x00000,0x7778F,0x7FFAF,0x80007,0x00001
+#define T_KBD 0x00002,0x00000,0x00000,0x2778F,0x77FBF,0x00003,0x00000
+#define T_KEYGEN 0x00040,0x00000,0x00000,0x07FB7,0x37FB7,0x80070,0x00001
+#define T_LABEL 0x00002,0x0304F,0x0FFFF,0x0679F,0x36FBF,0x00007,0x00000
+#define T_LEGEND 0x00002,0x0B04F,0x8FF7F,0x00200,0xB7FA7,0x00003,0x00000
+#define T_LH 0x00400,0x0BB7F,0x8FFFF,0x00800,0x97FFF,0x8071F,0x00001
+#define T_LI 0x00400,0x0BBFF,0x8FFFF,0x00800,0x97FFF,0x8071F,0x00001
+#define T_LINK 0x08000,0x00000,0x00000,0x50000,0x50000,0x0FF7F,0x00001
+#define T_LISTING 0x00800,0x00000,0x00000,0x136600,0x136F00,0x80F1F,0x00000
+#define T_MAIN 0x00200,0x8FBCF,0x8FFFF,0x1B66A0,0x1B7FFF,0x8031F,0x00004
+#define T_MAP 0x08000,0x08000,0x08000,0x37FCF,0x37FBF,0x0051F,0x00000
+#define T_MARQUEE 0x04000,0x0000F,0x8F01F,0x37787,0xB7FA7,0x8301C,0x00000
+#define T_MATH 0x00004,0x0B05F,0x8FFFF,0x2778F,0xF7FBF,0x0001F,0x00000
+#define T_MENU 0x00800,0x0B400,0x0F75F,0x117680,0x136FB7,0x88F7F,0x00000
+#define T_META 0x08000,0x00000,0x00000,0x50000,0x50000,0x0FF7F,0x00001
+#define T_NAV 0x00200,0x8FBCF,0x8FFFF,0x1B66A0,0x1B7FFF,0x8031F,0x00004
+#define T_NEXTID 0x01000,0x00000,0x00000,0x50000,0x1FFF7,0x00001,0x00001
+#define T_NOFRAMES 0x20000,0x12FB8F,0x10FFFF,0x17000,0x17000,0x0CF5F,0x00000
+#define T_NOTE 0x00200,0x0BBAF,0x8FFFF,0x1376B0,0x1B7FFF,0x8031F,0x00000
+#define T_OBJECT 0x02000,0x8FBCF,0x8FFFF,0xB679F,0xB6FBF,0x83D5F,0x00020
+#define T_OL 0x00800,0x0C400,0x8FFFF,0x137680,0x1B7FB7,0x88F7F,0x00000
+#define T_OPTION 0x08000,0x00000,0x00000,0x00040,0x37FFF,0x8031F,0x00001
+#define T_OVERLAY 0x04000,0x00000,0x00000,0x00200,0x37FBF,0x83F7F,0x00001
+#define T_P 0x00100,0x0B04F,0x8FFFF,0x136680,0x1B6FA7,0x80117,0x00001
+#define T_PARAM 0x01000,0x00000,0x00000,0x33500,0x37FFF,0x81560,0x00001
+#define T_PLAINTEXT 0x10000,0xFFFFF,0xFFFFF,0x90000,0x90000,0x3FFFF,0x00001
+#define T_PRE 0x00200,0x0F04F,0x0F05E,0x136680,0x136FF0,0x8071E,0x00000
+#define T_Q 0x00002,0x8B04F,0x8FFFF,0xA778F,0xF7FAF,0x00003,0x00000
+#define T_S 0x00001,0x8B04F,0x8FFFF,0xA778F,0xF7FBF,0x00001,0x00000
+#define T_SAMP 0x00002,0x8B04F,0x8FFFF,0xA778F,0xF7FBF,0x00002,0x00010
+#define T_SCRIPT 0x02000,0x00000,0x00000,0x77F9F,0x77FFF,0x87D5F,0x00000
+#define T_SECTION 0x00200,0x8FBCF,0x8FFFF,0x1B66A0,0x1B7FFF,0x8031F,0x00004
+#define T_SELECT 0x00040,0x08000,0x08000,0x03FAF,0x33FBF,0x80D5F,0x00008
+#define T_SHY 0x01000,0x00000,0x00000,0x3779F,0x77FBF,0x8101F,0x00001
+#define T_SMALL 0x00001,0x8B04F,0x8FFFF,0xA778F,0xF7FBF,0x00001,0x00014
+#define T_SPAN 0x00002,0x8B04F,0x8FFFF,0xA778F,0xF7FBF,0x80003,0x00000
+#define T_SPOT 0x00008,0x00000,0x00000,0x3FFF7,0x3FFF7,0x00008,0x00001
+#define T_STRIKE 0x00001,0x8B04F,0x8FFFF,0xA778F,0xF7FBF,0x00001,0x00000
+#define T_STRONG 0x00002,0x8B04F,0x8FFFF,0xA778F,0xF7FAF,0x00003,0x00010
+#define T_STYLE 0x40000,0x00000,0x00000,0x7638F,0x76FAF,0x8001F,0x00000
+#define T_SUB 0x00004,0x8B05F,0x8FFFF,0x8779F,0xF7FBF,0x00007,0x00000
+#define T_SUP 0x00004,0x8B05F,0x8FFFF,0x8779F,0xF7FBF,0x00007,0x00000
+#define T_TAB 0x01000,0x00000,0x00000,0x3778F,0x57FAF,0x00001,0x00001
+#define T_TABLE 0x00800,0x0F1E0,0x8FFFF,0x136680,0x1B6FA7,0x8C57F,0x00000
+#define T_TBODY 0x00020,0x00020,0x8FFFF,0x00880,0xB7FB7,0x8C75F,0x00003
+#define T_TD 0x00400,0x0FBCF,0x8FFFF,0x00020,0xB7FB7,0x8C75F,0x00001
+#define T_TEXTAREA 0x00040,0x00000,0x00000,0x07F8F,0x33FBF,0x80D5F,0x00040
+#define T_TEXTFLOW 0x20000,0x18FBFF,0x19FFFF,0x977B0,0xB7FB7,0x9B00F,0x00003
+#define T_TFOOT 0x00020,0x00020,0x8FFFF,0x00800,0xB7FB7,0x8CF5F,0x00001
+#define T_TH 0x00400,0x0FBCF,0x0FFFF,0x00020,0xB7FB7,0x8CF5F,0x00001
+#define T_THEAD 0x00020,0x00020,0x8FFFF,0x00800,0xB7FB7,0x8CF5F,0x00001
+#define T_TITLE 0x40000,0x00000,0x00000,0x50000,0x50000,0x0031F,0x0000C
+#define T_TR 0x00020,0x00400,0x8FFFF,0x00820,0xB7FB7,0x8C75F,0x00001
+#define T_TT 0x00001,0x8B04F,0x8FFFF,0xA778F,0xF7FBF,0x00001,0x00010
+#define T_U 0x00001,0x8B04F,0x8FFFF,0xA778F,0xF7FBF,0x00001,0x00014
+#define T_UL 0x00800,0x0C480,0x8FFFF,0x136680,0x1B7FFF,0x8075F,0x00000
+#define T_VAR 0x00002,0x8B04F,0x8FFFF,0xA778F,0xF7FBF,0x00001,0x00000
+#define T_WBR 0x00001,0x00000,0x00000,0x3778F,0x77FBF,0x8101F,0x00001
+#define T_XMP 0x00800,0x00000,0x00000,0x1367E0,0x136FFF,0x0875F,0x00001
+#define T_OBJECT_PCDATA 0x02000,0x8FBCF,0x8FFFF,0xB679F,0xB6FBF,0x83D5F,0x00008
+#define T__UNREC_ 0x00000,0x00000,0x00000,0x00000,0x00000,0x00000,0x00000
+#ifdef USE_PRETTYSRC
+# define N HTMLA_NORMAL
+# define i HTMLA_ANAME
+# define h HTMLA_HREF
+# define c HTMLA_CLASS
+# define x HTMLA_AUXCLASS
+# define T(t) , t
+#else
+# define T(t) /*nothing */
+#endif
+/* *INDENT-OFF* */
+
+#define ATTR_TYPE(name) #name, name##_attr_list
+
+/* generic attributes, used in different tags */
+static const attr core_attr_list[] = {
+ { "CLASS" T(c) },
+ { "ID" T(i) },
+ { "STYLE" T(N) },
+ { "TITLE" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType core_attr_type[] = {
+ { ATTR_TYPE(core) },
+ { 0, 0 },
+};
+
+static const attr i18n_attr_list[] = {
+ { "DIR" T(N) },
+ { "LANG" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType i18n_attr_type[] = {
+ { ATTR_TYPE(i18n) },
+ { 0, 0 },
+};
+
+static const attr events_attr_list[] = {
+ { "ONCLICK" T(N) },
+ { "ONDBLCLICK" T(N) },
+ { "ONKEYDOWN" T(N) },
+ { "ONKEYPRESS" T(N) },
+ { "ONKEYUP" T(N) },
+ { "ONMOUSEDOWN" T(N) },
+ { "ONMOUSEMOVE" T(N) },
+ { "ONMOUSEOUT" T(N) },
+ { "ONMOUSEOVER" T(N) },
+ { "ONMOUSEUP" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType events_attr_type[] = {
+ { ATTR_TYPE(events) },
+ { 0, 0 },
+};
+
+static const attr align_attr_list[] = {
+ { "ALIGN" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType align_attr_type[] = {
+ { ATTR_TYPE(align) },
+ { 0, 0 },
+};
+
+static const attr cellalign_attr_list[] = {
+ { "ALIGN" T(N) },
+ { "CHAR" T(N) },
+ { "CHAROFF" T(N) },
+ { "VALIGN" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType cellalign_attr_type[] = {
+ { ATTR_TYPE(cellalign) },
+ { 0, 0 },
+};
+
+static const attr bgcolor_attr_list[] = {
+ { "BGCOLOR" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType bgcolor_attr_type[] = {
+ { ATTR_TYPE(bgcolor) },
+ { 0, 0 },
+};
+
+
+/* tables defining attributes per-tag in terms of generic attributes (editable) */
+static const attr A_attr_list[] = {
+ { "ACCESSKEY" T(N) },
+ { "CHARSET" T(N) },
+ { "CLEAR" T(N) },
+ { "COORDS" T(N) },
+ { "HREF" T(h) },
+ { "HREFLANG" T(N) },
+ { "ISMAP" T(N) },
+ { "MD" T(N) },
+ { "NAME" T(i) },
+ { "NOTAB" T(N) },
+ { "ONBLUR" T(N) },
+ { "ONFOCUS" T(N) },
+ { "REL" T(N) },
+ { "REV" T(N) },
+ { "SHAPE" T(N) },
+ { "TABINDEX" T(N) },
+ { "TARGET" T(N) },
+ { "TYPE" T(N) },
+ { "URN" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType A_attr_type[] = {
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(events) },
+ { ATTR_TYPE(i18n) },
+ { ATTR_TYPE(A) },
+ { 0, 0 },
+};
+
+static const attr ADDRESS_attr_list[] = {
+ { "CLEAR" T(N) },
+ { "NOWRAP" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType ADDRESS_attr_type[] = {
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(i18n) },
+ { ATTR_TYPE(ADDRESS) },
+ { 0, 0 },
+};
+
+static const attr APPLET_attr_list[] = {
+ { "ALT" T(N) },
+ { "CLEAR" T(N) },
+ { "CODE" T(N) },
+ { "CODEBASE" T(h) },
+ { "DOWNLOAD" T(N) },
+ { "HEIGHT" T(N) },
+ { "HSPACE" T(N) },
+ { "NAME" T(i) },
+ { "VSPACE" T(N) },
+ { "WIDTH" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType APPLET_attr_type[] = {
+ { ATTR_TYPE(align) },
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(i18n) },
+ { ATTR_TYPE(APPLET) },
+ { 0, 0 },
+};
+
+static const attr AREA_attr_list[] = {
+ { "ACCESSKEY" T(N) },
+ { "ALT" T(N) },
+ { "CLEAR" T(N) },
+ { "COORDS" T(N) },
+ { "HREF" T(h) },
+ { "NOHREF" T(N) },
+ { "NOTAB" T(N) },
+ { "ONBLUR" T(N) },
+ { "ONFOCUS" T(N) },
+ { "SHAPE" T(N) },
+ { "TABINDEX" T(N) },
+ { "TARGET" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType AREA_attr_type[] = {
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(events) },
+ { ATTR_TYPE(i18n) },
+ { ATTR_TYPE(AREA) },
+ { 0, 0 },
+};
+
+static const attr BASE_attr_list[] = {
+ { "HREF" T(h) },
+ { "TARGET" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType BASE_attr_type[] = {
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(BASE) },
+ { 0, 0 },
+};
+
+static const attr BGSOUND_attr_list[] = {
+ { "CLEAR" T(N) },
+ { "LOOP" T(N) },
+ { "SRC" T(h) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType BGSOUND_attr_type[] = {
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(i18n) },
+ { ATTR_TYPE(BGSOUND) },
+ { 0, 0 },
+};
+
+static const attr BODY_attr_list[] = {
+ { "ALINK" T(N) },
+ { "BACKGROUND" T(h) },
+ { "CLEAR" T(N) },
+ { "LINK" T(N) },
+ { "ONLOAD" T(N) },
+ { "ONUNLOAD" T(N) },
+ { "TEXT" T(N) },
+ { "VLINK" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType BODY_attr_type[] = {
+ { ATTR_TYPE(bgcolor) },
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(i18n) },
+ { ATTR_TYPE(BODY) },
+ { 0, 0 },
+};
+
+static const attr BODYTEXT_attr_list[] = {
+ { "CLEAR" T(N) },
+ { "DATA" T(N) },
+ { "NAME" T(N) },
+ { "OBJECT" T(N) },
+ { "REF" T(N) },
+ { "TYPE" T(N) },
+ { "VALUE" T(N) },
+ { "VALUETYPE" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType BODYTEXT_attr_type[] = {
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(i18n) },
+ { ATTR_TYPE(BODYTEXT) },
+ { 0, 0 },
+};
+
+static const attr BQ_attr_list[] = {
+ { "CITE" T(h) },
+ { "CLEAR" T(N) },
+ { "NOWRAP" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType BQ_attr_type[] = {
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(i18n) },
+ { ATTR_TYPE(BQ) },
+ { 0, 0 },
+};
+
+static const attr BUTTON_attr_list[] = {
+ { "ACCESSKEY" T(N) },
+ { "CLEAR" T(N) },
+ { "DISABLED" T(N) },
+ { "FORMACTION" T(N) },
+ { "NAME" T(N) },
+ { "ONBLUR" T(N) },
+ { "ONFOCUS" T(N) },
+ { "READONLY" T(N) },
+ { "TABINDEX" T(N) },
+ { "TYPE" T(N) },
+ { "VALUE" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType BUTTON_attr_type[] = {
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(events) },
+ { ATTR_TYPE(i18n) },
+ { ATTR_TYPE(BUTTON) },
+ { 0, 0 },
+};
+
+static const attr CAPTION_attr_list[] = {
+ { "ACCESSKEY" T(N) },
+ { "CLEAR" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType CAPTION_attr_type[] = {
+ { ATTR_TYPE(align) },
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(events) },
+ { ATTR_TYPE(i18n) },
+ { ATTR_TYPE(CAPTION) },
+ { 0, 0 },
+};
+
+static const attr COL_attr_list[] = {
+ { "CLEAR" T(N) },
+ { "SPAN" T(N) },
+ { "WIDTH" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType COL_attr_type[] = {
+ { ATTR_TYPE(cellalign) },
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(events) },
+ { ATTR_TYPE(i18n) },
+ { ATTR_TYPE(COL) },
+ { 0, 0 },
+};
+
+static const attr DEL_attr_list[] = {
+ { "CITE" T(N) },
+ { "DATETIME" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType DEL_attr_type[] = {
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(events) },
+ { ATTR_TYPE(i18n) },
+ { ATTR_TYPE(DEL) },
+ { 0, 0 },
+};
+
+static const attr DIV_attr_list[] = {
+ { "CLEAR" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType DIV_attr_type[] = {
+ { ATTR_TYPE(align) },
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(i18n) },
+ { ATTR_TYPE(DIV) },
+ { 0, 0 },
+};
+
+static const attr DL_attr_list[] = {
+ { "CLEAR" T(N) },
+ { "COMPACT" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType DL_attr_type[] = {
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(i18n) },
+ { ATTR_TYPE(DL) },
+ { 0, 0 },
+};
+
+static const attr EMBED_attr_list[] = {
+ { "ALT" T(N) },
+ { "BORDER" T(N) },
+ { "CLEAR" T(N) },
+ { "HEIGHT" T(N) },
+ { "IMAGEMAP" T(N) },
+ { "ISMAP" T(N) },
+ { "MD" T(N) },
+ { "NAME" T(i) },
+ { "NOFLOW" T(N) },
+ { "PARAMS" T(N) },
+ { "SRC" T(h) },
+ { "UNITS" T(N) },
+ { "USEMAP" T(N) },
+ { "WIDTH" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType EMBED_attr_type[] = {
+ { ATTR_TYPE(align) },
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(i18n) },
+ { ATTR_TYPE(EMBED) },
+ { 0, 0 },
+};
+
+static const attr FIG_attr_list[] = {
+ { "BORDER" T(N) },
+ { "CLEAR" T(N) },
+ { "HEIGHT" T(N) },
+ { "IMAGEMAP" T(N) },
+ { "ISOBJECT" T(N) },
+ { "MD" T(N) },
+ { "NOFLOW" T(N) },
+ { "SRC" T(h) },
+ { "UNITS" T(N) },
+ { "WIDTH" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType FIG_attr_type[] = {
+ { ATTR_TYPE(align) },
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(i18n) },
+ { ATTR_TYPE(FIG) },
+ { 0, 0 },
+};
+
+static const attr FONT_attr_list[] = {
+ { "CLEAR" T(N) },
+ { "COLOR" T(N) },
+ { "END" T(N) },
+ { "FACE" T(N) },
+ { "SIZE" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType FONT_attr_type[] = {
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(i18n) },
+ { ATTR_TYPE(FONT) },
+ { 0, 0 },
+};
+
+static const attr FORM_attr_list[] = {
+ { "ACCEPT" T(N) },
+ { "ACCEPT-CHARSET" T(N) },
+ { "ACTION" T(h) },
+ { "CLEAR" T(N) },
+ { "ENCTYPE" T(N) },
+ { "METHOD" T(N) },
+ { "ONRESET" T(N) },
+ { "ONSUBMIT" T(N) },
+ { "SCRIPT" T(N) },
+ { "SUBJECT" T(N) },
+ { "TARGET" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType FORM_attr_type[] = {
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(i18n) },
+ { ATTR_TYPE(FORM) },
+ { 0, 0 },
+};
+
+static const attr FRAME_attr_list[] = {
+ { "FRAMEBORDER" T(N) },
+ { "LONGDESC" T(h) },
+ { "MARGINHEIGHT" T(N) },
+ { "MARGINWIDTH" T(N) },
+ { "NAME" T(N) },
+ { "NORESIZE" T(N) },
+ { "SCROLLING" T(N) },
+ { "SRC" T(h) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType FRAME_attr_type[] = {
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(FRAME) },
+ { 0, 0 },
+};
+
+static const attr FRAMESET_attr_list[] = {
+ { "COLS" T(N) },
+ { "ONLOAD" T(N) },
+ { "ONUNLOAD" T(N) },
+ { "ROWS" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType FRAMESET_attr_type[] = {
+ { ATTR_TYPE(FRAMESET) },
+ { 0, 0 },
+};
+
+static const attr GEN_attr_list[] = {
+ { "CLEAR" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType GEN_attr_type[] = {
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(events) },
+ { ATTR_TYPE(i18n) },
+ { ATTR_TYPE(GEN) },
+ { 0, 0 },
+};
+
+static const attr GEN5_attr_list[] = {
+ { "ROLE" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType GEN5_attr_type[] = {
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(events) },
+ { ATTR_TYPE(i18n) },
+ { ATTR_TYPE(GEN5) },
+ { 0, 0 },
+};
+
+static const attr H_attr_list[] = {
+ { "CLEAR" T(N) },
+ { "DINGBAT" T(N) },
+ { "MD" T(N) },
+ { "NOWRAP" T(N) },
+ { "SEQNUM" T(N) },
+ { "SKIP" T(N) },
+ { "SRC" T(h) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType H_attr_type[] = {
+ { ATTR_TYPE(align) },
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(events) },
+ { ATTR_TYPE(i18n) },
+ { ATTR_TYPE(H) },
+ { 0, 0 },
+};
+
+static const attr HR_attr_list[] = {
+ { "CLEAR" T(N) },
+ { "MD" T(N) },
+ { "NOSHADE" T(N) },
+ { "SIZE" T(N) },
+ { "SRC" T(h) },
+ { "WIDTH" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType HR_attr_type[] = {
+ { ATTR_TYPE(align) },
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(i18n) },
+ { ATTR_TYPE(HR) },
+ { 0, 0 },
+};
+
+static const attr IFRAME_attr_list[] = {
+ { "FRAMEBORDER" T(N) },
+ { "HEIGHT" T(N) },
+ { "LONGDESC" T(h) },
+ { "MARGINHEIGHT" T(N) },
+ { "MARGINWIDTH" T(N) },
+ { "NAME" T(N) },
+ { "SCROLLING" T(N) },
+ { "SRC" T(h) },
+ { "WIDTH" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType IFRAME_attr_type[] = {
+ { ATTR_TYPE(align) },
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(IFRAME) },
+ { 0, 0 },
+};
+
+static const attr IMG_attr_list[] = {
+ { "ALT" T(N) },
+ { "BORDER" T(N) },
+ { "CLEAR" T(N) },
+ { "HEIGHT" T(N) },
+ { "HSPACE" T(N) },
+ { "ISMAP" T(N) },
+ { "ISOBJECT" T(N) },
+ { "LONGDESC" T(h) },
+ { "MD" T(N) },
+ { "NAME" T(N) },
+ { "SRC" T(h) },
+ { "UNITS" T(N) },
+ { "USEMAP" T(h) },
+ { "VSPACE" T(N) },
+ { "WIDTH" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType IMG_attr_type[] = {
+ { ATTR_TYPE(align) },
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(events) },
+ { ATTR_TYPE(i18n) },
+ { ATTR_TYPE(IMG) },
+ { 0, 0 },
+};
+
+static const attr INPUT_attr_list[] = {
+ { "ACCEPT" T(N) },
+ { "ACCEPT-CHARSET" T(N) },
+ { "ACCESSKEY" T(N) },
+ { "ALT" T(N) },
+ { "CHECKED" T(N) },
+ { "CLEAR" T(N) },
+ { "DISABLED" T(N) },
+ { "ERROR" T(N) },
+ { "HEIGHT" T(N) },
+ { "ISMAP" T(N) },
+ { "MAX" T(N) },
+ { "MAXLENGTH" T(N) },
+ { "MD" T(N) },
+ { "MIN" T(N) },
+ { "NAME" T(N) },
+ { "NOTAB" T(N) },
+ { "ONBLUR" T(N) },
+ { "ONCHANGE" T(N) },
+ { "ONFOCUS" T(N) },
+ { "ONSELECT" T(N) },
+ { "READONLY" T(N) },
+ { "SIZE" T(N) },
+ { "SRC" T(h) },
+ { "TABINDEX" T(N) },
+ { "TYPE" T(N) },
+ { "USEMAP" T(N) },
+ { "VALUE" T(N) },
+ { "WIDTH" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType INPUT_attr_type[] = {
+ { ATTR_TYPE(align) },
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(events) },
+ { ATTR_TYPE(i18n) },
+ { ATTR_TYPE(INPUT) },
+ { 0, 0 },
+};
+
+static const attr ISINDEX_attr_list[] = {
+ { "ACTION" T(h) },
+ { "HREF" T(h) },
+ { "PROMPT" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType ISINDEX_attr_type[] = {
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(i18n) },
+ { ATTR_TYPE(ISINDEX) },
+ { 0, 0 },
+};
+
+static const attr KEYGEN_attr_list[] = {
+ { "CHALLENGE" T(N) },
+ { "NAME" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType KEYGEN_attr_type[] = {
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(i18n) },
+ { ATTR_TYPE(KEYGEN) },
+ { 0, 0 },
+};
+
+static const attr LABEL_attr_list[] = {
+ { "ACCESSKEY" T(N) },
+ { "CLEAR" T(N) },
+ { "FOR" T(N) },
+ { "ONBLUR" T(N) },
+ { "ONFOCUS" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType LABEL_attr_type[] = {
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(events) },
+ { ATTR_TYPE(i18n) },
+ { ATTR_TYPE(LABEL) },
+ { 0, 0 },
+};
+
+static const attr LI_attr_list[] = {
+ { "CLEAR" T(N) },
+ { "DINGBAT" T(N) },
+ { "MD" T(N) },
+ { "SKIP" T(N) },
+ { "SRC" T(h) },
+ { "TYPE" T(N) },
+ { "VALUE" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType LI_attr_type[] = {
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(events) },
+ { ATTR_TYPE(i18n) },
+ { ATTR_TYPE(LI) },
+ { 0, 0 },
+};
+
+static const attr LINK_attr_list[] = {
+ { "CHARSET" T(N) },
+ { "HREF" T(h) },
+ { "HREFLANG" T(N) },
+ { "MEDIA" T(N) },
+ { "REL" T(N) },
+ { "REV" T(N) },
+ { "TARGET" T(N) },
+ { "TYPE" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType LINK_attr_type[] = {
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(events) },
+ { ATTR_TYPE(i18n) },
+ { ATTR_TYPE(LINK) },
+ { 0, 0 },
+};
+
+static const attr MAP_attr_list[] = {
+ { "CLEAR" T(N) },
+ { "NAME" T(i) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType MAP_attr_type[] = {
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(i18n) },
+ { ATTR_TYPE(MAP) },
+ { 0, 0 },
+};
+
+static const attr MATH_attr_list[] = {
+ { "BOX" T(N) },
+ { "CLEAR" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType MATH_attr_type[] = {
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(i18n) },
+ { ATTR_TYPE(MATH) },
+ { 0, 0 },
+};
+
+static const attr META_attr_list[] = {
+ { "CHARSET" T(N) },
+ { "CONTENT" T(N) },
+ { "HTTP-EQUIV" T(N) },
+ { "NAME" T(N) },
+ { "SCHEME" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType META_attr_type[] = {
+ { ATTR_TYPE(META) },
+ { 0, 0 },
+};
+
+static const attr NEXTID_attr_list[] = {
+ { "N" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType NEXTID_attr_type[] = {
+ { ATTR_TYPE(NEXTID) },
+ { 0, 0 },
+};
+
+static const attr NOTE_attr_list[] = {
+ { "CLEAR" T(N) },
+ { "MD" T(N) },
+ { "ROLE" T(x) },
+ { "SRC" T(h) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType NOTE_attr_type[] = {
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(i18n) },
+ { ATTR_TYPE(NOTE) },
+ { 0, 0 },
+};
+
+static const attr OBJECT_attr_list[] = {
+ { "ARCHIVE" T(N) },
+ { "BORDER" T(N) },
+ { "CLASSID" T(h) },
+ { "CODEBASE" T(h) },
+ { "CODETYPE" T(N) },
+ { "DATA" T(h) },
+ { "DECLARE" T(N) },
+ { "HEIGHT" T(N) },
+ { "HSPACE" T(N) },
+ { "ISMAP" T(N) },
+ { "NAME" T(N) },
+ { "NOTAB" T(N) },
+ { "SHAPES" T(N) },
+ { "STANDBY" T(N) },
+ { "TABINDEX" T(N) },
+ { "TYPE" T(N) },
+ { "USEMAP" T(h) },
+ { "VSPACE" T(N) },
+ { "WIDTH" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType OBJECT_attr_type[] = {
+ { ATTR_TYPE(align) },
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(events) },
+ { ATTR_TYPE(i18n) },
+ { ATTR_TYPE(OBJECT) },
+ { 0, 0 },
+};
+
+static const attr OL_attr_list[] = {
+ { "CLEAR" T(N) },
+ { "COMPACT" T(N) },
+ { "CONTINUE" T(N) },
+ { "SEQNUM" T(N) },
+ { "START" T(N) },
+ { "TYPE" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType OL_attr_type[] = {
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(i18n) },
+ { ATTR_TYPE(OL) },
+ { 0, 0 },
+};
+
+static const attr OPTION_attr_list[] = {
+ { "CLEAR" T(N) },
+ { "DISABLED" T(N) },
+ { "ERROR" T(N) },
+ { "LABEL" T(N) },
+ { "SELECTED" T(N) },
+ { "SHAPE" T(N) },
+ { "VALUE" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType OPTION_attr_type[] = {
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(events) },
+ { ATTR_TYPE(i18n) },
+ { ATTR_TYPE(OPTION) },
+ { 0, 0 },
+};
+
+static const attr OVERLAY_attr_list[] = {
+ { "HEIGHT" T(N) },
+ { "IMAGEMAP" T(N) },
+ { "MD" T(N) },
+ { "SRC" T(h) },
+ { "UNITS" T(N) },
+ { "WIDTH" T(N) },
+ { "X" T(N) },
+ { "Y" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType OVERLAY_attr_type[] = {
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(OVERLAY) },
+ { 0, 0 },
+};
+
+static const attr P_attr_list[] = {
+ { "CLEAR" T(N) },
+ { "NOWRAP" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType P_attr_type[] = {
+ { ATTR_TYPE(align) },
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(i18n) },
+ { ATTR_TYPE(P) },
+ { 0, 0 },
+};
+
+static const attr PARAM_attr_list[] = {
+ { "ACCEPT" T(N) },
+ { "ACCEPT-CHARSET" T(N) },
+ { "ACCEPT-ENCODING" T(N) },
+ { "CLEAR" T(N) },
+ { "DATA" T(N) },
+ { "NAME" T(N) },
+ { "OBJECT" T(N) },
+ { "REF" T(N) },
+ { "TYPE" T(N) },
+ { "VALUE" T(N) },
+ { "VALUEREF" T(N) },
+ { "VALUETYPE" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType PARAM_attr_type[] = {
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(i18n) },
+ { ATTR_TYPE(PARAM) },
+ { 0, 0 },
+};
+
+static const attr Q_attr_list[] = {
+ { "CITE" T(h) },
+ { "CLEAR" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType Q_attr_type[] = {
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(i18n) },
+ { ATTR_TYPE(Q) },
+ { 0, 0 },
+};
+
+static const attr SCRIPT_attr_list[] = {
+ { "CHARSET" T(N) },
+ { "CLEAR" T(N) },
+ { "DEFER" T(N) },
+ { "EVENT" T(N) },
+ { "FOR" T(N) },
+ { "LANGUAGE" T(N) },
+ { "NAME" T(N) },
+ { "SCRIPTENGINE" T(N) },
+ { "SRC" T(h) },
+ { "TYPE" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType SCRIPT_attr_type[] = {
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(i18n) },
+ { ATTR_TYPE(SCRIPT) },
+ { 0, 0 },
+};
+
+static const attr SELECT_attr_list[] = {
+ { "CLEAR" T(N) },
+ { "DISABLED" T(N) },
+ { "ERROR" T(N) },
+ { "HEIGHT" T(N) },
+ { "MD" T(N) },
+ { "MULTIPLE" T(N) },
+ { "NAME" T(N) },
+ { "NOTAB" T(N) },
+ { "ONBLUR" T(N) },
+ { "ONCHANGE" T(N) },
+ { "ONFOCUS" T(N) },
+ { "SIZE" T(N) },
+ { "TABINDEX" T(N) },
+ { "UNITS" T(N) },
+ { "WIDTH" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType SELECT_attr_type[] = {
+ { ATTR_TYPE(align) },
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(i18n) },
+ { ATTR_TYPE(SELECT) },
+ { 0, 0 },
+};
+
+static const attr STYLE_attr_list[] = {
+ { "MEDIA" T(N) },
+ { "NOTATION" T(N) },
+ { "TYPE" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType STYLE_attr_type[] = {
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(i18n) },
+ { ATTR_TYPE(STYLE) },
+ { 0, 0 },
+};
+
+static const attr TAB_attr_list[] = {
+ { "CLEAR" T(N) },
+ { "DP" T(N) },
+ { "INDENT" T(N) },
+ { "TO" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType TAB_attr_type[] = {
+ { ATTR_TYPE(align) },
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(i18n) },
+ { ATTR_TYPE(TAB) },
+ { 0, 0 },
+};
+
+static const attr TABLE_attr_list[] = {
+ { "BACKGROUND" T(h) },
+ { "BORDER" T(N) },
+ { "CELLPADDING" T(N) },
+ { "CELLSPACING" T(N) },
+ { "CLEAR" T(N) },
+ { "COLS" T(N) },
+ { "COLSPEC" T(N) },
+ { "DP" T(N) },
+ { "FRAME" T(N) },
+ { "NOFLOW" T(N) },
+ { "NOWRAP" T(N) },
+ { "RULES" T(N) },
+ { "SUMMARY" T(N) },
+ { "UNITS" T(N) },
+ { "WIDTH" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType TABLE_attr_type[] = {
+ { ATTR_TYPE(align) },
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(events) },
+ { ATTR_TYPE(i18n) },
+ { ATTR_TYPE(TABLE) },
+ { 0, 0 },
+};
+
+static const attr TD_attr_list[] = {
+ { "ABBR" T(N) },
+ { "AXES" T(N) },
+ { "AXIS" T(N) },
+ { "BACKGROUND" T(h) },
+ { "CLEAR" T(N) },
+ { "COLSPAN" T(N) },
+ { "DP" T(N) },
+ { "HEADERS" T(N) },
+ { "HEIGHT" T(N) },
+ { "NOWRAP" T(N) },
+ { "ROWSPAN" T(N) },
+ { "SCOPE" T(N) },
+ { "WIDTH" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType TD_attr_type[] = {
+ { ATTR_TYPE(cellalign) },
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(i18n) },
+ { ATTR_TYPE(TD) },
+ { 0, 0 },
+};
+
+static const attr TEXTAREA_attr_list[] = {
+ { "ACCEPT-CHARSET" T(N) },
+ { "ACCESSKEY" T(N) },
+ { "CLEAR" T(N) },
+ { "COLS" T(N) },
+ { "DISABLED" T(N) },
+ { "ERROR" T(N) },
+ { "NAME" T(N) },
+ { "NOTAB" T(N) },
+ { "ONBLUR" T(N) },
+ { "ONCHANGE" T(N) },
+ { "ONFOCUS" T(N) },
+ { "ONSELECT" T(N) },
+ { "READONLY" T(N) },
+ { "ROWS" T(N) },
+ { "TABINDEX" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType TEXTAREA_attr_type[] = {
+ { ATTR_TYPE(align) },
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(events) },
+ { ATTR_TYPE(i18n) },
+ { ATTR_TYPE(TEXTAREA) },
+ { 0, 0 },
+};
+
+static const attr TR_attr_list[] = {
+ { "CLEAR" T(N) },
+ { "DP" T(N) },
+ { "NOWRAP" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType TR_attr_type[] = {
+ { ATTR_TYPE(cellalign) },
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(events) },
+ { ATTR_TYPE(i18n) },
+ { ATTR_TYPE(TR) },
+ { 0, 0 },
+};
+
+static const attr UL_attr_list[] = {
+ { "CLEAR" T(N) },
+ { "COMPACT" T(N) },
+ { "DINGBAT" T(N) },
+ { "MD" T(N) },
+ { "PLAIN" T(N) },
+ { "SRC" T(h) },
+ { "TYPE" T(N) },
+ { "WRAP" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const AttrType UL_attr_type[] = {
+ { ATTR_TYPE(core) },
+ { ATTR_TYPE(i18n) },
+ { ATTR_TYPE(UL) },
+ { 0, 0 },
+};
+
+
+/* attribute lists for the runtime (generated by dtd_util) */
+static const attr A_attr[] = { /* A attributes */
+ { "ACCESSKEY" T(N) },
+ { "CHARSET" T(N) },
+ { "CLASS" T(c) },
+ { "CLEAR" T(N) },
+ { "COORDS" T(N) },
+ { "DIR" T(N) },
+ { "HREF" T(h) },
+ { "HREFLANG" T(N) },
+ { "ID" T(i) },
+ { "ISMAP" T(N) },
+ { "LANG" T(N) },
+ { "MD" T(N) },
+ { "NAME" T(i) },
+ { "NOTAB" T(N) },
+ { "ONBLUR" T(N) },
+ { "ONFOCUS" T(N) },
+ { "REL" T(N) },
+ { "REV" T(N) },
+ { "SHAPE" T(N) },
+ { "STYLE" T(N) },
+ { "TABINDEX" T(N) },
+ { "TARGET" T(N) },
+ { "TITLE" T(N) },
+ { "TYPE" T(N) },
+ { "URN" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr ADDRESS_attr[] = { /* ADDRESS attributes */
+ { "CLASS" T(c) },
+ { "CLEAR" T(N) },
+ { "DIR" T(N) },
+ { "ID" T(i) },
+ { "LANG" T(N) },
+ { "NOWRAP" T(N) },
+ { "STYLE" T(N) },
+ { "TITLE" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr APPLET_attr[] = { /* APPLET attributes */
+ { "ALIGN" T(N) },
+ { "ALT" T(N) },
+ { "CLASS" T(c) },
+ { "CLEAR" T(N) },
+ { "CODE" T(N) },
+ { "CODEBASE" T(h) },
+ { "DIR" T(N) },
+ { "DOWNLOAD" T(N) },
+ { "HEIGHT" T(N) },
+ { "HSPACE" T(N) },
+ { "ID" T(i) },
+ { "LANG" T(N) },
+ { "NAME" T(i) },
+ { "STYLE" T(N) },
+ { "TITLE" T(N) },
+ { "VSPACE" T(N) },
+ { "WIDTH" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr AREA_attr[] = { /* AREA attributes */
+ { "ACCESSKEY" T(N) },
+ { "ALT" T(N) },
+ { "CLASS" T(c) },
+ { "CLEAR" T(N) },
+ { "COORDS" T(N) },
+ { "DIR" T(N) },
+ { "HREF" T(h) },
+ { "ID" T(i) },
+ { "LANG" T(N) },
+ { "NOHREF" T(N) },
+ { "NOTAB" T(N) },
+ { "ONBLUR" T(N) },
+ { "ONFOCUS" T(N) },
+ { "SHAPE" T(N) },
+ { "STYLE" T(N) },
+ { "TABINDEX" T(N) },
+ { "TARGET" T(N) },
+ { "TITLE" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr BASE_attr[] = { /* BASE attributes */
+ { "CLASS" T(c) },
+ { "HREF" T(h) },
+ { "ID" T(i) },
+ { "STYLE" T(N) },
+ { "TARGET" T(N) },
+ { "TITLE" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr BGSOUND_attr[] = { /* BGSOUND attributes */
+ { "CLASS" T(c) },
+ { "CLEAR" T(N) },
+ { "DIR" T(N) },
+ { "ID" T(i) },
+ { "LANG" T(N) },
+ { "LOOP" T(N) },
+ { "SRC" T(h) },
+ { "STYLE" T(N) },
+ { "TITLE" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr BODY_attr[] = { /* BODY attributes */
+ { "ALINK" T(N) },
+ { "BACKGROUND" T(h) },
+ { "BGCOLOR" T(N) },
+ { "CLASS" T(c) },
+ { "CLEAR" T(N) },
+ { "DIR" T(N) },
+ { "ID" T(i) },
+ { "LANG" T(N) },
+ { "LINK" T(N) },
+ { "ONLOAD" T(N) },
+ { "ONUNLOAD" T(N) },
+ { "STYLE" T(N) },
+ { "TEXT" T(N) },
+ { "TITLE" T(N) },
+ { "VLINK" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr BODYTEXT_attr[] = { /* BODYTEXT attributes */
+ { "CLASS" T(c) },
+ { "CLEAR" T(N) },
+ { "DATA" T(N) },
+ { "DIR" T(N) },
+ { "ID" T(i) },
+ { "LANG" T(N) },
+ { "NAME" T(N) },
+ { "OBJECT" T(N) },
+ { "REF" T(N) },
+ { "STYLE" T(N) },
+ { "TITLE" T(N) },
+ { "TYPE" T(N) },
+ { "VALUE" T(N) },
+ { "VALUETYPE" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr BQ_attr[] = { /* BLOCKQUOTE attributes */
+ { "CITE" T(h) },
+ { "CLASS" T(c) },
+ { "CLEAR" T(N) },
+ { "DIR" T(N) },
+ { "ID" T(i) },
+ { "LANG" T(N) },
+ { "NOWRAP" T(N) },
+ { "STYLE" T(N) },
+ { "TITLE" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr BUTTON_attr[] = { /* BUTTON attributes */
+ { "ACCESSKEY" T(N) },
+ { "CLASS" T(c) },
+ { "CLEAR" T(N) },
+ { "DIR" T(N) },
+ { "DISABLED" T(N) },
+ { "FORMACTION" T(N) },
+ { "ID" T(i) },
+ { "LANG" T(N) },
+ { "NAME" T(N) },
+ { "ONBLUR" T(N) },
+ { "ONFOCUS" T(N) },
+ { "READONLY" T(N) },
+ { "STYLE" T(N) },
+ { "TABINDEX" T(N) },
+ { "TITLE" T(N) },
+ { "TYPE" T(N) },
+ { "VALUE" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr CAPTION_attr[] = { /* CAPTION attributes */
+ { "ACCESSKEY" T(N) },
+ { "ALIGN" T(N) },
+ { "CLASS" T(c) },
+ { "CLEAR" T(N) },
+ { "DIR" T(N) },
+ { "ID" T(i) },
+ { "LANG" T(N) },
+ { "STYLE" T(N) },
+ { "TITLE" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr COL_attr[] = { /* COL attributes */
+ { "ALIGN" T(N) },
+ { "CHAR" T(N) },
+ { "CHAROFF" T(N) },
+ { "CLASS" T(c) },
+ { "CLEAR" T(N) },
+ { "DIR" T(N) },
+ { "ID" T(i) },
+ { "LANG" T(N) },
+ { "SPAN" T(N) },
+ { "STYLE" T(N) },
+ { "TITLE" T(N) },
+ { "VALIGN" T(N) },
+ { "WIDTH" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr DEL_attr[] = { /* DEL attributes */
+ { "CITE" T(N) },
+ { "CLASS" T(c) },
+ { "DATETIME" T(N) },
+ { "DIR" T(N) },
+ { "ID" T(i) },
+ { "LANG" T(N) },
+ { "STYLE" T(N) },
+ { "TITLE" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr DIV_attr[] = { /* CENTER attributes */
+ { "ALIGN" T(N) },
+ { "CLASS" T(c) },
+ { "CLEAR" T(N) },
+ { "DIR" T(N) },
+ { "ID" T(i) },
+ { "LANG" T(N) },
+ { "STYLE" T(N) },
+ { "TITLE" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr DL_attr[] = { /* DL attributes */
+ { "CLASS" T(c) },
+ { "CLEAR" T(N) },
+ { "COMPACT" T(N) },
+ { "DIR" T(N) },
+ { "ID" T(i) },
+ { "LANG" T(N) },
+ { "STYLE" T(N) },
+ { "TITLE" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr EMBED_attr[] = { /* EMBED attributes */
+ { "ALIGN" T(N) },
+ { "ALT" T(N) },
+ { "BORDER" T(N) },
+ { "CLASS" T(c) },
+ { "CLEAR" T(N) },
+ { "DIR" T(N) },
+ { "HEIGHT" T(N) },
+ { "ID" T(i) },
+ { "IMAGEMAP" T(N) },
+ { "ISMAP" T(N) },
+ { "LANG" T(N) },
+ { "MD" T(N) },
+ { "NAME" T(i) },
+ { "NOFLOW" T(N) },
+ { "PARAMS" T(N) },
+ { "SRC" T(h) },
+ { "STYLE" T(N) },
+ { "TITLE" T(N) },
+ { "UNITS" T(N) },
+ { "USEMAP" T(N) },
+ { "WIDTH" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr FIG_attr[] = { /* FIG attributes */
+ { "ALIGN" T(N) },
+ { "BORDER" T(N) },
+ { "CLASS" T(c) },
+ { "CLEAR" T(N) },
+ { "DIR" T(N) },
+ { "HEIGHT" T(N) },
+ { "ID" T(i) },
+ { "IMAGEMAP" T(N) },
+ { "ISOBJECT" T(N) },
+ { "LANG" T(N) },
+ { "MD" T(N) },
+ { "NOFLOW" T(N) },
+ { "SRC" T(h) },
+ { "STYLE" T(N) },
+ { "TITLE" T(N) },
+ { "UNITS" T(N) },
+ { "WIDTH" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr FONT_attr[] = { /* BASEFONT attributes */
+ { "CLASS" T(c) },
+ { "CLEAR" T(N) },
+ { "COLOR" T(N) },
+ { "DIR" T(N) },
+ { "END" T(N) },
+ { "FACE" T(N) },
+ { "ID" T(i) },
+ { "LANG" T(N) },
+ { "SIZE" T(N) },
+ { "STYLE" T(N) },
+ { "TITLE" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr FORM_attr[] = { /* FORM attributes */
+ { "ACCEPT" T(N) },
+ { "ACCEPT-CHARSET" T(N) },
+ { "ACTION" T(h) },
+ { "CLASS" T(c) },
+ { "CLEAR" T(N) },
+ { "DIR" T(N) },
+ { "ENCTYPE" T(N) },
+ { "ID" T(i) },
+ { "LANG" T(N) },
+ { "METHOD" T(N) },
+ { "ONRESET" T(N) },
+ { "ONSUBMIT" T(N) },
+ { "SCRIPT" T(N) },
+ { "STYLE" T(N) },
+ { "SUBJECT" T(N) },
+ { "TARGET" T(N) },
+ { "TITLE" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr FRAME_attr[] = { /* FRAME attributes */
+ { "CLASS" T(c) },
+ { "FRAMEBORDER" T(N) },
+ { "ID" T(i) },
+ { "LONGDESC" T(h) },
+ { "MARGINHEIGHT" T(N) },
+ { "MARGINWIDTH" T(N) },
+ { "NAME" T(N) },
+ { "NORESIZE" T(N) },
+ { "SCROLLING" T(N) },
+ { "SRC" T(h) },
+ { "STYLE" T(N) },
+ { "TITLE" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr FRAMESET_attr[] = { /* FRAMESET attributes */
+ { "COLS" T(N) },
+ { "ONLOAD" T(N) },
+ { "ONUNLOAD" T(N) },
+ { "ROWS" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr GEN_attr[] = { /* ABBR attributes */
+ { "CLASS" T(c) },
+ { "CLEAR" T(N) },
+ { "DIR" T(N) },
+ { "ID" T(i) },
+ { "LANG" T(N) },
+ { "STYLE" T(N) },
+ { "TITLE" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr GEN5_attr[] = { /* ARTICLE attributes */
+ { "CLASS" T(c) },
+ { "DIR" T(N) },
+ { "ID" T(i) },
+ { "LANG" T(N) },
+ { "ROLE" T(N) },
+ { "STYLE" T(N) },
+ { "TITLE" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr H_attr[] = { /* H1 attributes */
+ { "ALIGN" T(N) },
+ { "CLASS" T(c) },
+ { "CLEAR" T(N) },
+ { "DINGBAT" T(N) },
+ { "DIR" T(N) },
+ { "ID" T(i) },
+ { "LANG" T(N) },
+ { "MD" T(N) },
+ { "NOWRAP" T(N) },
+ { "SEQNUM" T(N) },
+ { "SKIP" T(N) },
+ { "SRC" T(h) },
+ { "STYLE" T(N) },
+ { "TITLE" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr HR_attr[] = { /* HR attributes */
+ { "ALIGN" T(N) },
+ { "CLASS" T(c) },
+ { "CLEAR" T(N) },
+ { "DIR" T(N) },
+ { "ID" T(i) },
+ { "LANG" T(N) },
+ { "MD" T(N) },
+ { "NOSHADE" T(N) },
+ { "SIZE" T(N) },
+ { "SRC" T(h) },
+ { "STYLE" T(N) },
+ { "TITLE" T(N) },
+ { "WIDTH" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr IFRAME_attr[] = { /* IFRAME attributes */
+ { "ALIGN" T(N) },
+ { "CLASS" T(c) },
+ { "FRAMEBORDER" T(N) },
+ { "HEIGHT" T(N) },
+ { "ID" T(i) },
+ { "LONGDESC" T(h) },
+ { "MARGINHEIGHT" T(N) },
+ { "MARGINWIDTH" T(N) },
+ { "NAME" T(N) },
+ { "SCROLLING" T(N) },
+ { "SRC" T(h) },
+ { "STYLE" T(N) },
+ { "TITLE" T(N) },
+ { "WIDTH" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr IMG_attr[] = { /* IMG attributes */
+ { "ALIGN" T(N) },
+ { "ALT" T(N) },
+ { "BORDER" T(N) },
+ { "CLASS" T(c) },
+ { "CLEAR" T(N) },
+ { "DIR" T(N) },
+ { "HEIGHT" T(N) },
+ { "HSPACE" T(N) },
+ { "ID" T(i) },
+ { "ISMAP" T(N) },
+ { "ISOBJECT" T(N) },
+ { "LANG" T(N) },
+ { "LONGDESC" T(h) },
+ { "MD" T(N) },
+ { "NAME" T(N) },
+ { "SRC" T(h) },
+ { "STYLE" T(N) },
+ { "TITLE" T(N) },
+ { "UNITS" T(N) },
+ { "USEMAP" T(h) },
+ { "VSPACE" T(N) },
+ { "WIDTH" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr INPUT_attr[] = { /* INPUT attributes */
+ { "ACCEPT" T(N) },
+ { "ACCEPT-CHARSET" T(N) },
+ { "ACCESSKEY" T(N) },
+ { "ALIGN" T(N) },
+ { "ALT" T(N) },
+ { "CHECKED" T(N) },
+ { "CLASS" T(c) },
+ { "CLEAR" T(N) },
+ { "DIR" T(N) },
+ { "DISABLED" T(N) },
+ { "ERROR" T(N) },
+ { "HEIGHT" T(N) },
+ { "ID" T(i) },
+ { "ISMAP" T(N) },
+ { "LANG" T(N) },
+ { "MAX" T(N) },
+ { "MAXLENGTH" T(N) },
+ { "MD" T(N) },
+ { "MIN" T(N) },
+ { "NAME" T(N) },
+ { "NOTAB" T(N) },
+ { "ONBLUR" T(N) },
+ { "ONCHANGE" T(N) },
+ { "ONFOCUS" T(N) },
+ { "ONSELECT" T(N) },
+ { "READONLY" T(N) },
+ { "SIZE" T(N) },
+ { "SRC" T(h) },
+ { "STYLE" T(N) },
+ { "TABINDEX" T(N) },
+ { "TITLE" T(N) },
+ { "TYPE" T(N) },
+ { "USEMAP" T(N) },
+ { "VALUE" T(N) },
+ { "WIDTH" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr ISINDEX_attr[] = { /* ISINDEX attributes */
+ { "ACTION" T(h) },
+ { "CLASS" T(c) },
+ { "DIR" T(N) },
+ { "HREF" T(h) },
+ { "ID" T(i) },
+ { "LANG" T(N) },
+ { "PROMPT" T(N) },
+ { "STYLE" T(N) },
+ { "TITLE" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr KEYGEN_attr[] = { /* KEYGEN attributes */
+ { "CHALLENGE" T(N) },
+ { "CLASS" T(c) },
+ { "DIR" T(N) },
+ { "ID" T(i) },
+ { "LANG" T(N) },
+ { "NAME" T(N) },
+ { "STYLE" T(N) },
+ { "TITLE" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr LABEL_attr[] = { /* LABEL attributes */
+ { "ACCESSKEY" T(N) },
+ { "CLASS" T(c) },
+ { "CLEAR" T(N) },
+ { "DIR" T(N) },
+ { "FOR" T(N) },
+ { "ID" T(i) },
+ { "LANG" T(N) },
+ { "ONBLUR" T(N) },
+ { "ONFOCUS" T(N) },
+ { "STYLE" T(N) },
+ { "TITLE" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr LI_attr[] = { /* LI attributes */
+ { "CLASS" T(c) },
+ { "CLEAR" T(N) },
+ { "DINGBAT" T(N) },
+ { "DIR" T(N) },
+ { "ID" T(i) },
+ { "LANG" T(N) },
+ { "MD" T(N) },
+ { "SKIP" T(N) },
+ { "SRC" T(h) },
+ { "STYLE" T(N) },
+ { "TITLE" T(N) },
+ { "TYPE" T(N) },
+ { "VALUE" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr LINK_attr[] = { /* LINK attributes */
+ { "CHARSET" T(N) },
+ { "CLASS" T(c) },
+ { "DIR" T(N) },
+ { "HREF" T(h) },
+ { "HREFLANG" T(N) },
+ { "ID" T(i) },
+ { "LANG" T(N) },
+ { "MEDIA" T(N) },
+ { "REL" T(N) },
+ { "REV" T(N) },
+ { "STYLE" T(N) },
+ { "TARGET" T(N) },
+ { "TITLE" T(N) },
+ { "TYPE" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr MAP_attr[] = { /* MAP attributes */
+ { "CLASS" T(c) },
+ { "CLEAR" T(N) },
+ { "DIR" T(N) },
+ { "ID" T(i) },
+ { "LANG" T(N) },
+ { "NAME" T(i) },
+ { "STYLE" T(N) },
+ { "TITLE" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr MATH_attr[] = { /* MATH attributes */
+ { "BOX" T(N) },
+ { "CLASS" T(c) },
+ { "CLEAR" T(N) },
+ { "DIR" T(N) },
+ { "ID" T(i) },
+ { "LANG" T(N) },
+ { "STYLE" T(N) },
+ { "TITLE" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr META_attr[] = { /* META attributes */
+ { "CHARSET" T(N) },
+ { "CONTENT" T(N) },
+ { "HTTP-EQUIV" T(N) },
+ { "NAME" T(N) },
+ { "SCHEME" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr NEXTID_attr[] = { /* NEXTID attributes */
+ { "N" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr NOTE_attr[] = { /* NOTE attributes */
+ { "CLASS" T(c) },
+ { "CLEAR" T(N) },
+ { "DIR" T(N) },
+ { "ID" T(i) },
+ { "LANG" T(N) },
+ { "MD" T(N) },
+ { "ROLE" T(x) },
+ { "SRC" T(h) },
+ { "STYLE" T(N) },
+ { "TITLE" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr OBJECT_attr[] = { /* OBJECT attributes */
+ { "ALIGN" T(N) },
+ { "ARCHIVE" T(N) },
+ { "BORDER" T(N) },
+ { "CLASS" T(c) },
+ { "CLASSID" T(h) },
+ { "CODEBASE" T(h) },
+ { "CODETYPE" T(N) },
+ { "DATA" T(h) },
+ { "DECLARE" T(N) },
+ { "DIR" T(N) },
+ { "HEIGHT" T(N) },
+ { "HSPACE" T(N) },
+ { "ID" T(i) },
+ { "ISMAP" T(N) },
+ { "LANG" T(N) },
+ { "NAME" T(N) },
+ { "NOTAB" T(N) },
+ { "SHAPES" T(N) },
+ { "STANDBY" T(N) },
+ { "STYLE" T(N) },
+ { "TABINDEX" T(N) },
+ { "TITLE" T(N) },
+ { "TYPE" T(N) },
+ { "USEMAP" T(h) },
+ { "VSPACE" T(N) },
+ { "WIDTH" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr OL_attr[] = { /* OL attributes */
+ { "CLASS" T(c) },
+ { "CLEAR" T(N) },
+ { "COMPACT" T(N) },
+ { "CONTINUE" T(N) },
+ { "DIR" T(N) },
+ { "ID" T(i) },
+ { "LANG" T(N) },
+ { "SEQNUM" T(N) },
+ { "START" T(N) },
+ { "STYLE" T(N) },
+ { "TITLE" T(N) },
+ { "TYPE" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr OPTION_attr[] = { /* OPTION attributes */
+ { "CLASS" T(c) },
+ { "CLEAR" T(N) },
+ { "DIR" T(N) },
+ { "DISABLED" T(N) },
+ { "ERROR" T(N) },
+ { "ID" T(i) },
+ { "LABEL" T(N) },
+ { "LANG" T(N) },
+ { "SELECTED" T(N) },
+ { "SHAPE" T(N) },
+ { "STYLE" T(N) },
+ { "TITLE" T(N) },
+ { "VALUE" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr OVERLAY_attr[] = { /* OVERLAY attributes */
+ { "CLASS" T(c) },
+ { "HEIGHT" T(N) },
+ { "ID" T(i) },
+ { "IMAGEMAP" T(N) },
+ { "MD" T(N) },
+ { "SRC" T(h) },
+ { "STYLE" T(N) },
+ { "TITLE" T(N) },
+ { "UNITS" T(N) },
+ { "WIDTH" T(N) },
+ { "X" T(N) },
+ { "Y" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr P_attr[] = { /* P attributes */
+ { "ALIGN" T(N) },
+ { "CLASS" T(c) },
+ { "CLEAR" T(N) },
+ { "DIR" T(N) },
+ { "ID" T(i) },
+ { "LANG" T(N) },
+ { "NOWRAP" T(N) },
+ { "STYLE" T(N) },
+ { "TITLE" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr PARAM_attr[] = { /* PARAM attributes */
+ { "ACCEPT" T(N) },
+ { "ACCEPT-CHARSET" T(N) },
+ { "ACCEPT-ENCODING" T(N) },
+ { "CLASS" T(c) },
+ { "CLEAR" T(N) },
+ { "DATA" T(N) },
+ { "DIR" T(N) },
+ { "ID" T(i) },
+ { "LANG" T(N) },
+ { "NAME" T(N) },
+ { "OBJECT" T(N) },
+ { "REF" T(N) },
+ { "STYLE" T(N) },
+ { "TITLE" T(N) },
+ { "TYPE" T(N) },
+ { "VALUE" T(N) },
+ { "VALUEREF" T(N) },
+ { "VALUETYPE" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr Q_attr[] = { /* Q attributes */
+ { "CITE" T(h) },
+ { "CLASS" T(c) },
+ { "CLEAR" T(N) },
+ { "DIR" T(N) },
+ { "ID" T(i) },
+ { "LANG" T(N) },
+ { "STYLE" T(N) },
+ { "TITLE" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr SCRIPT_attr[] = { /* SCRIPT attributes */
+ { "CHARSET" T(N) },
+ { "CLASS" T(c) },
+ { "CLEAR" T(N) },
+ { "DEFER" T(N) },
+ { "DIR" T(N) },
+ { "EVENT" T(N) },
+ { "FOR" T(N) },
+ { "ID" T(i) },
+ { "LANG" T(N) },
+ { "LANGUAGE" T(N) },
+ { "NAME" T(N) },
+ { "SCRIPTENGINE" T(N) },
+ { "SRC" T(h) },
+ { "STYLE" T(N) },
+ { "TITLE" T(N) },
+ { "TYPE" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr SELECT_attr[] = { /* SELECT attributes */
+ { "ALIGN" T(N) },
+ { "CLASS" T(c) },
+ { "CLEAR" T(N) },
+ { "DIR" T(N) },
+ { "DISABLED" T(N) },
+ { "ERROR" T(N) },
+ { "HEIGHT" T(N) },
+ { "ID" T(i) },
+ { "LANG" T(N) },
+ { "MD" T(N) },
+ { "MULTIPLE" T(N) },
+ { "NAME" T(N) },
+ { "NOTAB" T(N) },
+ { "ONBLUR" T(N) },
+ { "ONCHANGE" T(N) },
+ { "ONFOCUS" T(N) },
+ { "SIZE" T(N) },
+ { "STYLE" T(N) },
+ { "TABINDEX" T(N) },
+ { "TITLE" T(N) },
+ { "UNITS" T(N) },
+ { "WIDTH" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr STYLE_attr[] = { /* STYLE attributes */
+ { "CLASS" T(c) },
+ { "DIR" T(N) },
+ { "ID" T(i) },
+ { "LANG" T(N) },
+ { "MEDIA" T(N) },
+ { "NOTATION" T(N) },
+ { "STYLE" T(N) },
+ { "TITLE" T(N) },
+ { "TYPE" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr TAB_attr[] = { /* TAB attributes */
+ { "ALIGN" T(N) },
+ { "CLASS" T(c) },
+ { "CLEAR" T(N) },
+ { "DIR" T(N) },
+ { "DP" T(N) },
+ { "ID" T(i) },
+ { "INDENT" T(N) },
+ { "LANG" T(N) },
+ { "STYLE" T(N) },
+ { "TITLE" T(N) },
+ { "TO" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr TABLE_attr[] = { /* TABLE attributes */
+ { "ALIGN" T(N) },
+ { "BACKGROUND" T(h) },
+ { "BORDER" T(N) },
+ { "CELLPADDING" T(N) },
+ { "CELLSPACING" T(N) },
+ { "CLASS" T(c) },
+ { "CLEAR" T(N) },
+ { "COLS" T(N) },
+ { "COLSPEC" T(N) },
+ { "DIR" T(N) },
+ { "DP" T(N) },
+ { "FRAME" T(N) },
+ { "ID" T(i) },
+ { "LANG" T(N) },
+ { "NOFLOW" T(N) },
+ { "NOWRAP" T(N) },
+ { "RULES" T(N) },
+ { "STYLE" T(N) },
+ { "SUMMARY" T(N) },
+ { "TITLE" T(N) },
+ { "UNITS" T(N) },
+ { "WIDTH" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr TD_attr[] = { /* TD attributes */
+ { "ABBR" T(N) },
+ { "ALIGN" T(N) },
+ { "AXES" T(N) },
+ { "AXIS" T(N) },
+ { "BACKGROUND" T(h) },
+ { "CHAR" T(N) },
+ { "CHAROFF" T(N) },
+ { "CLASS" T(c) },
+ { "CLEAR" T(N) },
+ { "COLSPAN" T(N) },
+ { "DIR" T(N) },
+ { "DP" T(N) },
+ { "HEADERS" T(N) },
+ { "HEIGHT" T(N) },
+ { "ID" T(i) },
+ { "LANG" T(N) },
+ { "NOWRAP" T(N) },
+ { "ROWSPAN" T(N) },
+ { "SCOPE" T(N) },
+ { "STYLE" T(N) },
+ { "TITLE" T(N) },
+ { "VALIGN" T(N) },
+ { "WIDTH" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr TEXTAREA_attr[] = { /* TEXTAREA attributes */
+ { "ACCEPT-CHARSET" T(N) },
+ { "ACCESSKEY" T(N) },
+ { "ALIGN" T(N) },
+ { "CLASS" T(c) },
+ { "CLEAR" T(N) },
+ { "COLS" T(N) },
+ { "DIR" T(N) },
+ { "DISABLED" T(N) },
+ { "ERROR" T(N) },
+ { "ID" T(i) },
+ { "LANG" T(N) },
+ { "NAME" T(N) },
+ { "NOTAB" T(N) },
+ { "ONBLUR" T(N) },
+ { "ONCHANGE" T(N) },
+ { "ONFOCUS" T(N) },
+ { "ONSELECT" T(N) },
+ { "READONLY" T(N) },
+ { "ROWS" T(N) },
+ { "STYLE" T(N) },
+ { "TABINDEX" T(N) },
+ { "TITLE" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr TR_attr[] = { /* TBODY attributes */
+ { "ALIGN" T(N) },
+ { "CHAR" T(N) },
+ { "CHAROFF" T(N) },
+ { "CLASS" T(c) },
+ { "CLEAR" T(N) },
+ { "DIR" T(N) },
+ { "DP" T(N) },
+ { "ID" T(i) },
+ { "LANG" T(N) },
+ { "NOWRAP" T(N) },
+ { "STYLE" T(N) },
+ { "TITLE" T(N) },
+ { "VALIGN" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+static const attr UL_attr[] = { /* DIR attributes */
+ { "CLASS" T(c) },
+ { "CLEAR" T(N) },
+ { "COMPACT" T(N) },
+ { "DINGBAT" T(N) },
+ { "DIR" T(N) },
+ { "ID" T(i) },
+ { "LANG" T(N) },
+ { "MD" T(N) },
+ { "PLAIN" T(N) },
+ { "SRC" T(h) },
+ { "STYLE" T(N) },
+ { "TITLE" T(N) },
+ { "TYPE" T(N) },
+ { "WRAP" T(N) },
+ { 0 T(N) } /* Terminate list */
+};
+
+/* *INDENT-ON* */
+
+/* justification-flags */
+#undef N
+#undef i
+#undef h
+#undef c
+#undef x
+
+#undef T
+
+/* tag-names */
+#undef A
+#undef ABBR
+#undef ACRONYM
+#undef ADDRESS
+#undef APPLET
+#undef AREA
+#undef ARTICLE
+#undef ASIDE
+#undef AU
+#undef AUTHOR
+#undef B
+#undef BANNER
+#undef BASE
+#undef BASEFONT
+#undef BDO
+#undef BGSOUND
+#undef BIG
+#undef BLINK
+#undef BLOCKQUOTE
+#undef BODY
+#undef BODYTEXT
+#undef BQ
+#undef BR
+#undef BUTTON
+#undef CAPTION
+#undef CENTER
+#undef CITE
+#undef CODE
+#undef COL
+#undef COLGROUP
+#undef COMMENT
+#undef CREDIT
+#undef DD
+#undef DEL
+#undef DEL
+#undef DFN
+#undef DIR
+#undef DIV
+#undef DL
+#undef DLC
+#undef DT
+#undef EM
+#undef EMBED
+#undef FIELDSET
+#undef FIG
+#undef FIGURE
+#undef FN
+#undef FONT
+#undef FOOTER
+#undef FORM
+#undef FRAME
+#undef FRAMESET
+#undef H1
+#undef H2
+#undef H3
+#undef H4
+#undef H5
+#undef H6
+#undef HEAD
+#undef HEADER
+#undef HR
+#undef HTML
+#undef HY
+#undef I
+#undef IFRAME
+#undef IMG
+#undef INPUT
+#undef INS
+#undef INS
+#undef ISINDEX
+#undef KBD
+#undef KEYGEN
+#undef LABEL
+#undef LEGEND
+#undef LH
+#undef LI
+#undef LINK
+#undef LISTING
+#undef MAIN
+#undef MAP
+#undef MARQUEE
+#undef MATH
+#undef MENU
+#undef META
+#undef NAV
+#undef NEXTID
+#undef NOFRAMES
+#undef NOTE
+#undef OBJECT
+#undef OL
+#undef OPTION
+#undef OVERLAY
+#undef P
+#undef PARAM
+#undef PLAINTEXT
+#undef PRE
+#undef Q
+#undef S
+#undef SAMP
+#undef SCRIPT
+#undef SECTION
+#undef SELECT
+#undef SHY
+#undef SMALL
+#undef SPAN
+#undef SPOT
+#undef STRIKE
+#undef STRONG
+#undef STYLE
+#undef SUB
+#undef SUP
+#undef TAB
+#undef TABLE
+#undef TBODY
+#undef TD
+#undef TEXTAREA
+#undef TEXTFLOW
+#undef TFOOT
+#undef TH
+#undef THEAD
+#undef TITLE
+#undef TR
+#undef TT
+#undef U
+#undef UL
+#undef VAR
+#undef WBR
+#undef XMP
+#undef OBJECT_PCDATA
+
+/* these definitions are used in the tags-tables */
+#undef P
+#undef P_
+#ifdef USE_COLOR_STYLE
+#define P_(x) #x, (sizeof #x) -1
+#define NULL_HTTag_ NULL, 0
+#else
+#define P_(x) #x
+#define NULL_HTTag_ NULL
+#endif
+
+#ifdef USE_JUSTIFY_ELTS
+#define P(x) P_(x), 1
+#define P0(x) P_(x), 0
+#define NULL_HTTag NULL_HTTag_,0
+#else
+#define P(x) P_(x)
+#define P0(x) P_(x)
+#define NULL_HTTag NULL_HTTag_
+#endif
+
+#define ATTR_DATA(name) name##_attr, HTML_##name##_ATTRIBUTES, name##_attr_type
+
+#endif /* once_HTMLDTD */
+/* *INDENT-OFF* */
+static const HTTag tags_table1[HTML_ALL_ELEMENTS] = {
+ { P(A), ATTR_DATA(A), SGML_MIXED, T_A, 0, 0},
+ { P(ABBR), ATTR_DATA(GEN), SGML_MIXED, T_ABBR, 0, 0},
+ { P(ACRONYM), ATTR_DATA(GEN), SGML_MIXED, T_ACRONYM, 0, 0},
+ { P(ADDRESS), ATTR_DATA(ADDRESS), SGML_MIXED, T_ADDRESS, 0, 0},
+ { P(APPLET), ATTR_DATA(APPLET), SGML_MIXED, T_APPLET, 0, 0},
+ { P(AREA), ATTR_DATA(AREA), SGML_EMPTY, T_AREA, 0, 0},
+ { P(ARTICLE), ATTR_DATA(GEN5), SGML_MIXED, T_ARTICLE, 0, 0},
+ { P(ASIDE), ATTR_DATA(GEN5), SGML_MIXED, T_ASIDE, 0, 0},
+ { P(AU), ATTR_DATA(GEN), SGML_MIXED, T_AU, 0, 0},
+ { P(AUTHOR), ATTR_DATA(GEN), SGML_MIXED, T_AUTHOR, 0, 0},
+ { P(B), ATTR_DATA(GEN), SGML_MIXED, T_B, 0, 0},
+ { P0(BANNER), ATTR_DATA(GEN), SGML_MIXED, T_BANNER, 0, 0},
+ { P(BASE), ATTR_DATA(BASE), SGML_EMPTY, T_BASE, 0, 0},
+ { P(BASEFONT), ATTR_DATA(FONT), SGML_EMPTY, T_BASEFONT, 0, 0},
+ { P(BDO), ATTR_DATA(GEN), SGML_MIXED, T_BDO, 0, 0},
+ { P(BGSOUND), ATTR_DATA(BGSOUND), SGML_EMPTY, T_BGSOUND, 0, 0},
+ { P(BIG), ATTR_DATA(GEN), SGML_MIXED, T_BIG, 0, 0},
+ { P(BLINK), ATTR_DATA(GEN), SGML_MIXED, T_BLINK, 0, 0},
+ { P(BLOCKQUOTE), ATTR_DATA(BQ), SGML_MIXED, T_BLOCKQUOTE, 0, 0},
+ { P(BODY), ATTR_DATA(BODY), SGML_MIXED, T_BODY, 0, 0},
+ { P(BODYTEXT), ATTR_DATA(BODYTEXT), SGML_MIXED, T_BODYTEXT, 0, 0},
+ { P(BQ), ATTR_DATA(BQ), SGML_MIXED, T_BQ, 0, 0},
+ { P(BR), ATTR_DATA(GEN), SGML_EMPTY, T_BR, 0, 0},
+ { P(BUTTON), ATTR_DATA(BUTTON), SGML_MIXED, T_BUTTON, 0, 0},
+ { P(CAPTION), ATTR_DATA(CAPTION), SGML_MIXED, T_CAPTION, 0, 0},
+ { P(CENTER), ATTR_DATA(DIV), SGML_MIXED, T_CENTER, 0, 0},
+ { P(CITE), ATTR_DATA(GEN), SGML_MIXED, T_CITE, 0, 0},
+ { P(CODE), ATTR_DATA(GEN), SGML_MIXED, T_CODE, 0, 0},
+ { P(COL), ATTR_DATA(COL), SGML_EMPTY, T_COL, 0, 0},
+ { P(COLGROUP), ATTR_DATA(COL), SGML_ELEMENT, T_COLGROUP, 0, 0},
+ { P(COMMENT), ATTR_DATA(GEN), SGML_PCDATA, T_COMMENT, 0, 0},
+ { P(CREDIT), ATTR_DATA(GEN), SGML_MIXED, T_CREDIT, 0, 0},
+ { P(DD), ATTR_DATA(GEN), SGML_MIXED, T_DD, 0, 0},
+ { P(DEL), ATTR_DATA(DEL), SGML_MIXED, T_DEL, 0, 1},
+ { P(DEL), ATTR_DATA(DEL), SGML_MIXED, T_DEL_2, 1, 1},
+ { P(DFN), ATTR_DATA(GEN), SGML_MIXED, T_DFN, 0, 0},
+ { P(DIR), ATTR_DATA(UL), SGML_MIXED, T_DIR, 0, 0},
+ { P(DIV), ATTR_DATA(DIV), SGML_MIXED, T_DIV, 0, 0},
+ { P(DL), ATTR_DATA(DL), SGML_MIXED, T_DL, 0, 0},
+ { P(DLC), ATTR_DATA(DL), SGML_MIXED, T_DLC, 0, 0},
+ { P(DT), ATTR_DATA(GEN), SGML_MIXED, T_DT, 0, 0},
+ { P(EM), ATTR_DATA(GEN), SGML_MIXED, T_EM, 0, 0},
+ { P(EMBED), ATTR_DATA(EMBED), SGML_EMPTY, T_EMBED, 0, 0},
+ { P(FIELDSET), ATTR_DATA(GEN), SGML_MIXED, T_FIELDSET, 0, 0},
+ { P(FIG), ATTR_DATA(FIG), SGML_MIXED, T_FIG, 0, 0},
+ { P(FIGURE), ATTR_DATA(GEN5), SGML_MIXED, T_FIGURE, 0, 0},
+ { P(FN), ATTR_DATA(GEN), SGML_MIXED, T_FN, 0, 0},
+ { P(FONT), ATTR_DATA(FONT), SGML_MIXED, T_FONT, 0, 0},
+ { P(FOOTER), ATTR_DATA(GEN5), SGML_MIXED, T_FOOTER, 0, 0},
+ { P(FORM), ATTR_DATA(FORM), SGML_MIXED, T_FORM, 0, 0},
+ { P(FRAME), ATTR_DATA(FRAME), SGML_EMPTY, T_FRAME, 0, 0},
+ { P(FRAMESET), ATTR_DATA(FRAMESET), SGML_ELEMENT, T_FRAMESET, 0, 0},
+ { P0(H1), ATTR_DATA(H), SGML_MIXED, T_H1, 0, 0},
+ { P0(H2), ATTR_DATA(H), SGML_MIXED, T_H2, 0, 0},
+ { P0(H3), ATTR_DATA(H), SGML_MIXED, T_H3, 0, 0},
+ { P0(H4), ATTR_DATA(H), SGML_MIXED, T_H4, 0, 0},
+ { P0(H5), ATTR_DATA(H), SGML_MIXED, T_H5, 0, 0},
+ { P0(H6), ATTR_DATA(H), SGML_MIXED, T_H6, 0, 0},
+ { P(HEAD), ATTR_DATA(GEN), SGML_ELEMENT, T_HEAD, 0, 0},
+ { P(HEADER), ATTR_DATA(GEN5), SGML_MIXED, T_HEADER, 0, 0},
+ { P(HR), ATTR_DATA(HR), SGML_EMPTY, T_HR, 0, 0},
+ { P(HTML), ATTR_DATA(GEN), SGML_MIXED, T_HTML, 0, 0},
+ { P(HY), ATTR_DATA(GEN), SGML_EMPTY, T_HY, 0, 0},
+ { P(I), ATTR_DATA(GEN), SGML_MIXED, T_I, 0, 0},
+ { P(IFRAME), ATTR_DATA(IFRAME), SGML_MIXED, T_IFRAME, 0, 0},
+ { P(IMG), ATTR_DATA(IMG), SGML_EMPTY, T_IMG, 0, 0},
+ { P(INPUT), ATTR_DATA(INPUT), SGML_EMPTY, T_INPUT, 0, 0},
+ { P(INS), ATTR_DATA(DEL), SGML_MIXED, T_INS, 0, 1},
+ { P(INS), ATTR_DATA(DEL), SGML_MIXED, T_INS_2, 1, 1},
+ { P(ISINDEX), ATTR_DATA(ISINDEX), SGML_EMPTY, T_ISINDEX, 0, 0},
+ { P(KBD), ATTR_DATA(GEN), SGML_MIXED, T_KBD, 0, 0},
+ { P(KEYGEN), ATTR_DATA(KEYGEN), SGML_EMPTY, T_KEYGEN, 0, 0},
+ { P(LABEL), ATTR_DATA(LABEL), SGML_MIXED, T_LABEL, 0, 0},
+ { P(LEGEND), ATTR_DATA(CAPTION), SGML_MIXED, T_LEGEND, 0, 0},
+ { P(LH), ATTR_DATA(GEN), SGML_MIXED, T_LH, 0, 0},
+ { P(LI), ATTR_DATA(LI), SGML_MIXED, T_LI, 0, 0},
+ { P(LINK), ATTR_DATA(LINK), SGML_EMPTY, T_LINK, 0, 0},
+ { P(LISTING), ATTR_DATA(GEN), SGML_LITTERAL,T_LISTING, 0, 0},
+ { P(MAIN), ATTR_DATA(GEN5), SGML_MIXED, T_MAIN, 0, 0},
+ { P(MAP), ATTR_DATA(MAP), SGML_ELEMENT, T_MAP, 0, 0},
+ { P(MARQUEE), ATTR_DATA(GEN), SGML_MIXED, T_MARQUEE, 0, 0},
+ { P(MATH), ATTR_DATA(MATH), SGML_PCDATA, T_MATH, 0, 0},
+ { P(MENU), ATTR_DATA(UL), SGML_MIXED, T_MENU, 0, 0},
+ { P(META), ATTR_DATA(META), SGML_EMPTY, T_META, 0, 0},
+ { P(NAV), ATTR_DATA(GEN5), SGML_MIXED, T_NAV, 0, 0},
+ { P(NEXTID), ATTR_DATA(NEXTID), SGML_EMPTY, T_NEXTID, 0, 0},
+ { P(NOFRAMES), ATTR_DATA(GEN), SGML_MIXED, T_NOFRAMES, 0, 0},
+ { P(NOTE), ATTR_DATA(NOTE), SGML_MIXED, T_NOTE, 0, 0},
+ { P(OBJECT), ATTR_DATA(OBJECT), SGML_LITTERAL,T_OBJECT, 0, 0},
+ { P(OL), ATTR_DATA(OL), SGML_MIXED, T_OL, 0, 0},
+ { P(OPTION), ATTR_DATA(OPTION), SGML_PCDATA, T_OPTION, 0, 0},
+ { P(OVERLAY), ATTR_DATA(OVERLAY), SGML_PCDATA, T_OVERLAY, 0, 0},
+ { P(P), ATTR_DATA(P), SGML_MIXED, T_P, 0, 0},
+ { P(PARAM), ATTR_DATA(PARAM), SGML_EMPTY, T_PARAM, 0, 0},
+ { P(PLAINTEXT), ATTR_DATA(GEN), SGML_LITTERAL,T_PLAINTEXT, 0, 0},
+ { P0(PRE), ATTR_DATA(GEN), SGML_MIXED, T_PRE, 0, 0},
+ { P(Q), ATTR_DATA(Q), SGML_MIXED, T_Q, 0, 0},
+ { P(S), ATTR_DATA(GEN), SGML_MIXED, T_S, 0, 0},
+ { P(SAMP), ATTR_DATA(GEN), SGML_MIXED, T_SAMP, 0, 0},
+ { P(SCRIPT), ATTR_DATA(SCRIPT), SGML_SCRIPT, T_SCRIPT, 0, 0},
+ { P(SECTION), ATTR_DATA(GEN5), SGML_MIXED, T_SECTION, 0, 0},
+ { P(SELECT), ATTR_DATA(SELECT), SGML_ELEMENT, T_SELECT, 0, 0},
+ { P(SHY), ATTR_DATA(GEN), SGML_EMPTY, T_SHY, 0, 0},
+ { P(SMALL), ATTR_DATA(GEN), SGML_MIXED, T_SMALL, 0, 0},
+ { P(SPAN), ATTR_DATA(GEN), SGML_MIXED, T_SPAN, 0, 0},
+ { P(SPOT), ATTR_DATA(GEN), SGML_EMPTY, T_SPOT, 0, 0},
+ { P(STRIKE), ATTR_DATA(GEN), SGML_MIXED, T_STRIKE, 0, 0},
+ { P(STRONG), ATTR_DATA(GEN), SGML_MIXED, T_STRONG, 0, 0},
+ { P(STYLE), ATTR_DATA(STYLE), SGML_CDATA, T_STYLE, 0, 0},
+ { P(SUB), ATTR_DATA(GEN), SGML_MIXED, T_SUB, 0, 0},
+ { P(SUP), ATTR_DATA(GEN), SGML_MIXED, T_SUP, 0, 0},
+ { P(TAB), ATTR_DATA(TAB), SGML_EMPTY, T_TAB, 0, 0},
+ { P(TABLE), ATTR_DATA(TABLE), SGML_ELEMENT, T_TABLE, 0, 0},
+ { P(TBODY), ATTR_DATA(TR), SGML_ELEMENT, T_TBODY, 0, 0},
+ { P(TD), ATTR_DATA(TD), SGML_MIXED, T_TD, 0, 0},
+ { P(TEXTAREA), ATTR_DATA(TEXTAREA), SGML_PCDATA, T_TEXTAREA, 0, 0},
+ { P(TEXTFLOW), ATTR_DATA(BODYTEXT), SGML_MIXED, T_TEXTFLOW, 0, 0},
+ { P(TFOOT), ATTR_DATA(TR), SGML_ELEMENT, T_TFOOT, 0, 0},
+ { P(TH), ATTR_DATA(TD), SGML_MIXED, T_TH, 0, 0},
+ { P(THEAD), ATTR_DATA(TR), SGML_ELEMENT, T_THEAD, 0, 0},
+ { P(TITLE), ATTR_DATA(GEN), SGML_PCDATA, T_TITLE, 0, 0},
+ { P(TR), ATTR_DATA(TR), SGML_MIXED, T_TR, 0, 0},
+ { P(TT), ATTR_DATA(GEN), SGML_MIXED, T_TT, 0, 0},
+ { P(U), ATTR_DATA(GEN), SGML_MIXED, T_U, 0, 0},
+ { P(UL), ATTR_DATA(UL), SGML_MIXED, T_UL, 0, 0},
+ { P(VAR), ATTR_DATA(GEN), SGML_MIXED, T_VAR, 0, 0},
+ { P(WBR), ATTR_DATA(GEN), SGML_EMPTY, T_WBR, 0, 0},
+ { P0(XMP), ATTR_DATA(GEN), SGML_LITTERAL,T_XMP, 0, 0},
+/* additional (alternative variants), not counted in HTML_ELEMENTS: */
+/* This one will be used as a temporary substitute within the parser when
+ it has been signalled to parse OBJECT content as MIXED. - kw */
+ { P(OBJECT), ATTR_DATA(OBJECT), SGML_MIXED, T_OBJECT_PCDATA, 0, 0},
+};
+/* *INDENT-ON* */
+
+#endif /* src_HTMLDTD_H1 */
diff --git a/WWW/Library/Implementation/src1_HTMLDTD.txt b/WWW/Library/Implementation/src1_HTMLDTD.txt
new file mode 100644
index 0000000..df1521b
--- /dev/null
+++ b/WWW/Library/Implementation/src1_HTMLDTD.txt
@@ -0,0 +1,3901 @@
+60 attr_types
+ 0:align
+ 1 attributes:
+ 0:0:ALIGN
+ 1:bgcolor
+ 1 attributes:
+ 0:0:BGCOLOR
+ 2:cellalign
+ 4 attributes:
+ 0:0:ALIGN
+ 1:0:CHAR
+ 2:0:CHAROFF
+ 3:0:VALIGN
+ 3:core
+ 4 attributes:
+ 0:4:CLASS
+ 1:1:ID
+ 2:0:STYLE
+ 3:0:TITLE
+ 4:events
+ 10 attributes:
+ 0:0:ONCLICK
+ 1:0:ONDBLCLICK
+ 2:0:ONKEYDOWN
+ 3:0:ONKEYPRESS
+ 4:0:ONKEYUP
+ 5:0:ONMOUSEDOWN
+ 6:0:ONMOUSEMOVE
+ 7:0:ONMOUSEOUT
+ 8:0:ONMOUSEOVER
+ 9:0:ONMOUSEUP
+ 5:i18n
+ 2 attributes:
+ 0:0:DIR
+ 1:0:LANG
+ 6:A
+ 19 attributes:
+ 0:0:ACCESSKEY
+ 1:0:CHARSET
+ 2:0:CLEAR
+ 3:0:COORDS
+ 4:2:HREF
+ 5:0:HREFLANG
+ 6:0:ISMAP
+ 7:0:MD
+ 8:1:NAME
+ 9:0:NOTAB
+ 10:0:ONBLUR
+ 11:0:ONFOCUS
+ 12:0:REL
+ 13:0:REV
+ 14:0:SHAPE
+ 15:0:TABINDEX
+ 16:0:TARGET
+ 17:0:TYPE
+ 18:0:URN
+ 7:ADDRESS
+ 2 attributes:
+ 0:0:CLEAR
+ 1:0:NOWRAP
+ 8:APPLET
+ 10 attributes:
+ 0:0:ALT
+ 1:0:CLEAR
+ 2:0:CODE
+ 3:2:CODEBASE
+ 4:0:DOWNLOAD
+ 5:0:HEIGHT
+ 6:0:HSPACE
+ 7:1:NAME
+ 8:0:VSPACE
+ 9:0:WIDTH
+ 9:AREA
+ 12 attributes:
+ 0:0:ACCESSKEY
+ 1:0:ALT
+ 2:0:CLEAR
+ 3:0:COORDS
+ 4:2:HREF
+ 5:0:NOHREF
+ 6:0:NOTAB
+ 7:0:ONBLUR
+ 8:0:ONFOCUS
+ 9:0:SHAPE
+ 10:0:TABINDEX
+ 11:0:TARGET
+ 10:BASE
+ 2 attributes:
+ 0:2:HREF
+ 1:0:TARGET
+ 11:BGSOUND
+ 3 attributes:
+ 0:0:CLEAR
+ 1:0:LOOP
+ 2:2:SRC
+ 12:BODY
+ 8 attributes:
+ 0:0:ALINK
+ 1:2:BACKGROUND
+ 2:0:CLEAR
+ 3:0:LINK
+ 4:0:ONLOAD
+ 5:0:ONUNLOAD
+ 6:0:TEXT
+ 7:0:VLINK
+ 13:BODYTEXT
+ 8 attributes:
+ 0:0:CLEAR
+ 1:0:DATA
+ 2:0:NAME
+ 3:0:OBJECT
+ 4:0:REF
+ 5:0:TYPE
+ 6:0:VALUE
+ 7:0:VALUETYPE
+ 14:BQ
+ 3 attributes:
+ 0:2:CITE
+ 1:0:CLEAR
+ 2:0:NOWRAP
+ 15:BUTTON
+ 11 attributes:
+ 0:0:ACCESSKEY
+ 1:0:CLEAR
+ 2:0:DISABLED
+ 3:0:FORMACTION
+ 4:0:NAME
+ 5:0:ONBLUR
+ 6:0:ONFOCUS
+ 7:0:READONLY
+ 8:0:TABINDEX
+ 9:0:TYPE
+ 10:0:VALUE
+ 16:CAPTION
+ 2 attributes:
+ 0:0:ACCESSKEY
+ 1:0:CLEAR
+ 17:COL
+ 3 attributes:
+ 0:0:CLEAR
+ 1:0:SPAN
+ 2:0:WIDTH
+ 18:DEL
+ 2 attributes:
+ 0:0:CITE
+ 1:0:DATETIME
+ 19:DIV
+ 1 attributes:
+ 0:0:CLEAR
+ 20:DL
+ 2 attributes:
+ 0:0:CLEAR
+ 1:0:COMPACT
+ 21:EMBED
+ 14 attributes:
+ 0:0:ALT
+ 1:0:BORDER
+ 2:0:CLEAR
+ 3:0:HEIGHT
+ 4:0:IMAGEMAP
+ 5:0:ISMAP
+ 6:0:MD
+ 7:1:NAME
+ 8:0:NOFLOW
+ 9:0:PARAMS
+ 10:2:SRC
+ 11:0:UNITS
+ 12:0:USEMAP
+ 13:0:WIDTH
+ 22:FIG
+ 10 attributes:
+ 0:0:BORDER
+ 1:0:CLEAR
+ 2:0:HEIGHT
+ 3:0:IMAGEMAP
+ 4:0:ISOBJECT
+ 5:0:MD
+ 6:0:NOFLOW
+ 7:2:SRC
+ 8:0:UNITS
+ 9:0:WIDTH
+ 23:FONT
+ 5 attributes:
+ 0:0:CLEAR
+ 1:0:COLOR
+ 2:0:END
+ 3:0:FACE
+ 4:0:SIZE
+ 24:FORM
+ 11 attributes:
+ 0:0:ACCEPT
+ 1:0:ACCEPT-CHARSET
+ 2:2:ACTION
+ 3:0:CLEAR
+ 4:0:ENCTYPE
+ 5:0:METHOD
+ 6:0:ONRESET
+ 7:0:ONSUBMIT
+ 8:0:SCRIPT
+ 9:0:SUBJECT
+ 10:0:TARGET
+ 25:FRAME
+ 8 attributes:
+ 0:0:FRAMEBORDER
+ 1:2:LONGDESC
+ 2:0:MARGINHEIGHT
+ 3:0:MARGINWIDTH
+ 4:0:NAME
+ 5:0:NORESIZE
+ 6:0:SCROLLING
+ 7:2:SRC
+ 26:FRAMESET
+ 4 attributes:
+ 0:0:COLS
+ 1:0:ONLOAD
+ 2:0:ONUNLOAD
+ 3:0:ROWS
+ 27:GEN
+ 1 attributes:
+ 0:0:CLEAR
+ 28:GEN5
+ 1 attributes:
+ 0:0:ROLE
+ 29:H
+ 7 attributes:
+ 0:0:CLEAR
+ 1:0:DINGBAT
+ 2:0:MD
+ 3:0:NOWRAP
+ 4:0:SEQNUM
+ 5:0:SKIP
+ 6:2:SRC
+ 30:HR
+ 6 attributes:
+ 0:0:CLEAR
+ 1:0:MD
+ 2:0:NOSHADE
+ 3:0:SIZE
+ 4:2:SRC
+ 5:0:WIDTH
+ 31:IFRAME
+ 9 attributes:
+ 0:0:FRAMEBORDER
+ 1:0:HEIGHT
+ 2:2:LONGDESC
+ 3:0:MARGINHEIGHT
+ 4:0:MARGINWIDTH
+ 5:0:NAME
+ 6:0:SCROLLING
+ 7:2:SRC
+ 8:0:WIDTH
+ 32:IMG
+ 15 attributes:
+ 0:0:ALT
+ 1:0:BORDER
+ 2:0:CLEAR
+ 3:0:HEIGHT
+ 4:0:HSPACE
+ 5:0:ISMAP
+ 6:0:ISOBJECT
+ 7:2:LONGDESC
+ 8:0:MD
+ 9:0:NAME
+ 10:2:SRC
+ 11:0:UNITS
+ 12:2:USEMAP
+ 13:0:VSPACE
+ 14:0:WIDTH
+ 33:INPUT
+ 28 attributes:
+ 0:0:ACCEPT
+ 1:0:ACCEPT-CHARSET
+ 2:0:ACCESSKEY
+ 3:0:ALT
+ 4:0:CHECKED
+ 5:0:CLEAR
+ 6:0:DISABLED
+ 7:0:ERROR
+ 8:0:HEIGHT
+ 9:0:ISMAP
+ 10:0:MAX
+ 11:0:MAXLENGTH
+ 12:0:MD
+ 13:0:MIN
+ 14:0:NAME
+ 15:0:NOTAB
+ 16:0:ONBLUR
+ 17:0:ONCHANGE
+ 18:0:ONFOCUS
+ 19:0:ONSELECT
+ 20:0:READONLY
+ 21:0:SIZE
+ 22:2:SRC
+ 23:0:TABINDEX
+ 24:0:TYPE
+ 25:0:USEMAP
+ 26:0:VALUE
+ 27:0:WIDTH
+ 34:ISINDEX
+ 3 attributes:
+ 0:2:ACTION
+ 1:2:HREF
+ 2:0:PROMPT
+ 35:KEYGEN
+ 2 attributes:
+ 0:0:CHALLENGE
+ 1:0:NAME
+ 36:LABEL
+ 5 attributes:
+ 0:0:ACCESSKEY
+ 1:0:CLEAR
+ 2:0:FOR
+ 3:0:ONBLUR
+ 4:0:ONFOCUS
+ 37:LI
+ 7 attributes:
+ 0:0:CLEAR
+ 1:0:DINGBAT
+ 2:0:MD
+ 3:0:SKIP
+ 4:2:SRC
+ 5:0:TYPE
+ 6:0:VALUE
+ 38:LINK
+ 8 attributes:
+ 0:0:CHARSET
+ 1:2:HREF
+ 2:0:HREFLANG
+ 3:0:MEDIA
+ 4:0:REL
+ 5:0:REV
+ 6:0:TARGET
+ 7:0:TYPE
+ 39:MAP
+ 2 attributes:
+ 0:0:CLEAR
+ 1:1:NAME
+ 40:MATH
+ 2 attributes:
+ 0:0:BOX
+ 1:0:CLEAR
+ 41:META
+ 5 attributes:
+ 0:0:CHARSET
+ 1:0:CONTENT
+ 2:0:HTTP-EQUIV
+ 3:0:NAME
+ 4:0:SCHEME
+ 42:NEXTID
+ 1 attributes:
+ 0:0:N
+ 43:NOTE
+ 4 attributes:
+ 0:0:CLEAR
+ 1:0:MD
+ 2:8:ROLE
+ 3:2:SRC
+ 44:OBJECT
+ 19 attributes:
+ 0:0:ARCHIVE
+ 1:0:BORDER
+ 2:2:CLASSID
+ 3:2:CODEBASE
+ 4:0:CODETYPE
+ 5:2:DATA
+ 6:0:DECLARE
+ 7:0:HEIGHT
+ 8:0:HSPACE
+ 9:0:ISMAP
+ 10:0:NAME
+ 11:0:NOTAB
+ 12:0:SHAPES
+ 13:0:STANDBY
+ 14:0:TABINDEX
+ 15:0:TYPE
+ 16:2:USEMAP
+ 17:0:VSPACE
+ 18:0:WIDTH
+ 45:OL
+ 6 attributes:
+ 0:0:CLEAR
+ 1:0:COMPACT
+ 2:0:CONTINUE
+ 3:0:SEQNUM
+ 4:0:START
+ 5:0:TYPE
+ 46:OPTION
+ 7 attributes:
+ 0:0:CLEAR
+ 1:0:DISABLED
+ 2:0:ERROR
+ 3:0:LABEL
+ 4:0:SELECTED
+ 5:0:SHAPE
+ 6:0:VALUE
+ 47:OVERLAY
+ 8 attributes:
+ 0:0:HEIGHT
+ 1:0:IMAGEMAP
+ 2:0:MD
+ 3:2:SRC
+ 4:0:UNITS
+ 5:0:WIDTH
+ 6:0:X
+ 7:0:Y
+ 48:P
+ 2 attributes:
+ 0:0:CLEAR
+ 1:0:NOWRAP
+ 49:PARAM
+ 12 attributes:
+ 0:0:ACCEPT
+ 1:0:ACCEPT-CHARSET
+ 2:0:ACCEPT-ENCODING
+ 3:0:CLEAR
+ 4:0:DATA
+ 5:0:NAME
+ 6:0:OBJECT
+ 7:0:REF
+ 8:0:TYPE
+ 9:0:VALUE
+ 10:0:VALUEREF
+ 11:0:VALUETYPE
+ 50:Q
+ 2 attributes:
+ 0:2:CITE
+ 1:0:CLEAR
+ 51:SCRIPT
+ 10 attributes:
+ 0:0:CHARSET
+ 1:0:CLEAR
+ 2:0:DEFER
+ 3:0:EVENT
+ 4:0:FOR
+ 5:0:LANGUAGE
+ 6:0:NAME
+ 7:0:SCRIPTENGINE
+ 8:2:SRC
+ 9:0:TYPE
+ 52:SELECT
+ 15 attributes:
+ 0:0:CLEAR
+ 1:0:DISABLED
+ 2:0:ERROR
+ 3:0:HEIGHT
+ 4:0:MD
+ 5:0:MULTIPLE
+ 6:0:NAME
+ 7:0:NOTAB
+ 8:0:ONBLUR
+ 9:0:ONCHANGE
+ 10:0:ONFOCUS
+ 11:0:SIZE
+ 12:0:TABINDEX
+ 13:0:UNITS
+ 14:0:WIDTH
+ 53:STYLE
+ 3 attributes:
+ 0:0:MEDIA
+ 1:0:NOTATION
+ 2:0:TYPE
+ 54:TAB
+ 4 attributes:
+ 0:0:CLEAR
+ 1:0:DP
+ 2:0:INDENT
+ 3:0:TO
+ 55:TABLE
+ 15 attributes:
+ 0:2:BACKGROUND
+ 1:0:BORDER
+ 2:0:CELLPADDING
+ 3:0:CELLSPACING
+ 4:0:CLEAR
+ 5:0:COLS
+ 6:0:COLSPEC
+ 7:0:DP
+ 8:0:FRAME
+ 9:0:NOFLOW
+ 10:0:NOWRAP
+ 11:0:RULES
+ 12:0:SUMMARY
+ 13:0:UNITS
+ 14:0:WIDTH
+ 56:TD
+ 13 attributes:
+ 0:0:ABBR
+ 1:0:AXES
+ 2:0:AXIS
+ 3:2:BACKGROUND
+ 4:0:CLEAR
+ 5:0:COLSPAN
+ 6:0:DP
+ 7:0:HEADERS
+ 8:0:HEIGHT
+ 9:0:NOWRAP
+ 10:0:ROWSPAN
+ 11:0:SCOPE
+ 12:0:WIDTH
+ 57:TEXTAREA
+ 15 attributes:
+ 0:0:ACCEPT-CHARSET
+ 1:0:ACCESSKEY
+ 2:0:CLEAR
+ 3:0:COLS
+ 4:0:DISABLED
+ 5:0:ERROR
+ 6:0:NAME
+ 7:0:NOTAB
+ 8:0:ONBLUR
+ 9:0:ONCHANGE
+ 10:0:ONFOCUS
+ 11:0:ONSELECT
+ 12:0:READONLY
+ 13:0:ROWS
+ 14:0:TABINDEX
+ 58:TR
+ 3 attributes:
+ 0:0:CLEAR
+ 1:0:DP
+ 2:0:NOWRAP
+ 59:UL
+ 8 attributes:
+ 0:0:CLEAR
+ 1:0:COMPACT
+ 2:0:DINGBAT
+ 3:0:MD
+ 4:0:PLAIN
+ 5:2:SRC
+ 6:0:TYPE
+ 7:0:WRAP
+128 tags
+ 0:A
+ justify
+ 25 attributes:
+ 0:0:ACCESSKEY
+ 1:0:CHARSET
+ 2:4:CLASS
+ 3:0:CLEAR
+ 4:0:COORDS
+ 5:0:DIR
+ 6:2:HREF
+ 7:0:HREFLANG
+ 8:1:ID
+ 9:0:ISMAP
+ 10:0:LANG
+ 11:0:MD
+ 12:1:NAME
+ 13:0:NOTAB
+ 14:0:ONBLUR
+ 15:0:ONFOCUS
+ 16:0:REL
+ 17:0:REV
+ 18:0:SHAPE
+ 19:0:STYLE
+ 20:0:TABINDEX
+ 21:0:TARGET
+ 22:0:TITLE
+ 23:0:TYPE
+ 24:0:URN
+ 4 attr_types
+ core
+ events
+ i18n
+ A
+ contents: SGML_MIXED
+ tagclass: Alike
+ contains: FONTlike EMlike MATHlike BRlike APPLETlike MAPlike
+ icontains: FONTlike EMlike MATHlike formula Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike
+ contained: FONTlike EMlike MATHlike FORMlike Plike DIVlike LIlike BRlike APPLETlike HRlike outer BODYlike
+ icontained: FONTlike EMlike MATHlike TRlike FORMlike Plike DIVlike ULlike BRlike APPLETlike HRlike outer BODYlike HEADstuff
+ canclose: FONTlike EMlike MATHlike Alike SELECTlike APPLETlike HRlike same
+ flags: mafse nreie
+ 1:ABBR
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN
+ contents: SGML_MIXED
+ tagclass: EMlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike BRlike APPLETlike MAPlike same
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: FONTlike EMlike MATHlike Alike FORMlike Plike DIVlike LIlike BRlike APPLETlike HRlike BODYlike same
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike HEADstuff same
+ canclose: FONTlike EMlike
+ flags:
+ 2:ACRONYM
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN
+ contents: SGML_MIXED
+ tagclass: EMlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike BRlike APPLETlike MAPlike same
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: FONTlike EMlike MATHlike Alike FORMlike Plike DIVlike LIlike BRlike APPLETlike HRlike BODYlike same
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike HEADstuff same
+ canclose: FONTlike EMlike
+ flags:
+ 3:ADDRESS
+ justify
+ 8 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:NOWRAP
+ 6:0:STYLE
+ 7:0:TITLE
+ 3 attr_types
+ core
+ i18n
+ ADDRESS
+ contents: SGML_MIXED
+ tagclass: DIVlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike Plike BRlike APPLETlike HRlike MAPlike
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: FORMlike DIVlike LIlike APPLETlike HRlike outer BODYlike DELlike
+ icontained: FONTlike EMlike MATHlike Alike TRlike FORMlike Plike DIVlike LIlike ULlike APPLETlike HRlike outer BODYlike same DELlike
+ canclose: FONTlike EMlike MATHlike formula Plike DIVlike same
+ flags:
+ 4:APPLET
+ justify
+ 17 attributes:
+ 0:0:ALIGN
+ 1:0:ALT
+ 2:4:CLASS
+ 3:0:CLEAR
+ 4:0:CODE
+ 5:2:CODEBASE
+ 6:0:DIR
+ 7:0:DOWNLOAD
+ 8:0:HEIGHT
+ 9:0:HSPACE
+ 10:1:ID
+ 11:0:LANG
+ 12:1:NAME
+ 13:0:STYLE
+ 14:0:TITLE
+ 15:0:VSPACE
+ 16:0:WIDTH
+ 4 attr_types
+ align
+ core
+ i18n
+ APPLET
+ contents: SGML_MIXED
+ tagclass: APPLETlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike FORMlike BRlike APPLETlike MAPlike
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: FONTlike EMlike MATHlike Alike formula FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike same
+ canclose: FONTlike EMlike MATHlike Alike BRlike APPLETlike same
+ flags:
+ 5:AREA
+ justify
+ 18 attributes:
+ 0:0:ACCESSKEY
+ 1:0:ALT
+ 2:4:CLASS
+ 3:0:CLEAR
+ 4:0:COORDS
+ 5:0:DIR
+ 6:2:HREF
+ 7:1:ID
+ 8:0:LANG
+ 9:0:NOHREF
+ 10:0:NOTAB
+ 11:0:ONBLUR
+ 12:0:ONFOCUS
+ 13:0:SHAPE
+ 14:0:STYLE
+ 15:0:TABINDEX
+ 16:0:TARGET
+ 17:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ AREA
+ contents: SGML_EMPTY
+ tagclass: MAPlike
+ contains:
+ icontains:
+ contained: MAPlike
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike outer BODYlike
+ canclose: FONTlike EMlike MATHlike Alike formula Plike DIVlike LIlike ULlike
+ flags: endO
+ 6:ARTICLE
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:DIR
+ 2:1:ID
+ 3:0:LANG
+ 4:0:ROLE
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN5
+ contents: SGML_MIXED
+ tagclass: DIVlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike FORMlike Plike DIVlike ULlike BRlike APPLETlike HRlike MAPlike same
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: TRlike FORMlike DIVlike LIlike APPLETlike HRlike outer BODYlike same DELlike
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike same DELlike
+ canclose: FONTlike EMlike MATHlike Alike formula Plike DIVlike same
+ flags: mafse
+ 7:ASIDE
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:DIR
+ 2:1:ID
+ 3:0:LANG
+ 4:0:ROLE
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN5
+ contents: SGML_MIXED
+ tagclass: DIVlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike FORMlike Plike DIVlike ULlike BRlike APPLETlike HRlike MAPlike same
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: TRlike FORMlike DIVlike LIlike APPLETlike HRlike outer BODYlike same DELlike
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike same DELlike
+ canclose: FONTlike EMlike MATHlike Alike formula Plike DIVlike same
+ flags: mafse
+ 8:AU
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN
+ contents: SGML_MIXED
+ tagclass: EMlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike BRlike APPLETlike MAPlike same
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: FONTlike EMlike MATHlike Alike FORMlike Plike DIVlike LIlike BRlike APPLETlike HRlike BODYlike same
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike HEADstuff same
+ canclose: FONTlike EMlike
+ flags:
+ 9:AUTHOR
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN
+ contents: SGML_MIXED
+ tagclass: EMlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike BRlike APPLETlike MAPlike same
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: FONTlike EMlike MATHlike Alike FORMlike Plike DIVlike LIlike BRlike APPLETlike HRlike BODYlike same
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike HEADstuff same
+ canclose: FONTlike EMlike
+ flags:
+ 10:B
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN
+ contents: SGML_MIXED
+ tagclass: FONTlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike BRlike APPLETlike MAPlike same
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike BODYlike same
+ contained: FONTlike EMlike MATHlike Alike FORMlike Plike DIVlike LIlike BRlike APPLETlike HRlike BODYlike same
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike HEADstuff same
+ canclose: FONTlike
+ flags: mafse nreie
+ 11:BANNER
+ nojustify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN
+ contents: SGML_MIXED
+ tagclass: DIVlike
+ contains: FONTlike EMlike MATHlike Alike FORMlike Plike DIVlike ULlike BRlike APPLETlike HRlike MAPlike
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike
+ contained: outer BODYlike DELlike
+ icontained: outer BODYlike DELlike
+ canclose: FONTlike EMlike MATHlike Alike formula Plike DIVlike same
+ flags:
+ 12:BASE
+ justify
+ 6 attributes:
+ 0:4:CLASS
+ 1:2:HREF
+ 2:1:ID
+ 3:0:STYLE
+ 4:0:TARGET
+ 5:0:TITLE
+ 2 attr_types
+ core
+ BASE
+ contents: SGML_EMPTY
+ tagclass: HEADstuff
+ contains:
+ icontains:
+ contained: outer HEADstuff
+ icontained: outer HEADstuff
+ canclose: FONTlike EMlike MATHlike Alike same
+ flags: endO
+ 13:BASEFONT
+ justify
+ 11 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:COLOR
+ 3:0:DIR
+ 4:0:END
+ 5:0:FACE
+ 6:1:ID
+ 7:0:LANG
+ 8:0:SIZE
+ 9:0:STYLE
+ 10:0:TITLE
+ 3 attr_types
+ core
+ i18n
+ FONT
+ contents: SGML_EMPTY
+ tagclass: BRlike
+ contains:
+ icontains:
+ contained: FONTlike EMlike MATHlike Alike TRlike FORMlike Plike DIVlike LIlike BRlike APPLETlike HRlike outer BODYlike
+ icontained: FONTlike EMlike MATHlike Alike TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike
+ canclose: BRlike APPLETlike HRlike MAPlike same
+ flags: endO
+ 14:BDO
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN
+ contents: SGML_MIXED
+ tagclass: Plike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike BRlike APPLETlike MAPlike
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: FORMlike DIVlike LIlike APPLETlike HRlike outer BODYlike DELlike
+ icontained: FONTlike EMlike MATHlike Alike TRlike FORMlike Plike DIVlike LIlike ULlike APPLETlike HRlike outer BODYlike same DELlike
+ canclose: FONTlike EMlike MATHlike Alike formula TRlike Plike DIVlike
+ flags:
+ 15:BGSOUND
+ justify
+ 9 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:LOOP
+ 6:2:SRC
+ 7:0:STYLE
+ 8:0:TITLE
+ 3 attr_types
+ core
+ i18n
+ BGSOUND
+ contents: SGML_EMPTY
+ tagclass: BRlike
+ contains:
+ icontains:
+ contained: FONTlike EMlike MATHlike Alike TRlike FORMlike Plike DIVlike LIlike BRlike APPLETlike HRlike outer BODYlike HEADstuff
+ icontained: FONTlike EMlike MATHlike Alike TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike HEADstuff
+ canclose: FONTlike EMlike MATHlike Alike Plike DIVlike BRlike APPLETlike HRlike same
+ flags: endO
+ 16:BIG
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN
+ contents: SGML_MIXED
+ tagclass: FONTlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike BRlike APPLETlike MAPlike same
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: FONTlike EMlike MATHlike Alike FORMlike Plike DIVlike LIlike BRlike APPLETlike HRlike BODYlike same
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike HEADstuff same
+ canclose: FONTlike
+ flags: mafse nreie
+ 17:BLINK
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN
+ contents: SGML_MIXED
+ tagclass: FONTlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike BRlike APPLETlike MAPlike same
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: FONTlike EMlike MATHlike Alike FORMlike Plike DIVlike LIlike BRlike APPLETlike HRlike BODYlike same
+ icontained: FONTlike EMlike MATHlike Alike TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike HEADstuff same
+ canclose: FONTlike
+ flags: mafse nreie
+ 18:BLOCKQUOTE
+ justify
+ 9 attributes:
+ 0:2:CITE
+ 1:4:CLASS
+ 2:0:CLEAR
+ 3:0:DIR
+ 4:1:ID
+ 5:0:LANG
+ 6:0:NOWRAP
+ 7:0:STYLE
+ 8:0:TITLE
+ 3 attr_types
+ core
+ i18n
+ BQ
+ contents: SGML_MIXED
+ tagclass: DIVlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike FORMlike Plike DIVlike ULlike BRlike APPLETlike HRlike MAPlike BODYlike same
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike BODYlike same
+ contained: FORMlike DIVlike LIlike APPLETlike HRlike outer BODYlike same DELlike
+ icontained: FONTlike EMlike MATHlike Alike TRlike FORMlike Plike DIVlike LIlike ULlike APPLETlike HRlike outer BODYlike same DELlike
+ canclose: FONTlike EMlike MATHlike Alike formula Plike DIVlike same
+ flags:
+ 19:BODY
+ justify
+ 15 attributes:
+ 0:0:ALINK
+ 1:2:BACKGROUND
+ 2:0:BGCOLOR
+ 3:4:CLASS
+ 4:0:CLEAR
+ 5:0:DIR
+ 6:1:ID
+ 7:0:LANG
+ 8:0:LINK
+ 9:0:ONLOAD
+ 10:0:ONUNLOAD
+ 11:0:STYLE
+ 12:0:TEXT
+ 13:0:TITLE
+ 14:0:VLINK
+ 4 attr_types
+ bgcolor
+ core
+ i18n
+ BODY
+ contents: SGML_MIXED
+ tagclass: BODYlike
+ contains: FONTlike EMlike MATHlike Alike FORMlike Plike DIVlike ULlike BRlike APPLETlike HRlike MAPlike BODYlike DELlike
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike BODYlike DELlike
+ contained: outer BODYlike
+ icontained: outer BODYlike
+ canclose: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike outer HEADstuff same
+ flags: endO startO
+ 20:BODYTEXT
+ justify
+ 14 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DATA
+ 3:0:DIR
+ 4:1:ID
+ 5:0:LANG
+ 6:0:NAME
+ 7:0:OBJECT
+ 8:0:REF
+ 9:0:STYLE
+ 10:0:TITLE
+ 11:0:TYPE
+ 12:0:VALUE
+ 13:0:VALUETYPE
+ 3 attr_types
+ core
+ i18n
+ BODYTEXT
+ contents: SGML_MIXED
+ tagclass: BODYlike
+ contains: FONTlike EMlike MATHlike Alike FORMlike Plike DIVlike ULlike BRlike APPLETlike HRlike MAPlike DELlike
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike BODYlike same DELlike
+ contained: DIVlike outer BODYlike
+ icontained: FONTlike EMlike MATHlike Alike TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike same
+ canclose: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike Plike BRlike APPLETlike HRlike MAPlike same
+ flags: endO startO
+ 21:BQ
+ justify
+ 9 attributes:
+ 0:2:CITE
+ 1:4:CLASS
+ 2:0:CLEAR
+ 3:0:DIR
+ 4:1:ID
+ 5:0:LANG
+ 6:0:NOWRAP
+ 7:0:STYLE
+ 8:0:TITLE
+ 3 attr_types
+ core
+ i18n
+ BQ
+ contents: SGML_MIXED
+ tagclass: DIVlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike FORMlike Plike DIVlike ULlike BRlike APPLETlike HRlike MAPlike BODYlike same
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike BODYlike same
+ contained: FORMlike DIVlike LIlike APPLETlike HRlike outer BODYlike same DELlike
+ icontained: FONTlike EMlike MATHlike Alike TRlike FORMlike Plike DIVlike LIlike ULlike APPLETlike HRlike outer BODYlike same DELlike
+ canclose: FONTlike EMlike MATHlike Alike formula Plike DIVlike same
+ flags:
+ 22:BR
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN
+ contents: SGML_EMPTY
+ tagclass: BRlike
+ contains:
+ icontains:
+ contained: FONTlike EMlike MATHlike Alike formula TRlike FORMlike Plike DIVlike LIlike BRlike APPLETlike HRlike outer BODYlike
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike HEADstuff
+ canclose: FONTlike EMlike MATHlike Alike formula BRlike same
+ flags: endO
+ 23:BUTTON
+ justify
+ 17 attributes:
+ 0:0:ACCESSKEY
+ 1:4:CLASS
+ 2:0:CLEAR
+ 3:0:DIR
+ 4:0:DISABLED
+ 5:0:FORMACTION
+ 6:1:ID
+ 7:0:LANG
+ 8:0:NAME
+ 9:0:ONBLUR
+ 10:0:ONFOCUS
+ 11:0:READONLY
+ 12:0:STYLE
+ 13:0:TABINDEX
+ 14:0:TITLE
+ 15:0:TYPE
+ 16:0:VALUE
+ 4 attr_types
+ core
+ events
+ i18n
+ BUTTON
+ contents: SGML_MIXED
+ tagclass: APPLETlike
+ contains: FONTlike EMlike MATHlike Plike DIVlike ULlike BRlike APPLETlike MAPlike
+ icontains: FONTlike EMlike MATHlike formula TRlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike
+ contained: FONTlike EMlike MATHlike Alike FORMlike Plike DIVlike LIlike BRlike APPLETlike
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike
+ canclose: FONTlike EMlike MATHlike Alike formula SELECTlike Plike BRlike same
+ flags:
+ 24:CAPTION
+ justify
+ 9 attributes:
+ 0:0:ACCESSKEY
+ 1:0:ALIGN
+ 2:4:CLASS
+ 3:0:CLEAR
+ 4:0:DIR
+ 5:1:ID
+ 6:0:LANG
+ 7:0:STYLE
+ 8:0:TITLE
+ 5 attr_types
+ align
+ core
+ events
+ i18n
+ CAPTION
+ contents: SGML_MIXED
+ tagclass: Plike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike BRlike APPLETlike MAPlike
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: DIVlike ULlike APPLETlike HRlike DELlike
+ icontained: FONTlike EMlike MATHlike TRlike FORMlike Plike DIVlike LIlike ULlike APPLETlike HRlike outer BODYlike same DELlike
+ canclose: FONTlike EMlike MATHlike Alike formula SELECTlike Plike DIVlike same
+ flags:
+ 25:CENTER
+ justify
+ 8 attributes:
+ 0:0:ALIGN
+ 1:4:CLASS
+ 2:0:CLEAR
+ 3:0:DIR
+ 4:1:ID
+ 5:0:LANG
+ 6:0:STYLE
+ 7:0:TITLE
+ 4 attr_types
+ align
+ core
+ i18n
+ DIV
+ contents: SGML_MIXED
+ tagclass: DIVlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike FORMlike Plike DIVlike ULlike BRlike APPLETlike HRlike MAPlike same
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: FORMlike DIVlike LIlike APPLETlike HRlike outer BODYlike same DELlike
+ icontained: FONTlike EMlike MATHlike TRlike FORMlike Plike DIVlike LIlike ULlike APPLETlike HRlike outer BODYlike same DELlike
+ canclose: FONTlike EMlike MATHlike Alike formula Plike DIVlike LIlike same
+ flags:
+ 26:CITE
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN
+ contents: SGML_MIXED
+ tagclass: EMlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike BRlike APPLETlike MAPlike same
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: FONTlike EMlike MATHlike Alike FORMlike Plike DIVlike LIlike BRlike APPLETlike HRlike BODYlike same
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike HEADstuff same
+ canclose: EMlike
+ flags: nreie
+ 27:CODE
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN
+ contents: SGML_MIXED
+ tagclass: EMlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike BRlike APPLETlike MAPlike same
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: FONTlike EMlike MATHlike Alike FORMlike Plike DIVlike LIlike BRlike APPLETlike HRlike BODYlike same
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike HEADstuff same
+ canclose: EMlike
+ flags:
+ 28:COL
+ justify
+ 13 attributes:
+ 0:0:ALIGN
+ 1:0:CHAR
+ 2:0:CHAROFF
+ 3:4:CLASS
+ 4:0:CLEAR
+ 5:0:DIR
+ 6:1:ID
+ 7:0:LANG
+ 8:0:SPAN
+ 9:0:STYLE
+ 10:0:TITLE
+ 11:0:VALIGN
+ 12:0:WIDTH
+ 5 attr_types
+ cellalign
+ core
+ events
+ i18n
+ COL
+ contents: SGML_EMPTY
+ tagclass: HRlike
+ contains:
+ icontains:
+ contained: TRlike ULlike
+ icontained: FONTlike EMlike MATHlike TRlike FORMlike Plike DIVlike LIlike ULlike APPLETlike HRlike outer BODYlike
+ canclose: FONTlike EMlike MATHlike Alike formula SELECTlike Plike DIVlike LIlike ULlike MAPlike same
+ flags: endO
+ 29:COLGROUP
+ justify
+ 13 attributes:
+ 0:0:ALIGN
+ 1:0:CHAR
+ 2:0:CHAROFF
+ 3:4:CLASS
+ 4:0:CLEAR
+ 5:0:DIR
+ 6:1:ID
+ 7:0:LANG
+ 8:0:SPAN
+ 9:0:STYLE
+ 10:0:TITLE
+ 11:0:VALIGN
+ 12:0:WIDTH
+ 5 attr_types
+ cellalign
+ core
+ events
+ i18n
+ COL
+ contents: SGML_ELEMENT
+ tagclass: TRlike
+ contains: HRlike
+ icontains: HRlike
+ contained: ULlike
+ icontained: FONTlike EMlike MATHlike TRlike FORMlike Plike DIVlike LIlike ULlike APPLETlike HRlike outer BODYlike
+ canclose: FONTlike EMlike MATHlike Alike formula SELECTlike Plike DIVlike LIlike MAPlike same
+ flags: endO
+ 30:COMMENT
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN
+ contents: SGML_PCDATA
+ tagclass: MATHlike
+ contains:
+ icontains:
+ contained: FONTlike EMlike MATHlike Alike TRlike FORMlike Plike DIVlike LIlike BRlike APPLETlike HRlike BODYlike same
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike outer BODYlike HEADstuff
+ canclose: FONTlike EMlike
+ flags:
+ 31:CREDIT
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN
+ contents: SGML_MIXED
+ tagclass: Plike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike BRlike APPLETlike MAPlike
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: DIVlike ULlike APPLETlike HRlike DELlike
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike same DELlike
+ canclose: FONTlike EMlike MATHlike Alike Plike DIVlike same
+ flags:
+ 32:DD
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN
+ contents: SGML_MIXED
+ tagclass: LIlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike FORMlike Plike DIVlike ULlike BRlike APPLETlike HRlike MAPlike
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: ULlike
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike APPLETlike HRlike outer BODYlike same
+ canclose: FONTlike EMlike MATHlike Alike formula Plike DIVlike LIlike same
+ flags: endO
+ 33:DEL
+ justify
+ 8 attributes:
+ 0:0:CITE
+ 1:4:CLASS
+ 2:0:DATETIME
+ 3:0:DIR
+ 4:1:ID
+ 5:0:LANG
+ 6:0:STYLE
+ 7:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ DEL
+ contents: SGML_MIXED
+ tagclass: EMlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike BRlike APPLETlike MAPlike same
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: FONTlike EMlike MATHlike Alike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike same
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike HEADstuff same
+ canclose: FONTlike EMlike DELlike
+ flags:
+ 34:DEL!
+ justify
+ 8 attributes:
+ 0:0:CITE
+ 1:4:CLASS
+ 2:0:DATETIME
+ 3:0:DIR
+ 4:1:ID
+ 5:0:LANG
+ 6:0:STYLE
+ 7:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ DEL
+ contents: SGML_MIXED
+ tagclass: DELlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike FORMlike Plike DIVlike ULlike BRlike APPLETlike MAPlike same
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: FONTlike EMlike MATHlike Alike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike BODYlike same
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike HEADstuff same
+ canclose: FONTlike EMlike DELlike
+ flags:
+ 35:DFN
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN
+ contents: SGML_MIXED
+ tagclass: EMlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike FORMlike BRlike APPLETlike MAPlike same
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: FONTlike EMlike MATHlike Alike FORMlike Plike DIVlike LIlike BRlike APPLETlike HRlike same
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike HEADstuff same
+ canclose: FONTlike EMlike
+ flags:
+ 36:DIR
+ justify
+ 14 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:COMPACT
+ 3:0:DINGBAT
+ 4:0:DIR
+ 5:1:ID
+ 6:0:LANG
+ 7:0:MD
+ 8:0:PLAIN
+ 9:2:SRC
+ 10:0:STYLE
+ 11:0:TITLE
+ 12:0:TYPE
+ 13:0:WRAP
+ 3 attr_types
+ core
+ i18n
+ UL
+ contents: SGML_MIXED
+ tagclass: ULlike
+ contains: LIlike BRlike APPLETlike MAPlike
+ icontains: FONTlike EMlike MATHlike Alike formula SELECTlike Plike DIVlike LIlike BRlike APPLETlike HRlike MAPlike
+ contained: FORMlike DIVlike LIlike BRlike APPLETlike HRlike outer BODYlike DELlike
+ icontained: FONTlike EMlike MATHlike formula TRlike FORMlike Plike DIVlike LIlike ULlike APPLETlike HRlike outer BODYlike DELlike
+ canclose: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike Plike DIVlike LIlike ULlike HRlike same
+ flags:
+ 37:DIV
+ justify
+ 8 attributes:
+ 0:0:ALIGN
+ 1:4:CLASS
+ 2:0:CLEAR
+ 3:0:DIR
+ 4:1:ID
+ 5:0:LANG
+ 6:0:STYLE
+ 7:0:TITLE
+ 4 attr_types
+ align
+ core
+ i18n
+ DIV
+ contents: SGML_MIXED
+ tagclass: DIVlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike FORMlike Plike DIVlike ULlike BRlike APPLETlike HRlike MAPlike same
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: TRlike FORMlike DIVlike LIlike APPLETlike HRlike outer BODYlike same DELlike
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike same DELlike
+ canclose: FONTlike EMlike MATHlike formula Plike DIVlike same
+ flags: mafse
+ 38:DL
+ justify
+ 8 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:COMPACT
+ 3:0:DIR
+ 4:1:ID
+ 5:0:LANG
+ 6:0:STYLE
+ 7:0:TITLE
+ 3 attr_types
+ core
+ i18n
+ DL
+ contents: SGML_MIXED
+ tagclass: ULlike
+ contains: FORMlike LIlike HRlike MAPlike
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: FORMlike DIVlike LIlike APPLETlike HRlike outer BODYlike DELlike
+ icontained: FONTlike EMlike MATHlike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike same DELlike
+ canclose: FONTlike EMlike MATHlike formula SELECTlike Plike DIVlike LIlike
+ flags:
+ 39:DLC
+ justify
+ 8 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:COMPACT
+ 3:0:DIR
+ 4:1:ID
+ 5:0:LANG
+ 6:0:STYLE
+ 7:0:TITLE
+ 3 attr_types
+ core
+ i18n
+ DL
+ contents: SGML_MIXED
+ tagclass: ULlike
+ contains: FORMlike LIlike HRlike MAPlike
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: FORMlike DIVlike LIlike APPLETlike HRlike outer BODYlike DELlike
+ icontained: FONTlike EMlike MATHlike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike same DELlike
+ canclose: FONTlike EMlike MATHlike Alike formula SELECTlike Plike DIVlike LIlike
+ flags:
+ 40:DT
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN
+ contents: SGML_MIXED
+ tagclass: LIlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike BRlike APPLETlike MAPlike
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike BRlike APPLETlike MAPlike
+ contained: ULlike
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer
+ canclose: FONTlike EMlike MATHlike Alike formula Plike DIVlike LIlike same
+ flags: endO
+ 41:EM
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN
+ contents: SGML_MIXED
+ tagclass: EMlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike BRlike APPLETlike MAPlike same
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: FONTlike EMlike MATHlike Alike FORMlike Plike DIVlike LIlike BRlike APPLETlike HRlike BODYlike same
+ icontained: FONTlike EMlike MATHlike Alike TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike HEADstuff same
+ canclose: FONTlike EMlike
+ flags: nreie
+ 42:EMBED
+ justify
+ 21 attributes:
+ 0:0:ALIGN
+ 1:0:ALT
+ 2:0:BORDER
+ 3:4:CLASS
+ 4:0:CLEAR
+ 5:0:DIR
+ 6:0:HEIGHT
+ 7:1:ID
+ 8:0:IMAGEMAP
+ 9:0:ISMAP
+ 10:0:LANG
+ 11:0:MD
+ 12:1:NAME
+ 13:0:NOFLOW
+ 14:0:PARAMS
+ 15:2:SRC
+ 16:0:STYLE
+ 17:0:TITLE
+ 18:0:UNITS
+ 19:0:USEMAP
+ 20:0:WIDTH
+ 4 attr_types
+ align
+ core
+ i18n
+ EMBED
+ contents: SGML_EMPTY
+ tagclass: APPLETlike
+ contains: FONTlike EMlike MATHlike Plike BRlike APPLETlike HRlike MAPlike same
+ icontains: FONTlike EMlike MATHlike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: FONTlike EMlike MATHlike Alike formula TRlike FORMlike Plike DIVlike LIlike ULlike APPLETlike HRlike outer BODYlike same
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike same
+ canclose: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike outer
+ flags: endO
+ 43:FIELDSET
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN
+ contents: SGML_MIXED
+ tagclass: DIVlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike Plike DIVlike ULlike BRlike APPLETlike HRlike MAPlike same
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: FONTlike EMlike MATHlike FORMlike Plike DIVlike LIlike APPLETlike HRlike same DELlike
+ icontained: FONTlike EMlike MATHlike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike same DELlike
+ canclose: FONTlike EMlike MATHlike Alike formula SELECTlike MAPlike same
+ flags:
+ 44:FIG
+ justify
+ 17 attributes:
+ 0:0:ALIGN
+ 1:0:BORDER
+ 2:4:CLASS
+ 3:0:CLEAR
+ 4:0:DIR
+ 5:0:HEIGHT
+ 6:1:ID
+ 7:0:IMAGEMAP
+ 8:0:ISOBJECT
+ 9:0:LANG
+ 10:0:MD
+ 11:0:NOFLOW
+ 12:2:SRC
+ 13:0:STYLE
+ 14:0:TITLE
+ 15:0:UNITS
+ 16:0:WIDTH
+ 4 attr_types
+ align
+ core
+ i18n
+ FIG
+ contents: SGML_MIXED
+ tagclass: DIVlike
+ contains: Plike DIVlike ULlike BRlike APPLETlike HRlike MAPlike
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: FORMlike DIVlike LIlike APPLETlike HRlike outer BODYlike DELlike
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike FORMlike Plike DIVlike LIlike ULlike APPLETlike HRlike outer BODYlike same DELlike
+ canclose: FONTlike EMlike MATHlike Alike SELECTlike Plike DIVlike MAPlike same
+ flags:
+ 45:FIGURE
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:DIR
+ 2:1:ID
+ 3:0:LANG
+ 4:0:ROLE
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN5
+ contents: SGML_MIXED
+ tagclass: DIVlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike FORMlike Plike DIVlike ULlike BRlike APPLETlike HRlike MAPlike same
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: TRlike FORMlike DIVlike LIlike APPLETlike HRlike outer BODYlike same DELlike
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike same DELlike
+ canclose: FONTlike EMlike MATHlike Alike formula Plike DIVlike same
+ flags: mafse
+ 46:FN
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN
+ contents: SGML_MIXED
+ tagclass: DIVlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike FORMlike Plike DIVlike ULlike BRlike APPLETlike HRlike MAPlike same
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: FORMlike DIVlike LIlike APPLETlike HRlike outer BODYlike same DELlike
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike FORMlike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike same DELlike
+ canclose: FONTlike EMlike MATHlike Alike SELECTlike Plike BRlike same
+ flags:
+ 47:FONT
+ justify
+ 11 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:COLOR
+ 3:0:DIR
+ 4:0:END
+ 5:0:FACE
+ 6:1:ID
+ 7:0:LANG
+ 8:0:SIZE
+ 9:0:STYLE
+ 10:0:TITLE
+ 3 attr_types
+ core
+ i18n
+ FONT
+ contents: SGML_MIXED
+ tagclass: FONTlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike BRlike APPLETlike MAPlike same
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: FONTlike EMlike MATHlike Alike FORMlike Plike DIVlike LIlike BRlike APPLETlike HRlike outer BODYlike same
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike HEADstuff same
+ canclose: FONTlike
+ flags: mafse nreie
+ 48:FOOTER
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:DIR
+ 2:1:ID
+ 3:0:LANG
+ 4:0:ROLE
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN5
+ contents: SGML_MIXED
+ tagclass: DIVlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike FORMlike Plike DIVlike ULlike BRlike APPLETlike HRlike MAPlike same
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: TRlike FORMlike DIVlike LIlike APPLETlike HRlike outer BODYlike same DELlike
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike same DELlike
+ canclose: FONTlike EMlike MATHlike Alike formula Plike DIVlike same
+ flags: mafse
+ 49:FORM
+ justify
+ 17 attributes:
+ 0:0:ACCEPT
+ 1:0:ACCEPT-CHARSET
+ 2:2:ACTION
+ 3:4:CLASS
+ 4:0:CLEAR
+ 5:0:DIR
+ 6:0:ENCTYPE
+ 7:1:ID
+ 8:0:LANG
+ 9:0:METHOD
+ 10:0:ONRESET
+ 11:0:ONSUBMIT
+ 12:0:SCRIPT
+ 13:0:STYLE
+ 14:0:SUBJECT
+ 15:0:TARGET
+ 16:0:TITLE
+ 3 attr_types
+ core
+ i18n
+ FORM
+ contents: SGML_MIXED
+ tagclass: FORMlike
+ contains: FONTlike EMlike MATHlike Alike TRlike SELECTlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike
+ contained: FONTlike EMlike MATHlike DIVlike LIlike ULlike APPLETlike HRlike outer BODYlike DELlike
+ icontained: FONTlike EMlike MATHlike Plike DIVlike LIlike ULlike APPLETlike outer BODYlike DELlike
+ canclose: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike LIlike ULlike MAPlike same
+ flags:
+ 50:FRAME
+ justify
+ 12 attributes:
+ 0:4:CLASS
+ 1:0:FRAMEBORDER
+ 2:1:ID
+ 3:2:LONGDESC
+ 4:0:MARGINHEIGHT
+ 5:0:MARGINWIDTH
+ 6:0:NAME
+ 7:0:NORESIZE
+ 8:0:SCROLLING
+ 9:2:SRC
+ 10:0:STYLE
+ 11:0:TITLE
+ 2 attr_types
+ core
+ FRAME
+ contents: SGML_EMPTY
+ tagclass: outer
+ contains:
+ icontains:
+ contained: outer
+ icontained: outer
+ canclose: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike outer same
+ flags: endO
+ 51:FRAMESET
+ justify
+ 4 attributes:
+ 0:0:COLS
+ 1:0:ONLOAD
+ 2:0:ONUNLOAD
+ 3:0:ROWS
+ 1 attr_types
+ FRAMESET
+ contents: SGML_ELEMENT
+ tagclass: outer
+ contains: outer same
+ icontains: outer same
+ contained: outer same
+ icontained: BRlike APPLETlike outer same
+ canclose: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike outer same
+ flags:
+ 52:H1
+ nojustify
+ 14 attributes:
+ 0:0:ALIGN
+ 1:4:CLASS
+ 2:0:CLEAR
+ 3:0:DINGBAT
+ 4:0:DIR
+ 5:1:ID
+ 6:0:LANG
+ 7:0:MD
+ 8:0:NOWRAP
+ 9:0:SEQNUM
+ 10:0:SKIP
+ 11:2:SRC
+ 12:0:STYLE
+ 13:0:TITLE
+ 5 attr_types
+ align
+ core
+ events
+ i18n
+ H
+ contents: SGML_MIXED
+ tagclass: Plike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike BRlike APPLETlike MAPlike
+ icontains: FONTlike EMlike MATHlike Alike formula SELECTlike BRlike APPLETlike MAPlike
+ contained: FORMlike DIVlike LIlike APPLETlike HRlike outer BODYlike DELlike
+ icontained: FONTlike EMlike MATHlike Alike TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike DELlike
+ canclose: FONTlike EMlike MATHlike formula Plike same
+ flags:
+ 53:H2
+ nojustify
+ 14 attributes:
+ 0:0:ALIGN
+ 1:4:CLASS
+ 2:0:CLEAR
+ 3:0:DINGBAT
+ 4:0:DIR
+ 5:1:ID
+ 6:0:LANG
+ 7:0:MD
+ 8:0:NOWRAP
+ 9:0:SEQNUM
+ 10:0:SKIP
+ 11:2:SRC
+ 12:0:STYLE
+ 13:0:TITLE
+ 5 attr_types
+ align
+ core
+ events
+ i18n
+ H
+ contents: SGML_MIXED
+ tagclass: Plike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike BRlike APPLETlike MAPlike
+ icontains: FONTlike EMlike MATHlike Alike formula SELECTlike BRlike APPLETlike MAPlike
+ contained: FORMlike DIVlike LIlike APPLETlike HRlike outer BODYlike DELlike
+ icontained: FONTlike EMlike MATHlike Alike TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike DELlike
+ canclose: FONTlike EMlike MATHlike formula Plike same
+ flags:
+ 54:H3
+ nojustify
+ 14 attributes:
+ 0:0:ALIGN
+ 1:4:CLASS
+ 2:0:CLEAR
+ 3:0:DINGBAT
+ 4:0:DIR
+ 5:1:ID
+ 6:0:LANG
+ 7:0:MD
+ 8:0:NOWRAP
+ 9:0:SEQNUM
+ 10:0:SKIP
+ 11:2:SRC
+ 12:0:STYLE
+ 13:0:TITLE
+ 5 attr_types
+ align
+ core
+ events
+ i18n
+ H
+ contents: SGML_MIXED
+ tagclass: Plike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike BRlike APPLETlike MAPlike
+ icontains: FONTlike EMlike MATHlike Alike formula SELECTlike BRlike APPLETlike MAPlike
+ contained: FORMlike DIVlike LIlike APPLETlike HRlike outer BODYlike DELlike
+ icontained: FONTlike EMlike MATHlike Alike TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike DELlike
+ canclose: FONTlike EMlike MATHlike formula Plike same
+ flags:
+ 55:H4
+ nojustify
+ 14 attributes:
+ 0:0:ALIGN
+ 1:4:CLASS
+ 2:0:CLEAR
+ 3:0:DINGBAT
+ 4:0:DIR
+ 5:1:ID
+ 6:0:LANG
+ 7:0:MD
+ 8:0:NOWRAP
+ 9:0:SEQNUM
+ 10:0:SKIP
+ 11:2:SRC
+ 12:0:STYLE
+ 13:0:TITLE
+ 5 attr_types
+ align
+ core
+ events
+ i18n
+ H
+ contents: SGML_MIXED
+ tagclass: Plike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike BRlike APPLETlike MAPlike
+ icontains: FONTlike EMlike MATHlike Alike formula SELECTlike BRlike APPLETlike MAPlike
+ contained: FORMlike DIVlike LIlike APPLETlike HRlike outer BODYlike DELlike
+ icontained: FONTlike EMlike MATHlike Alike TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike DELlike
+ canclose: FONTlike EMlike MATHlike formula Plike same
+ flags:
+ 56:H5
+ nojustify
+ 14 attributes:
+ 0:0:ALIGN
+ 1:4:CLASS
+ 2:0:CLEAR
+ 3:0:DINGBAT
+ 4:0:DIR
+ 5:1:ID
+ 6:0:LANG
+ 7:0:MD
+ 8:0:NOWRAP
+ 9:0:SEQNUM
+ 10:0:SKIP
+ 11:2:SRC
+ 12:0:STYLE
+ 13:0:TITLE
+ 5 attr_types
+ align
+ core
+ events
+ i18n
+ H
+ contents: SGML_MIXED
+ tagclass: Plike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike BRlike APPLETlike MAPlike
+ icontains: FONTlike EMlike MATHlike Alike formula SELECTlike BRlike APPLETlike MAPlike
+ contained: FORMlike DIVlike LIlike APPLETlike HRlike outer BODYlike DELlike
+ icontained: FONTlike EMlike MATHlike Alike TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike DELlike
+ canclose: FONTlike EMlike MATHlike formula Plike same
+ flags:
+ 57:H6
+ nojustify
+ 14 attributes:
+ 0:0:ALIGN
+ 1:4:CLASS
+ 2:0:CLEAR
+ 3:0:DINGBAT
+ 4:0:DIR
+ 5:1:ID
+ 6:0:LANG
+ 7:0:MD
+ 8:0:NOWRAP
+ 9:0:SEQNUM
+ 10:0:SKIP
+ 11:2:SRC
+ 12:0:STYLE
+ 13:0:TITLE
+ 5 attr_types
+ align
+ core
+ events
+ i18n
+ H
+ contents: SGML_MIXED
+ tagclass: Plike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike BRlike APPLETlike MAPlike
+ icontains: FONTlike EMlike MATHlike Alike formula SELECTlike BRlike APPLETlike MAPlike
+ contained: FORMlike DIVlike LIlike APPLETlike HRlike outer BODYlike DELlike
+ icontained: FONTlike EMlike MATHlike Alike TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike DELlike
+ canclose: FONTlike EMlike MATHlike formula Plike same
+ flags:
+ 58:HEAD
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN
+ contents: SGML_ELEMENT
+ tagclass: HEADstuff
+ contains: BRlike APPLETlike HRlike MAPlike HEADstuff
+ icontains: BRlike APPLETlike HRlike HEADstuff
+ contained: outer
+ icontained: outer
+ canclose: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike outer same
+ flags: endO startO mafse
+ 59:HEADER
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:DIR
+ 2:1:ID
+ 3:0:LANG
+ 4:0:ROLE
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN5
+ contents: SGML_MIXED
+ tagclass: DIVlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike FORMlike Plike DIVlike ULlike BRlike APPLETlike HRlike MAPlike same
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: TRlike FORMlike DIVlike LIlike APPLETlike HRlike outer BODYlike same DELlike
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike same DELlike
+ canclose: FONTlike EMlike MATHlike Alike formula Plike DIVlike same
+ flags: mafse
+ 60:HR
+ justify
+ 13 attributes:
+ 0:0:ALIGN
+ 1:4:CLASS
+ 2:0:CLEAR
+ 3:0:DIR
+ 4:1:ID
+ 5:0:LANG
+ 6:0:MD
+ 7:0:NOSHADE
+ 8:0:SIZE
+ 9:2:SRC
+ 10:0:STYLE
+ 11:0:TITLE
+ 12:0:WIDTH
+ 4 attr_types
+ align
+ core
+ i18n
+ HR
+ contents: SGML_EMPTY
+ tagclass: HRlike
+ contains:
+ icontains:
+ contained: FORMlike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike outer BODYlike
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike outer BODYlike
+ canclose: FONTlike EMlike MATHlike formula TRlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike same
+ flags: endO
+ 61:HTML
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN
+ contents: SGML_MIXED
+ tagclass: outer
+ contains: FONTlike EMlike MATHlike Alike FORMlike Plike DIVlike ULlike BRlike APPLETlike HRlike MAPlike outer BODYlike HEADstuff
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike outer BODYlike HEADstuff
+ contained:
+ icontained:
+ canclose: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike outer
+ flags: endO startO
+ 62:HY
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN
+ contents: SGML_EMPTY
+ tagclass: BRlike
+ contains:
+ icontains:
+ contained: FONTlike EMlike MATHlike Alike formula FORMlike Plike DIVlike LIlike BRlike APPLETlike HRlike outer BODYlike
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike HEADstuff
+ canclose: FONTlike EMlike MATHlike Alike formula BRlike same
+ flags: endO
+ 63:I
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN
+ contents: SGML_MIXED
+ tagclass: FONTlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike BRlike APPLETlike MAPlike same
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: FONTlike EMlike MATHlike Alike FORMlike Plike DIVlike LIlike BRlike APPLETlike HRlike BODYlike same
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike HEADstuff same
+ canclose: FONTlike
+ flags: mafse nreie
+ 64:IFRAME
+ justify
+ 14 attributes:
+ 0:0:ALIGN
+ 1:4:CLASS
+ 2:0:FRAMEBORDER
+ 3:0:HEIGHT
+ 4:1:ID
+ 5:2:LONGDESC
+ 6:0:MARGINHEIGHT
+ 7:0:MARGINWIDTH
+ 8:0:NAME
+ 9:0:SCROLLING
+ 10:2:SRC
+ 11:0:STYLE
+ 12:0:TITLE
+ 13:0:WIDTH
+ 3 attr_types
+ align
+ core
+ IFRAME
+ contents: SGML_MIXED
+ tagclass: APPLETlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike FORMlike Plike DIVlike ULlike BRlike APPLETlike HRlike MAPlike same
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: FONTlike EMlike MATHlike Alike formula FORMlike Plike DIVlike LIlike APPLETlike HRlike outer BODYlike same
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike FORMlike Plike DIVlike LIlike ULlike APPLETlike HRlike outer BODYlike same
+ canclose: FONTlike EMlike MATHlike Alike formula SELECTlike Plike BRlike APPLETlike outer HEADstuff same
+ flags:
+ 65:IMG
+ justify
+ 22 attributes:
+ 0:0:ALIGN
+ 1:0:ALT
+ 2:0:BORDER
+ 3:4:CLASS
+ 4:0:CLEAR
+ 5:0:DIR
+ 6:0:HEIGHT
+ 7:0:HSPACE
+ 8:1:ID
+ 9:0:ISMAP
+ 10:0:ISOBJECT
+ 11:0:LANG
+ 12:2:LONGDESC
+ 13:0:MD
+ 14:0:NAME
+ 15:2:SRC
+ 16:0:STYLE
+ 17:0:TITLE
+ 18:0:UNITS
+ 19:2:USEMAP
+ 20:0:VSPACE
+ 21:0:WIDTH
+ 5 attr_types
+ align
+ core
+ events
+ i18n
+ IMG
+ contents: SGML_EMPTY
+ tagclass: BRlike
+ contains:
+ icontains:
+ contained: FONTlike EMlike MATHlike Alike formula FORMlike Plike DIVlike LIlike BRlike APPLETlike HRlike outer BODYlike
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike
+ canclose: same
+ flags: endO
+ 66:INPUT
+ justify
+ 35 attributes:
+ 0:0:ACCEPT
+ 1:0:ACCEPT-CHARSET
+ 2:0:ACCESSKEY
+ 3:0:ALIGN
+ 4:0:ALT
+ 5:0:CHECKED
+ 6:4:CLASS
+ 7:0:CLEAR
+ 8:0:DIR
+ 9:0:DISABLED
+ 10:0:ERROR
+ 11:0:HEIGHT
+ 12:1:ID
+ 13:0:ISMAP
+ 14:0:LANG
+ 15:0:MAX
+ 16:0:MAXLENGTH
+ 17:0:MD
+ 18:0:MIN
+ 19:0:NAME
+ 20:0:NOTAB
+ 21:0:ONBLUR
+ 22:0:ONCHANGE
+ 23:0:ONFOCUS
+ 24:0:ONSELECT
+ 25:0:READONLY
+ 26:0:SIZE
+ 27:2:SRC
+ 28:0:STYLE
+ 29:0:TABINDEX
+ 30:0:TITLE
+ 31:0:TYPE
+ 32:0:USEMAP
+ 33:0:VALUE
+ 34:0:WIDTH
+ 5 attr_types
+ align
+ core
+ events
+ i18n
+ INPUT
+ contents: SGML_EMPTY
+ tagclass: SELECTlike
+ contains:
+ icontains:
+ contained: FONTlike EMlike MATHlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike
+ icontained: FONTlike EMlike MATHlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike
+ canclose: FONTlike EMlike MATHlike Alike SELECTlike BRlike MAPlike same
+ flags: endO
+ 67:INS
+ justify
+ 8 attributes:
+ 0:0:CITE
+ 1:4:CLASS
+ 2:0:DATETIME
+ 3:0:DIR
+ 4:1:ID
+ 5:0:LANG
+ 6:0:STYLE
+ 7:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ DEL
+ contents: SGML_MIXED
+ tagclass: EMlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike BRlike APPLETlike MAPlike same
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: FONTlike EMlike MATHlike Alike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike same
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike HEADstuff same
+ canclose: FONTlike EMlike DELlike
+ flags:
+ 68:INS!
+ justify
+ 8 attributes:
+ 0:0:CITE
+ 1:4:CLASS
+ 2:0:DATETIME
+ 3:0:DIR
+ 4:1:ID
+ 5:0:LANG
+ 6:0:STYLE
+ 7:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ DEL
+ contents: SGML_MIXED
+ tagclass: DELlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike FORMlike Plike DIVlike ULlike BRlike APPLETlike MAPlike same
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: FONTlike EMlike MATHlike Alike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike BODYlike same
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike HEADstuff same
+ canclose: FONTlike EMlike DELlike
+ flags:
+ 69:ISINDEX
+ justify
+ 9 attributes:
+ 0:2:ACTION
+ 1:4:CLASS
+ 2:0:DIR
+ 3:2:HREF
+ 4:1:ID
+ 5:0:LANG
+ 6:0:PROMPT
+ 7:0:STYLE
+ 8:0:TITLE
+ 3 attr_types
+ core
+ i18n
+ ISINDEX
+ contents: SGML_EMPTY
+ tagclass: MAPlike
+ contains:
+ icontains:
+ contained: FONTlike EMlike MATHlike Alike FORMlike Plike DIVlike LIlike BRlike APPLETlike HRlike outer BODYlike HEADstuff
+ icontained: FONTlike EMlike MATHlike Alike TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike outer BODYlike HEADstuff
+ canclose: FONTlike EMlike MATHlike same
+ flags: endO
+ 70:KBD
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN
+ contents: SGML_MIXED
+ tagclass: EMlike
+ contains:
+ icontains:
+ contained: FONTlike EMlike MATHlike Alike FORMlike Plike DIVlike LIlike BRlike APPLETlike HRlike BODYlike
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike HEADstuff
+ canclose: FONTlike EMlike
+ flags:
+ 71:KEYGEN
+ justify
+ 8 attributes:
+ 0:0:CHALLENGE
+ 1:4:CLASS
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:NAME
+ 6:0:STYLE
+ 7:0:TITLE
+ 3 attr_types
+ core
+ i18n
+ KEYGEN
+ contents: SGML_EMPTY
+ tagclass: SELECTlike
+ contains:
+ icontains:
+ contained: FONTlike EMlike MATHlike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike
+ icontained: FONTlike EMlike MATHlike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike
+ canclose: formula TRlike SELECTlike same
+ flags: endO
+ 72:LABEL
+ justify
+ 11 attributes:
+ 0:0:ACCESSKEY
+ 1:4:CLASS
+ 2:0:CLEAR
+ 3:0:DIR
+ 4:0:FOR
+ 5:1:ID
+ 6:0:LANG
+ 7:0:ONBLUR
+ 8:0:ONFOCUS
+ 9:0:STYLE
+ 10:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ LABEL
+ contents: SGML_MIXED
+ tagclass: EMlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike BRlike APPLETlike
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike
+ contained: FONTlike EMlike MATHlike Alike formula FORMlike Plike DIVlike LIlike APPLETlike HRlike
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike FORMlike Plike DIVlike LIlike ULlike APPLETlike HRlike outer BODYlike
+ canclose: FONTlike EMlike MATHlike
+ flags:
+ 73:LEGEND
+ justify
+ 9 attributes:
+ 0:0:ACCESSKEY
+ 1:0:ALIGN
+ 2:4:CLASS
+ 3:0:CLEAR
+ 4:0:DIR
+ 5:1:ID
+ 6:0:LANG
+ 7:0:STYLE
+ 8:0:TITLE
+ 5 attr_types
+ align
+ core
+ events
+ i18n
+ CAPTION
+ contents: SGML_MIXED
+ tagclass: EMlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike BRlike APPLETlike MAPlike
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: DIVlike
+ icontained: FONTlike EMlike MATHlike TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike same
+ canclose: FONTlike EMlike
+ flags:
+ 74:LH
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN
+ contents: SGML_MIXED
+ tagclass: LIlike
+ contains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike Plike DIVlike ULlike BRlike APPLETlike MAPlike
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: ULlike
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer same
+ canclose: FONTlike EMlike MATHlike Alike formula Plike DIVlike LIlike same
+ flags: endO
+ 75:LI
+ justify
+ 13 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DINGBAT
+ 3:0:DIR
+ 4:1:ID
+ 5:0:LANG
+ 6:0:MD
+ 7:0:SKIP
+ 8:2:SRC
+ 9:0:STYLE
+ 10:0:TITLE
+ 11:0:TYPE
+ 12:0:VALUE
+ 4 attr_types
+ core
+ events
+ i18n
+ LI
+ contents: SGML_MIXED
+ tagclass: LIlike
+ contains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike ULlike BRlike APPLETlike MAPlike
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: ULlike
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer same
+ canclose: FONTlike EMlike MATHlike Alike formula Plike DIVlike LIlike same
+ flags: endO
+ 76:LINK
+ justify
+ 14 attributes:
+ 0:0:CHARSET
+ 1:4:CLASS
+ 2:0:DIR
+ 3:2:HREF
+ 4:0:HREFLANG
+ 5:1:ID
+ 6:0:LANG
+ 7:0:MEDIA
+ 8:0:REL
+ 9:0:REV
+ 10:0:STYLE
+ 11:0:TARGET
+ 12:0:TITLE
+ 13:0:TYPE
+ 4 attr_types
+ core
+ events
+ i18n
+ LINK
+ contents: SGML_EMPTY
+ tagclass: MAPlike
+ contains:
+ icontains:
+ contained: outer HEADstuff
+ icontained: outer HEADstuff
+ canclose: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike
+ flags: endO
+ 77:LISTING
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN
+ contents: SGML_LITTERAL
+ tagclass: ULlike
+ contains:
+ icontains:
+ contained: DIVlike LIlike APPLETlike HRlike outer BODYlike DELlike
+ icontained: Plike DIVlike LIlike ULlike APPLETlike HRlike outer BODYlike DELlike
+ canclose: FONTlike EMlike MATHlike Alike formula Plike DIVlike LIlike ULlike same
+ flags:
+ 78:MAIN
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:DIR
+ 2:1:ID
+ 3:0:LANG
+ 4:0:ROLE
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN5
+ contents: SGML_MIXED
+ tagclass: DIVlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike FORMlike Plike DIVlike ULlike BRlike APPLETlike HRlike MAPlike same
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: TRlike FORMlike DIVlike LIlike APPLETlike HRlike outer BODYlike same DELlike
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike same DELlike
+ canclose: FONTlike EMlike MATHlike Alike formula Plike DIVlike same
+ flags: mafse
+ 79:MAP
+ justify
+ 8 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:1:NAME
+ 6:0:STYLE
+ 7:0:TITLE
+ 3 attr_types
+ core
+ i18n
+ MAP
+ contents: SGML_ELEMENT
+ tagclass: MAPlike
+ contains: MAPlike
+ icontains: MAPlike
+ contained: FONTlike EMlike MATHlike Alike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike
+ canclose: FONTlike EMlike MATHlike Alike formula Plike LIlike
+ flags:
+ 80:MARQUEE
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN
+ contents: SGML_MIXED
+ tagclass: HRlike
+ contains: FONTlike EMlike MATHlike Alike
+ icontains: FONTlike EMlike MATHlike Alike formula BRlike APPLETlike HRlike MAPlike same
+ contained: FONTlike EMlike MATHlike FORMlike Plike DIVlike LIlike BRlike APPLETlike HRlike outer BODYlike
+ icontained: FONTlike EMlike MATHlike TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike same
+ canclose: MATHlike Alike formula BRlike APPLETlike same
+ flags:
+ 81:MATH
+ justify
+ 8 attributes:
+ 0:0:BOX
+ 1:4:CLASS
+ 2:0:CLEAR
+ 3:0:DIR
+ 4:1:ID
+ 5:0:LANG
+ 6:0:STYLE
+ 7:0:TITLE
+ 3 attr_types
+ core
+ i18n
+ MATH
+ contents: SGML_PCDATA
+ tagclass: MATHlike
+ contains: FONTlike EMlike MATHlike Alike formula SELECTlike BRlike APPLETlike MAPlike
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: FONTlike EMlike MATHlike Alike FORMlike Plike DIVlike LIlike BRlike APPLETlike HRlike BODYlike
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike HEADstuff same
+ canclose: FONTlike EMlike MATHlike Alike formula
+ flags:
+ 82:MENU
+ justify
+ 14 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:COMPACT
+ 3:0:DINGBAT
+ 4:0:DIR
+ 5:1:ID
+ 6:0:LANG
+ 7:0:MD
+ 8:0:PLAIN
+ 9:2:SRC
+ 10:0:STYLE
+ 11:0:TITLE
+ 12:0:TYPE
+ 13:0:WRAP
+ 3 attr_types
+ core
+ i18n
+ UL
+ contents: SGML_MIXED
+ tagclass: ULlike
+ contains: LIlike BRlike APPLETlike MAPlike
+ icontains: FONTlike EMlike MATHlike Alike formula SELECTlike Plike DIVlike LIlike BRlike APPLETlike HRlike MAPlike
+ contained: FORMlike DIVlike LIlike BRlike APPLETlike HRlike outer DELlike
+ icontained: FONTlike EMlike MATHlike formula TRlike FORMlike Plike DIVlike LIlike ULlike APPLETlike HRlike outer BODYlike DELlike
+ canclose: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike Plike DIVlike LIlike ULlike MAPlike same
+ flags:
+ 83:META
+ justify
+ 5 attributes:
+ 0:0:CHARSET
+ 1:0:CONTENT
+ 2:0:HTTP-EQUIV
+ 3:0:NAME
+ 4:0:SCHEME
+ 1 attr_types
+ META
+ contents: SGML_EMPTY
+ tagclass: MAPlike
+ contains:
+ icontains:
+ contained: outer HEADstuff
+ icontained: outer HEADstuff
+ canclose: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike
+ flags: endO
+ 84:NAV
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:DIR
+ 2:1:ID
+ 3:0:LANG
+ 4:0:ROLE
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN5
+ contents: SGML_MIXED
+ tagclass: DIVlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike FORMlike Plike DIVlike ULlike BRlike APPLETlike HRlike MAPlike same
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: TRlike FORMlike DIVlike LIlike APPLETlike HRlike outer BODYlike same DELlike
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike same DELlike
+ canclose: FONTlike EMlike MATHlike Alike formula Plike DIVlike same
+ flags: mafse
+ 85:NEXTID
+ justify
+ 1 attributes:
+ 0:0:N
+ 1 attr_types
+ NEXTID
+ contents: SGML_EMPTY
+ tagclass: BRlike
+ contains:
+ icontains:
+ contained: outer HEADstuff
+ icontained: FONTlike EMlike MATHlike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike outer
+ canclose: FONTlike
+ flags: endO
+ 86:NOFRAMES
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN
+ contents: SGML_MIXED
+ tagclass: BODYlike
+ contains: FONTlike EMlike MATHlike Alike FORMlike Plike DIVlike ULlike BRlike APPLETlike HRlike MAPlike BODYlike DELlike
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike DELlike
+ contained: BRlike APPLETlike HRlike outer
+ icontained: BRlike APPLETlike HRlike outer
+ canclose: FONTlike EMlike MATHlike Alike formula SELECTlike Plike DIVlike LIlike ULlike HRlike MAPlike
+ flags:
+ 87:NOTE
+ justify
+ 10 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:MD
+ 6:8:ROLE
+ 7:2:SRC
+ 8:0:STYLE
+ 9:0:TITLE
+ 3 attr_types
+ core
+ i18n
+ NOTE
+ contents: SGML_MIXED
+ tagclass: DIVlike
+ contains: FONTlike EMlike MATHlike Alike TRlike FORMlike Plike DIVlike ULlike BRlike APPLETlike MAPlike
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: formula TRlike FORMlike DIVlike LIlike BRlike APPLETlike HRlike outer BODYlike DELlike
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike same DELlike
+ canclose: FONTlike EMlike MATHlike Alike formula Plike DIVlike same
+ flags:
+ 88:OBJECT
+ justify
+ 26 attributes:
+ 0:0:ALIGN
+ 1:0:ARCHIVE
+ 2:0:BORDER
+ 3:4:CLASS
+ 4:2:CLASSID
+ 5:2:CODEBASE
+ 6:0:CODETYPE
+ 7:2:DATA
+ 8:0:DECLARE
+ 9:0:DIR
+ 10:0:HEIGHT
+ 11:0:HSPACE
+ 12:1:ID
+ 13:0:ISMAP
+ 14:0:LANG
+ 15:0:NAME
+ 16:0:NOTAB
+ 17:0:SHAPES
+ 18:0:STANDBY
+ 19:0:STYLE
+ 20:0:TABINDEX
+ 21:0:TITLE
+ 22:0:TYPE
+ 23:2:USEMAP
+ 24:0:VSPACE
+ 25:0:WIDTH
+ 5 attr_types
+ align
+ core
+ events
+ i18n
+ OBJECT
+ contents: SGML_LITTERAL
+ tagclass: APPLETlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike FORMlike Plike DIVlike ULlike BRlike APPLETlike HRlike MAPlike same
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: FONTlike EMlike MATHlike Alike formula FORMlike Plike DIVlike LIlike APPLETlike HRlike outer BODYlike same
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike FORMlike Plike DIVlike LIlike ULlike APPLETlike HRlike outer BODYlike same
+ canclose: FONTlike EMlike MATHlike Alike formula SELECTlike Plike LIlike ULlike BRlike APPLETlike same
+ flags: frecyc
+ 89:OL
+ justify
+ 12 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:COMPACT
+ 3:0:CONTINUE
+ 4:0:DIR
+ 5:1:ID
+ 6:0:LANG
+ 7:0:SEQNUM
+ 8:0:START
+ 9:0:STYLE
+ 10:0:TITLE
+ 11:0:TYPE
+ 3 attr_types
+ core
+ i18n
+ OL
+ contents: SGML_MIXED
+ tagclass: ULlike
+ contains: LIlike HRlike MAPlike
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: FORMlike DIVlike LIlike BRlike APPLETlike HRlike outer BODYlike DELlike
+ icontained: FONTlike EMlike MATHlike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike same DELlike
+ canclose: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike Plike DIVlike LIlike ULlike MAPlike same
+ flags:
+ 90:OPTION
+ justify
+ 13 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:0:DISABLED
+ 4:0:ERROR
+ 5:1:ID
+ 6:0:LABEL
+ 7:0:LANG
+ 8:0:SELECTED
+ 9:0:SHAPE
+ 10:0:STYLE
+ 11:0:TITLE
+ 12:0:VALUE
+ 4 attr_types
+ core
+ events
+ i18n
+ OPTION
+ contents: SGML_PCDATA
+ tagclass: MAPlike
+ contains:
+ icontains:
+ contained: SELECTlike
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike
+ canclose: FONTlike EMlike MATHlike Alike formula Plike DIVlike same
+ flags: endO
+ 91:OVERLAY
+ justify
+ 12 attributes:
+ 0:4:CLASS
+ 1:0:HEIGHT
+ 2:1:ID
+ 3:0:IMAGEMAP
+ 4:0:MD
+ 5:2:SRC
+ 6:0:STYLE
+ 7:0:TITLE
+ 8:0:UNITS
+ 9:0:WIDTH
+ 10:0:X
+ 11:0:Y
+ 2 attr_types
+ core
+ OVERLAY
+ contents: SGML_PCDATA
+ tagclass: HRlike
+ contains:
+ icontains:
+ contained: DIVlike
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike
+ canclose: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike Plike DIVlike LIlike ULlike BRlike APPLETlike same
+ flags: endO
+ 92:P
+ justify
+ 9 attributes:
+ 0:0:ALIGN
+ 1:4:CLASS
+ 2:0:CLEAR
+ 3:0:DIR
+ 4:1:ID
+ 5:0:LANG
+ 6:0:NOWRAP
+ 7:0:STYLE
+ 8:0:TITLE
+ 4 attr_types
+ align
+ core
+ i18n
+ P
+ contents: SGML_MIXED
+ tagclass: Plike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike BRlike APPLETlike MAPlike
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: FORMlike DIVlike LIlike APPLETlike HRlike outer BODYlike DELlike
+ icontained: FONTlike EMlike MATHlike TRlike FORMlike Plike DIVlike LIlike ULlike APPLETlike HRlike outer BODYlike same DELlike
+ canclose: FONTlike EMlike MATHlike formula Plike same
+ flags: endO
+ 93:PARAM
+ justify
+ 18 attributes:
+ 0:0:ACCEPT
+ 1:0:ACCEPT-CHARSET
+ 2:0:ACCEPT-ENCODING
+ 3:4:CLASS
+ 4:0:CLEAR
+ 5:0:DATA
+ 6:0:DIR
+ 7:1:ID
+ 8:0:LANG
+ 9:0:NAME
+ 10:0:OBJECT
+ 11:0:REF
+ 12:0:STYLE
+ 13:0:TITLE
+ 14:0:TYPE
+ 15:0:VALUE
+ 16:0:VALUEREF
+ 17:0:VALUETYPE
+ 3 attr_types
+ core
+ i18n
+ PARAM
+ contents: SGML_EMPTY
+ tagclass: BRlike
+ contains:
+ icontains:
+ contained: Plike LIlike BRlike APPLETlike outer BODYlike
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike
+ canclose: TRlike SELECTlike Plike LIlike BRlike same
+ flags: endO
+ 94:PLAINTEXT
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN
+ contents: SGML_LITTERAL
+ tagclass: outer
+ contains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike outer BODYlike HEADstuff same
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike outer BODYlike HEADstuff same
+ contained: outer same
+ icontained: outer same
+ canclose: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike outer BODYlike
+ flags: endO
+ 95:PRE
+ nojustify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN
+ contents: SGML_MIXED
+ tagclass: DIVlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike BRlike APPLETlike HRlike MAPlike
+ icontains: EMlike MATHlike Alike formula SELECTlike BRlike APPLETlike HRlike MAPlike
+ contained: FORMlike DIVlike LIlike APPLETlike HRlike outer BODYlike DELlike
+ icontained: formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike APPLETlike HRlike outer BODYlike DELlike
+ canclose: EMlike MATHlike Alike formula Plike DIVlike LIlike same
+ flags:
+ 96:Q
+ justify
+ 8 attributes:
+ 0:2:CITE
+ 1:4:CLASS
+ 2:0:CLEAR
+ 3:0:DIR
+ 4:1:ID
+ 5:0:LANG
+ 6:0:STYLE
+ 7:0:TITLE
+ 3 attr_types
+ core
+ i18n
+ Q
+ contents: SGML_MIXED
+ tagclass: EMlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike BRlike APPLETlike MAPlike same
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: FONTlike EMlike MATHlike Alike FORMlike Plike DIVlike LIlike BRlike APPLETlike HRlike BODYlike same
+ icontained: FONTlike EMlike MATHlike Alike TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike HEADstuff same
+ canclose: FONTlike EMlike
+ flags:
+ 97:S
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN
+ contents: SGML_MIXED
+ tagclass: FONTlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike BRlike APPLETlike MAPlike same
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: FONTlike EMlike MATHlike Alike FORMlike Plike DIVlike LIlike BRlike APPLETlike HRlike BODYlike same
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike HEADstuff same
+ canclose: FONTlike
+ flags:
+ 98:SAMP
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN
+ contents: SGML_MIXED
+ tagclass: EMlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike BRlike APPLETlike MAPlike same
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: FONTlike EMlike MATHlike Alike FORMlike Plike DIVlike LIlike BRlike APPLETlike HRlike BODYlike same
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike HEADstuff same
+ canclose: EMlike
+ flags: nreie
+ 99:SCRIPT
+ justify
+ 16 attributes:
+ 0:0:CHARSET
+ 1:4:CLASS
+ 2:0:CLEAR
+ 3:0:DEFER
+ 4:0:DIR
+ 5:0:EVENT
+ 6:0:FOR
+ 7:1:ID
+ 8:0:LANG
+ 9:0:LANGUAGE
+ 10:0:NAME
+ 11:0:SCRIPTENGINE
+ 12:2:SRC
+ 13:0:STYLE
+ 14:0:TITLE
+ 15:0:TYPE
+ 3 attr_types
+ core
+ i18n
+ SCRIPT
+ contents: SGML_SCRIPT
+ tagclass: APPLETlike
+ contains:
+ icontains:
+ contained: FONTlike EMlike MATHlike Alike formula FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike HEADstuff
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike HEADstuff
+ canclose: FONTlike EMlike MATHlike Alike formula SELECTlike Plike LIlike ULlike BRlike APPLETlike HRlike same
+ flags:
+ 100:SECTION
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:DIR
+ 2:1:ID
+ 3:0:LANG
+ 4:0:ROLE
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN5
+ contents: SGML_MIXED
+ tagclass: DIVlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike FORMlike Plike DIVlike ULlike BRlike APPLETlike HRlike MAPlike same
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: TRlike FORMlike DIVlike LIlike APPLETlike HRlike outer BODYlike same DELlike
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike same DELlike
+ canclose: FONTlike EMlike MATHlike Alike formula Plike DIVlike same
+ flags: mafse
+ 101:SELECT
+ justify
+ 22 attributes:
+ 0:0:ALIGN
+ 1:4:CLASS
+ 2:0:CLEAR
+ 3:0:DIR
+ 4:0:DISABLED
+ 5:0:ERROR
+ 6:0:HEIGHT
+ 7:1:ID
+ 8:0:LANG
+ 9:0:MD
+ 10:0:MULTIPLE
+ 11:0:NAME
+ 12:0:NOTAB
+ 13:0:ONBLUR
+ 14:0:ONCHANGE
+ 15:0:ONFOCUS
+ 16:0:SIZE
+ 17:0:STYLE
+ 18:0:TABINDEX
+ 19:0:TITLE
+ 20:0:UNITS
+ 21:0:WIDTH
+ 4 attr_types
+ align
+ core
+ i18n
+ SELECT
+ contents: SGML_ELEMENT
+ tagclass: SELECTlike
+ contains: MAPlike
+ icontains: MAPlike
+ contained: FONTlike EMlike MATHlike Alike TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike outer BODYlike
+ canclose: FONTlike EMlike MATHlike Alike formula SELECTlike Plike LIlike ULlike same
+ flags: strict
+ 102:SHY
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN
+ contents: SGML_EMPTY
+ tagclass: BRlike
+ contains:
+ icontains:
+ contained: FONTlike EMlike MATHlike Alike formula FORMlike Plike DIVlike LIlike BRlike APPLETlike HRlike outer BODYlike
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike HEADstuff
+ canclose: FONTlike EMlike MATHlike Alike formula BRlike same
+ flags: endO
+ 103:SMALL
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN
+ contents: SGML_MIXED
+ tagclass: FONTlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike BRlike APPLETlike MAPlike same
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: FONTlike EMlike MATHlike Alike FORMlike Plike DIVlike LIlike BRlike APPLETlike HRlike BODYlike same
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike HEADstuff same
+ canclose: FONTlike
+ flags: mafse nreie
+ 104:SPAN
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN
+ contents: SGML_MIXED
+ tagclass: EMlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike BRlike APPLETlike MAPlike same
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: FONTlike EMlike MATHlike Alike FORMlike Plike DIVlike LIlike BRlike APPLETlike HRlike BODYlike same
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike HEADstuff same
+ canclose: FONTlike EMlike same
+ flags:
+ 105:SPOT
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN
+ contents: SGML_EMPTY
+ tagclass: Alike
+ contains:
+ icontains:
+ contained: FONTlike EMlike MATHlike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike outer BODYlike
+ icontained: FONTlike EMlike MATHlike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike outer BODYlike
+ canclose: Alike
+ flags: endO
+ 106:STRIKE
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN
+ contents: SGML_MIXED
+ tagclass: FONTlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike BRlike APPLETlike MAPlike same
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: FONTlike EMlike MATHlike Alike FORMlike Plike DIVlike LIlike BRlike APPLETlike HRlike BODYlike same
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike HEADstuff same
+ canclose: FONTlike
+ flags:
+ 107:STRONG
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN
+ contents: SGML_MIXED
+ tagclass: EMlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike BRlike APPLETlike MAPlike same
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: FONTlike EMlike MATHlike Alike FORMlike Plike DIVlike LIlike BRlike APPLETlike HRlike BODYlike same
+ icontained: FONTlike EMlike MATHlike Alike TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike HEADstuff same
+ canclose: FONTlike EMlike
+ flags: nreie
+ 108:STYLE
+ justify
+ 9 attributes:
+ 0:4:CLASS
+ 1:0:DIR
+ 2:1:ID
+ 3:0:LANG
+ 4:0:MEDIA
+ 5:0:NOTATION
+ 6:0:STYLE
+ 7:0:TITLE
+ 8:0:TYPE
+ 3 attr_types
+ core
+ i18n
+ STYLE
+ contents: SGML_CDATA
+ tagclass: HEADstuff
+ contains:
+ icontains:
+ contained: FONTlike EMlike MATHlike Alike FORMlike Plike DIVlike APPLETlike HRlike outer BODYlike HEADstuff
+ icontained: FONTlike EMlike MATHlike Alike TRlike FORMlike Plike DIVlike LIlike ULlike APPLETlike HRlike outer BODYlike HEADstuff
+ canclose: FONTlike EMlike MATHlike Alike formula same
+ flags:
+ 109:SUB
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN
+ contents: SGML_MIXED
+ tagclass: MATHlike
+ contains: FONTlike EMlike MATHlike Alike formula SELECTlike BRlike APPLETlike MAPlike same
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: FONTlike EMlike MATHlike Alike formula FORMlike Plike DIVlike LIlike BRlike APPLETlike HRlike same
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike HEADstuff same
+ canclose: FONTlike EMlike MATHlike
+ flags:
+ 110:SUP
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN
+ contents: SGML_MIXED
+ tagclass: MATHlike
+ contains: FONTlike EMlike MATHlike Alike formula SELECTlike BRlike APPLETlike MAPlike same
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: FONTlike EMlike MATHlike Alike formula FORMlike Plike DIVlike LIlike BRlike APPLETlike HRlike same
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike HEADstuff same
+ canclose: FONTlike EMlike MATHlike
+ flags:
+ 111:TAB
+ justify
+ 11 attributes:
+ 0:0:ALIGN
+ 1:4:CLASS
+ 2:0:CLEAR
+ 3:0:DIR
+ 4:0:DP
+ 5:1:ID
+ 6:0:INDENT
+ 7:0:LANG
+ 8:0:STYLE
+ 9:0:TITLE
+ 10:0:TO
+ 4 attr_types
+ align
+ core
+ i18n
+ TAB
+ contents: SGML_EMPTY
+ tagclass: BRlike
+ contains:
+ icontains:
+ contained: FONTlike EMlike MATHlike Alike FORMlike Plike DIVlike LIlike BRlike APPLETlike HRlike outer BODYlike
+ icontained: FONTlike EMlike MATHlike Alike TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer HEADstuff
+ canclose: FONTlike
+ flags: endO
+ 112:TABLE
+ justify
+ 22 attributes:
+ 0:0:ALIGN
+ 1:2:BACKGROUND
+ 2:0:BORDER
+ 3:0:CELLPADDING
+ 4:0:CELLSPACING
+ 5:4:CLASS
+ 6:0:CLEAR
+ 7:0:COLS
+ 8:0:COLSPEC
+ 9:0:DIR
+ 10:0:DP
+ 11:0:FRAME
+ 12:1:ID
+ 13:0:LANG
+ 14:0:NOFLOW
+ 15:0:NOWRAP
+ 16:0:RULES
+ 17:0:STYLE
+ 18:0:SUMMARY
+ 19:0:TITLE
+ 20:0:UNITS
+ 21:0:WIDTH
+ 5 attr_types
+ align
+ core
+ events
+ i18n
+ TABLE
+ contents: SGML_ELEMENT
+ tagclass: ULlike
+ contains: TRlike SELECTlike FORMlike Plike BRlike APPLETlike HRlike MAPlike
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: FORMlike DIVlike LIlike APPLETlike HRlike outer BODYlike DELlike
+ icontained: FONTlike EMlike MATHlike TRlike FORMlike Plike DIVlike LIlike ULlike APPLETlike HRlike outer BODYlike same DELlike
+ canclose: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike Plike LIlike HRlike MAPlike same
+ flags:
+ 113:TBODY
+ justify
+ 13 attributes:
+ 0:0:ALIGN
+ 1:0:CHAR
+ 2:0:CHAROFF
+ 3:4:CLASS
+ 4:0:CLEAR
+ 5:0:DIR
+ 6:0:DP
+ 7:1:ID
+ 8:0:LANG
+ 9:0:NOWRAP
+ 10:0:STYLE
+ 11:0:TITLE
+ 12:0:VALIGN
+ 5 attr_types
+ cellalign
+ core
+ events
+ i18n
+ TR
+ contents: SGML_ELEMENT
+ tagclass: TRlike
+ contains: TRlike
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: FORMlike ULlike
+ icontained: FONTlike EMlike MATHlike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike same
+ canclose: FONTlike EMlike MATHlike Alike formula SELECTlike Plike DIVlike LIlike HRlike MAPlike same
+ flags: endO startO
+ 114:TD
+ justify
+ 23 attributes:
+ 0:0:ABBR
+ 1:0:ALIGN
+ 2:0:AXES
+ 3:0:AXIS
+ 4:2:BACKGROUND
+ 5:0:CHAR
+ 6:0:CHAROFF
+ 7:4:CLASS
+ 8:0:CLEAR
+ 9:0:COLSPAN
+ 10:0:DIR
+ 11:0:DP
+ 12:0:HEADERS
+ 13:0:HEIGHT
+ 14:1:ID
+ 15:0:LANG
+ 16:0:NOWRAP
+ 17:0:ROWSPAN
+ 18:0:SCOPE
+ 19:0:STYLE
+ 20:0:TITLE
+ 21:0:VALIGN
+ 22:0:WIDTH
+ 4 attr_types
+ cellalign
+ core
+ i18n
+ TD
+ contents: SGML_MIXED
+ tagclass: LIlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike FORMlike Plike DIVlike ULlike BRlike APPLETlike HRlike MAPlike
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: TRlike
+ icontained: FONTlike EMlike MATHlike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike same
+ canclose: FONTlike EMlike MATHlike Alike formula SELECTlike Plike DIVlike LIlike HRlike MAPlike same
+ flags: endO
+ 115:TEXTAREA
+ justify
+ 22 attributes:
+ 0:0:ACCEPT-CHARSET
+ 1:0:ACCESSKEY
+ 2:0:ALIGN
+ 3:4:CLASS
+ 4:0:CLEAR
+ 5:0:COLS
+ 6:0:DIR
+ 7:0:DISABLED
+ 8:0:ERROR
+ 9:1:ID
+ 10:0:LANG
+ 11:0:NAME
+ 12:0:NOTAB
+ 13:0:ONBLUR
+ 14:0:ONCHANGE
+ 15:0:ONFOCUS
+ 16:0:ONSELECT
+ 17:0:READONLY
+ 18:0:ROWS
+ 19:0:STYLE
+ 20:0:TABINDEX
+ 21:0:TITLE
+ 5 attr_types
+ align
+ core
+ events
+ i18n
+ TEXTAREA
+ contents: SGML_PCDATA
+ tagclass: SELECTlike
+ contains:
+ icontains:
+ contained: FONTlike EMlike MATHlike Alike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike outer BODYlike
+ canclose: FONTlike EMlike MATHlike Alike formula SELECTlike Plike LIlike ULlike same
+ flags: nolyspcl
+ 116:TEXTFLOW
+ justify
+ 14 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DATA
+ 3:0:DIR
+ 4:1:ID
+ 5:0:LANG
+ 6:0:NAME
+ 7:0:OBJECT
+ 8:0:REF
+ 9:0:STYLE
+ 10:0:TITLE
+ 11:0:TYPE
+ 12:0:VALUE
+ 13:0:VALUETYPE
+ 3 attr_types
+ core
+ i18n
+ BODYTEXT
+ contents: SGML_MIXED
+ tagclass: BODYlike
+ contains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike ULlike BRlike APPLETlike HRlike MAPlike same DELlike
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike outer same DELlike
+ contained: formula TRlike FORMlike Plike DIVlike LIlike BRlike APPLETlike HRlike outer same
+ icontained: FONTlike EMlike MATHlike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike same
+ canclose: FONTlike EMlike MATHlike Alike BRlike APPLETlike MAPlike outer same
+ flags: endO startO
+ 117:TFOOT
+ justify
+ 13 attributes:
+ 0:0:ALIGN
+ 1:0:CHAR
+ 2:0:CHAROFF
+ 3:4:CLASS
+ 4:0:CLEAR
+ 5:0:DIR
+ 6:0:DP
+ 7:1:ID
+ 8:0:LANG
+ 9:0:NOWRAP
+ 10:0:STYLE
+ 11:0:TITLE
+ 12:0:VALIGN
+ 5 attr_types
+ cellalign
+ core
+ events
+ i18n
+ TR
+ contents: SGML_ELEMENT
+ tagclass: TRlike
+ contains: TRlike
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: ULlike
+ icontained: FONTlike EMlike MATHlike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike same
+ canclose: FONTlike EMlike MATHlike Alike formula SELECTlike Plike DIVlike LIlike ULlike HRlike MAPlike same
+ flags: endO
+ 118:TH
+ justify
+ 23 attributes:
+ 0:0:ABBR
+ 1:0:ALIGN
+ 2:0:AXES
+ 3:0:AXIS
+ 4:2:BACKGROUND
+ 5:0:CHAR
+ 6:0:CHAROFF
+ 7:4:CLASS
+ 8:0:CLEAR
+ 9:0:COLSPAN
+ 10:0:DIR
+ 11:0:DP
+ 12:0:HEADERS
+ 13:0:HEIGHT
+ 14:1:ID
+ 15:0:LANG
+ 16:0:NOWRAP
+ 17:0:ROWSPAN
+ 18:0:SCOPE
+ 19:0:STYLE
+ 20:0:TITLE
+ 21:0:VALIGN
+ 22:0:WIDTH
+ 4 attr_types
+ cellalign
+ core
+ i18n
+ TD
+ contents: SGML_MIXED
+ tagclass: LIlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike FORMlike Plike DIVlike ULlike BRlike APPLETlike HRlike MAPlike
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike
+ contained: TRlike
+ icontained: FONTlike EMlike MATHlike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike same
+ canclose: FONTlike EMlike MATHlike Alike formula SELECTlike Plike DIVlike LIlike ULlike HRlike MAPlike same
+ flags: endO
+ 119:THEAD
+ justify
+ 13 attributes:
+ 0:0:ALIGN
+ 1:0:CHAR
+ 2:0:CHAROFF
+ 3:4:CLASS
+ 4:0:CLEAR
+ 5:0:DIR
+ 6:0:DP
+ 7:1:ID
+ 8:0:LANG
+ 9:0:NOWRAP
+ 10:0:STYLE
+ 11:0:TITLE
+ 12:0:VALIGN
+ 5 attr_types
+ cellalign
+ core
+ events
+ i18n
+ TR
+ contents: SGML_ELEMENT
+ tagclass: TRlike
+ contains: TRlike
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: ULlike
+ icontained: FONTlike EMlike MATHlike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike same
+ canclose: FONTlike EMlike MATHlike Alike formula SELECTlike Plike DIVlike LIlike ULlike HRlike MAPlike same
+ flags: endO
+ 120:TITLE
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN
+ contents: SGML_PCDATA
+ tagclass: HEADstuff
+ contains:
+ icontains:
+ contained: outer HEADstuff
+ icontained: outer HEADstuff
+ canclose: FONTlike EMlike MATHlike Alike formula Plike DIVlike
+ flags: mafse strict
+ 121:TR
+ justify
+ 13 attributes:
+ 0:0:ALIGN
+ 1:0:CHAR
+ 2:0:CHAROFF
+ 3:4:CLASS
+ 4:0:CLEAR
+ 5:0:DIR
+ 6:0:DP
+ 7:1:ID
+ 8:0:LANG
+ 9:0:NOWRAP
+ 10:0:STYLE
+ 11:0:TITLE
+ 12:0:VALIGN
+ 5 attr_types
+ cellalign
+ core
+ events
+ i18n
+ TR
+ contents: SGML_MIXED
+ tagclass: TRlike
+ contains: LIlike
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: TRlike ULlike
+ icontained: FONTlike EMlike MATHlike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike same
+ canclose: FONTlike EMlike MATHlike Alike formula SELECTlike Plike DIVlike LIlike HRlike MAPlike same
+ flags: endO
+ 122:TT
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN
+ contents: SGML_MIXED
+ tagclass: FONTlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike BRlike APPLETlike MAPlike same
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: FONTlike EMlike MATHlike Alike FORMlike Plike DIVlike LIlike BRlike APPLETlike HRlike BODYlike same
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike HEADstuff same
+ canclose: FONTlike
+ flags: nreie
+ 123:U
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN
+ contents: SGML_MIXED
+ tagclass: FONTlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike BRlike APPLETlike MAPlike same
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: FONTlike EMlike MATHlike Alike FORMlike Plike DIVlike LIlike BRlike APPLETlike HRlike BODYlike same
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike HEADstuff same
+ canclose: FONTlike
+ flags: mafse nreie
+ 124:UL
+ justify
+ 14 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:COMPACT
+ 3:0:DINGBAT
+ 4:0:DIR
+ 5:1:ID
+ 6:0:LANG
+ 7:0:MD
+ 8:0:PLAIN
+ 9:2:SRC
+ 10:0:STYLE
+ 11:0:TITLE
+ 12:0:TYPE
+ 13:0:WRAP
+ 3 attr_types
+ core
+ i18n
+ UL
+ contents: SGML_MIXED
+ tagclass: ULlike
+ contains: FORMlike LIlike HRlike MAPlike
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: FORMlike DIVlike LIlike APPLETlike HRlike outer BODYlike DELlike
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike same DELlike
+ canclose: FONTlike EMlike MATHlike Alike formula SELECTlike Plike DIVlike LIlike same
+ flags:
+ 125:VAR
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN
+ contents: SGML_MIXED
+ tagclass: EMlike
+ contains: FONTlike EMlike MATHlike Alike SELECTlike BRlike APPLETlike MAPlike same
+ icontains: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike MAPlike same
+ contained: FONTlike EMlike MATHlike Alike FORMlike Plike DIVlike LIlike BRlike APPLETlike HRlike BODYlike same
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike HEADstuff same
+ canclose: FONTlike
+ flags:
+ 126:WBR
+ justify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN
+ contents: SGML_EMPTY
+ tagclass: FONTlike
+ contains:
+ icontains:
+ contained: FONTlike EMlike MATHlike Alike FORMlike Plike DIVlike LIlike BRlike APPLETlike HRlike outer BODYlike
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike FORMlike Plike DIVlike LIlike ULlike BRlike APPLETlike HRlike outer BODYlike HEADstuff
+ canclose: FONTlike EMlike MATHlike Alike formula BRlike same
+ flags: endO
+ 127:XMP
+ nojustify
+ 7 attributes:
+ 0:4:CLASS
+ 1:0:CLEAR
+ 2:0:DIR
+ 3:1:ID
+ 4:0:LANG
+ 5:0:STYLE
+ 6:0:TITLE
+ 4 attr_types
+ core
+ events
+ i18n
+ GEN
+ contents: SGML_LITTERAL
+ tagclass: ULlike
+ contains:
+ icontains:
+ contained: TRlike SELECTlike FORMlike Plike DIVlike LIlike APPLETlike HRlike outer BODYlike DELlike
+ icontained: FONTlike EMlike MATHlike Alike formula TRlike SELECTlike FORMlike Plike DIVlike LIlike ULlike APPLETlike HRlike outer BODYlike DELlike
+ canclose: FONTlike EMlike MATHlike Alike formula SELECTlike Plike DIVlike LIlike MAPlike
+ flags: endO
diff --git a/WWW/Library/Implementation/tidy_tls.h b/WWW/Library/Implementation/tidy_tls.h
new file mode 100644
index 0000000..56b3636
--- /dev/null
+++ b/WWW/Library/Implementation/tidy_tls.h
@@ -0,0 +1,147 @@
+/*
+ * $LynxId: tidy_tls.h,v 1.8 2015/10/12 00:28:18 tom Exp $
+ * Copyright 2008-2013,2015 Thomas E. Dickey
+ */
+#ifndef TIDY_TLS_H
+#define TIDY_TLS_H
+
+#include <gnutls/gnutls.h>
+
+#define OPENSSL_VERSION_NUMBER (0x0090604F)
+#define SSLEAY_VERSION_NUMBER OPENSSL_VERSION_NUMBER
+
+#define SSLeay_add_ssl_algorithms() SSL_library_init()
+
+#define SSL_ST_OK (1)
+
+#define SSL_OP_ALL (0x000FFFFF)
+#define SSL_OP_NO_SSLv2 (0x00100000)
+#define SSL_OP_NO_SSLv3 (0x00200000)
+#define SSL_OP_NO_TLSv1 (0x00400000)
+
+#define SSL_get_cipher_name(ssl) SSL_CIPHER_get_name(SSL_get_current_cipher(ssl))
+#define SSL_get_cipher(ssl) SSL_get_cipher_name(ssl)
+#define SSL_get_cipher_bits(ssl,bp) SSL_CIPHER_get_bits(SSL_get_current_cipher(ssl),(bp))
+#define SSL_get_cipher_version(ssl) SSL_CIPHER_get_version(SSL_get_current_cipher(ssl))
+
+#define TIDY_TLS_BUFSIZE 256
+
+typedef struct {
+ char common_name[TIDY_TLS_BUFSIZE];
+ char country[TIDY_TLS_BUFSIZE];
+ char email[TIDY_TLS_BUFSIZE];
+ char locality_name[TIDY_TLS_BUFSIZE];
+ char organization[TIDY_TLS_BUFSIZE];
+ char organizational_unit_name[TIDY_TLS_BUFSIZE];
+ char state_or_province_name[TIDY_TLS_BUFSIZE];
+} X509_NAME;
+
+typedef struct _SSL SSL;
+
+typedef gnutls_datum_t X509;
+
+typedef struct {
+ unsigned connend;
+ struct {
+ int protocol[GNUTLS_MAX_ALGORITHM_NUM];
+ int encrypts[GNUTLS_MAX_ALGORITHM_NUM];
+ int compress[GNUTLS_MAX_ALGORITHM_NUM];
+ int key_xchg[GNUTLS_MAX_ALGORITHM_NUM];
+ int msg_code[GNUTLS_MAX_ALGORITHM_NUM];
+ } priority;
+} SSL_METHOD;
+
+typedef struct {
+ SSL *ssl;
+ int error;
+ const gnutls_datum_t *cert_list;
+#define current_cert cert_list
+} X509_STORE_CTX;
+
+typedef struct {
+ gnutls_certificate_type_t cert;
+ gnutls_cipher_algorithm_t encrypts;
+ gnutls_compression_method_t compress;
+ gnutls_kx_algorithm_t key_xchg;
+ gnutls_mac_algorithm_t msg_code;
+ gnutls_protocol_t protocol;
+} SSL_CIPHER;
+
+typedef struct _SSL_CTX {
+ SSL_METHOD *method;
+ char *certfile;
+ int certfile_type;
+ char *keyfile;
+ int keyfile_type;
+ unsigned long options;
+
+ int (*verify_callback) (int, X509_STORE_CTX *);
+ int verify_mode;
+
+ char *client_certfile;
+ int client_certfile_type;
+ char *client_keyfile;
+ int client_keyfile_type;
+
+} SSL_CTX;
+
+struct _SSL {
+ gnutls_session_t gnutls_state;
+
+ gnutls_certificate_client_credentials gnutls_cred;
+
+ SSL_CTX *ctx;
+ SSL_CIPHER ciphersuite;
+
+ int last_error;
+ int shutdown;
+ int state;
+ unsigned long options;
+
+ int (*verify_callback) (int, X509_STORE_CTX *);
+ int verify_mode;
+
+ gnutls_transport_ptr_t rfd;
+ gnutls_transport_ptr_t wfd;
+
+ void *sendbuffer;
+ size_t bytes_sent;
+};
+
+/* use either SSL_VERIFY_NONE or SSL_VERIFY_PEER, the last 2 options
+ * are 'ored' with SSL_VERIFY_PEER if they are desired */
+#define SSL_VERIFY_PEER 0x01
+/* *INDENT-OFF* */
+extern SSL *SSL_new(SSL_CTX * ctx);
+extern SSL_CIPHER *SSL_get_current_cipher(SSL * ssl);
+extern SSL_CTX *SSL_CTX_new(SSL_METHOD * method);
+extern SSL_METHOD *SSLv23_client_method(void);
+extern const X509 *SSL_get_peer_certificate(SSL * ssl);
+extern X509_NAME *X509_get_issuer_name(const X509 * cert);
+extern X509_NAME *X509_get_subject_name(const X509 * cert);
+extern char *X509_NAME_oneline(X509_NAME * name, char *buf, int len);
+extern const char *ERR_error_string(unsigned long e, char *buf);
+extern const char *RAND_file_name(char *buf, size_t len);
+extern const char *SSL_CIPHER_get_name(SSL_CIPHER * cipher);
+extern const char *SSL_CIPHER_get_version(SSL_CIPHER * cipher);
+extern int RAND_bytes(unsigned char *buf, int num);
+extern int RAND_load_file(const char *name, long maxbytes);
+extern int RAND_status(void);
+extern int RAND_write_file(const char *name);
+extern int SSL_CIPHER_get_bits(SSL_CIPHER * cipher, int *bits);
+extern int SSL_CTX_set_default_verify_paths(SSL_CTX * ctx);
+extern int SSL_connect(SSL * ssl);
+extern int SSL_library_init(void);
+extern int SSL_read(SSL * ssl, void *buf, int len);
+extern int SSL_set_fd(SSL * ssl, int fd);
+extern int SSL_write(SSL * ssl, const void *buf, int len);
+extern unsigned long ERR_get_error(void);
+extern unsigned long SSL_CTX_set_options(SSL_CTX * ctx, unsigned long options);
+extern void RAND_seed(const void *buf, int num);
+extern void SSL_CTX_free(SSL_CTX * ctx);
+extern void SSL_CTX_set_verify(SSL_CTX * ctx, int verify_mode, int (*verify_callback) (int, X509_STORE_CTX *));
+extern void SSL_free(SSL * ssl);
+extern void SSL_load_error_strings(void);
+/* *INDENT-ON* */
+
+#endif /* TIDY_TLS_H */
diff --git a/WWW/Library/Implementation/www_tcp.h b/WWW/Library/Implementation/www_tcp.h
new file mode 100644
index 0000000..ef754c8
--- /dev/null
+++ b/WWW/Library/Implementation/www_tcp.h
@@ -0,0 +1,1017 @@
+/* System dependencies in the W3 library
+ * $LynxId: www_tcp.h,v 1.61 2018/12/26 12:30:14 tom Exp $
+ *
+ SYSTEM DEPENDENCIES
+
+ System-system differences for TCP include files and macros. This
+ file includes for each system the files necessary for network and
+ file I/O. It should be used in conjunction with HTUtils.h to help
+ ensure portability across as many platforms and flavors of platforms
+ as possible.
+
+ AUTHORS
+
+ TBL Tim Berners-Lee, W3 project, CERN, <timbl@info.cern.ch>
+ EvA Eelco van Asperen <evas@cs.few.eur.nl>
+ MA Marc Andreessen NCSA
+ AT Aleksandar Totic <atotic@ncsa.uiuc.edu>
+ SCW Susan C. Weber <sweber@kyle.eitech.com>
+
+ HISTORY:
+ 22 Feb 91 Written (TBL) as part of the WWW library.
+ 16 Jan 92 PC code from EvA
+ 22 Apr 93 Merged diffs bits from xmosaic release
+ 29 Apr 93 Windows/NT code from SCW
+ 20 May 94 A.Harper Add support for VMS CMU TCP/IP transport
+ 3 Oct 94 A.Harper Add support for VMS SOCKETSHR/NETLIB
+ 15 Jul 95 S. Bjorndahl Gnu C for VMS Globaldef/ref support
+
+*/
+
+#ifndef TCP_H
+#define TCP_H
+
+/*
+
+Default values
+
+ These values may be reset and altered by system-specific sections
+ later on. there are also a bunch of defaults at the end .
+
+ */
+/* Default values of those: */
+ /* Routine to close a TCP-IP socket */
+#define NETCLOSE close
+ /* Routine to read from a TCP-IP socket */
+#define NETREAD(s,p,n) \
+ HTDoRead(s, p, (unsigned)(n))
+ /* Routine to write to a TCP-IP socket */
+#define NETWRITE(s,p,n) write(s, p, (size_t)(n))
+#define SOCKET_READ(s,b,l) read(s,b,(size_t)(l))
+#define IOCTL(s,cmd,arg) ioctl(s,(long)(cmd),arg)
+#define SOCKET_ERRNO errno /* normal socket errno */
+
+/* Unless stated otherwise, */
+#define SELECT /* Can handle >1 channel. */
+#define GOT_SYSTEM /* Can call shell with string */
+
+#ifdef UNIX
+#define GOT_PIPE
+#endif /* UNIX */
+
+#define INVSOC (-1) /* Unix invalid socket */
+ /* NB: newer libwww has something different for Windows */
+
+#ifndef VMS
+
+#include <sys/types.h>
+
+#if defined(__BORLANDC__)
+#define DECL_ERRNO
+#endif
+
+#if defined(__DJGPP__) || defined(__BORLANDC__)
+#undef HAVE_DIRENT_H
+#define HAVE_DIRENT_H
+#undef HAVE_SYS_FILIO_H
+#endif /* DJGPP or __BORLANDC__ */
+
+#if defined(_MSC_VER)
+#undef HAVE_DIRENT_H
+#define HAVE_DIRENT_H
+#undef HAVE_SYS_FILIO_H
+#endif /* _MSC_VER */
+
+#ifdef HAVE_DIRENT_H
+# include <dirent.h>
+# define D_NAMLEN(dirent) strlen((dirent)->d_name)
+# define STRUCT_DIRENT struct dirent
+#else
+# define D_NAMLEN(dirent) (dirent)->d_namlen
+# define STRUCT_DIRENT struct direct
+# ifdef HAVE_SYS_NDIR_H
+# include <sys/ndir.h>
+# endif
+# ifdef HAVE_SYS_DIR_H
+# include <sys/dir.h>
+# endif
+# ifdef HAVE_NDIR_H
+# include <ndir.h>
+# endif
+#endif /* HAVE_DIRENT_H */
+
+#ifdef HAVE_STRUCT_DIRENT64
+# undef STRUCT_DIRENT
+# define STRUCT_DIRENT struct dirent64
+#endif
+
+#if !(defined(DOSPATH) || defined(__EMX__) || defined(__CYGWIN__))
+#define STRUCT_DIRENT__D_INO 1
+#endif
+
+#endif /* !VMS */
+
+#ifdef TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+# ifdef HAVE_SYS_TIME_H
+# include <sys/time.h>
+# else
+# include <time.h>
+# endif
+#endif
+
+#if defined(_AIX) && !defined(AIX)
+#define AIX
+#endif /* _AIX */
+
+#ifndef __CYGWIN__
+#ifdef WIN_EX
+#define HAVE_FTIME 1
+#define HAVE_SYS_TIMEB_H 1
+#endif
+#endif /* __CYGWIN__ */
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#else
+#ifdef HAVE_SYS_FCNTL_H
+#include <sys/fcntl.h>
+#endif
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h> /* For bzero etc */
+#endif /* HAVE_STRING_H */
+
+/*
+
+ MACROS FOR CONVERTING CHARACTERS
+
+ */
+#ifndef TOASCII
+#ifdef EBCDIC /* S/390 -- gil -- 1327 */
+
+extern const char un_IBM1047[];
+extern const unsigned char IBM1047[];
+
+/* For debugging
+#include <assert.h>
+#define TOASCII(c) (assert((c)>=0 && (c)<256), un_IBM1047[c])
+*/
+/* for production */
+#define TOASCII(c) (un_IBM1047[c])
+
+#define FROMASCII(c) (IBM1047[c])
+
+#else /* EBCDIC */
+
+#if '0' != 48
+error Host character set is not ASCII.
+#endif
+
+#define TOASCII(c) (c)
+#define FROMASCII(c) (c)
+
+#endif /* EBCDIC */
+#endif /* !TOASCII */
+
+/* convert a char to an unsigned, needed if we have signed characters for ctype.h */
+#define UCH(ch) ((unsigned char)(ch))
+
+/*
+ * These parameters were provided by Nigel Horne, using BeOS professional 5.0
+ */
+#ifdef __BEOS__
+#undef NETREAD
+#undef NETWRITE
+#undef NETCLOSE
+#define NETREAD(s,b,l) recv((s),(b),(l),0)
+#define NETWRITE(s,b,l) send((s),(b),(l),0)
+#define NETCLOSE(s) closesocket(s)
+#endif
+
+/*
+IBM-PC running Windows NT
+
+ These parameters provided by Susan C. Weber <sweber@kyle.eitech.com>.
+*/
+
+#ifdef _WINDOWS
+
+#ifndef _WINDOWS_NSL
+#define _WINDOWS_NSL
+#endif
+
+#include <fcntl.h> /* For HTFile.c */
+#include <sys/types.h> /* For HTFile.c */
+#include <sys/stat.h> /* For HTFile.c */
+#undef NETREAD
+#undef NETWRITE
+#undef NETCLOSE
+#undef IOCTL
+extern int ws_netread(int fd, char *buf, int len);
+
+#define NETREAD(s,b,l) ws_netread((s),(b),(l)) /* 1997/11/06 (Thu) */
+#define NETWRITE(s,b,l) send((s),(b),(l),0)
+#define NETCLOSE(s) closesocket(s)
+#define IOCTL(s,cmd,arg) ioctlsocket(s,cmd,arg)
+#include <io.h>
+#include <string.h>
+#include <process.h>
+#include <time.h>
+#include <errno.h>
+#include <direct.h>
+
+#ifdef ENABLE_IPV6
+#undef USE_WINSOCK2_H
+#define USE_WINSOCK2_H
+
+/* Avoid including <winsock*.h> in <windows.h> */
+#ifndef WIN32_LEAN_AND_MEAN
+#error Define "WIN32_LEAN_AND_MEAN" in your makefile
+#endif
+
+#ifdef _WINSOCKAPI_
+#error windows.h included before winsock2.h
+#endif
+
+#if defined(_MSC_VER) && (!defined(_WIN32_WINNT) || _WIN32_WINNT < 0x0501)
+/*
+ * Needed to pull in the real getaddrinfo() and not the inline version
+ * in <wspiAPI.H> which doesn't support IPv6 (IPv4 only). <wspiAPI.H> is
+ * included from <ws2tcpip.h> for <= 0x0500 SDKs.
+ */
+#undef _WIN32_WINNT
+#define _WIN32_WINNT 0x0501
+#endif
+#endif /* ENABLE_IPV6 */
+
+#ifdef USE_WINSOCK2_H
+#include <winsock2.h> /* normally included in windows.h */
+
+#ifdef ENABLE_IPV6
+#include <ws2tcpip.h>
+#endif
+
+#undef EINPROGRESS
+#undef EALREADY
+#undef EISCONN
+#undef EINTR
+#undef EAGAIN
+#undef ENOTCONN
+#undef ECONNRESET
+#undef ETIMEDOUT
+
+#define EINPROGRESS WSAEINPROGRESS
+#define EALREADY WSAEALREADY
+#define EISCONN WSAEISCONN
+#define EINTR WSAEINTR
+/* fine EAGAIN WSAEAGAIN */
+#define ENOTCONN WSAENOTCONN
+#define ECONNRESET WSAECONNRESET
+#define ETIMEDOUT WSAETIMEDOUT
+
+#else /* USE_WINSOCK2_H */
+
+#include <winsock.h>
+
+#if defined(_MSC_VER) || defined(__MINGW32__)
+#undef EINTR
+#undef EAGAIN
+#endif /* _MSC_VER */
+
+#undef EINPROGRESS
+#define EINPROGRESS (WSABASEERR+36)
+
+#undef EALREADY
+#define EALREADY (WSABASEERR+37)
+
+#undef EISCONN
+#define EISCONN (WSABASEERR+56)
+
+#undef EINTR
+#define EINTR (WSABASEERR+4)
+
+#undef EAGAIN
+#define EAGAIN (WSABASEERR+1002)
+
+#undef ENOTCONN
+#define ENOTCONN (WSABASEERR+57)
+
+#undef ECONNRESET
+#define ECONNRESET (WSABASEERR+54)
+
+#undef ETIMEDOUT
+#define ETIMEDOUT WSAETIMEDOUT
+
+#endif /* USE_WINSOCK2_H */
+
+#undef SOCKET_ERRNO
+#define SOCKET_ERRNO WSAGetLastError()
+
+#define INCLUDES_DONE
+#define TCP_INCLUDES_DONE
+
+#define LYNX_FD SOCKET /* WINSOCK uses an unsigned value for FD_SET, etc */
+
+#else
+
+#define LYNX_FD int /* POSIX says FD_SET descriptor is int */
+
+#endif /* WINDOWS */
+
+/*
+
+VAX/VMS
+
+ Under VMS, there are many versions of TCP-IP. Define one if you do
+ not use Digital's UCX product:
+
+ UCX DEC's "Ultrix connection" (default)
+ CMU_TCP Available via FTP from sacusr.mp.usbr.gov
+ SOCKETSHR Eckhart Meyer's interface to NETLIB
+ WIN_TCP From Wollongong, now GEC software.
+ MULTINET From SRI, became TGV, then Cisco.
+ DECNET Cern's TCP socket emulation over DECnet
+ TCPIP_SERVICES TCP/IP Services (newer than UCX)
+
+ WIN_TCP, MULTINET and DECNET do not interfere with the
+ unix i/o library, and so they need special calls to read, write and
+ close sockets. In these cases the socket number is a VMS channel
+ number, so we make the @@@ HORRIBLE @@@ assumption that a channel
+ number will be greater than 10 but a unix file descriptor less than
+ 10. It works.
+
+ */
+#ifdef VMS
+
+#ifdef UCX
+#undef IOCTL
+#define IOCTL(s,cmd,arg) HTioctl(s,cmd,arg)
+#endif /* UCX */
+
+#ifdef WIN_TCP
+#undef SOCKET_READ
+#undef NETWRITE
+#undef NETCLOSE
+#define SOCKET_READ(s,b,l) ((s)>10 ? netread((s),(b),(l)) : read((s),(b),(l)))
+#define NETWRITE(s,b,l) ((s)>10 ? netwrite((s),(b),(l)) : write((s),(b),(l)))
+#define NETCLOSE(s) ((s)>10 ? netclose(s) : close(s))
+#undef IOCTL
+#define IOCTL(a,b,c) -1 /* disables ioctl function */
+#define NO_IOCTL /* flag to check if ioctl is disabled */
+#endif /* WIN_TCP */
+
+#ifdef CMU_TCP
+#undef SOCKET_READ
+#undef NETREAD
+#undef NETWRITE
+#undef NETCLOSE
+#define SOCKET_READ(s,b,l) (cmu_get_sdc((s)) != 0 ? cmu_read((s),(b),(l)) : read((s),(b),(l)))
+#define NETREAD(s,b,l) (cmu_get_sdc((s)) != 0 ? HTDoRead((s),(b),(l)) : read((s),(b),(l)))
+#define NETWRITE(s,b,l) (cmu_get_sdc((s)) != 0 ? cmu_write((s),(b),(l)) : write((s),(b),(l)))
+#define NETCLOSE(s) (cmu_get_sdc((s)) != 0 ? cmu_close((s)) : close((s)))
+#endif /* CMU_TCP */
+
+#ifdef MULTINET
+#undef NETCLOSE
+#undef SOCKET_READ
+#undef NETWRITE
+#undef IOCTL
+#undef SOCKET_ERRNO
+/*
+ * Delete these socket_foo() prototypes as MultiNet adds them
+ * to it's socket library headers. Compiler warnings due to
+ * the absence of arguments in the generic prototypes here will
+ * include the names of those which can be deleted. - FM
+ */
+extern int socket_read();
+extern int socket_write();
+extern int socket_close();
+extern int socket_ioctl();
+
+#define SOCKET_READ(s,b,l) ((s)>10 ? socket_read((s),(b),(l)) : \
+ read((s),(b),(l)))
+#define NETWRITE(s,b,l) ((s)>10 ? socket_write((s),(b),(l)) : \
+ write((s),(b),(l)))
+#define NETCLOSE(s) ((s)>10 ? socket_close(s) : close(s))
+#define IOCTL(s,cmd,arg) socket_ioctl(s,cmd,arg)
+#define SOCKET_ERRNO socket_errno
+#endif /* MULTINET */
+
+#ifdef SOCKETSHR_TCP
+#undef SOCKET_READ
+#undef NETREAD
+#undef NETWRITE
+#undef NETCLOSE
+#undef IOCTL
+#define SOCKET_READ(s,b,l) (si_get_sdc((s)) != 0 ? si_read((s),(b),(l)) : \
+ read((s),(b),(l)))
+#define NETREAD(s,b,l) (si_get_sdc((s)) != 0 ? HTDoRead((s),(b),(l)) : \
+ read((s),(b),(l)))
+#define NETWRITE(s,b,l) (si_get_sdc((s)) != 0 ? si_write((s),(b),(l)) : \
+ write((s),(b),(l)))
+#define NETCLOSE(s) (si_get_sdc((s)) != 0 ? si_close((s)) : close((s)))
+#define IOCTL(s,cmd,arg) si_ioctl(s,cmd,arg)
+#endif /* SOCKETSHR_TCP */
+
+#ifdef TCPIP_SERVICES
+/*
+ * TCPIP Services has all of the entrypoints including ioctl().
+ */
+#undef NETWRITE
+#define NETWRITE(s,b,l) send((s),(char *)(b),(l),0)
+
+#define TYPE_FD_SET int
+
+#if 0 /* this should be declared via time.h */
+typedef TYPE_FD_SET fd_set;
+#endif
+
+#endif /* TCPIP_SERVICES */
+
+#include <string.h>
+
+#include <file.h>
+#include <stat.h>
+#include <unixio.h>
+#include <unixlib.h>
+
+#define INCLUDES_DONE
+
+#ifdef MULTINET /* Include from standard Multinet directories */
+/*
+ * Delete any of these multinet_foo() and associated prototypes
+ * as MultiNet adds them to its socket library headers. You'll
+ * get compiler warnings about them, due the absence of arguments
+ * in the generic prototyping here, and the warnings will include
+ * the names of the functions whose prototype entries can be
+ * deleted here. - FM
+ */
+extern int multinet_accept();
+extern int multinet_bind();
+extern int multinet_connect();
+extern int multinet_gethostname();
+extern int multinet_getsockname();
+extern unsigned short multinet_htons(unsigned short __val);
+extern unsigned short multinet_ntohs(unsigned short __val);
+extern int multinet_listen();
+extern int multinet_select();
+extern int multinet_socket();
+extern char *vms_errno_string();
+
+#ifndef __SOCKET_TYPEDEFS
+#define __SOCKET_TYPEDEFS 1
+#endif /* !__SOCKET_TYPEDEFS */
+
+#include <time.h>
+#include <types.h>
+/*
+ * DEC C before version 5.2 added some typedefs to <types.h> which happen
+ * to be suppressed if the version-4 compatibility define is set. In
+ * particular, lynx uses "off_t". VAX-C used "unsigned", DEC-C uses "int".
+ */
+#if defined(_DECC_V4_SOURCE) && !defined(____OFF_T)
+#undef off_t
+#define off_t int
+#endif
+
+#ifdef __TIME_T
+#undef __TYPES
+#define __TYPES 1
+#define __TYPES_LOADED 1
+#endif /* __TIME_T */
+
+#ifdef __SOCKET_TYPEDEFS
+#undef __SOCKET_TYPEDEFS
+#endif /* __SOCKET_TYPEDEFS */
+
+#include "multinet_root:[multinet.include.sys]types.h"
+
+#ifndef __SOCKET_TYPEDEFS
+#define __SOCKET_TYPEDEFS 1
+#endif /* !__SOCKET_TYPEDEFS */
+
+#include "multinet_root:[multinet.include]errno.h"
+
+#ifdef __TYPES
+#undef __TIME_T
+#define __TIME_T 1
+#endif /* __TYPE */
+
+#ifdef __TIME_LOADED
+#undef __TIME
+#define __TIME 1 /* to avoid double definitions in in.h */
+#endif /* __TIME_LOADED */
+
+#include "multinet_root:[multinet.include.sys]time.h"
+
+#define MULTINET_NO_PROTOTYPES /* DECC is compatible-but-different */
+#include "multinet_root:[multinet.include.sys]socket.h"
+#undef MULTINET_NO_PROTOTYPES
+#include "multinet_root:[multinet.include.netinet]in.h"
+#include "multinet_root:[multinet.include.arpa]inet.h"
+#include "multinet_root:[multinet.include]netdb.h"
+#include "multinet_root:[multinet.include.sys]ioctl.h"
+#define TCP_INCLUDES_DONE
+/*
+ * Uncomment this if you get compiler messages
+ * about struct timeval having no linkage. - FM
+ */
+/*#define NO_TIMEVAL*/
+#ifdef NO_TIMEVAL
+struct timeval {
+ long tv_sec; /* seconds since Jan. 1, 1970 */
+ long tv_usec; /* microseconds */
+};
+#endif /* NO_TIMEVAL */
+#endif /* MULTINET */
+
+#ifdef DECNET
+#include <types.h>
+#include <errno.h>
+#include <time.h>
+#include <types.h> /* for socket.h */
+#include <socket.h>
+#include <dn>
+#include <dnetdb>
+/* #include "vms.h" */
+#define TCP_INCLUDES_DONE
+#endif /* DECNET */
+
+#ifdef UCX
+#include <types.h>
+#include <errno.h>
+#include <time.h>
+#include <socket.h>
+#include <in.h>
+#include <inet.h>
+#if defined(TCPWARE) && !defined(__DECC)
+#include "tcpware_include:netdb.h"
+#include "tcpware_include:ucx$inetdef.h"
+#else
+#include <netdb.h>
+#ifdef MUCX
+#include <multinet_root:[multinet.include.vms]ucx$inetdef.h>
+#else
+#include <ucx$inetdef.h>
+#endif /* MUCX */
+#endif /* TCPWARE */
+#define TCP_INCLUDES_DONE
+#endif /* UCX */
+
+#ifdef CMU_TCP
+#include <types.h>
+#include <errno.h>
+#include "cmuip_root:[syslib]time.h"
+#include "cmuip_root:[syslib]socket.h"
+#include <in.h>
+#include <inet.h>
+#include <netdb.h>
+#include "cmuip_root:[syslib]ioctl.h"
+#define TCP_INCLUDES_DONE
+#endif /* CMU_TCP */
+
+#ifdef SOCKETSHR_TCP
+#include <types.h>
+#include <errno.h>
+#include <time.h>
+#include <socket.h>
+#include <in.h>
+#include <inet.h>
+#include <netdb.h>
+#include "socketshr_library:socketshr.h"
+#include "socketshr_library:ioctl.h"
+#define TCP_INCLUDES_DONE
+#endif /* SOCKETSHR_TCP */
+
+#ifdef TCPIP_SERVICES
+#include <types.h>
+#include <errno.h>
+#include <time.h>
+#include <ioctl.h>
+#include <socket.h>
+#include <in.h>
+#include <inet.h>
+#include <netdb.h>
+#define TCP_INCLUDES_DONE
+#endif /* TCPIP_SERVICES */
+
+#ifdef WIN_TCP
+#include <types.h>
+#include <errno.h>
+#include <time.h>
+#include <socket.h>
+#include <in.h>
+#include <inet.h>
+#include <netdb.h>
+#ifndef NO_IOCTL
+#include <ioctl.h>
+#endif /* !NO_IOCTL */
+#define TCP_INCLUDES_DONE
+#endif /* WIN_TCP */
+
+#ifndef TCP_INCLUDES_DONE
+#include <types.h>
+#include <errno.h>
+#include <time.h>
+#ifdef VMS_SOCKET_HEADERS
+/*
+ * Not all versions of VMS have the full set of headers
+ * for socket library functions, because the TCP/IP
+ * packages were layered products. If we want these
+ * specifically, instead of those for the above packages,
+ * the module should be compiled with VMS_SOCKET_HEADERS
+ * defined instead of layered product definitions, above.
+ * If the module is not using socket library functions,
+ * none of the definitions need be used, and we include
+ * only the above three headers. - FM
+ */
+#include <socket.h>
+#include <in.h>
+#include <inet.h>
+#include <netdb.h>
+#include <ioctl.h>
+#endif /* VMS_SOCKET_HEADERS */
+#define TCP_INCLUDES_DONE
+#endif /* !TCP_INCLUDES_DONE */
+
+/*
+ * On VMS machines, the linker needs to be told to put global data sections
+ * into a data segment using these storage classes. (MarkDonszelmann)
+ */
+#if defined(VAXC) && !defined(__DECC)
+#define GLOBALDEF globaldef
+#define GLOBALREF globalref
+#else
+#ifdef __GNUC__ /* this added by Sterling Bjorndahl */
+#define GLOBALREF_IS_MACRO 1
+#define GLOBALDEF_IS_MACRO 1
+#include <gnu_hacks.h> /* defines GLOBALREF and GLOBALDEF for GNUC on VMS */
+#endif /* __GNUC__ */
+#endif /* VAXC && !DECC */
+
+#include <perror.h>
+#ifndef errno
+extern int errno;
+#endif /* !errno */
+
+#endif /* VMS */
+
+/*
+ * On non-VMS machines and for DECC on VMS, the GLOBALDEF and GLOBALREF
+ * storage types default to normal C storage types.
+ */
+#ifndef GLOBALREF
+#define GLOBALDEF
+#define GLOBALREF extern
+#endif /* !GLOBALREF */
+
+#ifdef __DJGPP__
+#undef SELECT
+#define TCP_INCLUDES_DONE
+#undef IOCTL
+#define IOCTL(s,cmd,arg) ioctlsocket(s,cmd,(char*)(arg))
+#define DECL_ERRNO
+#include <errno.h>
+#include <sys/types.h>
+#include <io.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <tcp.h>
+#ifdef word
+#undef word
+#endif /* word */
+#ifdef set_timeout
+#undef set_timeout
+#endif /* set_timeout */
+#define select select_s
+
+#undef NETWRITE
+#define NETWRITE write_s
+#undef NETREAD
+#define NETREAD read_s
+#undef NETCLOSE
+#define NETCLOSE close_s
+#ifdef UNIX
+#undef UNIX
+#endif /* UNIX */
+#ifdef HAVE_GETTEXT
+#define gettext gettext__
+#endif
+#if !defined(NCURSES) && !defined(USE_SLANG)
+#define HAVE_CBREAK 1
+#endif /* !NCURSES && !USE_SLANG */
+#if defined(USE_SLANG) && !defined(NO_DJ_KEYHANDLER) && defined(HAVE_CONFIG_H)
+#define DJGPP_KEYHANDLER
+#endif /* USE_SLANG && !NO_DJ_KEYHANDLER && HAVE_CONFIG_H */
+#endif /* DJGPP */
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif /* HAVE_UNISTD_H */
+
+#ifdef HAVE_SYS_FILIO_H
+#include <sys/filio.h>
+#endif /* HAVE_SYS_FILIO_H */
+
+#if !defined(HAVE_LSTAT) && !defined(lstat)
+#define lstat(path,block) stat(path,block)
+#endif
+
+#if defined(DECL_ERRNO) && !defined(errno)
+extern int errno;
+#endif /* DECL_ERRNO */
+
+/*
+Regular BSD unix versions
+=========================
+ These are a default unix where not already defined specifically.
+ */
+#ifndef INCLUDES_DONE
+#include <sys/types.h>
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif /* HAVE_STRING_H */
+#include <errno.h> /* independent */
+#ifdef __MVS__ /* S/390 -- gil -- 1361 */
+#include <time.h>
+#endif /* __MVS__ */
+#ifdef SCO
+#include <sys/timeb.h>
+#include <time.h>
+#endif /* SCO */
+#if defined(AIX) || defined(SVR4)
+#include <time.h>
+#endif /* AIX || SVR4 */
+#include <sys/time.h> /* independent */
+#include <sys/stat.h>
+#ifndef __MVS__ /* S/390 -- gil -- 1373 */
+#include <sys/param.h>
+#endif /* __MVS__ */
+#include <sys/file.h> /* For open() etc */
+
+#if defined(NeXT) || defined(sony_news)
+#ifndef mode_t
+typedef unsigned short mode_t;
+#endif /* !mode_t */
+
+#endif /* NeXT || sony_news */
+
+#define INCLUDES_DONE
+#endif /* Normal includes */
+
+/* FIXME: this should be autoconf'd */
+/* Interactive UNIX for i386 and i486 -- Thanks to jeffrey@itm.itm.org */
+#ifdef ISC
+#include <net/errno.h>
+#include <sys/types.h>
+#include <sys/tty.h>
+#include <sys/sioctl.h>
+#include <sys/bsdtypes.h>
+#ifndef MERGE
+#define MERGE
+#include <sys/pty.h>
+#undef MERGE
+#else
+#include <sys/pty.h>
+#endif /* !MERGE */
+#ifndef USE_DIRENT
+#define USE_DIRENT /* sys V style directory open */
+#endif /* USE_DIRENT */
+#include <sys/dirent.h>
+#endif /* ISC */
+
+/* Directory reading stuff - BSD or SYS V
+*/
+#ifdef HAVE_CONFIG_H
+
+# ifdef HAVE_LIMITS_H
+# include <limits.h>
+# endif /* HAVE_LIMITS_H */
+# if !defined(MAXINT) && defined(INT_MAX)
+# define MAXINT INT_MAX
+# endif /* !MAXINT && INT_MAX */
+
+#else
+
+#if !(defined(VM) || defined(VMS) || defined(THINK_C) || defined(PCNFS) || defined(_WINDOWS))
+#define DECL_SYS_ERRLIST 1
+#endif
+
+#if defined(VMS)
+#define socklen_t unsigned
+#else
+#define socklen_t int /* used for default LY_SOCKLEN definition */
+#endif
+
+#endif /* !HAVE_CONFIG_H */
+
+#ifdef HAVE_LIBINTL_H
+#include <libintl.h>
+#endif
+
+#ifdef HAVE_LIBGETTEXT_H
+#include <libgettext.h>
+#endif
+
+#define N_(s) s
+
+#ifndef HAVE_GETTEXT
+#define gettext(s) s
+#endif
+
+#ifndef NLS_TEXTDOMAIN
+#define NLS_TEXTDOMAIN "lynx"
+#endif
+
+/*
+Defaults
+========
+ INCLUDE FILES FOR TCP
+ */
+#ifndef TCP_INCLUDES_DONE
+#ifndef NO_IOCTL
+#include <sys/ioctl.h> /* EJB */
+#endif /* !NO_IOCTL */
+#include <sys/socket.h>
+#include <netinet/in.h>
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h> /* Must be after netinet/in.h */
+#endif
+#include <netdb.h>
+#endif /* TCP includes */
+
+typedef unsigned short PortNumber;
+
+#ifndef S_ISLNK
+#define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
+#endif /* S_ISLNK */
+
+#ifndef S_ISDIR
+#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
+#endif /* S_ISDIR */
+
+#ifndef S_ISREG
+#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
+#endif /* S_ISREG */
+
+#ifndef S_ISUID
+#define S_ISUID 0004000
+#endif
+#ifndef S_ISGID
+#define S_ISGID 0002000
+#endif
+#ifndef S_ISVTX
+#define S_ISVTX 0001000
+#endif
+
+#ifndef S_IRWXU
+#define S_IRWXU 00700
+#endif
+
+#ifndef S_IRUSR
+#define S_IRUSR 00400
+#endif
+#ifndef S_IWUSR
+#define S_IWUSR 00200
+#endif
+#ifndef S_IXUSR
+#define S_IXUSR 00100
+#endif
+
+#ifndef S_IRWXG
+#define S_IRWXG 00070
+#endif
+
+#ifndef S_IRGRP
+#define S_IRGRP 00040
+#endif
+#ifndef S_IWGRP
+#define S_IWGRP 00020
+#endif
+#ifndef S_IXGRP
+#define S_IXGRP 00010
+#endif
+
+#ifndef S_IRWXO
+#define S_IRWXO 00007
+#endif
+
+#ifndef S_IROTH
+#define S_IROTH 00004
+#endif
+#ifndef S_IWOTH
+#define S_IWOTH 00002
+#endif
+#ifndef S_IXOTH
+#define S_IXOTH 00001
+#endif
+
+/*
+
+ROUGH ESTIMATE OF MAX PATH LENGTH
+
+*/
+#ifndef HT_MAX_PATH
+#ifdef MAXPATHLEN
+#define HT_MAX_PATH MAXPATHLEN
+#else
+#ifdef PATH_MAX
+#define HT_MAX_PATH PATH_MAX
+#else
+#define HT_MAX_PATH 1024 /* Any better ideas? */
+#endif
+#endif
+#endif /* HT_MAX_PATH */
+
+#if HT_MAX_PATH < 256
+#undef HT_MAX_PATH
+#define HT_MAX_PATH 256
+#endif
+
+/*
+ MACROS FOR MANIPULATING MASKS FOR SELECT()
+ */
+#ifdef SELECT
+#ifndef FD_SET
+#ifndef TYPE_FD_SET
+#define TYPE_FD_SET unsigned
+typedef TYPE_FD_SET fd_set;
+#endif /* !TYPE_FD_SET */
+
+#define FD_SET(fd,pmask) (*(pmask)) |= (1 << (fd))
+#define FD_CLR(fd,pmask) (*(pmask)) &= ~(1 << (fd))
+#define FD_ZERO(pmask) (*(pmask)) = 0
+#define FD_ISSET(fd,pmask) (*(pmask) & (1 << (fd)))
+#endif /* !FD_SET */
+#endif /* SELECT */
+
+/*
+ * Macro for setting errno - only define this if you really can do it.
+ */
+#if defined(CAN_SET_ERRNO) || (!defined(errno) && (!defined(VMS) || defined(UCX)))
+#define set_errno(value) errno = value
+#else
+#define set_errno(value) /* we do not know how */
+#endif
+
+/*
+ * IPv6 support
+ */
+#if defined(HAVE_GETADDRINFO) && defined(ENABLE_IPV6)
+#if defined(HAVE_GAI_STRERROR)
+#define INET6
+#elif defined(_WINDOWS)
+#define INET6
+#ifndef WIN_EX
+#error Define "WIN_EX" in your makefile.
+#endif
+#ifndef _MSC_VER /* MSVC has this inlined in <ws2tcpip.h> */
+#undef gai_strerror
+#define gai_strerror(err) w32_strerror (err)
+#endif
+#endif
+#endif /* HAVE_GETADDRINFO && ENABLE_IPV6 */
+
+typedef union {
+ struct sockaddr_in soc_in;
+ struct sockaddr soc_address;
+#ifdef INET6
+ struct sockaddr_storage soc_storage;
+#endif
+} LY_SOCKADDR;
+
+#ifdef INET6
+typedef struct sockaddr_storage SockA;
+
+#define SOCKADDR_OF(param) (&((param).soc_address))
+
+#ifndef SA_LEN
+#define SA_LEN(x) (((x)->sa_family == AF_INET6) \
+ ? sizeof(struct sockaddr_in6) \
+ : (((x)->sa_family == AF_INET) \
+ ? sizeof(struct sockaddr_in) \
+ : sizeof(struct sockaddr))) /* AF_UNSPEC? */
+#endif
+
+#ifdef SIN6_LEN
+#define SOCKADDR_LEN(param) (SOCKADDR_OF(param)->sa_len)
+#else
+#define SOCKADDR_LEN(param) (socklen_t) SA_LEN(SOCKADDR_OF(param))
+#endif /* SIN6_LEN */
+#else
+typedef struct sockaddr_in SockA;
+
+#define SOCKADDR_OF(param) ((struct sockaddr *)&(param))
+#define SOCKADDR_LEN(param) sizeof(param)
+#endif /* INET6 */
+
+#ifndef MAXHOSTNAMELEN
+#define MAXHOSTNAMELEN 128 /* Max label is 63. Should handle 2 of those */
+#endif /* MAXHOSTNAMELEN */
+
+#endif /* TCP_H */
diff --git a/WWW/Library/Implementation/www_wait.h b/WWW/Library/Implementation/www_wait.h
new file mode 100644
index 0000000..3e40e88
--- /dev/null
+++ b/WWW/Library/Implementation/www_wait.h
@@ -0,0 +1,34 @@
+#ifndef WWW_WAIT_H
+#define WWW_WAIT_H 1
+
+#include <HTUtils.h>
+
+#ifdef HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
+
+#ifndef WEXITSTATUS
+# ifdef HAVE_TYPE_UNIONWAIT
+# define WEXITSTATUS(status) (status.w_retcode)
+# else
+# define WEXITSTATUS(status) (((status) & 0xff00) >> 8)
+# endif
+#endif
+
+#ifndef WTERMSIG
+# ifdef HAVE_TYPE_UNIONWAIT
+# define WTERMSIG(status) (status.w_termsig)
+# else
+# define WTERMSIG(status) ((status) & 0x7f)
+# endif
+#endif
+
+#ifndef WSTOPSIG
+# ifdef HAVE_TYPE_UNIONWAIT
+# define WSTOPSIG(status) (status.w_stopsig)
+# else
+# define WSTOPSIG(status) WEXITSTATUS(status)
+# endif
+#endif
+
+#endif /* WWW_WAIT_H */