Infer the file type from the file name To determine the type of a file, the filename is compared against a number of patterns that are read from a file. When a pattern matches, the associated MIME type is returned. This method of file typing is, of course, inherently unreliable. There are two files with heuristic rules. One is normally located in `/usr/local/lib/argo/heuristics', the other in the user's home directory as `~/.argo-heurisics'. The latter is read first and therefore has priority. Lines that are empty or that start with `[[#]]' are ignored. Other lines start with a pattern that may include wild cards `[[*]]' and `[[?]]', with the same meaning as in the Unix (Bourne) shell. Note that the pattern matches the whole path, not just the file name or the suffix. Most patterns will therefore start with a [[*]] wildcard. Here is an example: [[*.gif b 8bit image/gif]] After the pattern there must be some whitespace (spaces, tabs) and then a letter `t' or `b', meaning that the file should be transferred as text (with end-of-line translation) or as binary. After the letter there should be again whitespace and then a string representing the encoding, such as `8bit', `7bit', or `x-uuencoded'. The rest of the line is the content-type. It starts with a MIME type such as `text/plain' or `audio/basic' and it may have parameters, such as `arg1=12'. The function sets the [[type]] argument to the content type and [[encoding]] to the encoding. [[binary]] will be true when the letter `b' was found. The function allocates space on the heap. [[type]] and [[encoding]] may be NULL, in which case no space is allocated (and the argument is left unchanged.) The [[force_binary]] parameter is used to force [[binary]] to true, independent of the setting in the heuristics file. <<*>>= #include #include #ifndef HEURISTICS #define HEURISTICS "heuristics" /* /usr/local/lib/argo/heuristics */ #endif #ifndef LOCAL_HEURISTICS #define LOCAL_HEURISTICS ".argo-heuristics" #endif static struct { char *pattern; Bool binary; char *encoding; char *type; } defaults[] = { {"*.[Gg][Ii][Ff]", TRUE, "8bit", "image/gif"}, {"*.[Tt][Ii][Ff][Ff]", TRUE, "8bit", "image/tiff"}, {"*.[Tt][Ii][Ff]", TRUE, "8bit", "image/tiff"}, {"*.[Jj][Pp][Ee][Gg]", TRUE, "8bit", "image/jpeg"}, {"*.[Jj][Pp][Gg]", TRUE, "8bit", "image/jpeg"}, {"*.bm", FALSE, "7bit", "image/x-xbm"}, {"*.xbm", FALSE, "7bit", "image/x-xbm"}, {"*.pm", FALSE, "7bit", "image/x-xpm"}, {"*.xpm", FALSE, "7bit", "image/x-xpm"}, {"*.[Pp][Bb][Mm]", TRUE, "8bit", "image/x-pbm"}, {"*.[Pp][Gg][Mm]", TRUE, "8bit", "image/x-pgm"}, {"*.[Pp][PpNn][Mm]", TRUE, "8bit", "image/x-ppm"}, {"*.[Tt][Xx][Tt]", FALSE, "8bit", "text/plain"}, {"*.[Hh][Tt][Mm][Ll]", FALSE, "8bit", "text/html"}, {"*.[Hh][Tt][Mm]", FALSE, "8bit", "text/html"}, {"*.[Tt][Ee][Xx]", FALSE, "8bit", "text/x-tex"}, {"*.[Dd][Vv][Ii]", TRUE, "8bit", "application/x-dvi"}, {"*.[1-9]", FALSE, "7bit", "text/x-man"}, {"*.man", FALSE, "7bit", "text/x-man"}, {"*/README*", FALSE, "8bit", "text/plain"}, {"*.tar.Z", TRUE, "x-compress", "application/x-tar"}, {"*.tar.z", TRUE, "x-gzip", "application/x-tar"}, {"*.tar.gz", TRUE, "x-gzip", "application/x-tar"}, {"*/Mail/*", FALSE, "7bit", "text/x-mailbox"}, {"*/.cache", FALSE, "8bit", "text/x-gopher-menu"}, {"*.Z", TRUE, "x-compress", "text/plain"}, {"*.z", TRUE, "x-gzip", "text/plain"}, {"*.gz", TRUE, "x-gzip", "text/plain"}, {"*.[Uu][Uu][Ee]", FALSE, "x-uuencode", "application/binary"}, {"*", TRUE, "8bit", "text/plain"}, {NULL, FALSE, NULL, NULL}, }; static Bool infer(FILE *f, char *path, char **type, char **enc, Bool *binary) { char line[BUFSIZ], *pattern, *h, *rest; while (fgets(line, sizeof(line), f)) { pattern = tokenize(line, " \t\n", &rest); if (pattern && pattern[0] != '#' && fnmatch(pattern, path, 0) == 0) { h = tokenize(rest, " \t\n", &rest); if (binary) *binary = h[0] == 'b'; if (enc) *enc = newstring(tokenize(rest, " \t\n", &rest)); if (type) *type = newstring(tokenize(rest, " \t\n", &rest)); return TRUE; } } return FALSE; } static Bool infer_builtin(char *path, char **type, char **enc, Bool *binary) { int i; for (i = 0; defaults[i].pattern; i++) { if (fnmatch(defaults[i].pattern, path, 0) == 0) { if (binary) *binary = defaults[i].binary; if (enc) *enc = newstring(defaults[i].encoding); if (type) *type = newstring(defaults[i].type); return TRUE; } } return FALSE; } EXPORT Bool URL_infer_type(char *path, Bool force_binary, char **type, char **encoding, Bool *binary) { FILE *f; Bool ok = FALSE; if ((f = fopen(LOCAL_HEURISTICS, "r"))) { ok = infer(f, path, type, encoding, binary); fclose(f); } if (! ok && (f = fopen(HEURISTICS, "r"))) { ok = infer(f, path, type, encoding, binary); fclose(f); } if (! ok) { ok = infer_builtin(path, type, encoding, binary); } if (force_binary) *binary = TRUE; return ok; }