2009-12-09 Rodrigo Kumpera <rkumpera@novell.com>
[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  * Copyright 2002-2003 Ximian, Inc (http://www.ximian.com)
9  * Copyright 2004-2009 Novell, Inc (http://www.novell.com)
10  */
11 #include "config.h"
12 #include <glib.h>
13 #include <string.h>
14
15 #include "mono/metadata/assembly.h"
16 #include "mono/metadata/loader.h"
17 #include "mono/metadata/mono-config.h"
18 #include "mono/metadata/metadata-internals.h"
19 #include "mono/metadata/object-internals.h"
20 #include "mono/utils/mono-logger.h"
21
22 #if defined(TARGET_PS3)
23 #define CONFIG_OS "CellOS"
24 #elif defined(__linux__)
25 #define CONFIG_OS "linux"
26 #elif defined(__APPLE__)
27 #define CONFIG_OS "osx"
28 #elif defined(sun)
29 #define CONFIG_OS "solaris"
30 #elif defined(__FreeBSD__)
31 #define CONFIG_OS "freebsd"
32 #elif defined(__NetBSD__)
33 #define CONFIG_OS "netbsd"
34 #elif defined(__OpenBSD__)
35 #define CONFIG_OS "openbsd"
36 #elif defined(__WIN32__)
37 #define CONFIG_OS "windows"
38 #elif defined(_IBMR2)
39 #define CONFIG_OS "aix"
40 #elif defined(__hpux)
41 #define CONFIG_OS "hpux"
42 #else
43 #warning Unknown operating system
44 #define CONFIG_OS "unknownOS"
45 #endif
46
47 #ifndef CONFIG_CPU
48 #if defined(__i386__)
49 #define CONFIG_CPU "x86"
50 #define CONFIG_WORDSIZE "32"
51 #elif defined(__x86_64__)
52 #define CONFIG_CPU "x86-64"
53 #define CONFIG_WORDSIZE "64"
54 #elif defined(sparc) || defined(__sparc__)
55 #define CONFIG_CPU "sparc"
56 #define CONFIG_WORDSIZE "32"
57 #elif defined(__ppc64__) || defined(__powerpc64__) || defined(TARGET_POWERPC)
58 #define CONFIG_WORDSIZE "64"
59 #ifdef __mono_ppc_ilp32__ 
60 #   define CONFIG_CPU "ppc64ilp32"
61 #else
62 #   define CONFIG_CPU "ppc64"
63 #endif
64 #elif defined(__ppc__) || defined(__powerpc__)
65 #define CONFIG_CPU "ppc"
66 #define CONFIG_WORDSIZE "32"
67 #elif defined(__s390x__)
68 #define CONFIG_CPU "s390x"
69 #define CONFIG_WORDSIZE "64"
70 #elif defined(__s390__)
71 #define CONFIG_CPU "s390"
72 #define CONFIG_WORDSIZE "32"
73 #elif defined(__arm__)
74 #define CONFIG_CPU "arm"
75 #define CONFIG_WORDSIZE "32"
76 #elif defined(__ia64__)
77 #define CONFIG_CPU "ia64"
78 #define CONFIG_WORDSIZE "64"
79 #elif defined(__alpha__)
80 #define CONFIG_CPU "alpha"
81 #define CONFIG_WORDSIZE "64"
82 #elif defined(hppa) || defined(__hppa__)
83 #define CONFIG_CPU "hppa"
84 #define CONFIG_WORDSIZE "32"
85 #elif defined(mips) || defined(__mips) || defined(_mips)
86 #define CONFIG_CPU "mips"
87 #define CONFIG_WORDSIZE "32"
88 #else
89 #error Unknown CPU
90 #define CONFIG_CPU "unknownCPU"
91 #endif
92 #endif
93
94 static void start_element (GMarkupParseContext *context, 
95                            const gchar         *element_name,
96                            const gchar        **attribute_names,
97                            const gchar        **attribute_values,
98                            gpointer             user_data,
99                            GError             **error);
100
101 static void end_element   (GMarkupParseContext *context,
102                            const gchar         *element_name,
103                            gpointer             user_data,
104                            GError             **error);
105
106 static void parse_text    (GMarkupParseContext *context,
107                            const gchar         *text,
108                            gsize                text_len,
109                            gpointer             user_data,
110                            GError             **error);
111
112 static void passthrough   (GMarkupParseContext *context,
113                            const gchar         *text,
114                            gsize                text_len,
115                            gpointer             user_data,
116                            GError             **error);
117
118 static void parse_error   (GMarkupParseContext *context,
119                            GError              *error,
120                            gpointer             user_data);
121
122 static const GMarkupParser 
123 mono_parser = {
124         start_element,
125         end_element,
126         parse_text,
127         passthrough,
128         parse_error
129 };
130
131 static GHashTable *config_handlers;
132
133 /* when this interface is stable, export it. */
134 typedef struct MonoParseHandler MonoParseHandler;
135
136 struct MonoParseHandler {
137         const char *element_name;
138         void*(*init)   (MonoImage *assembly);
139         void (*start)  (gpointer user_data, const gchar *name,
140                         const gchar **attributes,
141                         const gchar **values);
142         void (*text)   (gpointer user_data, const char *text, gsize test_len);
143         void (*end)    (gpointer user_data, const char *name);
144         void (*finish) (gpointer user_data);
145 };
146
147 typedef struct {
148         MonoParseHandler *current;
149         void *user_data;
150         MonoImage *assembly;
151         int inited;
152 } ParseState;
153
154 static void start_element (GMarkupParseContext *context, 
155                            const gchar         *element_name,
156                            const gchar        **attribute_names,
157                            const gchar        **attribute_values,
158                            gpointer             user_data,
159                            GError             **error)
160 {
161         ParseState *state = user_data;
162         if (!state->current) {
163                 state->current = g_hash_table_lookup (config_handlers, element_name);
164                 if (state->current && state->current->init)
165                         state->user_data = state->current->init (state->assembly);
166         }
167         if (state->current && state->current->start)
168                 state->current->start (state->user_data, element_name, attribute_names, attribute_values);
169 }
170
171 static void end_element   (GMarkupParseContext *context,
172                            const gchar         *element_name,
173                            gpointer             user_data,
174                            GError             **error)
175 {
176         ParseState *state = user_data;
177         if (state->current) {
178                 if (state->current->end)
179                         state->current->end (state->user_data, element_name);
180                 if (strcmp (state->current->element_name, element_name) == 0) {
181                         if (state->current->finish)
182                                 state->current->finish (state->user_data);
183                         state->current = NULL;
184                         state->user_data = NULL;
185                 }
186         }
187 }
188
189 static void parse_text    (GMarkupParseContext *context,
190                            const gchar         *text,
191                            gsize                text_len,
192                            gpointer             user_data,
193                            GError             **error)
194 {
195         ParseState *state = user_data;
196         if (state->current && state->current->text)
197                 state->current->text (state->user_data, text, text_len);
198 }
199
200 static void passthrough   (GMarkupParseContext *context,
201                            const gchar         *text,
202                            gsize                text_len,
203                            gpointer             user_data,
204                            GError             **error)
205 {
206         /* do nothing */
207 }
208
209 static void parse_error   (GMarkupParseContext *context,
210                            GError              *error,
211                            gpointer             user_data)
212 {
213         ParseState *state = user_data;
214         const gchar *msg;
215         const gchar *filename;
216
217         filename = state && state->user_data ? (gchar *) state->user_data : "<unknown>";
218         msg = error && error->message ? error->message : "";
219         g_warning ("Error parsing %s: %s", filename, msg);
220 }
221
222 static int
223 arch_matches (const char* arch, const char *value)
224 {
225         char **splitted, **p;
226         int found = FALSE;
227         if (value [0] == '!')
228                 return !arch_matches (arch, value + 1);
229         p = splitted = g_strsplit (value, ",", 0);
230         while (*p) {
231                 if (strcmp (arch, *p) == 0) {
232                         found = TRUE;
233                         break;
234                 }
235                 p++;
236         }
237         g_strfreev (splitted);
238         return found;
239 }
240
241 typedef struct {
242         char *dll;
243         char *target;
244         int ignore;
245         MonoImage *assembly;
246 } DllInfo;
247
248 static void*
249 dllmap_init (MonoImage *assembly) {
250         DllInfo *info = g_new0 (DllInfo, 1);
251         info->assembly = assembly;
252         return info;
253 }
254
255 static void
256 dllmap_start (gpointer user_data, 
257               const gchar         *element_name,
258               const gchar        **attribute_names,
259               const gchar        **attribute_values)
260 {
261         int i;
262         DllInfo *info = user_data;
263         
264         if (strcmp (element_name, "dllmap") == 0) {
265                 g_free (info->dll);
266                 g_free (info->target);
267                 info->dll = info->target = NULL;
268                 info->ignore = FALSE;
269                 for (i = 0; attribute_names [i]; ++i) {
270                         if (strcmp (attribute_names [i], "dll") == 0)
271                                 info->dll = g_strdup (attribute_values [i]);
272                         else if (strcmp (attribute_names [i], "target") == 0)
273                                 info->target = g_strdup (attribute_values [i]);
274                         else if (strcmp (attribute_names [i], "os") == 0 && !arch_matches (CONFIG_OS, attribute_values [i]))
275                                 info->ignore = TRUE;
276                         else if (strcmp (attribute_names [i], "cpu") == 0 && !arch_matches (CONFIG_CPU, attribute_values [i]))
277                                 info->ignore = TRUE;
278                         else if (strcmp (attribute_names [i], "wordsize") == 0 && !arch_matches (CONFIG_WORDSIZE, attribute_values [i]))
279                                 info->ignore = TRUE;
280                 }
281                 if (!info->ignore)
282                         mono_dllmap_insert (info->assembly, info->dll, NULL, info->target, NULL);
283         } else if (strcmp (element_name, "dllentry") == 0) {
284                 const char *name = NULL, *target = NULL, *dll = NULL;
285                 int ignore = FALSE;
286                 for (i = 0; attribute_names [i]; ++i) {
287                         if (strcmp (attribute_names [i], "dll") == 0)
288                                 dll = attribute_values [i];
289                         else if (strcmp (attribute_names [i], "target") == 0)
290                                 target = attribute_values [i];
291                         else if (strcmp (attribute_names [i], "name") == 0)
292                                 name = attribute_values [i];
293                         else if (strcmp (attribute_names [i], "os") == 0 && !arch_matches (CONFIG_OS, attribute_values [i]))
294                                 ignore = TRUE;
295                         else if (strcmp (attribute_names [i], "cpu") == 0 && !arch_matches (CONFIG_CPU, attribute_values [i]))
296                                 ignore = TRUE;
297                         else if (strcmp (attribute_names [i], "wordsize") == 0 && !arch_matches (CONFIG_WORDSIZE, attribute_values [i]))
298                                 ignore = TRUE;
299                 }
300                 if (!dll)
301                         dll = info->dll;
302                 if (!info->ignore && !ignore)
303                         mono_dllmap_insert (info->assembly, info->dll, name, dll, target);
304         }
305 }
306
307 static void
308 dllmap_finish (gpointer user_data)
309 {
310         DllInfo *info = user_data;
311
312         g_free (info->dll);
313         g_free (info->target);
314         g_free (info);
315 }
316
317 static const MonoParseHandler
318 dllmap_handler = {
319         "dllmap",
320         dllmap_init,
321         dllmap_start,
322         NULL, /* text */
323         NULL, /* end */
324         dllmap_finish
325 };
326
327 static void
328 legacyUEP_start (gpointer user_data, 
329               const gchar         *element_name,
330               const gchar        **attribute_names,
331               const gchar        **attribute_values) {
332         if ((strcmp (element_name, "legacyUnhandledExceptionPolicy") == 0) &&
333                         (attribute_names [0] != NULL) &&
334                         (strcmp (attribute_names [0], "enabled") == 0)) {
335                 if ((strcmp (attribute_values [0], "1") == 0) ||
336                                 (g_ascii_strcasecmp (attribute_values [0], "true") == 0)) {
337                         mono_runtime_unhandled_exception_policy_set (MONO_UNHANDLED_POLICY_LEGACY);
338                 }
339         }
340 }
341
342 static const MonoParseHandler
343 legacyUEP_handler = {
344         "legacyUnhandledExceptionPolicy",
345         NULL, /* init */
346         legacyUEP_start,
347         NULL, /* text */
348         NULL, /* end */
349         NULL, /* finish */
350 };
351
352 static int inited = 0;
353
354 static void
355 mono_config_init (void)
356 {
357         inited = 1;
358         config_handlers = g_hash_table_new (g_str_hash, g_str_equal);
359         g_hash_table_insert (config_handlers, (gpointer) dllmap_handler.element_name, (gpointer) &dllmap_handler);
360         g_hash_table_insert (config_handlers, (gpointer) legacyUEP_handler.element_name, (gpointer) &legacyUEP_handler);
361 }
362
363 /* FIXME: error handling */
364
365 static void
366 mono_config_parse_xml_with_context (ParseState *state, const char *text, gsize len)
367 {
368         GMarkupParseContext *context;
369
370         if (!inited)
371                 mono_config_init ();
372
373         context = g_markup_parse_context_new (&mono_parser, 0, state, NULL);
374         if (g_markup_parse_context_parse (context, text, len, NULL)) {
375                 g_markup_parse_context_end_parse (context, NULL);
376         }
377         g_markup_parse_context_free (context);
378 }
379
380 /* If assembly is NULL, parse in the global context */
381 static int
382 mono_config_parse_file_with_context (ParseState *state, const char *filename)
383 {
384         gchar *text;
385         gsize len;
386         gint offset;
387
388         mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_CONFIG,
389                         "Config attempting to parse: '%s'.", filename);
390
391         if (!g_file_get_contents (filename, &text, &len, NULL))
392                 return 0;
393
394
395         offset = 0;
396         if (len > 3 && text [0] == '\xef' && text [1] == (gchar) '\xbb' && text [2] == '\xbf')
397                 offset = 3; /* Skip UTF-8 BOM */
398         if (state->user_data == NULL)
399                 state->user_data = (gpointer) filename;
400         mono_config_parse_xml_with_context (state, text + offset, len - offset);
401         g_free (text);
402         return 1;
403 }
404
405 /**
406  * mono_config_parse_memory:
407  * @buffer: a pointer to an string XML representation of the configuration
408  *
409  * Parses the configuration from a buffer
410  */
411 void
412 mono_config_parse_memory (const char *buffer)
413 {
414         ParseState state = {NULL};
415
416         state.user_data = (gpointer) "<buffer>";
417         mono_config_parse_xml_with_context (&state, buffer, strlen (buffer));
418 }
419
420 static void
421 mono_config_parse_file (const char *filename)
422 {
423         ParseState state = {NULL};
424         state.user_data = (gpointer) filename;
425         mono_config_parse_file_with_context (&state, filename);
426 }
427
428 /* 
429  * use the equivalent lookup code from the GAC when available.
430  * Depending on state, this should give something like:
431  *      aname/version-pubtoken/
432  *      aname/version/
433  *      aname
434  */
435 static char*
436 get_assembly_filename (MonoImage *image, int state)
437 {
438         switch (state) {
439         case 0:
440                 return g_strdup (mono_image_get_name (image));
441         default:
442                 return NULL;
443         }
444 }
445
446 typedef struct _BundledConfig BundledConfig;
447
448 struct _BundledConfig {
449         BundledConfig *next;
450         const char* aname;
451         const char* config_xml;
452 };
453
454 static BundledConfig *bundled_configs = NULL;
455
456 static const char *bundled_machine_config = NULL;
457
458 void
459 mono_register_config_for_assembly (const char* assembly_name, const char* config_xml)
460 {
461         BundledConfig *bconfig;
462
463         bconfig = g_new0 (BundledConfig, 1);
464         bconfig->aname = assembly_name;
465         bconfig->config_xml = config_xml;
466         bconfig->next = bundled_configs;
467         bundled_configs = bconfig;
468 }
469
470 const char *
471 mono_config_string_for_assembly_file (const char *filename)
472 {
473         BundledConfig *bconfig;
474         
475         for (bconfig = bundled_configs; bconfig; bconfig = bconfig->next) {
476                 if (bconfig->aname && strcmp (bconfig->aname, filename) == 0)
477                         return bconfig->config_xml;
478         }
479         return NULL;
480 }
481
482 void 
483 mono_config_for_assembly (MonoImage *assembly)
484 {
485         ParseState state = {NULL};
486         int got_it = 0, i;
487         char *aname, *cfg, *cfg_name;
488         const char *bundled_config;
489         const char *home;
490         
491         state.assembly = assembly;
492
493         bundled_config = mono_config_string_for_assembly_file (assembly->module_name);
494         if (bundled_config) {
495                 state.user_data = (gpointer) "<bundled>";
496                 mono_config_parse_xml_with_context (&state, bundled_config, strlen (bundled_config));
497         }
498
499         cfg_name = g_strdup_printf ("%s.config", mono_image_get_filename (assembly));
500         mono_config_parse_file_with_context (&state, cfg_name);
501         g_free (cfg_name);
502
503         cfg_name = g_strdup_printf ("%s.config", mono_image_get_name (assembly));
504
505         home = g_get_home_dir ();
506
507         for (i = 0; (aname = get_assembly_filename (assembly, i)) != NULL; ++i) {
508                 cfg = g_build_filename (mono_get_config_dir (), "mono", "assemblies", aname, cfg_name, NULL);
509                 got_it += mono_config_parse_file_with_context (&state, cfg);
510                 g_free (cfg);
511
512 #ifdef TARGET_WIN32
513                 cfg = g_build_filename (home, ".mono", "assemblies", aname, cfg_name, NULL);
514                 got_it += mono_config_parse_file_with_context (&state, cfg);
515                 g_free (cfg);
516 #endif
517                 g_free (aname);
518                 if (got_it)
519                         break;
520         }
521         g_free (cfg_name);
522 }
523
524 /**
525  * mono_config_parse:
526  * @filename: the filename to load the configuration variables from.
527  *
528  * Pass a NULL filename to parse the default config files
529  * (or the file in the MONO_CONFIG env var).
530  */
531 void
532 mono_config_parse (const char *filename) {
533         const char *home;
534         char *mono_cfg;
535 #ifndef TARGET_WIN32
536         char *user_cfg;
537 #endif
538
539         if (filename) {
540                 mono_config_parse_file (filename);
541                 return;
542         }
543
544         home = g_getenv ("MONO_CONFIG");
545         if (home) {
546                 mono_config_parse_file (home);
547                 return;
548         }
549
550         mono_cfg = g_build_filename (mono_get_config_dir (), "mono", "config", NULL);
551         mono_config_parse_file (mono_cfg);
552         g_free (mono_cfg);
553
554 #ifndef TARGET_WIN32
555         home = g_get_home_dir ();
556         user_cfg = g_strconcat (home, G_DIR_SEPARATOR_S, ".mono/config", NULL);
557         mono_config_parse_file (user_cfg);
558         g_free (user_cfg);
559 #endif
560 }
561
562 static const char *mono_cfg_dir = NULL;
563
564 /* Invoked during startup */
565 void
566 mono_set_config_dir (const char *dir)
567 {
568         /* If this variable is set, overrides the directory computed */
569         mono_cfg_dir = g_getenv ("MONO_CFG_DIR");
570         if (mono_cfg_dir == NULL)
571                 mono_cfg_dir = g_strdup (dir);
572 }
573
574 const char* 
575 mono_get_config_dir (void)
576 {
577         if (mono_cfg_dir == NULL)
578                 mono_set_dirs (NULL, NULL);
579
580         return mono_cfg_dir;
581 }
582
583 void
584 mono_register_machine_config (const char *config_xml)
585 {
586         bundled_machine_config = config_xml;
587 }
588
589 const char *
590 mono_get_machine_config (void)
591 {
592         return bundled_machine_config;
593 }
594
595 static void
596 publisher_policy_start (gpointer user_data,
597                 const gchar *element_name,
598                 const gchar **attribute_names,
599                 const gchar **attribute_values)
600 {
601         MonoAssemblyBindingInfo *info;
602         int n;
603
604         info = user_data;
605         if (!strcmp (element_name, "assemblyIdentity")) {
606                 for (n = 0; attribute_names [n]; n++) {
607                         const gchar *attribute_name = attribute_names [n];
608                         
609                         if (!strcmp (attribute_name, "name"))
610                                 info->name = g_strdup (attribute_values [n]);
611                         else if (!strcmp (attribute_name, "publicKeyToken")) {
612                                 if (strlen (attribute_values [n]) == MONO_PUBLIC_KEY_TOKEN_LENGTH - 1)
613                                         g_strlcpy ((char *) info->public_key_token, attribute_values [n], MONO_PUBLIC_KEY_TOKEN_LENGTH);
614                         } else if (!strcmp (attribute_name, "culture")) {
615                                 if (!strcmp (attribute_values [n], "neutral"))
616                                         info->culture = g_strdup ("");
617                                 else
618                                         info->culture = g_strdup (attribute_values [n]);
619                         }
620                 }
621         } else if (!strcmp (element_name, "bindingRedirect")) {
622                 for (n = 0; attribute_names [n]; n++) {
623                         const gchar *attribute_name = attribute_names [n];
624
625                         if (!strcmp (attribute_name, "oldVersion")) {
626                                 gchar **numbers, **version, **versions;
627                                 gint major, minor, build, revision;
628
629                                 /* Invalid value */
630                                 if (!strcmp (attribute_values [n], ""))
631                                         return;
632                                 
633                                 versions = g_strsplit (attribute_values [n], "-", 2);
634                                 version = g_strsplit (*versions, ".", 4);
635
636                                 /* We assign the values to gint vars to do the checks */
637                                 numbers = version;
638                                 major = *numbers ? atoi (*numbers++) : -1;
639                                 minor = *numbers ? atoi (*numbers++) : -1;
640                                 build = *numbers ? atoi (*numbers++) : -1;
641                                 revision = *numbers ? atoi (*numbers) : -1;
642                                 g_strfreev (version);
643                                 if (major < 0 || minor < 0 || build < 0 || revision < 0) {
644                                         g_strfreev (versions);
645                                         return;
646                                 }
647
648                                 info->old_version_bottom.major = major;
649                                 info->old_version_bottom.minor = minor;
650                                 info->old_version_bottom.build = build;
651                                 info->old_version_bottom.revision = revision;
652                                 info->has_old_version_bottom = TRUE;
653
654                                 if (!*(versions + 1)) {
655                                         g_strfreev (versions);
656                                         continue;
657                                 }
658                                 
659                                 numbers = version = g_strsplit (*(versions + 1), ".", 4);
660                                 major = *numbers ? atoi (*numbers++) : -1;
661                                 minor = *numbers ? atoi (*numbers++) : -1;
662                                 build = *numbers ? atoi (*numbers++) : -1;
663                                 revision = *numbers ? atoi (*numbers) : 1;
664                                 g_strfreev (version);
665                                 if (major < 0 || minor < 0 || build < 0 || revision < 0) {
666                                         g_strfreev (versions);
667                                         return;
668                                 }
669
670                                 info->old_version_top.major = major;
671                                 info->old_version_top.minor = minor;
672                                 info->old_version_top.build = build;
673                                 info->old_version_top.revision = revision;
674                                 info->has_old_version_top = TRUE;
675
676                                 g_strfreev (versions);
677                         } else if (!strcmp (attribute_name, "newVersion")) {
678                                 gchar **numbers, **version;
679
680                                 /* Invalid value */
681                                 if (!strcmp (attribute_values [n], ""))
682                                         return;
683
684                                 numbers = version = g_strsplit (attribute_values [n], ".", 4);
685                                 info->new_version.major = *numbers ? atoi (*numbers++) : -1;
686                                 info->new_version.minor = *numbers ? atoi (*numbers++) : -1;
687                                 info->new_version.build = *numbers ? atoi (*numbers++) : -1;
688                                 info->new_version.revision = *numbers ? atoi (*numbers) : -1;
689                                 info->has_new_version = TRUE;
690                                 g_strfreev (version);
691                         }
692                 }
693         }
694 }
695
696 static MonoParseHandler
697 publisher_policy_parser = {
698         "", /* We don't need to use declare an xml element */
699         NULL,
700         publisher_policy_start,
701         NULL,
702         NULL,
703         NULL
704 };
705
706 void
707 mono_config_parse_publisher_policy (const gchar *filename, MonoAssemblyBindingInfo *info)
708 {
709         ParseState state = {
710                 &publisher_policy_parser, /* MonoParseHandler */
711                 info, /* user_data */
712                 NULL, /* MonoImage (we don't need it right now)*/
713                 TRUE /* We are already inited */
714         };
715         
716         mono_config_parse_file_with_context (&state, filename);
717 }
718