diff options
Diffstat (limited to 'src/HTInit.c')
-rw-r--r-- | src/HTInit.c | 1503 |
1 files changed, 1503 insertions, 0 deletions
diff --git a/src/HTInit.c b/src/HTInit.c new file mode 100644 index 0000000..466bc72 --- /dev/null +++ b/src/HTInit.c @@ -0,0 +1,1503 @@ +/* + * $LynxId: HTInit.c,v 1.98 2022/06/12 21:17:37 tom Exp $ + * + * Configuration-specific Initialization HTInit.c + * ---------------------------------------- + */ + +/* Define a basic set of suffixes and presentations + * ------------------------------------------------ + */ + +#include <HTUtils.h> + +/* Implements: +*/ +#include <HTInit.h> + +#include <HTML.h> +#include <HTPlain.h> +#include <HTMLGen.h> +#include <HTFile.h> +#include <HTFormat.h> +#include <HTMIME.h> +#include <HTWSRC.h> + +#include <HTSaveToFile.h> /* LJM */ +#include <LYStrings.h> +#include <LYUtils.h> +#include <LYGlobalDefs.h> + +#include <LYexit.h> +#include <LYLeaks.h> + +#define CTrace(p) CTRACE2(TRACE_CFG, p) + +static int HTLoadTypesConfigFile(char *fn, AcceptMedia media); +static int HTLoadExtensionsConfigFile(char *fn); + +#define SET_SUFFIX1(suffix, description, type) \ + HTSetSuffix(suffix, description, type, 1.0) + +#define SET_SUFFIX5(suffix, mimetype, type, description) \ + HTSetSuffix5(suffix, mimetype, type, description, 1.0) + +#define SET_PRESENT(mimetype, command, quality, delay) \ + HTSetPresentation(mimetype, command, 0, quality, delay, 0.0, 0L, media) + +#define SET_EXTERNL(rep_in, rep_out, command, quality) \ + HTSetConversion(rep_in, rep_out, command, quality, 3.0, 0.0, 0L, mediaEXT) + +#define SET_INTERNL(rep_in, rep_out, command, quality) \ + HTSetConversion(rep_in, rep_out, command, quality, 0.0, 0.0, 0L, mediaINT) + +void HTFormatInit(void) +{ + AcceptMedia media = mediaEXT; + + CTrace((tfp, "HTFormatInit\n")); +#ifdef NeXT + SET_PRESENT("application/postscript", "open %s", 1.0, 2.0); + SET_PRESENT("image/x-tiff", "open %s", 2.0, 2.0); + SET_PRESENT("image/tiff", "open %s", 1.0, 2.0); + SET_PRESENT("audio/basic", "open %s", 1.0, 2.0); + SET_PRESENT("*", "open %s", 1.0, 0.0); +#else + if (LYgetXDisplay() != 0) { /* Must have X11 */ + SET_PRESENT("application/postscript", "ghostview %s&", 1.0, 3.0); + if (non_empty(XLoadImageCommand)) { + /* *INDENT-OFF* */ + SET_PRESENT("image/gif", XLoadImageCommand, 1.0, 3.0); + SET_PRESENT("image/x-xbm", XLoadImageCommand, 1.0, 3.0); + SET_PRESENT("image/x-xbitmap", XLoadImageCommand, 1.0, 3.0); + SET_PRESENT("image/x-png", XLoadImageCommand, 2.0, 3.0); + SET_PRESENT("image/png", XLoadImageCommand, 1.0, 3.0); + SET_PRESENT("image/x-rgb", XLoadImageCommand, 1.0, 3.0); + SET_PRESENT("image/x-tiff", XLoadImageCommand, 2.0, 3.0); + SET_PRESENT("image/tiff", XLoadImageCommand, 1.0, 3.0); + SET_PRESENT("image/jpeg", XLoadImageCommand, 1.0, 3.0); + /* *INDENT-ON* */ + + } + SET_PRESENT("video/mpeg", "mpeg_play %s &", 1.0, 3.0); + + } +#endif + +#ifdef EXEC_SCRIPTS + /* set quality to 999.0 for protected exec applications */ +#ifndef VMS + SET_PRESENT("application/x-csh", "csh %s", 999.0, 3.0); + SET_PRESENT("application/x-sh", "sh %s", 999.0, 3.0); + SET_PRESENT("application/x-ksh", "ksh %s", 999.0, 3.0); +#else + SET_PRESENT("application/x-VMS_script", "@%s", 999.0, 3.0); +#endif /* not VMS */ +#endif /* EXEC_SCRIPTS */ + + /* + * Add our header handlers. + */ + SET_INTERNL("message/x-http-redirection", "*", HTMIMERedirect, 2.0); + SET_INTERNL("message/x-http-redirection", STR_PRESENT, HTMIMERedirect, 2.0); + SET_INTERNL("message/x-http-redirection", "www/debug", HTMIMERedirect, 1.0); + SET_INTERNL("www/mime", STR_PRESENT, HTMIMEConvert, 1.0); + SET_INTERNL("www/mime", STR_DOWNLOAD, HTMIMEConvert, 1.0); + SET_INTERNL("www/mime", STR_SOURCE, HTMIMEConvert, 1.0); + SET_INTERNL("www/mime", STR_DUMP, HTMIMEConvert, 1.0); + + /* + * Add our compressed file handlers. + */ + SET_INTERNL("www/compressed", STR_DOWNLOAD, HTCompressed, 1.0); + SET_INTERNL("www/compressed", STR_PRESENT, HTCompressed, 1.0); + SET_INTERNL("www/compressed", STR_SOURCE, HTCompressed, 1.0); + SET_INTERNL("www/compressed", STR_DUMP, HTCompressed, 1.0); + + /* + * The following support some content types seen here/there: + */ + SET_INTERNL("application/html", "text/x-c", HTMLToC, 0.5); + SET_INTERNL("application/html", STR_PLAINTEXT, HTMLToPlain, 0.5); + SET_INTERNL("application/html", STR_PRESENT, HTMLPresent, 2.0); + SET_INTERNL("application/html", STR_SOURCE, HTPlainPresent, 1.0); + SET_INTERNL("application/xml", STR_PRESENT, HTMLPresent, 2.0); + SET_INTERNL("application/x-wais-source", STR_SOURCE, HTPlainPresent, 1.0); + SET_INTERNL("application/x-wais-source", STR_PRESENT, HTWSRCConvert, 2.0); + SET_INTERNL("application/x-wais-source", STR_DOWNLOAD, HTWSRCConvert, 1.0); + SET_INTERNL("application/x-wais-source", STR_DUMP, HTWSRCConvert, 1.0); + + /* + * Save all unknown mime types to disk. + */ + SET_EXTERNL(STR_SOURCE, STR_PRESENT, HTSaveToFile, 1.0); + SET_EXTERNL(STR_SOURCE, STR_SOURCE, HTSaveToFile, 1.0); + SET_EXTERNL(STR_SOURCE, STR_DOWNLOAD, HTSaveToFile, 1.0); + SET_EXTERNL(STR_SOURCE, "*", HTSaveToFile, 1.0); + + /* + * Output all www/dump presentations to stdout. + */ + SET_EXTERNL(STR_SOURCE, STR_DUMP, HTDumpToStdout, 1.0); + + /* + * Other internal types, which must precede the "www/present" entries + * below (otherwise, they will be filtered out in HTFilterPresentations()). + */ + SET_INTERNL("text/css", STR_PLAINTEXT, HTMLToPlain, 0.5); + SET_INTERNL(STR_HTML, STR_PLAINTEXT, HTMLToPlain, 0.5); + SET_INTERNL(STR_HTML, "text/x-c", HTMLToC, 0.5); + SET_INTERNL(STR_HTML, STR_SOURCE, HTPlainPresent, 1.0); + SET_INTERNL(STR_PLAINTEXT, STR_SOURCE, HTPlainPresent, 1.0); + SET_INTERNL("text/sgml", STR_SOURCE, HTPlainPresent, 1.0); + SET_INTERNL("text/x-sgml", STR_SOURCE, HTPlainPresent, 1.0); + + /* + * Now add our basic conversions. These include the types which will + * be listed in a "Accept:" line sent to a server. These criteria are + * used in HTFilterPresentations() to select acceptable types: + * + * a) input is not "www/mime" or "www/compressed" + * b) output is "www/present" + * c) quality is in the range 0.0 to 1.0, i.e., excludes the 2.0's. + * + * For reference: + * RFC 1874 - text/sgml + * RFC 2046 - text/plain + * RFC 2318 - text/css + * RFC 3023 - text/xml + * obsolete - text/x-sgml + * + * as well as + * http://www.iana.org/assignments/media-types/media-types.xhtml + * + * and + * http://www.w3.org/TR/xhtml-media-types/ + * + * which describes + * application/xhtml+xml + * text/html + */ + SET_INTERNL("application/xhtml+xml", STR_PRESENT, XHTMLPresent, 1.0); + SET_INTERNL("application/xhtml+xml", STR_SOURCE, HTPlainPresent, 1.0); + SET_INTERNL("text/css", STR_PRESENT, HTPlainPresent, 1.0); + SET_INTERNL(STR_HTML, STR_PRESENT, HTMLPresent, 1.0); + SET_INTERNL(STR_PLAINTEXT, STR_PRESENT, HTPlainPresent, 1.0); + SET_INTERNL("text/sgml", STR_PRESENT, HTMLPresent, 1.0); + SET_INTERNL("text/x-sgml", STR_PRESENT, HTMLPresent, 2.0); + SET_INTERNL("text/xml", STR_PRESENT, HTMLPresent, 2.0); + + if (LYisAbsPath(global_type_map)) { + /* These should override the default types as necessary. */ + HTLoadTypesConfigFile(global_type_map, mediaSYS); + } + + /* + * Load the local maps. + */ + if (IsOurFile(LYAbsOrHomePath(&personal_type_map)) + && LYCanReadFile(personal_type_map)) { + /* These should override everything else. */ + HTLoadTypesConfigFile(personal_type_map, mediaUSR); + } + + /* + * Put text/html and text/plain at beginning of list. - kw + */ + HTReorderPresentation(WWW_PLAINTEXT, WWW_PRESENT); + HTReorderPresentation(WWW_HTML, WWW_PRESENT); + + /* + * Analyze the list, and set 'get_accept' for those whose representations + * are not redundant. + */ + HTFilterPresentations(); +} + +void HTPreparsedFormatInit(void) +{ + CTrace((tfp, "HTPreparsedFormatInit\n")); + if (LYPreparsedSource) { + SET_INTERNL(STR_HTML, STR_SOURCE, HTMLParsedPresent, 1.0); + SET_INTERNL(STR_HTML, STR_DUMP, HTMLParsedPresent, 1.0); + } +} + +/* Some of the following is taken from: */ + +/* +Copyright (c) 1991 Bell Communications Research, Inc. (Bellcore) + +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 Bellcore not be +used in advertising or publicity pertaining to this +material without the specific, prior written permission +of an authorized representative of Bellcore. BELLCORE +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. +*/ +/****************************************************** + Metamail -- A tool to help diverse mail readers + cope with diverse multimedia mail formats. + + Author: Nathaniel S. Borenstein, Bellcore + + ******************************************************* */ + +struct MailcapEntry { + char *contenttype; + char *command; + char *testcommand; + int needsterminal; + int copiousoutput; + int needtofree; + char *label; + char *printcommand; + char *nametemplate; + float quality; + long int maxbytes; +}; + +static int ExitWithError(const char *txt); +static int PassesTest(struct MailcapEntry *mc); + +static char *GetCommand(char *s, char **t) +{ + char *s2; + int quoted = 0; + + s = LYSkipBlanks(s); + /* marca -- added + 1 for error case -- oct 24, 1993. */ + s2 = typeMallocn(char, strlen(s) * 2 + 1); /* absolute max, if all % signs */ + + if (!s2) + ExitWithError(MEMORY_EXHAUSTED_ABORT); + + *t = s2; + while (non_empty(s)) { + if (quoted) { + if (*s == '%') + *s2++ = '%'; /* Quote through next level, ugh! */ + + *s2++ = *s++; + quoted = 0; + } else { + if (*s == ';') { + *s2 = '\0'; + return (++s); + } + if (*s == ESCAPE) { + quoted = 1; + ++s; + } else { + *s2++ = *s++; + } + } + } + *s2 = '\0'; + return (NULL); +} + +/* no leading or trailing space, all lower case */ +static char *Cleanse(char *s) +{ + LYTrimLeading(s); + LYTrimTrailing(s); + LYLowerCase(s); + return (s); +} + +/* remove unnecessary (unquoted) blanks in a shell command */ +static void TrimCommand(char *command) +{ + LYTrimTrailing(command); +#ifdef UNIX + { + char *s = command; + char *d = command; + int ch; + int c0 = ' '; + BOOL escape = FALSE; + BOOL dquote = FALSE; + BOOL squote = FALSE; + + while ((ch = *s++) != '\0') { + if (escape) { + escape = FALSE; + } else if (squote) { + if (ch == SQUOTE) + squote = FALSE; + } else if (dquote) { + switch (ch) { + case DQUOTE: + dquote = FALSE; + break; + case ESCAPE: + escape = TRUE; + break; + } + } else { + switch (ch) { + case DQUOTE: + dquote = TRUE; + break; + case SQUOTE: + squote = TRUE; + break; + } + } + if (!escape && !dquote && !squote) { + if (ch == '\t') + ch = ' '; + if (ch == ' ') { + if (c0 == ' ') + continue; + } + } + *d++ = (char) ch; + c0 = ch; + } + *d = '\0'; + } +#endif +} + +static int ProcessMailcapEntry(FILE *fp, struct MailcapEntry *mc, AcceptMedia media) +{ + size_t rawentryalloc = 2000, len, need; + char *rawentry, *s, *t; + char *LineBuf = NULL; + + rawentry = (char *) malloc(rawentryalloc); + if (!rawentry) + ExitWithError(MEMORY_EXHAUSTED_ABORT); + + *rawentry = '\0'; + while (LYSafeGets(&LineBuf, fp) != 0) { + LYTrimNewline(LineBuf); + if (LineBuf[0] == '#' || LineBuf[0] == '\0') + continue; + len = strlen(LineBuf); + need = len + strlen(rawentry) + 1; + if (need > rawentryalloc) { + rawentryalloc += (2000 + need); + rawentry = typeRealloc(char, rawentry, rawentryalloc); + + if (!rawentry) + ExitWithError(MEMORY_EXHAUSTED_ABORT); + } + if (len > 0 && LineBuf[len - 1] == ESCAPE) { + LineBuf[len - 1] = '\0'; + strcat(rawentry, LineBuf); + } else { + strcat(rawentry, LineBuf); + break; + } + } + FREE(LineBuf); + + t = s = LYSkipBlanks(rawentry); + if (!*s) { + /* totally blank entry -- quietly ignore */ + FREE(rawentry); + return (0); + } + s = StrChr(rawentry, ';'); + if (s == NULL) { + CTrace((tfp, + "ProcessMailcapEntry: Ignoring invalid mailcap entry: %s\n", + rawentry)); + FREE(rawentry); + return (0); + } + *s++ = '\0'; + if (!strncasecomp(t, STR_HTML, 9) || + !strncasecomp(t, STR_PLAINTEXT, 10)) { + --s; + *s = ';'; + CTrace((tfp, "ProcessMailcapEntry: Ignoring mailcap entry: %s\n", + rawentry)); + FREE(rawentry); + return (0); + } + LYRemoveBlanks(rawentry); + LYLowerCase(rawentry); + + mc->needsterminal = 0; + mc->copiousoutput = 0; + mc->needtofree = 1; + mc->testcommand = NULL; + mc->label = NULL; + mc->printcommand = NULL; + mc->contenttype = NULL; + StrAllocCopy(mc->contenttype, rawentry); + mc->quality = (float) 1.0; + mc->maxbytes = 0; + t = GetCommand(s, &mc->command); + if (!t) { + goto assign_presentation; + } + s = LYSkipBlanks(t); + while (s) { + char *arg, *eq, *mallocd_string; + + t = GetCommand(s, &mallocd_string); + arg = mallocd_string; + eq = StrChr(arg, '='); + if (eq) { + *eq++ = '\0'; + eq = LYSkipBlanks(eq); + } + if (non_empty(arg)) { + arg = Cleanse(arg); + if (!strcmp(arg, "needsterminal")) { + mc->needsterminal = 1; + } else if (!strcmp(arg, "copiousoutput")) { + mc->copiousoutput = 1; + } else if (eq && !strcmp(arg, "test")) { + mc->testcommand = NULL; + StrAllocCopy(mc->testcommand, eq); + TrimCommand(mc->testcommand); + CTrace((tfp, "ProcessMailcapEntry: Found testcommand:%s\n", + mc->testcommand)); + } else if (eq && !strcmp(arg, "description")) { + mc->label = eq; /* ignored */ + } else if (eq && !strcmp(arg, "label")) { + mc->label = eq; /* ignored: bogus old name for description */ + } else if (eq && !strcmp(arg, "print")) { + mc->printcommand = eq; /* ignored */ + } else if (eq && !strcmp(arg, "textualnewlines")) { + /* no support for now. What does this do anyways? */ + /* ExceptionalNewline(mc->contenttype, atoi(eq)); */ + } else if (eq && !strcmp(arg, "q")) { + mc->quality = (float) atof(eq); + if (mc->quality > 0.000 && mc->quality < 0.001) + mc->quality = (float) 0.001; + } else if (eq && !strcmp(arg, "mxb")) { + mc->maxbytes = atol(eq); + if (mc->maxbytes < 0) + mc->maxbytes = 0; + } else if (strcmp(arg, "notes")) { /* IGNORE notes field */ + if (*arg) + CTrace((tfp, + "ProcessMailcapEntry: Ignoring mailcap flag '%s'.\n", + arg)); + } + + } + FREE(mallocd_string); + s = t; + } + + assign_presentation: + FREE(rawentry); + + if (PassesTest(mc)) { + CTrace((tfp, "ProcessMailcapEntry Setting up conversion %s : %s\n", + mc->contenttype, mc->command)); + HTSetPresentation(mc->contenttype, + mc->command, + mc->testcommand, + mc->quality, + 3.0, 0.0, mc->maxbytes, media); + } + FREE(mc->command); + FREE(mc->testcommand); + FREE(mc->contenttype); + + return (1); +} + +#define L_CURL '{' +#define R_CURL '}' + +static const char *LYSkipQuoted(const char *s) +{ + int escaped = 0; + + ++s; /* skip first quote */ + while (*s != 0) { + if (escaped) { + escaped = 0; + } else if (*s == ESCAPE) { + escaped = 1; + } else if (*s == DQUOTE) { + ++s; + break; + } + ++s; + } + return s; +} + +/* + * Note: the tspecials[] here are those defined for Content-Type header, so + * this function is not really general-purpose. + */ +static const char *LYSkipToken(const char *s) +{ + static const char tspecials[] = "\"()<>@,;:\\/[]?.="; + + while (*s != '\0' && !WHITE(*s) && StrChr(tspecials, *s) == 0) { + ++s; + } + return s; +} + +static const char *LYSkipValue(const char *s) +{ + if (*s == DQUOTE) + s = LYSkipQuoted(s); + else + s = LYSkipToken(s); + return s; +} + +/* + * Copy the value from the source, dequoting if needed. + */ +static char *LYCopyValue(const char *s) +{ + const char *t; + char *result = 0; + int j, k; + + if (*s == DQUOTE) { + t = LYSkipQuoted(s); + StrAllocCopy(result, s + 1); + result[t - s - 2] = '\0'; + for (j = k = 0;; ++j, ++k) { + if (result[j] == ESCAPE) { + ++j; + } + if ((result[k] = result[j]) == '\0') + break; + } + } else { + t = LYSkipToken(s); + StrAllocCopy(result, s); + result[t - s] = '\0'; + } + return result; +} + +/* + * The "Content-Type:" field, contains zero or more parameters after a ';'. + * Return the value of the named parameter, or null. + */ +static char *LYGetContentType(const char *name, + const char *params) +{ + char *result = 0; + + if (params != 0) { + if (name != 0) { + size_t length = strlen(name); + const char *test = StrChr(params, ';'); /* skip type/subtype */ + const char *next; + + while (test != 0) { + BOOL found = FALSE; + + ++test; /* skip the ';' */ + test = LYSkipCBlanks(test); + next = LYSkipToken(test); + if ((next - test) == (int) length + && !StrNCmp(test, name, length)) { + found = TRUE; + } + test = LYSkipCBlanks(next); + if (*test == '=') { + ++test; + test = LYSkipCBlanks(test); + if (found) { + result = LYCopyValue(test); + break; + } else { + test = LYSkipValue(test); + } + test = LYSkipCBlanks(test); + } + if (*test != ';') { + break; /* we're lost */ + } + } + } else { /* return the content-type */ + StrAllocCopy(result, params); + *LYSkipNonBlanks(result) = '\0'; + } + } + return result; +} + +/* + * Check if the command uses a "%s" substitution. We need to know this, to + * decide when to create temporary files, etc. + */ +BOOL LYMailcapUsesPctS(const char *controlstring) +{ + BOOL result = FALSE; + const char *from; + const char *next; + int prefixed = 0; + int escaped = 0; + + for (from = controlstring; *from != '\0'; from++) { + if (escaped) { + escaped = 0; + } else if (*from == ESCAPE) { + escaped = 1; + } else if (prefixed) { + prefixed = 0; + switch (*from) { + case '%': /* not defined */ + case 'n': + case 'F': + case 't': + break; + case 's': + result = TRUE; + break; + case L_CURL: + next = StrChr(from, R_CURL); + if (next != 0) { + from = next; + break; + } + /* FALLTHRU */ + default: + break; + } + } else if (*from == '%') { + prefixed = 1; + } + } + return result; +} + +/* + * Build the command string for testing or executing a mailcap entry. + * If a substitution from the Content-Type header is requested but no + * parameters are available, return -1, otherwise 0. + * + * This does not support multipart %n or %F (does this apply to lynx?) + */ +static int BuildCommand(HTChunk *cmd, + const char *controlstring, + const char *TmpFileName, + const char *params) +{ + int result = 0; + size_t TmpFileLen = strlen(TmpFileName); + const char *from; + const char *next; + char *name, *value; + int prefixed = 0; + int escaped = 0; + + for (from = controlstring; *from != '\0'; from++) { + if (escaped) { + escaped = 0; + HTChunkPutc(cmd, UCH(*from)); + } else if (*from == ESCAPE) { + escaped = 1; + } else if (prefixed) { + prefixed = 0; + switch (*from) { + case '%': /* not defined */ + HTChunkPutc(cmd, UCH(*from)); + break; + case 'n': + /* FALLTHRU */ + case 'F': + CTrace((tfp, "BuildCommand: Bad mailcap \"test\" clause: %s\n", + controlstring)); + break; + case 't': + if ((value = LYGetContentType(NULL, params)) != 0) { + HTChunkPuts(cmd, value); + FREE(value); + } + break; + case 's': + if (TmpFileLen) { + HTChunkPuts(cmd, TmpFileName); + } + break; + case L_CURL: + next = StrChr(from, R_CURL); + if (next != 0) { + if (params != 0) { + ++from; + name = 0; + HTSprintf0(&name, "%.*s", (int) (next - from), from); + if ((value = LYGetContentType(name, params)) != 0) { + HTChunkPuts(cmd, value); + FREE(value); + } else if (name) { + if (!strcmp(name, "charset")) { + HTChunkPuts(cmd, "ISO-8859-1"); + } else { + CTrace((tfp, "BuildCommand no value for %s\n", name)); + } + } + FREE(name); + } else { + result = -1; + } + from = next; + break; + } + /* FALLTHRU */ + default: + CTrace((tfp, + "BuildCommand: Ignoring unrecognized format code in mailcap file '%%%c'.\n", + *from)); + break; + } + } else if (*from == '%') { + prefixed = 1; + } else { + HTChunkPutc(cmd, UCH(*from)); + } + } + HTChunkTerminate(cmd); + return result; +} + +/* + * Build the mailcap test-command and execute it. This is only invoked when + * we cannot tell just by looking at the command if it would succeed. + * + * Returns 0 for success, -1 for error and 1 for deferred. + */ +int LYTestMailcapCommand(const char *testcommand, + const char *params) +{ + int result; + char TmpFileName[LY_MAXPATH]; + HTChunk *expanded = 0; + + if (LYMailcapUsesPctS(testcommand)) { + if (LYOpenTemp(TmpFileName, HTML_SUFFIX, "w") == 0) + ExitWithError(CANNOT_OPEN_TEMP); + LYCloseTemp(TmpFileName); + } else { + /* We normally don't need a temp file name - kw */ + TmpFileName[0] = '\0'; + } + expanded = HTChunkCreate(1024); + if (BuildCommand(expanded, testcommand, TmpFileName, params) != 0) { + result = 1; + CTrace((tfp, "PassesTest: Deferring test command: %s\n", expanded->data)); + } else { + CTrace((tfp, "PassesTest: Executing test command: %s\n", expanded->data)); + if ((result = LYSystem(expanded->data)) != 0) { + result = -1; + CTrace((tfp, "PassesTest: Test failed!\n")); + } else { + CTrace((tfp, "PassesTest: Test passed!\n")); + } + } + + HTChunkFree(expanded); + (void) LYRemoveTemp(TmpFileName); + + return result; +} + +char *LYMakeMailcapCommand(const char *command, + const char *params, + const char *filename) +{ + HTChunk *expanded = 0; + char *result = 0; + + expanded = HTChunkCreate(1024); + BuildCommand(expanded, command, filename, params); + StrAllocCopy(result, expanded->data); + HTChunkFree(expanded); + return result; +} + +#define RTR_forget 0 +#define RTR_lookup 1 +#define RTR_add 2 + +static int RememberTestResult(int mode, char *cmd, int result) +{ + struct cmdlist_s { + char *cmd; + int result; + struct cmdlist_s *next; + }; + static struct cmdlist_s *cmdlist = NULL; + struct cmdlist_s *cur; + + switch (mode) { + case RTR_forget: + while (cmdlist) { + cur = cmdlist->next; + FREE(cmdlist->cmd); + FREE(cmdlist); + cmdlist = cur; + } + break; + case RTR_lookup: + for (cur = cmdlist; cur; cur = cur->next) + if (!strcmp(cmd, cur->cmd)) + return cur->result; + return -1; + case RTR_add: + cur = typecalloc(struct cmdlist_s); + + if (cur == NULL) + outofmem(__FILE__, "RememberTestResult"); + + cur->next = cmdlist; + StrAllocCopy(cur->cmd, cmd); + cur->result = result; + cmdlist = cur; + break; + } + return 0; +} + +/* FIXME: this sometimes used caseless comparison, e.g., strcasecomp */ +#define SameCommand(tst,ref) !strcmp(tst,ref) + +static int PassesTest(struct MailcapEntry *mc) +{ + int result; + + /* + * Make sure we have a command + */ + if (!mc->testcommand) + return (1); + + /* + * Save overhead of system() calls by faking these. - FM + */ + if (SameCommand(mc->testcommand, "test \"$DISPLAY\"") || + SameCommand(mc->testcommand, "test \"$DISPLAY\" != \"\"") || + SameCommand(mc->testcommand, "test -n \"$DISPLAY\"")) { + FREE(mc->testcommand); + CTrace((tfp, "PassesTest: Testing for XWINDOWS environment.\n")); + if (LYgetXDisplay() != NULL) { + CTrace((tfp, "PassesTest: Test passed!\n")); + return (0 == 0); + } else { + CTrace((tfp, "PassesTest: Test failed!\n")); + return (-1 == 0); + } + } + if (SameCommand(mc->testcommand, "test -z \"$DISPLAY\"")) { + FREE(mc->testcommand); + CTrace((tfp, "PassesTest: Testing for NON_XWINDOWS environment.\n")); + if (LYgetXDisplay() == NULL) { + CTrace((tfp, "PassesTest: Test passed!\n")); + return (0 == 0); + } else { + CTrace((tfp, "PassesTest: Test failed!\n")); + return (-1 == 0); + } + } + + /* + * Why do anything but return success for this one! - FM + */ + if (SameCommand(mc->testcommand, "test -n \"$LYNX_VERSION\"")) { + FREE(mc->testcommand); + CTrace((tfp, "PassesTest: Testing for LYNX environment.\n")); + CTrace((tfp, "PassesTest: Test passed!\n")); + return (0 == 0); + } else + /* + * ... or failure for this one! - FM + */ + if (SameCommand(mc->testcommand, "test -z \"$LYNX_VERSION\"")) { + FREE(mc->testcommand); + CTrace((tfp, "PassesTest: Testing for non-LYNX environment.\n")); + CTrace((tfp, "PassesTest: Test failed!\n")); + return (-1 == 0); + } + + result = RememberTestResult(RTR_lookup, mc->testcommand, 0); + if (result == -1) { + result = LYTestMailcapCommand(mc->testcommand, NULL); + RememberTestResult(RTR_add, mc->testcommand, result ? 1 : 0); + } + + /* + * Free the test command as well since + * we won't be needing it anymore. + */ + if (result != 1) + FREE(mc->testcommand); + + if (result < 0) { + CTrace((tfp, "PassesTest: Test failed!\n")); + } else if (result == 0) { + CTrace((tfp, "PassesTest: Test passed!\n")); + } + + return (result >= 0); +} + +static int ProcessMailcapFile(char *file, AcceptMedia media) +{ + struct MailcapEntry mc; + FILE *fp; + + CTrace((tfp, "ProcessMailcapFile: Loading file '%s'.\n", + file)); + if ((fp = fopen(file, TXT_R)) == NULL) { + CTrace((tfp, "ProcessMailcapFile: Could not open '%s'.\n", + file)); + return (-1 == 0); + } + + while (fp && !feof(fp)) { + ProcessMailcapEntry(fp, &mc, media); + } + LYCloseInput(fp); + RememberTestResult(RTR_forget, NULL, 0); + return (0 == 0); +} + +static int ExitWithError(const char *txt) +{ + if (txt) + fprintf(tfp, "Lynx: %s\n", txt); + exit_immediately(EXIT_FAILURE); + return (-1); +} + +/* Reverse the entries from each mailcap after it has been read, so that + * earlier entries have precedence. Set to 0 to get traditional lynx + * behavior, which means that the last match wins. - kw */ +static int reverse_mailcap = 1; + +static int HTLoadTypesConfigFile(char *fn, AcceptMedia media) +{ + int result = 0; + HTList *saved = HTPresentations; + + if (reverse_mailcap) { /* temporarily hide existing list */ + HTPresentations = NULL; + } + + result = ProcessMailcapFile(fn, media); + + if (reverse_mailcap) { + if (result && HTPresentations) { + HTList_reverse(HTPresentations); + HTList_appendList(HTPresentations, saved); + FREE(saved); + } else { + HTPresentations = saved; + } + } + return result; +} + +/* ------------------------------------------------------------------------ */ +/* ------------------------------------------------------------------------ */ +/* ------------------------------------------------------------------------ */ + +/* Define a basic set of suffixes + * ------------------------------ + * + * The LAST suffix for a type is that used for temporary files + * of that type. + * The quality is an apriori bias as to whether the file should be + * used. Not that different suffixes can be used to represent files + * which are of the same format but are originals or regenerated, + * with different values. + */ +/* + * Additional notes: the encoding parameter may be taken into account when + * looking for a match; for that purpose "7bit", "8bit", and "binary" are + * equivalent. + * + * Use of mixed case and of pseudo MIME types with embedded spaces should be + * avoided. It was once necessary for getting the fancy strings into type + * labels in FTP directory listings, but that can now be done with the + * description field (using HTSetSuffix5). AFAIK the only effect of such + * "fancy" (and mostly invalid) types that cannot be reproduced by using a + * description fields is some statusline messages in SaveToFile (HTFWriter.c). + * And showing the user an invalid MIME type as the 'Content-type:' is not such + * a hot idea anyway, IMO. Still, if you want it, it is still possible (even + * in lynx.cfg now), but use of it in the defaults below has been reduced. + * + * Case variations rely on peculiar behavior of HTAtom.c for matching. They + * lead to surprising behavior, Lynx retains the case of a string in the form + * first encountered after starting up. So while later suffix rules generally + * override or modify earlier ones, the case used for a MIME time is determined + * by the first suffix rule (or other occurrence). Matching in HTAtom_for is + * effectively case insensitive, except for the first character of the string + * which is treated as case-sensitive by the hash function there; best not to + * rely on that, rather convert MIME types to lowercase on input as is already + * done in most places (And HTAtom could become consistently case-sensitive, as + * in newer W3C libwww). + * - kw 1999-10-12 + */ +void HTFileInit(void) +{ +#ifdef BUILTIN_SUFFIX_MAPS + if (LYUseBuiltinSuffixes) { + CTrace((tfp, "HTFileInit: Loading default (HTInit) extension maps.\n")); + + /* default suffix interpretation */ + SET_SUFFIX1("*", STR_PLAINTEXT, "8bit"); + SET_SUFFIX1("*.*", STR_PLAINTEXT, "8bit"); + +#ifdef EXEC_SCRIPTS + /* + * define these extensions for exec scripts. + */ +#ifndef VMS + /* for csh exec links */ + HTSetSuffix(".csh", "application/x-csh", "8bit", 0.8); + HTSetSuffix(".sh", "application/x-sh", "8bit", 0.8); + HTSetSuffix(".ksh", "application/x-ksh", "8bit", 0.8); +#else + HTSetSuffix(".com", "application/x-VMS_script", "8bit", 0.8); +#endif /* !VMS */ +#endif /* EXEC_SCRIPTS */ + + /* + * Some of the old incarnation of the mappings is preserved and can be had + * by defining TRADITIONAL_SUFFIXES. This is for some cases where I felt + * the old rules might be preferred by someone, for some reason. It's not + * done consistently. A lot more of this stuff could probably be changed + * too or omitted, now that nearly the equivalent functionality is + * available in lynx.cfg. - kw 1999-10-12 + */ + /* *INDENT-OFF* */ + SET_SUFFIX1(".saveme", "application/x-Binary", "binary"); + SET_SUFFIX1(".dump", "application/x-Binary", "binary"); + SET_SUFFIX1(".bin", "application/x-Binary", "binary"); + + SET_SUFFIX1(".arc", "application/x-Compressed", "binary"); + + SET_SUFFIX1(".alpha-exe", "application/x-Executable", "binary"); + SET_SUFFIX1(".alpha_exe", "application/x-Executable", "binary"); + SET_SUFFIX1(".AXP-exe", "application/x-Executable", "binary"); + SET_SUFFIX1(".AXP_exe", "application/x-Executable", "binary"); + SET_SUFFIX1(".VAX-exe", "application/x-Executable", "binary"); + SET_SUFFIX1(".VAX_exe", "application/x-Executable", "binary"); + SET_SUFFIX5(".exe", STR_BINARY, "binary", "Executable"); + +#ifdef TRADITIONAL_SUFFIXES + SET_SUFFIX1(".exe.Z", "application/x-Comp. Executable", "binary"); + SET_SUFFIX1(".Z", "application/UNIX Compressed", "binary"); + SET_SUFFIX1(".tar_Z", "application/UNIX Compr. Tar", "binary"); + SET_SUFFIX1(".tar.Z", "application/UNIX Compr. Tar", "binary"); +#else + SET_SUFFIX5(".Z", "application/x-compress", "binary", "UNIX Compressed"); + SET_SUFFIX5(".Z", NULL, "compress", "UNIX Compressed"); + SET_SUFFIX5(".exe.Z", STR_BINARY, "compress", "Executable"); + SET_SUFFIX5(".tar_Z", "application/x-tar", "compress", "UNIX Compr. Tar"); + SET_SUFFIX5(".tar.Z", "application/x-tar", "compress", "UNIX Compr. Tar"); +#endif + +#ifdef TRADITIONAL_SUFFIXES + SET_SUFFIX1("-gz", "application/GNU Compressed", "binary"); + SET_SUFFIX1("_gz", "application/GNU Compressed", "binary"); + SET_SUFFIX1(".gz", "application/GNU Compressed", "binary"); + + SET_SUFFIX5(".tar.gz", "application/x-tar", "binary", "GNU Compr. Tar"); + SET_SUFFIX5(".tgz", "application/x-tar", "gzip", "GNU Compr. Tar"); +#else + SET_SUFFIX5("-gz", "application/x-gzip", "binary", "GNU Compressed"); + SET_SUFFIX5("_gz", "application/x-gzip", "binary", "GNU Compressed"); + SET_SUFFIX5(".gz", "application/x-gzip", "binary", "GNU Compressed"); + SET_SUFFIX5("-gz", NULL, "gzip", "GNU Compressed"); + SET_SUFFIX5("_gz", NULL, "gzip", "GNU Compressed"); + SET_SUFFIX5(".gz", NULL, "gzip", "GNU Compressed"); + + SET_SUFFIX5(".tar.gz", "application/x-tar", "gzip", "GNU Compr. Tar"); + SET_SUFFIX5(".tgz", "application/x-tar", "gzip", "GNU Compr. Tar"); +#endif + +#ifdef TRADITIONAL_SUFFIXES + SET_SUFFIX1(".src", "application/x-WAIS-source", "8bit"); + SET_SUFFIX1(".wsrc", "application/x-WAIS-source", "8bit"); +#else + SET_SUFFIX5(".wsrc", "application/x-wais-source", "8bit", "WAIS-source"); +#endif + + SET_SUFFIX5(".zip", "application/zip", "binary", "Zip File"); + + SET_SUFFIX1(".zz", "application/x-deflate", "binary"); + SET_SUFFIX1(".zz", "application/deflate", "binary"); + + SET_SUFFIX1(".bz2", "application/x-bzip2", "binary"); + SET_SUFFIX1(".bz2", "application/bzip2", "binary"); + + SET_SUFFIX1(".br", "application/x-brotli", "binary"); + + SET_SUFFIX1(".xz", "application/x-xz", "binary"); + + SET_SUFFIX1(".lz", "application/x-lzip", "binary"); + SET_SUFFIX1(".lzma", "application/x-lzma", "binary"); + +#ifdef TRADITIONAL_SUFFIXES + SET_SUFFIX1(".uu", "application/x-UUencoded", "8bit"); + + SET_SUFFIX1(".hqx", "application/x-Binhex", "8bit"); + + SET_SUFFIX1(".o", "application/x-Prog. Object", "binary"); + SET_SUFFIX1(".a", "application/x-Prog. Library", "binary"); +#else + SET_SUFFIX5(".uu", "application/x-uuencoded", "7bit", "UUencoded"); + + SET_SUFFIX5(".hqx", "application/mac-binhex40", "8bit", "Mac BinHex"); + + HTSetSuffix5(".o", STR_BINARY, "binary", "Prog. Object", 0.5); + HTSetSuffix5(".a", STR_BINARY, "binary", "Prog. Library", 0.5); + HTSetSuffix5(".so", STR_BINARY, "binary", "Shared Lib", 0.5); +#endif + + SET_SUFFIX5(".oda", "application/oda", "binary", "ODA"); + + SET_SUFFIX5(".pdf", "application/pdf", "binary", "PDF"); + + SET_SUFFIX5(".eps", "application/postscript", "8bit", "Postscript"); + SET_SUFFIX5(".ai", "application/postscript", "8bit", "Postscript"); + SET_SUFFIX5(".ps", "application/postscript", "8bit", "Postscript"); + + SET_SUFFIX5(".rtf", "application/rtf", "8bit", "RTF"); + + SET_SUFFIX5(".dvi", "application/x-dvi", "8bit", "DVI"); + + SET_SUFFIX5(".hdf", "application/x-hdf", "8bit", "HDF"); + + SET_SUFFIX1(".cdf", "application/x-netcdf", "8bit"); + SET_SUFFIX1(".nc", "application/x-netcdf", "8bit"); + +#ifdef TRADITIONAL_SUFFIXES + SET_SUFFIX1(".latex", "application/x-Latex", "8bit"); + SET_SUFFIX1(".tex", "application/x-Tex", "8bit"); + SET_SUFFIX1(".texinfo", "application/x-Texinfo", "8bit"); + SET_SUFFIX1(".texi", "application/x-Texinfo", "8bit"); +#else + SET_SUFFIX5(".latex", "application/x-latex", "8bit", "LaTeX"); + SET_SUFFIX5(".tex", "text/x-tex", "8bit", "TeX"); + SET_SUFFIX5(".texinfo", "application/x-texinfo", "8bit", "Texinfo"); + SET_SUFFIX5(".texi", "application/x-texinfo", "8bit", "Texinfo"); +#endif + +#ifdef TRADITIONAL_SUFFIXES + SET_SUFFIX1(".t", "application/x-Troff", "8bit"); + SET_SUFFIX1(".tr", "application/x-Troff", "8bit"); + SET_SUFFIX1(".roff", "application/x-Troff", "8bit"); + + SET_SUFFIX1(".man", "application/x-Troff-man", "8bit"); + SET_SUFFIX1(".me", "application/x-Troff-me", "8bit"); + SET_SUFFIX1(".ms", "application/x-Troff-ms", "8bit"); +#else + SET_SUFFIX5(".t", "application/x-troff", "8bit", "Troff"); + SET_SUFFIX5(".tr", "application/x-troff", "8bit", "Troff"); + SET_SUFFIX5(".roff", "application/x-troff", "8bit", "Troff"); + + SET_SUFFIX5(".man", "application/x-troff-man", "8bit", "Man Page"); + SET_SUFFIX5(".me", "application/x-troff-me", "8bit", "Troff me"); + SET_SUFFIX5(".ms", "application/x-troff-ms", "8bit", "Troff ms"); +#endif + + SET_SUFFIX1(".zoo", "application/x-Zoo File", "binary"); + +#if defined(TRADITIONAL_SUFFIXES) || defined(VMS) + SET_SUFFIX1(".bak", "application/x-VMS BAK File", "binary"); + SET_SUFFIX1(".bkp", "application/x-VMS BAK File", "binary"); + SET_SUFFIX1(".bck", "application/x-VMS BAK File", "binary"); + + SET_SUFFIX5(".bkp_gz", STR_BINARY, "gzip", "GNU BAK File"); + SET_SUFFIX5(".bkp-gz", STR_BINARY, "gzip", "GNU BAK File"); + SET_SUFFIX5(".bck_gz", STR_BINARY, "gzip", "GNU BAK File"); + SET_SUFFIX5(".bck-gz", STR_BINARY, "gzip", "GNU BAK File"); + + SET_SUFFIX5(".bkp-Z", STR_BINARY, "compress", "Comp. BAK File"); + SET_SUFFIX5(".bkp_Z", STR_BINARY, "compress", "Comp. BAK File"); + SET_SUFFIX5(".bck-Z", STR_BINARY, "compress", "Comp. BAK File"); + SET_SUFFIX5(".bck_Z", STR_BINARY, "compress", "Comp. BAK File"); +#else + HTSetSuffix5(".bak", NULL, "binary", "Backup", 0.5); + SET_SUFFIX5(".bkp", STR_BINARY, "binary", "VMS BAK File"); + SET_SUFFIX5(".bck", STR_BINARY, "binary", "VMS BAK File"); +#endif + +#if defined(TRADITIONAL_SUFFIXES) || defined(VMS) + SET_SUFFIX1(".hlb", "application/x-VMS Help Libr.", "binary"); + SET_SUFFIX1(".olb", "application/x-VMS Obj. Libr.", "binary"); + SET_SUFFIX1(".tlb", "application/x-VMS Text Libr.", "binary"); + SET_SUFFIX1(".obj", "application/x-VMS Prog. Obj.", "binary"); + SET_SUFFIX1(".decw$book", "application/x-DEC BookReader", "binary"); + SET_SUFFIX1(".mem", "application/x-RUNOFF-MANUAL", "8bit"); +#else + SET_SUFFIX5(".hlb", STR_BINARY, "binary", "VMS Help Libr."); + SET_SUFFIX5(".olb", STR_BINARY, "binary", "VMS Obj. Libr."); + SET_SUFFIX5(".tlb", STR_BINARY, "binary", "VMS Text Libr."); + SET_SUFFIX5(".obj", STR_BINARY, "binary", "Prog. Object"); + SET_SUFFIX5(".decw$book", STR_BINARY, "binary", "DEC BookReader"); + SET_SUFFIX5(".mem", "text/x-runoff-manual", "8bit", "RUNOFF-MANUAL"); +#endif + + SET_SUFFIX1(".vsd", "application/visio", "binary"); + + SET_SUFFIX5(".lha", "application/x-lha", "binary", "lha File"); + SET_SUFFIX5(".lzh", "application/x-lzh", "binary", "lzh File"); + SET_SUFFIX5(".sea", "application/x-sea", "binary", "sea File"); +#ifdef TRADITIONAL_SUFFIXES + SET_SUFFIX5(".sit", "application/x-sit", "binary", "sit File"); +#else + SET_SUFFIX5(".sit", "application/x-stuffit", "binary", "StuffIt"); +#endif + SET_SUFFIX5(".dms", "application/x-dms", "binary", "dms File"); + SET_SUFFIX5(".iff", "application/x-iff", "binary", "iff File"); + + SET_SUFFIX1(".bcpio", "application/x-bcpio", "binary"); + SET_SUFFIX1(".cpio", "application/x-cpio", "binary"); + +#ifdef TRADITIONAL_SUFFIXES + SET_SUFFIX1(".gtar", "application/x-gtar", "binary"); +#endif + + SET_SUFFIX1(".shar", "application/x-shar", "8bit"); + SET_SUFFIX1(".share", "application/x-share", "8bit"); + +#ifdef TRADITIONAL_SUFFIXES + SET_SUFFIX1(".sh", "application/x-sh", "8bit"); /* xtra */ +#endif + + SET_SUFFIX1(".sv4cpio", "application/x-sv4cpio", "binary"); + SET_SUFFIX1(".sv4crc", "application/x-sv4crc", "binary"); + + SET_SUFFIX5(".tar", "application/x-tar", "binary", "Tar File"); + SET_SUFFIX1(".ustar", "application/x-ustar", "binary"); + + SET_SUFFIX1(".snd", "audio/basic", "binary"); + SET_SUFFIX1(".au", "audio/basic", "binary"); + + SET_SUFFIX1(".aifc", "audio/x-aiff", "binary"); + SET_SUFFIX1(".aif", "audio/x-aiff", "binary"); + SET_SUFFIX1(".aiff", "audio/x-aiff", "binary"); + SET_SUFFIX1(".wav", "audio/x-wav", "binary"); + SET_SUFFIX1(".midi", "audio/midi", "binary"); + SET_SUFFIX1(".mod", "audio/mod", "binary"); + + SET_SUFFIX1(".gif", "image/gif", "binary"); + SET_SUFFIX1(".ief", "image/ief", "binary"); + SET_SUFFIX1(".jfif", "image/jpeg", "binary"); /* xtra */ + SET_SUFFIX1(".jfif-tbnl", "image/jpeg", "binary"); /* xtra */ + SET_SUFFIX1(".jpe", "image/jpeg", "binary"); + SET_SUFFIX1(".jpg", "image/jpeg", "binary"); + SET_SUFFIX1(".jpeg", "image/jpeg", "binary"); + SET_SUFFIX1(".tif", "image/tiff", "binary"); + SET_SUFFIX1(".tiff", "image/tiff", "binary"); + SET_SUFFIX1(".ham", "image/ham", "binary"); + SET_SUFFIX1(".ras", "image/x-cmu-rast", "binary"); + SET_SUFFIX1(".pnm", "image/x-portable-anymap", "binary"); + SET_SUFFIX1(".pbm", "image/x-portable-bitmap", "binary"); + SET_SUFFIX1(".pgm", "image/x-portable-graymap", "binary"); + SET_SUFFIX1(".ppm", "image/x-portable-pixmap", "binary"); + SET_SUFFIX1(".png", "image/png", "binary"); + SET_SUFFIX1(".rgb", "image/x-rgb", "binary"); + SET_SUFFIX1(".xbm", "image/x-xbitmap", "binary"); + SET_SUFFIX1(".xpm", "image/x-xpixmap", "binary"); + SET_SUFFIX1(".xwd", "image/x-xwindowdump", "binary"); + + SET_SUFFIX1(".rtx", "text/richtext", "8bit"); + SET_SUFFIX1(".tsv", "text/tab-separated-values", "8bit"); + SET_SUFFIX1(".etx", "text/x-setext", "8bit"); + + SET_SUFFIX1(".mpg", "video/mpeg", "binary"); + SET_SUFFIX1(".mpe", "video/mpeg", "binary"); + SET_SUFFIX1(".mpeg", "video/mpeg", "binary"); + SET_SUFFIX1(".mov", "video/quicktime", "binary"); + SET_SUFFIX1(".qt", "video/quicktime", "binary"); + SET_SUFFIX1(".avi", "video/x-msvideo", "binary"); + SET_SUFFIX1(".movie", "video/x-sgi-movie", "binary"); + SET_SUFFIX1(".mv", "video/x-sgi-movie", "binary"); + + SET_SUFFIX1(".mime", "message/rfc822", "8bit"); + + SET_SUFFIX1(".c", STR_PLAINTEXT, "8bit"); + SET_SUFFIX1(".cc", STR_PLAINTEXT, "8bit"); + SET_SUFFIX1(".c++", STR_PLAINTEXT, "8bit"); + SET_SUFFIX1(".css", STR_PLAINTEXT, "8bit"); + SET_SUFFIX1(".h", STR_PLAINTEXT, "8bit"); + SET_SUFFIX1(".pl", STR_PLAINTEXT, "8bit"); + SET_SUFFIX1(".text", STR_PLAINTEXT, "8bit"); + SET_SUFFIX1(".txt", STR_PLAINTEXT, "8bit"); + + SET_SUFFIX1(".php", STR_HTML, "8bit"); + SET_SUFFIX1(".php3", STR_HTML, "8bit"); + SET_SUFFIX1(".html3", STR_HTML, "8bit"); + SET_SUFFIX1(".ht3", STR_HTML, "8bit"); + SET_SUFFIX1(".phtml", STR_HTML, "8bit"); + SET_SUFFIX1(".shtml", STR_HTML, "8bit"); + SET_SUFFIX1(".sht", STR_HTML, "8bit"); + SET_SUFFIX1(".htmlx", STR_HTML, "8bit"); + SET_SUFFIX1(".htm", STR_HTML, "8bit"); + SET_SUFFIX1(".html", STR_HTML, "8bit"); + /* *INDENT-ON* */ + + } else { /* LYSuffixRules */ + /* + * Note that even .html -> text/html, .htm -> text/html are omitted if + * default maps are compiled in but then skipped because of a + * configuration file directive. Whoever changes the config file in + * this way can easily also add the SUFFIX rules there. - kw + */ + CTrace((tfp, + "HTFileInit: Skipping all default (HTInit) extension maps!\n")); + } /* LYSuffixRules */ + +#else /* BUILTIN_SUFFIX_MAPS */ + + CTrace((tfp, + "HTFileInit: Default (HTInit) extension maps not compiled in.\n")); + /* + * The following two are still used if BUILTIN_SUFFIX_MAPS was undefined. + * Without one of them, lynx would always need to have a mapping specified + * in a lynx.cfg or mime.types file to be usable for local HTML files at + * all. That includes many of the generated user interface pages. - kw + */ + SET_SUFFIX1(".htm", STR_HTML, "8bit"); + SET_SUFFIX1(".html", STR_HTML, "8bit"); +#endif /* BUILTIN_SUFFIX_MAPS */ + + if (LYisAbsPath(global_extension_map)) { + /* These should override the default extensions as necessary. */ + HTLoadExtensionsConfigFile(global_extension_map); + } + + /* + * Load the local maps. + */ + if (IsOurFile(LYAbsOrHomePath(&personal_extension_map)) + && LYCanReadFile(personal_extension_map)) { + /* These should override everything else. */ + HTLoadExtensionsConfigFile(personal_extension_map); + } +} + +/* -------------------- Extension config file reading --------------------- */ + +/* + * The following is lifted from NCSA httpd 1.0a1, by Rob McCool; + * NCSA httpd is in the public domain, as is this code. + * + * Modified Oct 97 - KW + */ + +#define MAX_STRING_LEN 256 + +static int HTGetLine(char *s, int n, FILE *f) +{ + register int i = 0, r; + + if (!f) + return (1); + + while (1) { + r = fgetc(f); + s[i] = (char) r; + + if (s[i] == CR) { + r = fgetc(f); + if (r == LF) + s[i] = (char) r; + else if (r != EOF) + ungetc(r, f); + } + + if ((r == EOF) || (s[i] == LF) || (s[i] == CR) || (i == (n - 1))) { + s[i] = '\0'; + return (feof(f) ? 1 : 0); + } + ++i; + } +} + +static void HTGetWord(char *word, char *line, int stop, int stop2) +{ + int x = 0, y; + + for (x = 0; (line[x] + && UCH(line[x]) != UCH(stop) + && UCH(line[x]) != UCH(stop2)); x++) { + word[x] = line[x]; + } + + word[x] = '\0'; + if (line[x]) + ++x; + y = 0; + + while ((line[y++] = line[x++])) { + ; + } + + return; +} + +static int HTLoadExtensionsConfigFile(char *fn) +{ + char line[MAX_STRING_LEN]; + char word[MAX_STRING_LEN]; + char *ct; + FILE *f; + int count = 0; + + CTrace((tfp, "HTLoadExtensionsConfigFile: Loading file '%s'.\n", fn)); + + if ((f = fopen(fn, TXT_R)) == NULL) { + CTrace((tfp, "HTLoadExtensionsConfigFile: Could not open '%s'.\n", fn)); + return count; + } + + while (!(HTGetLine(line, (int) sizeof(line), f))) { + HTGetWord(word, line, ' ', '\t'); + if (line[0] == '\0' || word[0] == '#') + continue; + ct = NULL; + StrAllocCopy(ct, word); + LYLowerCase(ct); + + while (line[0]) { + HTGetWord(word, line, ' ', '\t'); + if (word[0] && (word[0] != ' ')) { + char *ext = NULL; + + HTSprintf0(&ext, ".%s", word); + LYLowerCase(ext); + + CTrace((tfp, "setting suffix '%s' to '%s'.\n", ext, ct)); + + if (strstr(ct, "tex") != NULL || + strstr(ct, "postscript") != NULL || + strstr(ct, "sh") != NULL || + strstr(ct, "troff") != NULL || + strstr(ct, "rtf") != NULL) + SET_SUFFIX1(ext, ct, "8bit"); + else + SET_SUFFIX1(ext, ct, "binary"); + count++; + + FREE(ext); + } + } + FREE(ct); + } + LYCloseInput(f); + + return count; +} |