summaryrefslogtreecommitdiff
path: root/Build/source/texk/xdvik/mime.c
diff options
context:
space:
mode:
Diffstat (limited to 'Build/source/texk/xdvik/mime.c')
-rw-r--r--Build/source/texk/xdvik/mime.c653
1 files changed, 653 insertions, 0 deletions
diff --git a/Build/source/texk/xdvik/mime.c b/Build/source/texk/xdvik/mime.c
new file mode 100644
index 00000000000..1f1e13ee282
--- /dev/null
+++ b/Build/source/texk/xdvik/mime.c
@@ -0,0 +1,653 @@
+/* Copyright (c) 1994-1999 All rights reserved. */
+/* Written from scratch July 30, 1994, by A. P. Smith. */
+
+/* Copyright (c) 2003-2004 the xdvik development team */
+
+/*
+ Read mime.types and mailcap entries to check what
+ viewers to invoke on a new file.
+
+ Patch by Allin Cottrell (cottrell@ricardo.ecn.wfu.edu) to
+ invokeviewer applied 30/11/98.
+
+ Patched further in january 1999 by Nicolai Langfeldt
+ (janl@math.uio.no) to allow saner mime typing.
+
+ Rewritten by Stefan Ulrich <stefanulrich@users.sourceforge.net>
+ on 2003/03/25 for better RFC 1343 conformance.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to
+deal in the Software without restriction, including without limitation the
+rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+sell copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL PAUL VOJTA OR ANY OTHER AUTHOR OF THIS SOFTWARE BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+*/
+
+#include "xdvi-config.h"
+#include "xdvi.h"
+#include "hypertex.h"
+#include "xdvi-debug.h"
+#include "util.h"
+#include "string-utils.h"
+#include "mime.h"
+#include "browser.h"
+#include "message-window.h"
+
+#include <ctype.h>
+#include "kpathsea/c-fopen.h"
+#include "kpathsea/variable.h"
+#include "kpathsea/c-pathmx.h"
+
+/* default settings if $EXTENSIONMAPS and $MAILCAPS is not set */
+
+static const char *const DEFAULT_EXTENSIONMAPS =
+ "$HOME/.mime.types:/etc/mime.types:/usr/etc/mime.types:/usr/local/etc/mimetypes";
+
+static const char *const DEFAULT_MAILCAPS =
+ "$HOME/.mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap";
+
+struct mime_map {
+ char *content_type;
+ char *extensions;
+};
+
+static struct mime_map default_mimemap[] = {
+ {"application/postscript", "ai eps epsf ps",},
+ {"application/octet-stream", "bin",},
+ {"application/oda", "oda",},
+ {"application/pdf", "pdf",},
+ {"application/rtf", "rtf",},
+ {"application/x-mif", "mif",},
+ {"application/x-csh", "csh",},
+ {"application/x-dvi", "dvi Dvi DVI",},
+ {"application/x-hdf", "hdf",},
+ {"application/x-latex", "latex",},
+ {"application/x-netcdf", "nc cdf",},
+ {"application/x-sh", "sh",},
+ {"application/x-tcl", "tcl",},
+ {"application/x-tex", "tex",},
+ {"application/x-texinfo", "texinfo texi",},
+ {"application/x-troff", "t tr roff",},
+ {"application/x-troff-man", "man",},
+ {"application/x-troff-me", "me",},
+ {"application/x-troff-ms", "ms",},
+ {"application/x-wais-source", "src",},
+ {"application/zip", "zip",},
+ {"application/x-bcpio", "bcpio",},
+ {"application/x-cpio", "cpio",},
+ {"application/x-gtar", "gtar",},
+ {"application/x-shar", "shar",},
+ {"application/x-sv4cpio", "sv4cpio",},
+ {"application/x-sv4crc", "sv4crc",},
+ {"application/x-tar", "tar",},
+ {"application/x-ustar", "ustar",},
+ {"audio/basic", "au snd",},
+ {"audio/x-aiff", "aif aiff aifc",},
+ {"audio/x-wav", "wav",},
+ {"image/gif", "gif",},
+ {"image/ief", "ief",},
+ {"image/jpeg", "jpeg jpg jpe",},
+ {"image/tiff", "tiff tif",},
+ {"image/x-cmu-raster", "ras",},
+ {"image/x-portable-anymap", "pnm",},
+ {"image/x-portable-bitmap", "pbm",},
+ {"image/x-portable-graymap", "pgm",},
+ {"image/x-portable-pixmap", "ppm",},
+ {"image/x-rgb", "rgb",},
+ {"image/x-xbitmap", "xbm",},
+ {"image/x-xpixmap", "xpm",},
+ {"image/x-xwindowdump", "xwd",},
+ {"text/html", "html htm sht shtml",},
+ {"text/plain", "txt",},
+ {"text/richtext", "rtx",},
+ {"text/tab-separated-values", "tsv",},
+ {"text/x-setext", "etx",},
+ {"video/mpeg", "mpeg mpg mpe",},
+ {"video/quicktime", "qt mov",},
+ {"video/x-msvideo", "avi",},
+ {"video/x-sgi-movie", "movie",},
+ {"application/gzip", "gz",},
+ {"application/compress", "Z",},
+ {"application/bzip", "bz",},
+ {"application/bzip2", "bz2",},
+};
+
+
+struct mailcap_map {
+ char *content_type;
+ char *command; /* command string */
+ char *testcmd; /* value of the `test=' field, or NULL if no test field present */
+ Boolean needsterminal; /* whether the `needsterminal' flag has been specified for this entry */
+ const char *format_string; /* format specifier ("%s" or "%u" or "") */
+};
+
+static struct mailcap_map default_mailcap[] = {
+ {"audio/*", "showaudio %s", NULL, False, False},
+ {"image/*", "xv %s", NULL, False, False},
+ {"video/mpeg", "mpeg_play %s", NULL, False, False},
+ {"application/pdf", "acroread %s", NULL, False, False},
+ {"text/html", "netscape-raise -remote 'openURL(%s,new-window)'", NULL, False, False},
+ {"application/postscript", "ghostview %s", NULL, False, False},
+ {"application/x-dvi", "xdvi %s", NULL, False, False},
+};
+
+static struct mime_map *m_mimemap = NULL;
+static int m_mimemap_currlen = 0;
+static int m_mimemap_size = 0;
+
+static struct mailcap_map *m_mailcap = NULL;
+static int m_mailcap_currlen, m_mailcap_size = 0;
+
+static const size_t MIME_ALLOC_STEP = 64;
+
+/* Utility function:
+ Read a line from a config file, dealing with continued lines
+ (ending with '\[ \t]*').
+ The *linebuf argument is re-alloced as needed from its initial size alloc_len.
+ The length of the read line is returned as ret_len.
+ The lineno int is by the number of lines read.
+ Trailing whitespace and newline is removed, and either the line,
+ or NULL for EOF is returned.
+*/
+static char *
+read_config_line(const char *filename, FILE *fp, char **linebuf,
+ size_t alloc_len, size_t *ret_len,
+ int *lineno)
+{
+ Boolean read_complete_line = False;
+ *ret_len = 0;
+ while (!read_complete_line) {
+ if ((fgets((*linebuf) + *ret_len, alloc_len - *ret_len, fp)) == NULL) {
+ if (*ret_len == 0) /* at EOF */
+ return NULL;
+ else /* nothing read, but we have a result from previous loop iteration */
+ return *linebuf;
+ }
+ (*lineno)++;
+ *ret_len = strlen(*linebuf);
+ if (*ret_len > 0 && (*linebuf)[*ret_len - 1] != '\n') {
+ /* catch the special case of missing NL at EOF */
+ if (*ret_len < alloc_len - 1) {
+ XDVI_WARNING((stderr, "%s, line %d: missing newline at end of file.", filename, *lineno));
+ return *linebuf;
+ }
+ /* buffer too short, need to re-allocate */
+ alloc_len *= 2;
+ *linebuf = xrealloc(*linebuf, alloc_len);
+ }
+ else if (*ret_len > 2
+ && (*linebuf)[*ret_len - 1] == '\n'
+ && ((*linebuf)[*ret_len - 2] == '\\'
+ || isspace((int)(*linebuf)[*ret_len - 2]))) {
+ /* we may have a continued line */
+ /* chop off trailing whitespace */
+ while (*ret_len > 1 && isspace((int)(*linebuf)[*ret_len - 2]))
+ (*ret_len)--;
+ if (*ret_len > 1 && (*linebuf)[*ret_len - 2] != '\\') { /* no continued line */
+ read_complete_line = True;
+ }
+ else /* overwrite backslash and newline at next read */
+ *ret_len -= 2;
+ }
+ else
+ read_complete_line = True;
+ }
+ /* chop off trailing NL */
+ (*linebuf)[--(*ret_len)] = '\0';
+ return *linebuf;
+}
+
+static void
+read_mime_file(FILE *fp, const char *filename, int *mime_line)
+{
+ int i;
+ const size_t line_len = 1024;
+ size_t ret_len = 0;
+ char *linebuf = xmalloc(line_len);
+ char *cp, *cp2;
+
+ int dummy = 0;
+ TRACE_HTEX((stderr, "reading mime file \"%s\"", filename));
+ while ((read_config_line(filename, fp, &linebuf, line_len, &ret_len, &dummy)) != NULL) {
+ if (linebuf[ret_len] == '\n')
+ linebuf[ret_len] = '\0';
+
+ cp = linebuf;
+ while (isspace((int)*cp))
+ cp++;
+ if (*cp == '#')
+ continue;
+
+ /* split either on tab or on whitespace */
+ if ((cp2 = strchr(cp, '\t')) == NULL && (cp2 = strchr(cp, ' ')) == NULL)
+ continue;
+ *cp2 = '\0'; /* Terminate cp string */
+ cp2++;
+ while (isspace((int)*cp2))
+ cp2++;
+ if (*cp2 == '\0')
+ continue; /* No extensions list */
+ if (*mime_line >= m_mimemap_size) {
+ m_mimemap_size += MIME_ALLOC_STEP;
+ m_mimemap = xrealloc(m_mimemap, m_mimemap_size * sizeof *m_mimemap);
+ for (i = *mime_line; i < m_mimemap_size; i++) {
+ m_mimemap[i].content_type = m_mimemap[i].extensions = NULL;
+ }
+ }
+#if 0
+ fprintf(stderr, "========== type %d: |%s| extension: |%s|\n", *mime_line, cp, cp2);
+#endif
+ free(m_mimemap[*mime_line].content_type);
+ m_mimemap[*mime_line].content_type = xstrdup(cp);
+ free(m_mimemap[*mime_line].extensions);
+ m_mimemap[*mime_line].extensions = xstrdup(cp2);
+
+ (*mime_line)++;
+ }
+ free(linebuf);
+}
+
+static char *
+maybe_expand_homedir(const char *path)
+{
+ char *newpath = NULL;
+ TRACE_HTEX((stderr, "maybe_expand_homedir: |%s|", path));
+ if (memcmp(path, "$HOME", strlen("$HOME")) == 0) {
+ newpath = xstrdup(getenv("HOME"));
+ newpath = xstrcat(newpath, path + strlen("$HOME"));
+ }
+ else if (path[0] == '~') {
+ newpath = expand_homedir(path);
+ if (newpath == NULL) {
+ XDVI_WARNING((stderr, "Couldn't expand path `%s'", path));
+ }
+ }
+ TRACE_HTEX((stderr, "maybe_expand_homedir: after expansion: |%s|", newpath ? newpath : "<NULL>"));
+ return newpath;
+}
+
+
+static void
+read_mailcap(FILE *fp, const char *filename, int *mailcap_line)
+{
+ size_t alloc_len = 1024;
+ size_t ret_len = 0;
+ char *linebuf = xmalloc(alloc_len);
+ int lineno = 0;
+
+ TRACE_HTEX((stderr, "reading mailcap file \"%s\"", filename));
+
+ while ((read_config_line(filename, fp, &linebuf, alloc_len, &ret_len, &lineno)) != NULL) {
+ Boolean error = False;
+ int i;
+ size_t n, num_items;
+ char **items;
+ char *ptr = linebuf;
+
+ while (isspace((int)*ptr)) {
+ ptr++;
+ ret_len--;
+ }
+ if (*ptr == '#' || *ptr == '\0') /* comments or empty lines */
+ continue;
+
+ /* split line into fields */
+ items = split_line(ptr, ';', 0, ret_len, &num_items);
+
+ if (num_items == 0) {
+ XDVI_WARNING((stderr, "%s, line %d: skipping malformed line \"%s\" (no command specified)",
+ filename, lineno, linebuf));
+ error = True;
+ }
+ else if (num_items == 1 || (num_items >= 2 && strlen(items[1]) == 0)) {
+ XDVI_WARNING((stderr, "%s, line %d: skipping malformed line \"%s\" (empty command)",
+ filename, lineno, linebuf));
+ error = True;
+ }
+
+ if (error) {
+ for (n = 0; n < num_items; n++)
+ free(items[n]);
+ free(items);
+ continue;
+ }
+
+ /* resize m_mailcap */
+ while (*mailcap_line >= m_mailcap_size) {
+ m_mailcap_size += MIME_ALLOC_STEP;
+ m_mailcap = xrealloc(m_mailcap, m_mailcap_size * sizeof *m_mailcap);
+ for (i = *mailcap_line; i < m_mailcap_size; i++) {
+ m_mailcap[i].content_type = NULL;
+ m_mailcap[i].command = NULL;
+ m_mailcap[i].needsterminal = False;
+ m_mailcap[i].testcmd = NULL;
+ }
+ }
+
+ for (n = 0; n < num_items; n++) {
+ if (n == 0) { /* first field: content-type */
+ m_mailcap[*mailcap_line].content_type = items[n];
+ /*
+ * add \/\* to content-type if it only consists of one field
+ * (it's not clear to me from RFC 1343 how this should be handled)
+ */
+ if (strchr(m_mailcap[*mailcap_line].content_type, '/') == NULL) {
+ m_mailcap[*mailcap_line].content_type = xstrcat(m_mailcap[*mailcap_line].content_type, "/*");
+ }
+ }
+ else if (memcmp(items[n], "test=", strlen("test=")) == 0) {
+ m_mailcap[*mailcap_line].testcmd = xstrdup(items[n] + strlen("test="));
+ free(items[n]);
+ }
+ else if (memcmp(items[n], "needsterminal", strlen("needsterminal")) == 0) { /* set flag */
+ m_mailcap[*mailcap_line].needsterminal = True;
+ free(items[n]);
+ }
+ else if (memcmp(items[n], "copiousoutput", strlen("copiousoutput")) == 0
+ || memcmp(items[n], "compose=", strlen("compose=")) == 0
+ || memcmp(items[n], "composetyped=", strlen("composetyped=")) == 0
+ || memcmp(items[n], "print=", strlen("print=")) == 0
+ || memcmp(items[n], "edit=", strlen("edit=")) == 0
+ || memcmp(items[n], "x11-bitmap=", strlen("x11-bitmap=")) == 0
+ || memcmp(items[n], "description=", strlen("description=")) == 0) {
+ free(items[n]);
+ continue;
+ }
+ else { /* command field */
+ m_mailcap[*mailcap_line].command = items[n];
+
+ if (find_format_str(m_mailcap[*mailcap_line].command, "%s") != NULL)
+ m_mailcap[*mailcap_line].format_string = "%s";
+ else if (find_format_str(m_mailcap[*mailcap_line].command, "%u") != NULL)
+ m_mailcap[*mailcap_line].format_string = "%u";
+ else
+ m_mailcap[*mailcap_line].format_string = "";
+ }
+ }
+ free(items);
+
+ (*mailcap_line)++;
+ }
+ free(linebuf);
+}
+
+/* parse mime or mailcap file, return true if successful. */
+static Boolean
+parse_mime_mailcap(const char *env_var,
+ const char *default_var_value,
+ int *lines,
+ void(*parse_func)(FILE *fp, const char *filename, int *mime_line))
+{
+ const char *path = NULL;
+ char **path_elems;
+ size_t elem_cnt, i;
+ FILE *fp;
+ Boolean success = False;
+
+ if ((path = getenv(env_var)) == NULL) {
+ path = default_var_value;
+ }
+
+ path_elems = split_line(path, ':', 0, strlen(path), &elem_cnt);
+ for (i = 0; i < elem_cnt; i++) {
+ char *newpath;
+ /* expand paths */
+ if ((newpath = maybe_expand_homedir(path_elems[i])) != NULL) {
+ free(path_elems[i]);
+ path_elems[i] = newpath;
+ }
+
+ if ((fp = XFOPEN(path_elems[i], FOPEN_R_MODE)) != NULL) {
+ parse_func(fp, path_elems[i], lines);
+ success = True;
+ }
+ free(path_elems[i]);
+ }
+ free(path_elems);
+ return success;
+}
+
+static void
+parsemimes(void)
+{
+ static Boolean already_called = False;
+
+ if (already_called)
+ return;
+ already_called = True;
+
+ m_mimemap_currlen = 0;
+
+ if (!parse_mime_mailcap("EXTENSIONMAPS", DEFAULT_EXTENSIONMAPS, &m_mimemap_currlen, read_mime_file)) {
+ m_mimemap_currlen = XtNumber(default_mimemap);
+ m_mimemap = default_mimemap;
+ }
+}
+
+
+/*
+ Partial parsing of mailcap file.
+
+ Currently not implemented:
+ - specifiers %t, %{xyz}, %n, %F
+ - realplayer's %u is treated as %s (couldn't find any documentation on %u)
+ */
+static void
+parsemailcap(void)
+{
+ static Boolean already_called = False;
+
+ if (already_called)
+ return;
+
+ already_called = True;
+
+ m_mailcap_currlen = 0;
+
+ if (!parse_mime_mailcap("MAILCAPS", DEFAULT_MAILCAPS, &m_mailcap_currlen, read_mailcap)) {
+ m_mailcap_currlen = XtNumber(default_mailcap);
+ m_mailcap = default_mailcap;
+ }
+}
+
+
+/*
+ Try to match test against a content type definition pattern.
+ The strings have one of the following formats (ignore spaces around `/'
+ - these are just to avoid breaking the C comment syntax):
+ * / *
+ text / *
+ * / plain
+ text / plain
+ where * matches everything.
+*/
+static Boolean
+match_content_type(const char *pattern, const char *test)
+{
+ /* chop both strings into parts at the '/' */
+ char *subtype_pattern, *subtype_test;
+ if ((subtype_pattern = strchr(pattern, '/')) == NULL) { /* malformed string? */
+ XDVI_WARNING((stderr, "Malformed content-type \"%s\" (should be \"type/subtype\")", pattern));
+ return False;
+ }
+ subtype_pattern++;
+ if ((subtype_test = strchr(test, '/')) == NULL) { /* malformed string? */
+ XDVI_WARNING((stderr, "Malformed content-type \"%s\" (should be \"type/subtype\")", test));
+ return False;
+ }
+ subtype_test++;
+ /* check whether it either matches the strings or a wildcard */
+ if ((*pattern == '*' && *subtype_pattern == '*') ||
+ (*pattern == '*' && strcmp(subtype_pattern, subtype_test) == 0) ||
+ (*subtype_pattern == '*' && memcmp(pattern, test, subtype_test - test) == 0) ||
+ (strcmp(subtype_pattern, subtype_test) == 0 && memcmp(pattern, test, subtype_test - test) == 0)) {
+ return True;
+ }
+ return False;
+}
+
+static Boolean
+run_test_command(const char *cmd, const char *arg)
+{
+ int retval;
+ const char *ptr;
+ size_t len;
+ char *syscmd = NULL;
+
+ UNUSED(arg); /* FIXME */
+ if (cmd == NULL)
+ return True;
+
+ syscmd = xmalloc(strlen(cmd) + strlen(arg) + 1);
+ if ((ptr = find_format_str(cmd, "%s")) != NULL) {
+ len = 2;
+ }
+ else if ((ptr = find_format_str(cmd, "%u")) != NULL) {
+ len = 2;
+ }
+ else {
+ len = 0;
+ ptr = strchr(cmd, '\0');
+ }
+ /* build system command */
+ memcpy(syscmd, cmd, ptr - cmd);
+ if (len > 0) { /* append argument */
+ strcpy(syscmd + (ptr - cmd), arg);
+ strcpy(syscmd + (ptr - cmd) + strlen(arg), ptr + len);
+ }
+ else {
+ syscmd[ptr - cmd] = '\0';
+ }
+
+ retval = system(syscmd);
+ TRACE_HTEX((stderr, "Execution of test command `%s' returned value %d", syscmd, retval));
+ free(syscmd);
+ return retval == 0;
+}
+
+char *
+figure_viewer(const char *content_type, const char **format_string, Boolean *needs_terminal, const char *arg)
+{
+ int i;
+
+ /* special case so that xdvizilla isn't used, which would be
+ too dangerous since xdvizilla tries to unlink the DVI file by default.
+ FIXME: A better way would be to always copy the original file, like
+ e.g. Acroread does it; see comment in launch_program(), hypertex.c.
+ */
+ if (strcmp(content_type, "application/x-dvi") == 0) {
+ *format_string = "%s";
+ *needs_terminal = False;
+ return xstrdup("xdvi %s");
+ }
+ /* try each command from our mailcap list */
+ for (i = 0; i < m_mailcap_currlen; i++) {
+ if (globals.debug & DBG_HTEX) {
+ fprintf(stderr, "type |%s| viewer |%s| needsterminal=%s testcmd |%s|\n",
+ m_mailcap[i].content_type, m_mailcap[i].command,
+ m_mailcap[i].needsterminal ? "yes" : "no",
+ m_mailcap[i].testcmd ? m_mailcap[i].testcmd : "None");
+ }
+ if (match_content_type(m_mailcap[i].content_type, content_type)
+ && run_test_command(m_mailcap[i].testcmd, arg)) {
+ *format_string = m_mailcap[i].format_string;
+ *needs_terminal = m_mailcap[i].needsterminal;
+ return xstrdup(m_mailcap[i].command);
+ }
+ }
+ /* failure */
+ return NULL;
+}
+
+
+/* return the mime type for filename, or default types
+ for non-recognized extensions/no extensions */
+char *
+figure_mime_type(const char *filename)
+{
+ int i;
+ char *extension, *cp;
+ char *content_type = NULL;
+
+ /* First check for the mailcap and mime files */
+ parsemimes();
+ parsemailcap();
+
+ if (globals.debug & DBG_HTEX)
+ fprintf(stderr, "figure_mime_type: Called to find type of %s\n",
+ filename);
+
+ /* See if this is a directory */
+ if (filename[strlen(filename) - 1] == '/') {
+ if (globals.debug & DBG_HTEX)
+ fprintf(stderr, "It's a directory, returning unknownExtensionMimeType: %s\n",
+ resource.unknown_mime_suffix);
+ return resource.unknown_mime_suffix;
+ }
+
+ /* See if filename extension is on the mime list: */
+ extension = strrchr(filename, '.');
+
+ if (extension == NULL) {
+ TRACE_HTEX((stderr,
+ "No extension, defaulting to noExtensionMimeType: %s\n",
+ resource.no_mime_suffix));
+ return resource.no_mime_suffix;
+ }
+ extension++;
+ /*
+ * corrupt URLs might have empty extensions; we need to catch this,
+ * since the while loop below would not terminate in that case:
+ */
+ if (strcmp(extension, "") == 0) {
+ XDVI_WARNING((stderr, "Empty extension for file name or URL `%s'\n", filename));
+ return resource.no_mime_suffix;
+ }
+
+ for (i = 0; i < m_mimemap_currlen; i++) {
+ /*
+ * find extension in m_mimemap[i].extensions, a space-separated list
+ * of extension strings.
+ */
+ cp = m_mimemap[i].extensions;
+ while ((cp = strstr(cp, extension)) != NULL) {
+ if ((cp - m_mimemap[i].extensions > 0) && (cp[-1] != ' ')) {
+ cp++;
+ continue;
+ }
+ cp += strlen(extension);
+ if ((*cp != ' ') && (*cp != '\0'))
+ continue;
+ content_type = m_mimemap[i].content_type;
+ break;
+ }
+ if (content_type != NULL)
+ break;
+ }
+
+ if (content_type == NULL) {
+ content_type = xstrdup(resource.unknown_mime_suffix);
+ TRACE_HTEX((stderr,
+ "Unknown extension, defaulting to unknownExtensionMimeType: %s",
+ content_type));
+ }
+ else {
+ TRACE_HTEX((stderr, "Found mime type: %s", content_type));
+ }
+
+ return content_type;
+}
+