bug 764: Initialize the right member of union option_value
[elinks.git] / src / mime / backend / mimetypes.c
blob28c9b295c68168bc80a3379781d664a80da3dd69
1 /* Support for mime.types files for mapping file extensions to content types */
3 /* Copyright (C) 1996-2000 Michael R. Elkins <me@cs.hmc.edu>
4 * Copyright (C) 2003-2004 The ELinks Project */
6 #ifdef HAVE_CONFIG_H
7 #include "config.h"
8 #endif
10 #include <ctype.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
15 #include "elinks.h"
17 #include "config/options.h"
18 #include "intl/gettext/libintl.h"
19 #include "main/module.h"
20 #include "mime/backend/common.h"
21 #include "mime/backend/mimetypes.h"
22 #include "mime/mime.h"
23 #include "session/session.h"
24 #include "util/hash.h"
25 #include "util/lists.h"
26 #include "util/memory.h"
28 #define BACKEND_NAME "mimetypes"
30 struct mimetypes_entry {
31 unsigned char *content_type;
32 unsigned char extension[1];
35 enum mimetypes_option {
36 MIMETYPES_TREE,
38 MIMETYPES_ENABLE,
39 MIMETYPES_PATH,
41 MIMETYPES_OPTIONS
44 /* Keep options in alphabetical order. */
45 static union option_info mimetypes_options[] = {
46 INIT_OPT_TREE("mime", N_("Mimetypes files"),
47 "mimetypes", 0,
48 N_("Options for the support of mime.types files. These files "
49 "can be used to find the content type of a URL by looking at "
50 "the extension of the file name.")),
52 INIT_OPT_BOOL("mime.mimetypes", N_("Enable"),
53 "enable", 0, 1,
54 N_("Enable mime.types support.")),
56 INIT_OPT_STRING("mime.mimetypes", N_("Path"),
57 "path", 0, DEFAULT_MIMETYPES_PATH,
58 N_("The search path for mime.types files. "
59 "Colon-separated list of files.")),
61 NULL_OPTION_INFO,
64 #define get_opt_mimetypes(which) mimetypes_options[(which)].option
65 #define get_mimetypes(which) get_opt_mimetypes(which).value
66 #define get_mimetypes_enable() get_mimetypes(MIMETYPES_ENABLE).number
67 #define get_mimetypes_path() get_mimetypes(MIMETYPES_PATH).string
69 /* State variables */
70 static struct hash *mimetypes_map = NULL;
73 static void
74 done_mimetypes_entry(struct mimetypes_entry *entry)
76 if (!entry) return;
77 mem_free_if(entry->content_type);
78 mem_free(entry);
81 /* Parsing of a mime.types file with the format:
83 * basetype/subtype extension1 [extension2 ... extensionN]
85 * Comments starts with '#'. */
87 static inline void
88 parse_mimetypes_extensions(unsigned char *token, unsigned char *ctype)
90 int ctypelen = strlen(ctype);
92 /* Cycle through the file extensions */
93 while (*token) {
94 struct mimetypes_entry *entry;
95 unsigned char *extension;
96 struct hash_item *item;
97 int extlen;
99 skip_space(token);
101 extension = token;
102 skip_nonspace(token);
104 if (!*token) break;
105 *token++ = '\0';
107 extlen = strlen(extension);
108 /* First check if the type is already known. If it is
109 * drop it. This way first files are priotized. */
110 item = get_hash_item(mimetypes_map, extension, extlen);
111 if (item) continue;
113 entry = mem_calloc(1, sizeof(*entry) + extlen);
114 if (!entry) continue;
116 entry->content_type = memacpy(ctype, ctypelen);
117 if (!entry->content_type) {
118 done_mimetypes_entry(entry);
119 continue;
122 memcpy(entry->extension, extension, extlen);
124 item = add_hash_item(mimetypes_map, entry->extension, extlen,
125 entry);
126 if (!item)
127 done_mimetypes_entry(entry);
131 static void
132 parse_mimetypes_file(unsigned char *filename)
134 FILE *file = fopen(filename, "rb");
135 unsigned char line[MAX_STR_LEN];
137 if (!file) return;
139 while (fgets(line, MAX_STR_LEN - 1, file)) {
140 unsigned char *ctype = line;
141 unsigned char *token;
143 /* Weed out any comments */
144 token = strchr(line, '#');
145 if (token)
146 *token = '\0';
148 skip_space(ctype);
150 /* Position on the next field in this line */
151 token = ctype;
152 skip_nonspace(token);
154 if (!*token) continue;
155 *token++ = '\0';
157 /* Check if malformed content type */
158 if (!strchr(ctype, '/')) continue;
160 parse_mimetypes_extensions(token, ctype);
163 fclose(file);
166 static struct hash *
167 init_mimetypes_map(void)
169 unsigned char *path;
171 mimetypes_map = init_hash8();
172 if (!mimetypes_map)
173 return NULL;
175 /* Determine the path */
176 path = get_mimetypes_path();
177 if (!path || !*path) path = DEFAULT_MIMETYPES_PATH;
179 while (*path) {
180 unsigned char *filename = get_next_path_filename(&path, ':');
182 if (!filename) continue;
183 parse_mimetypes_file(filename);
184 mem_free(filename);
187 return mimetypes_map;
190 static void
191 done_mimetypes(struct module *module)
193 struct hash_item *item;
194 int i;
196 if (!mimetypes_map)
197 return;
199 foreach_hash_item (item, *mimetypes_map, i) {
200 if (item->value) {
201 struct mimetypes_entry *entry = item->value;
203 done_mimetypes_entry(entry);
207 free_hash(&mimetypes_map);
210 static int
211 change_hook_mimetypes(struct session *ses, struct option *current, struct option *changed)
213 if (changed == &get_opt_mimetypes(MIMETYPES_PATH)
214 || (changed == &get_opt_mimetypes(MIMETYPES_ENABLE)
215 && !get_mimetypes_enable())) {
216 done_mimetypes(&mimetypes_mime_module);
219 return 0;
222 static void
223 init_mimetypes(struct module *module)
225 static const struct change_hook_info mimetypes_change_hooks[] = {
226 { "mime.mimetypes", change_hook_mimetypes },
227 { NULL, NULL },
230 register_change_hooks(mimetypes_change_hooks);
232 if (get_cmd_opt_bool("anonymous"))
233 get_mimetypes_enable() = 0;
237 static unsigned char *
238 get_content_type_mimetypes(unsigned char *extension)
240 struct hash_item *item;
241 int extensionlen;
243 if (!get_mimetypes_enable()
244 || (!mimetypes_map && !init_mimetypes_map()))
245 return NULL;
247 extension++; /* Skip the leading '.' */
248 extensionlen = strlen(extension);
249 while (extensionlen) {
250 unsigned char *trimmed;
252 /* First the given type is looked up. */
253 item = get_hash_item(mimetypes_map, extension, extensionlen);
255 /* Check list of entries */
256 if (item && item->value) {
257 struct mimetypes_entry *entry = item->value;
259 return stracpy(entry->content_type);
262 /* Try to trim the extension from the left. */
263 trimmed = strchr(extension, '.');
264 if (!trimmed)
265 break;
267 extensionlen -= trimmed - extension + 1;
268 extension = trimmed + 1;
271 return NULL;
274 const struct mime_backend mimetypes_mime_backend = {
275 /* get_content_type: */ get_content_type_mimetypes,
276 /* get_mime_handler: */ NULL,
279 struct module mimetypes_mime_module = struct_module(
280 /* name: */ N_("Mimetypes files"),
281 /* options: */ mimetypes_options,
282 /* hooks: */ NULL,
283 /* submodules: */ NULL,
284 /* data: */ NULL,
285 /* init: */ init_mimetypes,
286 /* done: */ done_mimetypes