Back to index

tetex-bin  3.0
mime.c
Go to the documentation of this file.
00001 /* Copyright (c) 1994-1999  All rights reserved. */
00002 /* Written from scratch July 30, 1994, by A. P. Smith. */
00003 
00004 /*  Copyright (c) 2003-2004 the xdvik development team */
00005 
00006 /*
00007   Read mime.types and mailcap entries to check what
00008   viewers to invoke on a new file.
00009 
00010   Patch by Allin Cottrell (cottrell@ricardo.ecn.wfu.edu) to
00011   invokeviewer applied 30/11/98.
00012 
00013   Patched further in january 1999 by Nicolai Langfeldt
00014   (janl@math.uio.no) to allow saner mime typing.
00015 
00016   Rewritten by Stefan Ulrich <stefanulrich@users.sourceforge.net>
00017   on 2003/03/25 for better RFC 1343 conformance.
00018   
00019 Permission is hereby granted, free of charge, to any person obtaining a copy
00020 of this software and associated documentation files (the "Software"), to
00021 deal in the Software without restriction, including without limitation the
00022 rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
00023 sell copies of the Software, and to permit persons to whom the Software is
00024 furnished to do so, subject to the following conditions:
00025 
00026 The above copyright notice and this permission notice shall be included in
00027 all copies or substantial portions of the Software.
00028 
00029 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
00030 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
00031 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
00032 IN NO EVENT SHALL PAUL VOJTA OR ANY OTHER AUTHOR OF THIS SOFTWARE BE
00033 LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
00034 OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
00035 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
00036 
00037 */
00038 
00039 #include "xdvi-config.h"
00040 #include "xdvi.h"
00041 #include "hypertex.h"
00042 #include "xdvi-debug.h"
00043 #include "util.h"
00044 #include "string-utils.h"
00045 #include "mime.h"
00046 #include "browser.h"
00047 #include "message-window.h"
00048 
00049 #include <ctype.h>
00050 #include "kpathsea/c-fopen.h"
00051 #include "kpathsea/variable.h"
00052 #include "kpathsea/c-pathmx.h"
00053 
00054 /* default settings if $EXTENSIONMAPS and $MAILCAPS is not set */
00055 
00056 static const char *const DEFAULT_EXTENSIONMAPS =
00057     "$HOME/.mime.types:/etc/mime.types:/usr/etc/mime.types:/usr/local/etc/mimetypes";
00058 
00059 static const char *const DEFAULT_MAILCAPS =
00060     "$HOME/.mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap";
00061 
00062 struct mime_map {
00063     char *content_type;
00064     char *extensions;
00065 };
00066 
00067 static struct mime_map default_mimemap[] = {
00068     {"application/postscript", "ai eps epsf ps",},
00069     {"application/octet-stream", "bin",},
00070     {"application/oda", "oda",},
00071     {"application/pdf", "pdf",},
00072     {"application/rtf", "rtf",},
00073     {"application/x-mif", "mif",},
00074     {"application/x-csh", "csh",},
00075     {"application/x-dvi", "dvi Dvi DVI",},
00076     {"application/x-hdf", "hdf",},
00077     {"application/x-latex", "latex",},
00078     {"application/x-netcdf", "nc cdf",},
00079     {"application/x-sh", "sh",},
00080     {"application/x-tcl", "tcl",},
00081     {"application/x-tex", "tex",},
00082     {"application/x-texinfo", "texinfo texi",},
00083     {"application/x-troff", "t tr roff",},
00084     {"application/x-troff-man", "man",},
00085     {"application/x-troff-me", "me",},
00086     {"application/x-troff-ms", "ms",},
00087     {"application/x-wais-source", "src",},
00088     {"application/zip", "zip",},
00089     {"application/x-bcpio", "bcpio",},
00090     {"application/x-cpio", "cpio",},
00091     {"application/x-gtar", "gtar",},
00092     {"application/x-shar", "shar",},
00093     {"application/x-sv4cpio", "sv4cpio",},
00094     {"application/x-sv4crc", "sv4crc",},
00095     {"application/x-tar", "tar",},
00096     {"application/x-ustar", "ustar",},
00097     {"audio/basic", "au snd",},
00098     {"audio/x-aiff", "aif aiff aifc",},
00099     {"audio/x-wav", "wav",},
00100     {"image/gif", "gif",},
00101     {"image/ief", "ief",},
00102     {"image/jpeg", "jpeg jpg jpe",},
00103     {"image/tiff", "tiff tif",},
00104     {"image/x-cmu-raster", "ras",},
00105     {"image/x-portable-anymap", "pnm",},
00106     {"image/x-portable-bitmap", "pbm",},
00107     {"image/x-portable-graymap", "pgm",},
00108     {"image/x-portable-pixmap", "ppm",},
00109     {"image/x-rgb", "rgb",},
00110     {"image/x-xbitmap", "xbm",},
00111     {"image/x-xpixmap", "xpm",},
00112     {"image/x-xwindowdump", "xwd",},
00113     {"text/html", "html htm sht shtml",},
00114     {"text/plain", "txt",},
00115     {"text/richtext", "rtx",},
00116     {"text/tab-separated-values", "tsv",},
00117     {"text/x-setext", "etx",},
00118     {"video/mpeg", "mpeg mpg mpe",},
00119     {"video/quicktime", "qt mov",},
00120     {"video/x-msvideo", "avi",},
00121     {"video/x-sgi-movie", "movie",},
00122     {"application/gzip", "gz",},
00123     {"application/compress", "Z",},
00124     {"application/bzip", "bz",},
00125     {"application/bzip2", "bz2",},
00126 };
00127 
00128 
00129 struct mailcap_map {
00130     char *content_type;
00131     char *command;   /* command string */
00132     char *testcmd;   /* value of the `test=' field, or NULL if no test field present */
00133     Boolean needsterminal;  /* whether the `needsterminal' flag has been specified for this entry */
00134     const char *format_string;     /* format specifier ("%s" or "%u" or "") */
00135 };
00136 
00137 static struct mailcap_map default_mailcap[] = {
00138     {"audio/*", "showaudio %s", NULL, False, False},
00139     {"image/*", "xv %s", NULL, False, False},
00140     {"video/mpeg", "mpeg_play %s", NULL, False, False},
00141     {"application/pdf", "acroread %s", NULL, False, False},
00142     {"text/html", "netscape-raise  -remote 'openURL(%s,new-window)'", NULL, False, False},
00143     {"application/postscript", "ghostview %s", NULL, False, False},
00144     {"application/x-dvi", "xdvi %s", NULL, False, False},
00145 };
00146 
00147 static struct mime_map *m_mimemap = NULL;
00148 static int m_mimemap_currlen = 0;
00149 static int m_mimemap_size = 0;
00150 
00151 static struct mailcap_map *m_mailcap = NULL;
00152 static int m_mailcap_currlen, m_mailcap_size = 0;
00153 
00154 static const size_t MIME_ALLOC_STEP = 64;
00155 
00156 /* Utility function:
00157    Read a line from a config file, dealing with continued lines
00158    (ending with '\[ \t]*').
00159    The *linebuf argument is re-alloced as needed from its initial size alloc_len.
00160    The length of the read line is returned as ret_len.
00161    The lineno int is by the number of lines read.
00162    Trailing whitespace and newline is removed, and either the line,
00163    or NULL for EOF is returned.
00164 */
00165 static char *
00166 read_config_line(const char *filename, FILE *fp, char **linebuf,
00167                size_t alloc_len, size_t *ret_len,
00168                int *lineno)
00169 {
00170     Boolean read_complete_line = False;
00171     *ret_len = 0;
00172     while (!read_complete_line) {
00173        if ((fgets((*linebuf) + *ret_len, alloc_len - *ret_len, fp)) == NULL) {
00174            if (*ret_len == 0) /* at EOF */
00175               return NULL;
00176            else /* nothing read, but we have a result from previous loop iteration */
00177               return *linebuf;
00178        }
00179        (*lineno)++;
00180        *ret_len = strlen(*linebuf);
00181        if (*ret_len > 0 && (*linebuf)[*ret_len - 1] != '\n') {
00182            /* catch the special case of missing NL at EOF */
00183            if (*ret_len < alloc_len - 1) {
00184               XDVI_WARNING((stderr, "%s, line %d: missing newline at end of file.", filename, *lineno));
00185               return *linebuf;
00186            }
00187            /* buffer too short, need to re-allocate */
00188            alloc_len *= 2;
00189            *linebuf = xrealloc(*linebuf, alloc_len);
00190        }
00191        else if (*ret_len > 2
00192                && (*linebuf)[*ret_len - 1] == '\n'
00193                && ((*linebuf)[*ret_len - 2] == '\\'
00194                    || isspace((int)(*linebuf)[*ret_len - 2]))) {
00195            /* we may have a continued line */
00196            /* chop off trailing whitespace */
00197            while (*ret_len > 1 && isspace((int)(*linebuf)[*ret_len - 2]))
00198               (*ret_len)--;
00199            if (*ret_len > 1 && (*linebuf)[*ret_len - 2] != '\\') { /* no continued line */
00200               read_complete_line = True;
00201            }
00202            else /* overwrite backslash and newline at next read */
00203               *ret_len -= 2;
00204        }
00205        else
00206            read_complete_line = True;
00207     }
00208     /* chop off trailing NL */
00209     (*linebuf)[--(*ret_len)] = '\0';
00210     return *linebuf;
00211 }
00212 
00213 static void
00214 read_mime_file(FILE *fp, const char *filename, int *mime_line)
00215 {
00216     int i;
00217     const size_t line_len = 1024;
00218     size_t ret_len = 0;
00219     char *linebuf = xmalloc(line_len);
00220     char *cp, *cp2;
00221 
00222     int dummy = 0;
00223     TRACE_HTEX((stderr, "reading mime file \"%s\"", filename));
00224     while ((read_config_line(filename, fp, &linebuf, line_len, &ret_len, &dummy)) != NULL) {
00225        if (linebuf[ret_len] == '\n')
00226            linebuf[ret_len] = '\0';
00227 
00228        cp = linebuf;
00229        while (isspace((int)*cp))
00230            cp++;
00231        if (*cp == '#')
00232            continue;
00233 
00234        /* split either on tab or on whitespace */
00235        if ((cp2 = strchr(cp, '\t')) == NULL && (cp2 = strchr(cp, ' ')) == NULL)
00236            continue;
00237        *cp2 = '\0';  /* Terminate cp string */
00238        cp2++;
00239        while (isspace((int)*cp2))
00240            cp2++;
00241        if (*cp2 == '\0')
00242            continue; /* No extensions list */
00243        if (*mime_line >= m_mimemap_size) {
00244            m_mimemap_size += MIME_ALLOC_STEP;
00245            m_mimemap = xrealloc(m_mimemap, m_mimemap_size * sizeof *m_mimemap);
00246            for (i = *mime_line; i < m_mimemap_size; i++) {
00247               m_mimemap[i].content_type = m_mimemap[i].extensions = NULL;
00248            }
00249        }
00250 #if 0
00251        fprintf(stderr, "========== type %d: |%s| extension: |%s|\n", *mime_line, cp, cp2);
00252 #endif
00253        free(m_mimemap[*mime_line].content_type);
00254        m_mimemap[*mime_line].content_type = xstrdup(cp);
00255        free(m_mimemap[*mime_line].extensions);
00256        m_mimemap[*mime_line].extensions = xstrdup(cp2);
00257 
00258        (*mime_line)++;
00259     }
00260     free(linebuf);
00261 }
00262 
00263 static char *
00264 maybe_expand_homedir(const char *path)
00265 {
00266     char *newpath = NULL;
00267     TRACE_HTEX((stderr, "maybe_expand_homedir: |%s|", path));
00268     if (memcmp(path, "$HOME", strlen("$HOME")) == 0) {
00269        newpath = xstrdup(getenv("HOME"));
00270        newpath = xstrcat(newpath, path + strlen("$HOME"));
00271     }
00272     else if (path[0] == '~') {
00273        newpath = expand_homedir(path);
00274        if (newpath == NULL) {
00275            XDVI_WARNING((stderr, "Couldn't expand path `%s'", path));
00276        }
00277     }
00278     TRACE_HTEX((stderr, "maybe_expand_homedir: after expansion: |%s|", newpath ? newpath : "<NULL>"));
00279     return newpath;
00280 }
00281 
00282 
00283 static void
00284 read_mailcap(FILE *fp, const char *filename, int *mailcap_line)
00285 {
00286     size_t alloc_len = 1024;
00287     size_t ret_len = 0;
00288     char *linebuf = xmalloc(alloc_len);
00289     int lineno = 0;
00290 
00291     TRACE_HTEX((stderr, "reading mailcap file \"%s\"", filename));
00292 
00293     while ((read_config_line(filename, fp, &linebuf, alloc_len, &ret_len, &lineno)) != NULL) {
00294        Boolean error = False;
00295        int i;
00296        size_t n, num_items;
00297        char **items;
00298        char *ptr = linebuf;
00299 
00300        while (isspace((int)*ptr)) {
00301            ptr++;
00302            ret_len--;
00303        }
00304        if (*ptr == '#' || *ptr == '\0') /* comments or empty lines */
00305            continue;
00306 
00307        /* split line into fields */
00308        items = split_line(ptr, ';', 0, ret_len, &num_items);
00309 
00310        if (num_items == 0) {
00311            XDVI_WARNING((stderr, "%s, line %d: skipping malformed line \"%s\" (no command specified)",
00312                        filename, lineno, linebuf));
00313            error = True;
00314        }
00315        else if (num_items == 1 || (num_items >= 2 && strlen(items[1]) == 0)) {
00316            XDVI_WARNING((stderr, "%s, line %d: skipping malformed line \"%s\" (empty command)",
00317                        filename, lineno, linebuf));
00318            error = True;
00319        }
00320        
00321        if (error) {
00322            for (n = 0; n < num_items; n++)
00323               free(items[n]);
00324            free(items);
00325            continue;
00326        }
00327        
00328        /* resize m_mailcap */
00329        while (*mailcap_line >= m_mailcap_size) {
00330            m_mailcap_size += MIME_ALLOC_STEP;
00331            m_mailcap = xrealloc(m_mailcap, m_mailcap_size * sizeof *m_mailcap);
00332            for (i = *mailcap_line; i < m_mailcap_size; i++) {
00333               m_mailcap[i].content_type = NULL;
00334               m_mailcap[i].command = NULL;
00335               m_mailcap[i].needsterminal = False;
00336               m_mailcap[i].testcmd = NULL;
00337            }
00338        }
00339        
00340        for (n = 0; n < num_items; n++) {
00341            if (n == 0) { /* first field: content-type */
00342               m_mailcap[*mailcap_line].content_type = items[n];
00343               /*
00344                * add \/\* to content-type if it only consists of one field
00345                * (it's not clear to me from RFC 1343 how this should be handled)
00346                */
00347               if (strchr(m_mailcap[*mailcap_line].content_type, '/') == NULL) {
00348                   m_mailcap[*mailcap_line].content_type = xstrcat(m_mailcap[*mailcap_line].content_type, "/*");
00349               }
00350            }
00351            else if (memcmp(items[n], "test=", strlen("test=")) == 0) {
00352               m_mailcap[*mailcap_line].testcmd = xstrdup(items[n] + strlen("test="));
00353               free(items[n]);
00354            }
00355            else if (memcmp(items[n], "needsterminal", strlen("needsterminal")) == 0) { /* set flag */
00356               m_mailcap[*mailcap_line].needsterminal = True;
00357               free(items[n]);
00358            }
00359            else if (memcmp(items[n], "copiousoutput", strlen("copiousoutput")) == 0
00360                    || memcmp(items[n], "compose=", strlen("compose=")) == 0
00361                    || memcmp(items[n], "composetyped=", strlen("composetyped=")) == 0
00362                    || memcmp(items[n], "print=", strlen("print=")) == 0
00363                    || memcmp(items[n], "edit=", strlen("edit=")) == 0
00364                    || memcmp(items[n], "x11-bitmap=", strlen("x11-bitmap=")) == 0
00365                    || memcmp(items[n], "description=", strlen("description=")) == 0) {
00366               free(items[n]);
00367               continue;
00368            }
00369            else { /* command field */
00370               m_mailcap[*mailcap_line].command = items[n];
00371 
00372               if (find_format_str(m_mailcap[*mailcap_line].command, "%s") != NULL)
00373                   m_mailcap[*mailcap_line].format_string = "%s";
00374               else if (find_format_str(m_mailcap[*mailcap_line].command, "%u") != NULL)
00375                   m_mailcap[*mailcap_line].format_string = "%u";
00376               else
00377                   m_mailcap[*mailcap_line].format_string = "";
00378            }
00379        }
00380        free(items);
00381        
00382        (*mailcap_line)++;
00383     }
00384     free(linebuf);
00385 }
00386 
00387 /* parse mime or mailcap file, return true if successful. */
00388 static Boolean
00389 parse_mime_mailcap(const char *env_var,
00390                  const char *default_var_value,
00391                  int *lines,
00392                  void(*parse_func)(FILE *fp, const char *filename, int *mime_line))
00393 {
00394     const char *path = NULL;
00395     char **path_elems;
00396     size_t elem_cnt, i;
00397     FILE *fp;
00398     Boolean success = False;
00399     
00400     if ((path = getenv(env_var)) == NULL) {
00401        path = default_var_value;
00402     }
00403 
00404     path_elems = split_line(path, ':', 0, strlen(path), &elem_cnt);
00405     for (i = 0; i < elem_cnt; i++) {
00406        char *newpath;
00407        /* expand paths */
00408        if ((newpath = maybe_expand_homedir(path_elems[i])) != NULL) {
00409            free(path_elems[i]);
00410            path_elems[i] = newpath;
00411        }
00412 
00413        if ((fp = XFOPEN(path_elems[i], FOPEN_R_MODE)) != NULL) {
00414            parse_func(fp, path_elems[i], lines);
00415            success = True;
00416        }
00417        free(path_elems[i]);
00418     }
00419     free(path_elems);
00420     return success;
00421 }    
00422 
00423 static void
00424 parsemimes(void)
00425 {
00426     static Boolean already_called = False;
00427     
00428     if (already_called)
00429        return;
00430     already_called = True;
00431 
00432     m_mimemap_currlen = 0;
00433 
00434     if (!parse_mime_mailcap("EXTENSIONMAPS", DEFAULT_EXTENSIONMAPS, &m_mimemap_currlen, read_mime_file)) {
00435        m_mimemap_currlen = XtNumber(default_mimemap);
00436        m_mimemap = default_mimemap;
00437     }
00438 }
00439 
00440 
00441 /*
00442   Partial parsing of mailcap file.
00443 
00444   Currently not implemented:
00445   - specifiers %t, %{xyz}, %n, %F
00446   - realplayer's %u is treated as %s (couldn't find any documentation on %u)
00447  */
00448 static void
00449 parsemailcap(void)
00450 {
00451     static Boolean already_called = False;
00452     
00453     if (already_called)
00454        return;
00455     
00456     already_called = True;
00457 
00458     m_mailcap_currlen = 0;
00459 
00460     if (!parse_mime_mailcap("MAILCAPS", DEFAULT_MAILCAPS, &m_mailcap_currlen, read_mailcap)) {
00461        m_mailcap_currlen = XtNumber(default_mailcap);
00462        m_mailcap = default_mailcap;
00463     }
00464 }
00465 
00466 
00467 /*
00468   Try to match test against a content type definition pattern.
00469   The strings have one of the following formats (ignore spaces around `/'
00470   - these are just to avoid breaking the C comment syntax):
00471   * / *
00472   text / *
00473   * / plain
00474   text / plain
00475   where * matches everything.
00476 */
00477 static Boolean
00478 match_content_type(const char *pattern, const char *test)
00479 {
00480     /* chop both strings into parts at the '/' */
00481     char *subtype_pattern, *subtype_test;
00482     if ((subtype_pattern = strchr(pattern, '/')) == NULL) { /* malformed string? */
00483        XDVI_WARNING((stderr, "Malformed content-type \"%s\" (should be \"type/subtype\")", pattern));
00484        return False;
00485     }
00486     subtype_pattern++;
00487     if ((subtype_test = strchr(test, '/')) == NULL) { /* malformed string? */
00488        XDVI_WARNING((stderr, "Malformed content-type \"%s\" (should be \"type/subtype\")", test));
00489        return False;
00490     }
00491     subtype_test++;
00492     /* check whether it either matches the strings or a wildcard */
00493     if ((*pattern == '*' && *subtype_pattern == '*') ||
00494        (*pattern == '*' && strcmp(subtype_pattern, subtype_test) == 0) ||
00495        (*subtype_pattern == '*' && memcmp(pattern, test, subtype_test - test) == 0) ||
00496        (strcmp(subtype_pattern, subtype_test) == 0 && memcmp(pattern, test, subtype_test - test) == 0)) {
00497        return True;
00498     }
00499     return False;
00500 }
00501 
00502 static Boolean
00503 run_test_command(const char *cmd, const char *arg)
00504 {
00505     int retval;
00506     const char *ptr;
00507     size_t len;
00508     char *syscmd = NULL;
00509     
00510     UNUSED(arg); /* FIXME */
00511     if (cmd == NULL)
00512        return True;
00513 
00514     syscmd = xmalloc(strlen(cmd) + strlen(arg) + 1);
00515     if ((ptr = find_format_str(cmd, "%s")) != NULL) {
00516        len = 2;
00517     }
00518     else if ((ptr = find_format_str(cmd, "%u")) != NULL) {
00519        len = 2;
00520     }
00521     else {
00522        len = 0;
00523        ptr = strchr(cmd, '\0');
00524     }
00525     /* build system command */
00526     memcpy(syscmd, cmd, ptr - cmd);
00527     if (len > 0) { /* append argument */
00528        strcpy(syscmd + (ptr - cmd), arg);
00529        strcpy(syscmd + (ptr - cmd) + strlen(arg), ptr + len);
00530     }
00531     else {
00532        syscmd[ptr - cmd] = '\0';
00533     }
00534     
00535     retval = system(syscmd);
00536     TRACE_HTEX((stderr, "Execution of test command `%s' returned value %d", syscmd, retval));
00537     free(syscmd);
00538     return retval == 0;
00539 }
00540 
00541 char *
00542 figure_viewer(const char *content_type, const char **format_string, Boolean *needs_terminal, const char *arg)
00543 {
00544     int i;
00545 
00546     /* special case so that xdvizilla isn't used, which would be
00547        too dangerous since xdvizilla tries to unlink the DVI file by default.
00548        FIXME: A better way would be to always copy the original file, like
00549        e.g. Acroread does it; see comment in launch_program(), hypertex.c.
00550     */
00551     if (strcmp(content_type, "application/x-dvi") == 0) {
00552        *format_string = "%s";
00553        *needs_terminal = False;
00554        return xstrdup("xdvi %s");
00555     }
00556     /* try each command from our mailcap list */
00557     for (i = 0; i < m_mailcap_currlen; i++) {
00558        if (globals.debug & DBG_HTEX) {
00559            fprintf(stderr, "type |%s| viewer |%s| needsterminal=%s testcmd |%s|\n",
00560                   m_mailcap[i].content_type, m_mailcap[i].command,
00561                   m_mailcap[i].needsterminal ? "yes" : "no",
00562                   m_mailcap[i].testcmd ? m_mailcap[i].testcmd : "None");
00563        }
00564        if (match_content_type(m_mailcap[i].content_type, content_type)
00565            && run_test_command(m_mailcap[i].testcmd, arg)) {
00566            *format_string = m_mailcap[i].format_string;
00567            *needs_terminal = m_mailcap[i].needsterminal;
00568            return xstrdup(m_mailcap[i].command);
00569        }
00570     }
00571     /* failure */
00572     return NULL;
00573 }
00574 
00575 
00576 /* return the mime type for filename, or default types
00577    for non-recognized extensions/no extensions */
00578 char *
00579 figure_mime_type(const char *filename)
00580 {
00581     int i;
00582     char *extension, *cp;
00583     char *content_type = NULL;
00584 
00585     /* First check for the mailcap and mime files */
00586     parsemimes();
00587     parsemailcap();
00588 
00589     if (globals.debug & DBG_HTEX)
00590        fprintf(stderr, "figure_mime_type: Called to find type of %s\n",
00591               filename);
00592     
00593     /* See if this is a directory */
00594     if (filename[strlen(filename) - 1] == '/') {
00595        if (globals.debug & DBG_HTEX)
00596            fprintf(stderr, "It's a directory, returning unknownExtensionMimeType: %s\n",
00597                   resource.unknown_mime_suffix);
00598        return resource.unknown_mime_suffix;
00599     }
00600 
00601     /* See if filename extension is on the mime list: */
00602     extension = strrchr(filename, '.');
00603 
00604     if (extension == NULL) {
00605        TRACE_HTEX((stderr,
00606                   "No extension, defaulting to noExtensionMimeType: %s\n",
00607                   resource.no_mime_suffix));
00608        return resource.no_mime_suffix;
00609     }
00610     extension++;
00611     /*
00612      * corrupt URLs might have empty extensions; we need to catch this,
00613      * since the while loop below would not terminate in that case:
00614      */
00615     if (strcmp(extension, "") == 0) {
00616        XDVI_WARNING((stderr, "Empty extension for file name or URL `%s'\n", filename));
00617        return resource.no_mime_suffix;
00618     }
00619 
00620     for (i = 0; i < m_mimemap_currlen; i++) {
00621        /*
00622         * find extension in m_mimemap[i].extensions, a space-separated list
00623         * of extension strings.
00624         */
00625        cp = m_mimemap[i].extensions;
00626        while ((cp = strstr(cp, extension)) != NULL) {
00627            if ((cp - m_mimemap[i].extensions > 0) && (cp[-1] != ' ')) {
00628               cp++;
00629               continue;
00630            }
00631            cp += strlen(extension);
00632            if ((*cp != ' ') && (*cp != '\0'))
00633               continue;
00634            content_type = m_mimemap[i].content_type;
00635            break;
00636        }
00637        if (content_type != NULL)
00638            break;
00639     }
00640 
00641     if (content_type == NULL) {
00642        content_type = xstrdup(resource.unknown_mime_suffix);
00643        TRACE_HTEX((stderr,
00644                   "Unknown extension, defaulting to unknownExtensionMimeType: %s",
00645                   content_type));
00646     }
00647     else {
00648        TRACE_HTEX((stderr, "Found mime type: %s", content_type));
00649     }
00650 
00651     return content_type;
00652 }
00653