copying the latest Sys.Web.Services from trunk.
[mono.git] / mono / metadata / mono-config.c
1 /*
2  * mono-config.c
3  *
4  * Runtime and assembly configuration file support routines.
5  *
6  * Author: Paolo Molaro (lupus@ximian.com)
7  *
8  * (C) 2002 Ximian, Inc.
9  */
10 #include "config.h"
11 #include <glib.h>
12 #include <string.h>
13
14 #include "mono/metadata/assembly.h"
15 #include "mono/metadata/loader.h"
16 #include "mono/metadata/mono-config.h"
17 #include "mono/metadata/metadata-internals.h"
18 #include "mono/utils/mono-logger.h"
19
20 static void start_element (GMarkupParseContext *context, 
21                            const gchar         *element_name,
22                            const gchar        **attribute_names,
23                            const gchar        **attribute_values,
24                            gpointer             user_data,
25                            GError             **error);
26
27 static void end_element   (GMarkupParseContext *context,
28                            const gchar         *element_name,
29                            gpointer             user_data,
30                            GError             **error);
31
32 static void parse_text    (GMarkupParseContext *context,
33                            const gchar         *text,
34                            gsize                text_len,
35                            gpointer             user_data,
36                            GError             **error);
37
38 static void passthrough   (GMarkupParseContext *context,
39                            const gchar         *text,
40                            gsize                text_len,
41                            gpointer             user_data,
42                            GError             **error);
43
44 static void parse_error   (GMarkupParseContext *context,
45                            GError              *error,
46                            gpointer             user_data);
47
48 static const GMarkupParser 
49 mono_parser = {
50         start_element,
51         end_element,
52         parse_text,
53         passthrough,
54         parse_error
55 };
56
57 static GHashTable *config_handlers;
58
59 /* when this interface is stable, export it. */
60 typedef struct MonoParseHandler MonoParseHandler;
61
62 struct MonoParseHandler {
63         const char *element_name;
64         void*(*init)   (MonoImage *assembly);
65         void (*start)  (gpointer user_data, const gchar *name,
66                         const gchar **attributes,
67                         const gchar **values);
68         void (*text)   (gpointer user_data, const char *text, gsize test_len);
69         void (*end)    (gpointer user_data, const char *name);
70         void (*finish) (gpointer user_data);
71 };
72
73 typedef struct {
74         MonoParseHandler *current;
75         void *user_data;
76         MonoImage *assembly;
77         int inited;
78 } ParseState;
79
80 static void start_element (GMarkupParseContext *context, 
81                            const gchar         *element_name,
82                            const gchar        **attribute_names,
83                            const gchar        **attribute_values,
84                            gpointer             user_data,
85                            GError             **error)
86 {
87         ParseState *state = user_data;
88         if (!state->current) {
89                 state->current = g_hash_table_lookup (config_handlers, element_name);
90                 if (state->current && state->current->init)
91                         state->user_data = state->current->init (state->assembly);
92         }
93         if (state->current && state->current->start)
94                 state->current->start (state->user_data, element_name, attribute_names, attribute_values);
95 }
96
97 static void end_element   (GMarkupParseContext *context,
98                            const gchar         *element_name,
99                            gpointer             user_data,
100                            GError             **error)
101 {
102         ParseState *state = user_data;
103         if (state->current) {
104                 if (state->current->end)
105                         state->current->end (state->user_data, element_name);
106                 if (strcmp (state->current->element_name, element_name) == 0) {
107                         if (state->current->finish)
108                                 state->current->finish (state->user_data);
109                         state->current = NULL;
110                         state->user_data = NULL;
111                 }
112         }
113 }
114
115 static void parse_text    (GMarkupParseContext *context,
116                            const gchar         *text,
117                            gsize                text_len,
118                            gpointer             user_data,
119                            GError             **error)
120 {
121         ParseState *state = user_data;
122         if (state->current && state->current->text)
123                 state->current->text (state->user_data, text, text_len);
124 }
125
126 static void passthrough   (GMarkupParseContext *context,
127                            const gchar         *text,
128                            gsize                text_len,
129                            gpointer             user_data,
130                            GError             **error)
131 {
132         /* do nothing */
133 }
134
135 static void parse_error   (GMarkupParseContext *context,
136                            GError              *error,
137                            gpointer             user_data)
138 {
139 }
140
141 typedef struct {
142         char *dll;
143         char *target;
144         MonoImage *assembly;
145 } DllInfo;
146
147 static void*
148 dllmap_init (MonoImage *assembly) {
149         DllInfo *info = g_new0 (DllInfo, 1);
150         info->assembly = assembly;
151         return info;
152 }
153
154 static void
155 dllmap_start (gpointer user_data, 
156               const gchar         *element_name,
157               const gchar        **attribute_names,
158               const gchar        **attribute_values)
159 {
160         int i;
161         DllInfo *info = user_data;
162         
163         if (strcmp (element_name, "dllmap") == 0) {
164                 g_free (info->dll);
165                 g_free (info->target);
166                 info->dll = info->target = NULL;
167                 for (i = 0; attribute_names [i]; ++i) {
168                         if (strcmp (attribute_names [i], "dll") == 0)
169                                 info->dll = g_strdup (attribute_values [i]);
170                         else if (strcmp (attribute_names [i], "target") == 0)
171                                 info->target = g_strdup (attribute_values [i]);
172                 }
173                 mono_dllmap_insert (info->assembly, info->dll, NULL, info->target, NULL);
174         } else if (strcmp (element_name, "dllentry") == 0) {
175                 const char *name = NULL, *target = NULL, *dll = NULL;
176                 for (i = 0; attribute_names [i]; ++i) {
177                         if (strcmp (attribute_names [i], "dll") == 0)
178                                 dll = attribute_values [i];
179                         else if (strcmp (attribute_names [i], "target") == 0)
180                                 target = attribute_values [i];
181                         else if (strcmp (attribute_names [i], "name") == 0)
182                                 name = attribute_values [i];
183                 }
184                 if (!dll)
185                         dll = info->dll;
186                 mono_dllmap_insert (info->assembly, info->dll, name, dll, target);
187         }
188 }
189
190 static void
191 dllmap_finish (gpointer user_data)
192 {
193         DllInfo *info = user_data;
194
195         g_free (info->dll);
196         g_free (info->target);
197         g_free (info);
198 }
199
200 static const MonoParseHandler
201 dllmap_handler = {
202         "dllmap",
203         dllmap_init,
204         dllmap_start,
205         NULL, /* text */
206         NULL, /* end */
207         dllmap_finish
208 };
209
210 static int inited = 0;
211
212 static void
213 mono_config_init (void)
214 {
215         inited = 1;
216         config_handlers = g_hash_table_new (g_str_hash, g_str_equal);
217         g_hash_table_insert (config_handlers, (gpointer) dllmap_handler.element_name, (gpointer) &dllmap_handler);
218 }
219
220 /* FIXME: error handling */
221
222 static void
223 mono_config_parse_xml_with_context (ParseState *state, const char *text, gsize len)
224 {
225         GMarkupParseContext *context;
226
227         if (!inited)
228                 mono_config_init ();
229
230         context = g_markup_parse_context_new (&mono_parser, 0, state, NULL);
231         if (g_markup_parse_context_parse (context, text, len, NULL)) {
232                 g_markup_parse_context_end_parse (context, NULL);
233         }
234         g_markup_parse_context_free (context);
235 }
236
237 /* If assembly is NULL, parse in the global context */
238 static int
239 mono_config_parse_file_with_context (ParseState *state, const char *filename)
240 {
241         char *text;
242         gsize len;
243
244         mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_CONFIG,
245                         "Config attempting to parse: '%s'.", filename);
246
247         if (!g_file_get_contents (filename, &text, &len, NULL))
248                 return 0;
249         mono_config_parse_xml_with_context (state, text, len);
250         g_free (text);
251         return 1;
252 }
253
254 /**
255  * mono_config_parse_memory:
256  * @buffer: a pointer to an string XML representation of the configuration
257  *
258  * Parses the configuration from a buffer
259  */
260 void
261 mono_config_parse_memory (const char *buffer)
262 {
263         ParseState state = {NULL};
264         
265         mono_config_parse_xml_with_context (&state, buffer, strlen (buffer));
266 }
267
268 static void
269 mono_config_parse_file (const char *filename)
270 {
271         ParseState state = {NULL};
272         mono_config_parse_file_with_context (&state, filename);
273 }
274
275 /* 
276  * use the equivalent lookup code from the GAC when available.
277  * Depending on state, this should give something like:
278  *      aname/version-pubtoken/
279  *      aname/version/
280  *      aname
281  */
282 static char*
283 get_assembly_filename (MonoImage *image, int state)
284 {
285         switch (state) {
286         case 0:
287                 return g_strdup (mono_image_get_name (image));
288         default:
289                 return NULL;
290         }
291 }
292
293 typedef struct _BundledConfig BundledConfig;
294
295 struct _BundledConfig {
296         BundledConfig *next;
297         const char* aname;
298         const char* config_xml;
299 };
300
301 static BundledConfig *bundled_configs = NULL;
302
303 void
304 mono_register_config_for_assembly (const char* assembly_name, const char* config_xml)
305 {
306         BundledConfig *bconfig;
307
308         bconfig = g_new0 (BundledConfig, 1);
309         bconfig->aname = assembly_name;
310         bconfig->config_xml = config_xml;
311         bconfig->next = bundled_configs;
312         bundled_configs = bconfig;
313 }
314
315 void 
316 mono_config_for_assembly (MonoImage *assembly)
317 {
318         ParseState state = {NULL};
319         int got_it = 0, i;
320         char *aname, *cfg, *cfg_name;
321         const char *home;
322         BundledConfig *bconfig;
323         
324         state.assembly = assembly;
325
326         for (bconfig = bundled_configs; bconfig; bconfig = bconfig->next) {
327                 if (bconfig->aname && strcmp (bconfig->aname, assembly->module_name) == 0)
328                         mono_config_parse_xml_with_context (&state, bconfig->config_xml, strlen (bconfig->config_xml));
329         }
330
331         cfg_name = g_strdup_printf ("%s.config", mono_image_get_filename (assembly));
332         mono_config_parse_file_with_context (&state, cfg_name);
333         g_free (cfg_name);
334
335         cfg_name = g_strdup_printf ("%s.config", mono_image_get_name (assembly));
336
337         home = g_get_home_dir ();
338
339         for (i = 0; (aname = get_assembly_filename (assembly, i)) != NULL; ++i) {
340                 cfg = g_build_filename (mono_get_config_dir (), "mono", "assemblies", aname, cfg_name, NULL);
341                 got_it += mono_config_parse_file_with_context (&state, cfg);
342                 g_free (cfg);
343
344 #ifndef PLATFORM_WIN32
345                 cfg = g_build_filename (home, ".mono", "assemblies", aname, cfg_name, NULL);
346                 got_it += mono_config_parse_file_with_context (&state, cfg);
347                 g_free (cfg);
348 #endif
349                 g_free (aname);
350                 if (got_it)
351                         break;
352         }
353         g_free (cfg_name);
354 }
355
356 /*
357  * Pass a NULL filename to parse the default config files
358  * (or the file in the MONO_CONFIG env var).
359  */
360 void
361 mono_config_parse (const char *filename) {
362         const char *home;
363         char *mono_cfg;
364 #ifndef PLATFORM_WIN32
365         char *user_cfg;
366 #endif
367
368         if (filename) {
369                 mono_config_parse_file (filename);
370                 return;
371         }
372
373         home = g_getenv ("MONO_CONFIG");
374         if (home) {
375                 mono_config_parse_file (home);
376                 return;
377         }
378
379         mono_cfg = g_build_filename (mono_get_config_dir (), "mono", "config", NULL);
380         mono_config_parse_file (mono_cfg);
381         g_free (mono_cfg);
382
383 #ifndef PLATFORM_WIN32
384         home = g_get_home_dir ();
385         user_cfg = g_strconcat (home, G_DIR_SEPARATOR_S, ".mono/config", NULL);
386         mono_config_parse_file (user_cfg);
387         g_free (user_cfg);
388 #endif
389 }
390
391 static const char *mono_cfg_dir = NULL;
392
393 static void    
394 mono_install_get_config_dir (void)
395 {
396 #ifdef PLATFORM_WIN32
397   gchar *prefix;
398 #endif
399
400   mono_cfg_dir = g_getenv ("MONO_CFG_DIR");
401
402   if (!mono_cfg_dir) {
403 #ifndef PLATFORM_WIN32
404     mono_cfg_dir = MONO_CFG_DIR;
405 #else
406     prefix = g_path_get_dirname (mono_assembly_getrootdir ());
407     mono_cfg_dir = g_build_filename (prefix, "etc", NULL);
408     g_free (prefix);
409 #endif
410   }
411 }
412
413 const char* 
414 mono_get_config_dir (void)
415 {
416         if (!mono_cfg_dir)
417                 mono_install_get_config_dir ();
418         return mono_cfg_dir;
419 }
420