+typedef struct {
+ int size;
+ int count;
+ int *elems;
+} TableFilter;
+
+typedef struct {
+ char *name;
+ char *guid;
+ TableFilter types;
+ TableFilter fields;
+ TableFilter methods;
+} ImageFilter;
+
+static GList *filter_list = NULL;
+static ImageFilter *cur_filter = NULL;
+
+static void
+setup_filter (MonoImage *image)
+{
+ ImageFilter *ifilter;
+ GList *item;
+ const char *name = mono_image_get_name (image);
+
+ for (item = filter_list; item; item = item->next) {
+ ifilter = item->data;
+ if (strcmp (ifilter->name, name) == 0) {
+ cur_filter = ifilter;
+ return;
+ }
+ }
+ cur_filter = NULL;
+}
+
+static int
+int_cmp (const void *e1, const void *e2)
+{
+ const int *i1 = e1;
+ const int *i2 = e2;
+ return *i1 - *i2;
+}
+
+static gboolean
+table_includes (TableFilter *tf, int idx)
+{
+ if (!tf->count)
+ return FALSE;
+ return bsearch (&idx, tf->elems, tf->count, sizeof (int), int_cmp) != NULL;
+}
+
+static gboolean
+should_include_type (int idx)
+{
+ if (!cur_filter)
+ return TRUE;
+ return table_includes (&cur_filter->types, idx);
+}
+
+static gboolean
+should_include_method (int idx)
+{
+ if (!cur_filter)
+ return TRUE;
+ return table_includes (&cur_filter->methods, idx);
+}
+
+static gboolean
+should_include_field (int idx)
+{
+ if (!cur_filter)
+ return TRUE;
+ return table_includes (&cur_filter->fields, idx);
+}
+
+static ImageFilter*
+add_filter (const char *name)
+{
+ ImageFilter *ifilter;
+ GList *item;
+
+ for (item = filter_list; item; item = item->next) {
+ ifilter = item->data;
+ if (strcmp (ifilter->name, name) == 0)
+ return ifilter;
+ }
+ ifilter = g_new0 (ImageFilter, 1);
+ ifilter->name = g_strdup (name);
+ filter_list = g_list_prepend (filter_list, ifilter);
+ return ifilter;
+}
+
+static void
+add_item (TableFilter *tf, int val)
+{
+ if (tf->count >= tf->size) {
+ if (!tf->size) {
+ tf->size = 8;
+ tf->elems = g_malloc (sizeof (int) * tf->size);
+ } else {
+ tf->size *= 2;
+ tf->elems = g_realloc (tf->elems, sizeof (int) * tf->size);
+ }
+ }
+ tf->elems [tf->count++] = val;
+}
+
+static void
+sort_filter_elems (void)
+{
+ ImageFilter *ifilter;
+ GList *item;
+
+ for (item = filter_list; item; item = item->next) {
+ ifilter = item->data;
+ qsort (ifilter->types.elems, ifilter->types.count, sizeof (int), int_cmp);
+ qsort (ifilter->fields.elems, ifilter->fields.count, sizeof (int), int_cmp);
+ qsort (ifilter->methods.elems, ifilter->methods.count, sizeof (int), int_cmp);
+ }
+}
+
+static void
+load_filter (const char* filename)
+{
+ FILE *file;
+ char buf [1024];
+ char *p, *s, *endptr;
+ int line = 0;
+ ImageFilter *ifilter = NULL;
+ int value = 0;
+
+ if (!(file = fopen (filename, "r"))) {
+ g_print ("Cannot open filter file '%s'\n", filename);
+ exit (1);
+ }
+ while (fgets (buf, sizeof (buf), file) != NULL) {
+ ++line;
+ s = buf;
+ while (*s && g_ascii_isspace (*s)) ++s;
+ switch (*s) {
+ case 0:
+ case '#':
+ break;
+ case '[':
+ p = strchr (s, ']');
+ if (!p)
+ g_error ("No matching ']' in filter at line %d\n", line);
+ *p = 0;
+ ifilter = add_filter (s + 1);
+ break;
+ case 'T':
+ if (!ifilter)
+ g_error ("Invalid format in filter at line %d\n", line);
+ if ((s [1] != ':') || !(value = strtol (s + 2, &endptr, 0)) || (endptr == s + 2))
+ g_error ("Invalid type number in filter at line %d\n", line);
+ add_item (&ifilter->types, value);
+ break;
+ case 'M':
+ if (!ifilter)
+ g_error ("Invalid format in filter at line %d\n", line);
+ if ((s [1] != ':') || !(value = strtol (s + 2, &endptr, 0)) || (endptr == s + 2))
+ g_error ("Invalid method number in filter at line %d\n", line);
+ add_item (&ifilter->methods, value);
+ break;
+ case 'F':
+ if (!ifilter)
+ g_error ("Invalid format in filter at line %d\n", line);
+ if ((s [1] != ':') || !(value = strtol (s + 2, &endptr, 0)) || (endptr == s + 2))
+ g_error ("Invalid field number in filter at line %d\n", line);
+ add_item (&ifilter->fields, value);
+ break;
+ default:
+ g_error ("Invalid format in filter at line %d\n", line);
+ }
+ }
+ fclose (file);
+ sort_filter_elems ();
+}
+
+
+static gboolean
+try_load_from (MonoAssembly **assembly, const gchar *path1, const gchar *path2,
+ const gchar *path3, const gchar *path4, gboolean refonly)
+{
+ gchar *fullpath;
+
+ *assembly = NULL;
+ fullpath = g_build_filename (path1, path2, path3, path4, NULL);
+ if (g_file_test (fullpath, G_FILE_TEST_IS_REGULAR))
+ *assembly = mono_assembly_open_full (fullpath, NULL, refonly);
+
+ g_free (fullpath);
+ return (*assembly != NULL);
+}
+
+static MonoAssembly *
+real_load (gchar **search_path, const gchar *culture, const gchar *name, gboolean refonly)
+{
+ MonoAssembly *result = NULL;
+ gchar **path;
+ gchar *filename;
+ const gchar *local_culture;
+ gint len;
+
+ if (!culture || *culture == '\0') {
+ local_culture = "";
+ } else {
+ local_culture = culture;
+ }
+
+ filename = g_strconcat (name, ".dll", NULL);
+ len = strlen (filename);
+
+ for (path = search_path; *path; path++) {
+ if (**path == '\0')
+ continue; /* Ignore empty ApplicationBase */
+
+ /* See test cases in bug #58992 and bug #57710 */
+ /* 1st try: [culture]/[name].dll (culture may be empty) */
+ strcpy (filename + len - 4, ".dll");
+ if (try_load_from (&result, *path, local_culture, "", filename, refonly))
+ break;
+
+ /* 2nd try: [culture]/[name].exe (culture may be empty) */
+ strcpy (filename + len - 4, ".exe");
+ if (try_load_from (&result, *path, local_culture, "", filename, refonly))
+ break;
+
+ /* 3rd try: [culture]/[name]/[name].dll (culture may be empty) */
+ strcpy (filename + len - 4, ".dll");
+ if (try_load_from (&result, *path, local_culture, name, filename, refonly))
+ break;
+
+ /* 4th try: [culture]/[name]/[name].exe (culture may be empty) */
+ strcpy (filename + len - 4, ".exe");
+ if (try_load_from (&result, *path, local_culture, name, filename, refonly))
+ break;
+ }
+
+ g_free (filename);
+ return result;
+}
+
+/*
+ * Try to load referenced assemblies from assemblies_path.
+ */
+static MonoAssembly *
+monodis_preload (MonoAssemblyName *aname,
+ gchar **assemblies_path,
+ gpointer user_data)
+{
+ MonoAssembly *result = NULL;
+ gboolean refonly = GPOINTER_TO_UINT (user_data);
+
+ if (assemblies_path && assemblies_path [0] != NULL) {
+ result = real_load (assemblies_path, aname->culture, aname->name, refonly);
+ }
+
+ return result;
+}
+
+