/* ** vim: ft=c ts=4 sw=4 et */ /*---------------------------------------------------------------------------*/ /* ** Copyright (C) 1995-2006, 2008 The University of Melbourne. ** This file may only be copied under the terms of the GNU General ** Public License - see the file COPYING in the Mercury distribution. */ /* ** File: mdemangle.c ** Author: fjh ** ** A mercury symbol demangler. ** This is used to convert error messages from the linker back ** into a form that users can understand. ** ** This is implemented in C to minimize startup time and memory usage. ** ** BEWARE: ** This code is duplicated in profiler/demangle.m and profiler/mdemangle.m. ** Any changes here will need to be duplicated there and vice versa. */ /* mercury_std.h includes mercury_regs.h, and must precede system headers */ #include "mercury_std.h" #include #include #include #include /* We used this for the size of fixed-length buffers in a few places */ #define MAX_SYMBOL_LENGTH 1000 static void demangle(const char *name); static const char *strip_module_name(char **start_ptr, char *end, const char *special_prefixes[], const char *special_suffixes[]); static MR_bool check_for_suffix(char *start, char *position, const char *suffix, int sizeof_suffix, int *mode_num2); static char *fix_mangled_ascii(char *str, char **end); static MR_bool fix_mangled_special_case(char *str, char **end); static MR_bool find_double_underscore(char **str, char *end); static MR_bool cut_trailing_integer(char *str, char **end, int *num); static MR_bool cut_trailing_underscore_integer(char *str, char **end, int *num); static MR_bool strip_prefix(char **str, const char *prefix); static MR_bool strip_suffix(const char *str, char **end, const char *suffix); static MR_bool strip_leading_integer(char **start_ptr, int *num); /* ** Bloody SunOS 4.x doesn't have memmove()... ** Using memcpy() may not work, but it doesn't really matter ** if the demangler doesn't work 100% correctly on SunOS 4.x. */ #ifndef MR_HAVE_MEMMOVE #define memmove memcpy #endif /* ** This option indicates whether we should output verbose ** explanations of linker error messages. */ MR_bool explain_link_errors = MR_FALSE; /* ** This variable gets set if the symbols MR_grade_* or MR_mercury_grade ** were found. If it gets set, then we print out the error message below. */ char *found_grade_symbol = NULL; const char probably_grade_error[] = "Mercury Linker:\n" "\tNote: the symbol `%s' was mentioned.\n" "\tAny link errors are most likely due to linking together object\n" "\tfiles compiled with different compilation model options.\n" "\tTry doing `mmake clean' and then rebuilding.\n"; int main(int argc, char **argv) { const char *progname = argv[0]; /* We should use getopt_long(), but for one option, that is overkill. */ while (argc > 1 && argv[1][0] == '-') { if (strcmp(argv[1], "-e") == 0 || strcmp(argv[1], "--explain-link-errors") == 0) { explain_link_errors = MR_TRUE; argc--, argv++; } else if (strcmp(argv[1], "--") == 0) { argc--, argv++; break; } else { fprintf(stderr, "%s: unknown option `%s'\n", progname, argv[1]); exit(1); } } if (argc > 1) { int i; /* ** Invoke demangle() on each command line argument. */ for (i = 1; i < argc; i++) { demangle(argv[i]); putchar('\n'); } } else { /* ** Copy stdin to stdout, calling demangle() for every valid ** C identifier in the input. */ for (;;) { char buf[MAX_SYMBOL_LENGTH]; size_t len; int c; len = 0; c = getchar(); while (c != EOF && (isalnum(c) || c == '_')) { if (len >= sizeof(buf) - 1) { break; } buf[len++] = (char) c; c = getchar(); } if (len > 0) { buf[len] = '\0'; demangle(buf); fflush(stdout); } if (c == EOF) { break; } putchar(c); } } if (explain_link_errors && found_grade_symbol) { printf(probably_grade_error, found_grade_symbol); free(found_grade_symbol); } return 0; } /* ** demangle(): ** Convert a mangled Mercury identifier into human-readable form ** and then print it to stdout. */ static void demangle(const char *orig_name) { static const char entry[] = "_entry_"; static const char mercury[] = "mercury__"; static const char func_prefix[] = "fn__"; /* added for functions */ static const char unify1[] = "__Unify___"; static const char unify2[] = "__Unify____"; static const char compare1[] = "__Compare___"; static const char compare2[] = "__Compare____"; static const char index1[] = "__Index___"; static const char index2[] = "__Index____"; static const char introduced[] = "IntroducedFrom__"; static const char deforestation[] = "DeforestationIn__"; static const char accumulator[] = "AccFrom__"; static const char type_spec[] = "TypeSpecOf__"; static const char unused_arg[] = "UnusedArgs__"; static const char pred[] = "pred__"; static const char func[] = "func__"; static const char porf[] = "pred_or_func__"; /* ** XXX This is out-of-date. The compiler now generates names ** such as UnusedArgs__p__[1]. */ static const char ua_suffix[] = "__ua"; /* added by unused_args.m */ static const char ua_suffix2[] = "__uab"; /* added by unused_args.m */ static const char ho_suffix[] = "__ho"; /* added by higher_order.m */ static const char mercury_common[] = "mercury_common_"; static const char mercury_data[] = "mercury_data_"; static const char type_ctor_layout[] = "type_ctor_layout_"; static const char type_ctor_info[] = "type_ctor_info_"; static const char type_ctor_functors[] = "type_ctor_functors_"; static const char base_typeclass_info[] = "base_typeclass_info_"; static const char underscores_base_typeclass_info[] = "__base_typeclass_info_"; static const char common[] = "common"; static const char arity_string[] = "arity"; static const char MR_grade[] = "MR_grade_"; static const char MR_runtime_grade[] = "MR_runtime_grade"; static const char *trailing_context_1[] = { introduced, deforestation, accumulator, type_spec, unused_arg, unify1, compare1, index1, NULL }; static const char *trailing_context_1_hl_suffixes[] = { ua_suffix, ua_suffix2, ho_suffix, NULL }; static const char *trailing_context_2[] = { type_ctor_layout, type_ctor_info, type_ctor_functors, common, NULL }; static const char *trailing_context_3[] = { arity_string, NULL }; char name[MAX_SYMBOL_LENGTH]; char *start = name; const char *module = ""; /* module name */ char *end = name + strlen(orig_name); char *position; /* current position in string */ int mode_num; int mode_num2; int arity; MR_bool high_level = MR_TRUE; MR_bool matched = MR_FALSE; const char *pred_or_func; /* either "predicate" or "function" */ MR_bool unused_args = MR_FALSE; /* does this proc have any unused arguments */ MR_bool unused_args_extra = MR_FALSE; /* __uab suffix rather than __ua */ int unused_args_num = 0; MR_bool higher_order = MR_FALSE; /* has this proc been specialized */ int higher_order_num = 0; int internal = -1; char *name_before_prefixes = NULL; int lambda_line = 0; int lambda_seq_number = 0; char *lambda_pred_name = NULL; char *end_of_lambda_pred_name = NULL; const char *lambda_kind = NULL; enum { ORDINARY, UNIFY, COMPARE, INDEX, LAMBDA, DEFORESTATION, ACCUMULATOR, TYPE_SPEC } category; enum { COMMON, INFO, LAYOUT, FUNCTORS } data_category; const char *class_name; int class_arity; char class_arg_buf[MAX_SYMBOL_LENGTH]; int class_arg_num; const char *class_arg; const char *type_spec_sub; /* ** Copy orig_name to a local buffer which we can modify, ** making sure that we don't overflow the buffer. */ if (strlen(orig_name) >= sizeof(name)) { goto too_long; } strcpy(name, orig_name); /* ** Skip any leading underscore inserted by the C compiler ** (but don't skip it if it came from the `_entry_' prefix). */ if (*start == '_' && strncmp(start, entry, strlen(entry)) != 0) { start++; } /* Check for `MR_grade_*' and `MR_runtime_grade'. */ if (strncmp(start, MR_grade, strlen(MR_grade)) == 0 || strcmp(start, MR_runtime_grade) == 0) { if (found_grade_symbol == NULL) { found_grade_symbol = malloc(strlen(start) + 1); if (found_grade_symbol != NULL) { strcpy(found_grade_symbol, start); } } goto wrong_format; } /* Skip the `_entry_' prefix, if any. */ strip_prefix(&start, entry); /* Strip off the `mercury__' prefix, if any. */ if (strip_prefix(&start, mercury)) { matched = MR_TRUE; } /* ** Code for dealing with predicate symbols. */ /* ** Get integer from end of string (it might be the mode number, ** it might be the internal label number). We'll assume its mode ** number for the moment. */ if (!cut_trailing_integer(start, &end, &mode_num)) { goto not_plain_mercury; } if (end == start) { goto not_plain_mercury; } /* ** If we got to an `i', that means it is an internal label of the form ** `mercury__append_3_0_i1'. In that case, save the internal label number ** and then get the mode number. */ if (*--end == 'i') { internal = mode_num; if (end == start || *--end != '_') { goto not_plain_mercury; } if (!cut_trailing_underscore_integer(start, &end, &mode_num)) { goto not_plain_mercury; } } if (end == start) { goto not_plain_mercury; } /* Strip off the `fn__' prefix, if any. */ if (strip_prefix(&start, func_prefix)) { high_level = MR_FALSE; pred_or_func = "function"; } else if (strip_suffix(start, &end, "_f")) { high_level = MR_TRUE; matched = MR_TRUE; pred_or_func = "function"; } else if (strip_suffix(start, &end, "_p")) { high_level = MR_TRUE; matched = MR_TRUE; pred_or_func = "predicate"; } else { /* ** It is not a function. But it could be either an LLDS predicate, ** or an MLDS compiler-generated predicate. */ high_level = (strstr(start, unify2) || strstr(start, compare2) || strstr(start, index2)); pred_or_func = "predicate"; } if (end == start) { goto not_plain_mercury; } /* ** Scan back past the arity number and then parse it. */ if (!cut_trailing_underscore_integer(start, &end, &arity)) { goto not_plain_mercury; } if (high_level) { module = strip_module_name(&start, end, trailing_context_1, trailing_context_1_hl_suffixes); } /* ** Now start processing from the start of the string again. Check whether ** the start of the string matches the name of one of the special ** compiler-generated predicates; if so, set the `category' to the ** appropriate value and then skip past the prefix. */ if (strip_prefix(&start, unify1)) { category = UNIFY; } else if (strip_prefix(&start, compare1)) { category = COMPARE; if (mode_num != 0) goto not_plain_mercury; } else if (strip_prefix(&start, index1)) { category = INDEX; if (mode_num != 0) goto not_plain_mercury; } else { category = ORDINARY; /* ** For ordinary predicates, we should have matched against something ** by now -- ** either the "mercury__" prefix, for LLDS mangling, ** or the "_f" or "_p" suffix, for MLDS mangling. */ if (!matched) { goto not_plain_mercury; } } if (category != ORDINARY && start[0] == '_') { start++; } /* Fix any ascii codes mangled in the predicate name. */ start = fix_mangled_ascii(start, &end); /* ** Process the mangling introduced by unused_args.m. ** This involves stripping off the `__ua' or `__uab' added to ** the end of the predicate/function name, where m is the mode number. */ position = end; /* save end of name */ do { if (position == start) { goto wrong_format; } position--; } while (MR_isdigit(*position)); /* get the mode number */ if (check_for_suffix(start, position, ua_suffix, sizeof(ua_suffix), &mode_num2)) { unused_args = MR_TRUE; unused_args_extra = MR_FALSE; unused_args_num = mode_num; end = position + 1 - (sizeof(ua_suffix) - 1); mode_num = mode_num2 % 10000; } else if (check_for_suffix(start, position, ua_suffix2, sizeof(ua_suffix2), &mode_num2)) { unused_args = MR_TRUE; unused_args_extra = MR_TRUE; unused_args_num = mode_num; end = position + 1 - (sizeof(ua_suffix2) - 1); mode_num = mode_num2 % 10000; } /* ** Process the mangling introduced by higher_order.m. ** This involves stripping off the `__ho' where ** n is a unique identifier for this specialized version */ position = end; do { if (position == start) { goto wrong_format; } position--; } while (MR_isdigit(*position)); if (check_for_suffix(start, position, ho_suffix, sizeof(ho_suffix), &higher_order_num)) { end = position + 1 - (sizeof(ho_suffix) - 1); higher_order = MR_TRUE; } /* ** Cut off the string before the start of the arity number, ** and the unused_args and specialization information, ** i.e. at the end of the predicate name or type name. */ *end = '\0'; /* ** Make sure special predicates with unused_args ** are reported correctly. */ if (unused_args && category != ORDINARY) { if (!cut_trailing_integer(start, &end, &arity)) { goto wrong_format; } } if (!high_level) { module = strip_module_name(&start, end, trailing_context_1, NULL); } /* ** Look for "IntroducedFrom" or "DeforestationIn" or "AccFrom" ** or "TypeSpecOf". ** XXX This don't yet handle multiple prefixes. If we get an error after ** this point, just treat predicate name as an ordinary predicate. */ name_before_prefixes = start; if (category == ORDINARY) { if (strip_prefix(&start, introduced)) { category = LAMBDA; } else if (strip_prefix(&start, deforestation)) { category = DEFORESTATION; } else if (strip_prefix(&start, accumulator)) { category = ACCUMULATOR; } else if (strip_prefix(&start, type_spec)) { category = TYPE_SPEC; } } if (category == LAMBDA || category == DEFORESTATION || category == ACCUMULATOR || category == TYPE_SPEC) { if (strip_prefix(&start, pred)) { lambda_kind = "pred"; } else if (strip_prefix(&start, func)) { lambda_kind = "func"; } else if (category == TYPE_SPEC && strip_prefix(&start, porf)) { lambda_kind = ""; } else { goto wrong_format; } lambda_pred_name = start; if (!find_double_underscore(&start, end)) { category = ORDINARY; start = name_before_prefixes; } else { end_of_lambda_pred_name = start; start += 2; } if (category == TYPE_SPEC) { if (start < end && *start == '[') { int nest_level; nest_level = 1; type_spec_sub = start; start++; /* Handle matched brackets in type names. */ while (start < end) { if (*start == '[') { nest_level++; } if (*start == ']') { nest_level--; } if (nest_level == 0) { *(start + 1) = '\0'; break; } start++; } if (nest_level != 0) { category = ORDINARY; start = name_before_prefixes; } else { /* ** The compiler adds a redundant mode number to the ** predicate name to avoid creating two predicates ** with the same name (deep profiling doesn't like that). ** It isn't used here, so we just ignore it. The compiler ** also adds a version number for the argument order used ** for specialized versions, which can also be ignored. */ *end_of_lambda_pred_name = '\0'; start = lambda_pred_name; } } else { category = ORDINARY; start = name_before_prefixes; } } else if (category != ORDINARY) { lambda_line = 0; if (start >= end || !MR_isdigit(*start)) { category = ORDINARY; start = name_before_prefixes; } while (start < end && MR_isdigit(*start)) { lambda_line = lambda_line * 10 + (*start - '0'); start++; } if (strip_prefix(&start, "__")) { if (start < end && MR_isdigit(*start)) { lambda_seq_number = 0; while (start < end && MR_isdigit(*start)) { lambda_seq_number = lambda_seq_number * 10 + (*start - '0'); start++; } *end_of_lambda_pred_name = '\0'; } else { category = ORDINARY; start = name_before_prefixes; } } else { category = ORDINARY; start = name_before_prefixes; } } } /* ** Now, finally, we can print the demangled symbol name. */ printf("<"); switch(category) { case UNIFY: printf("unification predicate for type '%s.%s'/%d mode %d", module, start, arity, mode_num); break; case COMPARE: printf("compare/3 predicate for type '%s.%s'/%d", module, start, arity); break; case INDEX: printf("index/2 predicate for type '%s.%s'/%d", module, start, arity); break; case LAMBDA: printf("%s goal (#%d) from '%s' in module '%s' line %d", lambda_kind, lambda_seq_number, lambda_pred_name, module, lambda_line); break; case ACCUMULATOR: printf("accumulator procedure from '%s' in module '%s' line %d", lambda_pred_name, module, lambda_line); break; case DEFORESTATION: printf("deforestation procedure (#%d) from '%s' " "in module '%s' line %d", lambda_seq_number, lambda_pred_name, module, lambda_line); break; case TYPE_SPEC: default: if (*module == '\0') { printf("%s '%s'/%d mode %d", pred_or_func, start, arity, mode_num); } else { printf("%s '%s.%s'/%d mode %d", pred_or_func, module, start, arity, mode_num); } } if (category == TYPE_SPEC) { printf(" (type specialized %s)", type_spec_sub); } if (higher_order) { printf(" (specialized [#%d])", higher_order_num); } if (unused_args) { if (unused_args_extra) { printf(" (minus extra unused args [#%d])", unused_args_num); } else { printf(" (minus unused args [#%d])", unused_args_num); } } if (internal != -1) { printf(" label %d", internal); } printf(">"); return; /* ** Code to deal with mercury_data items. */ not_plain_mercury: /* ** Undo any in-place modifications done while trying to demangle ** predicate names. */ strcpy(name, orig_name); start = name; end = name + strlen(name); /* Skip any leading underscore inserted by the C compiler. */ if (*start == '_') { start++; } if (strip_prefix(&start, mercury_common)) { if (!strip_leading_integer(&start, &arity)) { goto wrong_format; } printf("", arity); return; } if (strip_prefix(&start, mercury_data)) { /* LLDS */ high_level = MR_FALSE; if (strip_prefix(&start, base_typeclass_info)) { goto typeclass_info; } /* Also try the old format, in case we're demangling old files. */ if (strip_prefix(&start, underscores_base_typeclass_info)) { goto typeclass_info; } } else { /* MLDS */ high_level = MR_TRUE; if (strip_prefix(&start, base_typeclass_info)) { goto typeclass_info; } strip_prefix(&start, mercury); } module = strip_module_name(&start, end, trailing_context_2, NULL); if (high_level) { /* ** For MLDS, the module name gets duplicated (XXX why?) ** So here we must replace `foo.foo' with just `foo'. */ size_t half_len; half_len = strlen(module) / 2; if (strncmp(module, module + half_len + 1, half_len) != 0) { goto wrong_format; } module += half_len + 1; } if (strip_prefix(&start, type_ctor_info)) { data_category = INFO; if (!cut_trailing_underscore_integer(start, &end, &arity)) { goto wrong_format; } } else if (strip_prefix(&start, type_ctor_layout)) { data_category = LAYOUT; if (!cut_trailing_underscore_integer(start, &end, &arity)) { goto wrong_format; } } else if (strip_prefix(&start, type_ctor_functors)) { data_category = FUNCTORS; if (!cut_trailing_underscore_integer(start, &end, &arity)) { goto wrong_format; } } else if (strip_prefix(&start, common)) { data_category = COMMON; if (!cut_trailing_underscore_integer(start, &end, &arity)) { goto wrong_format; } } else { goto wrong_format; } start = fix_mangled_ascii(start, &end); switch (data_category) { case INFO: if (*module == '\0') { printf("", start, arity); } else { printf("", module, start, arity); } break; case LAYOUT: if (*module == '\0') { printf("", start, arity); } else { printf("", module, start, arity); } break; case FUNCTORS: if (*module == '\0') { printf("", start, arity); } else { printf("", module, start, arity); } break; case COMMON: printf("", arity, module); break; default: goto wrong_format; } return; typeclass_info: /* ** Parse the class name and class arity, which have the following layout: ** __arity__ */ class_name = strip_module_name(&start, end, trailing_context_3, NULL); /* XXX fix_mangled_ascii() */ if (!(strip_prefix(&start, arity_string) && strip_leading_integer(&start, &class_arity) && strip_prefix(&start, "__"))) { goto wrong_format; } /* ** Parse the class argument types, which each have the following layout: ** __arity__ ** ** We store the human-readable formatted output in class_arg_buf as we go. */ fix_mangled_ascii(start, &end); strcpy(class_arg_buf, ""); for (class_arg_num = 0; class_arg_num < class_arity; class_arg_num++) { if (class_arg_num != 0) { strcat(class_arg_buf, ", "); } class_arg = strip_module_name(&start, end, trailing_context_3, NULL); if (!(strip_prefix(&start, arity_string) && strip_leading_integer(&start, &arity) && strip_prefix(&start, "__"))) { goto wrong_format; } sprintf(class_arg_buf + strlen(class_arg_buf), "%s/%d", class_arg, arity); } /* Now print the results. */ printf("", class_name, class_arg_buf); return; wrong_format: strcpy(name, orig_name); start = name; end = name + strlen(name); start = fix_mangled_ascii(start, &end); fputs(name, stdout); return; too_long: fputs(orig_name, stdout); return; } /* end demangle() */ /* ** Remove a module name prefix. ** Just keep munching up double-underscores until we get to something ** that matches the specified trailing context, at which point we stop, ** or until there are no double-underscores left. */ static const char * strip_module_name(char **start_ptr, char *end, const char *special_prefixes[], const char *special_suffixes[]) { const char *module; /* module name */ char *module_end; /* end of the module name */ char *next_double_underscore; char *start; start = *start_ptr; /* ** Strip off the module name */ module = start; module_end = start; while ((next_double_underscore = strstr(start, "__")) != NULL) { int len; int i; MR_bool stop; /* Check for special cases. */ stop = MR_FALSE; for (i = 0; special_prefixes[i] != NULL; i++) { if (strncmp(start, special_prefixes[i], strlen(special_prefixes[i])) == 0) { stop = MR_TRUE; } } for (i = 0; special_suffixes != NULL && special_suffixes[i] != NULL; i++) { if (strncmp(next_double_underscore, special_suffixes[i], strlen(special_suffixes[i])) == 0) { stop = MR_TRUE; } } if (stop) { break; } len = next_double_underscore - start; if (module != module_end) { /* ** Append a module qualifier, and shift the module name ** into the right place. */ *module_end = ':'; module_end++; memmove(module_end, start, len); } module_end += len; start = next_double_underscore + 2; } if (module == module_end) { module = ""; } else { *module_end = '\0'; } *start_ptr = start; return module; } /* ** Remove the prefix from a string, if it has it. ** Returns MR_TRUE if the string has that prefix, and *str will then point ** to the rest of that string. If the string doesn't have that prefix, ** *str will be unchanged, and the function will return MR_FALSE. */ static MR_bool strip_prefix(char **str, const char *prefix) { size_t len; len = strlen(prefix); if (strncmp(*str, prefix, len) == 0) { *str += len; return MR_TRUE; } return MR_FALSE; } /* ** Remove the suffix from a string, if it has it. ** Returns MR_TRUE if the string between start and *end has the specified ** suffix, and sets *end to point to the beginning of the suffix. */ static MR_bool strip_suffix(const char *start, char **end, const char *suffix) { size_t len; len = strlen(suffix); if (*end - start >= len && strncmp(*end - len, suffix, len) == 0) { *end -= len; return MR_TRUE; } return MR_FALSE; } /* ** If the string pointed to by *start_ptr starts with an integer, ** then advance *start_ptr past the leading integer, store the value ** of the integer in the int pointed to by `num', and return true; ** otherwise leave *start_ptr unchanged and return false. ** (The string itself is always left unchanged.) */ static MR_bool strip_leading_integer(char **start_ptr, int *num) { char *start; char save_char; MR_bool got_int; start = *start_ptr; while(MR_isdigit(*start)) { start++; } if (start == *start_ptr) { return MR_FALSE; } save_char = *start; *start = '\0'; got_int = (sscanf(*start_ptr, "%d", num) == 1); *start = save_char; if (got_int) { *start_ptr = start; return MR_TRUE; } else { return MR_FALSE; } } /* ** Remove trailing integer (at the supplied `real_end' of the string), ** and return it in the int pointed to by `num'. We return true if there is ** an integer at the end, and false if there is not. If we return false, ** the string will not be cut. `real_end' is updated with the new end ** of the string. ** ** Requires *str to contain more than just a number; doesn't work ** if the trailing integer starts at the first character of str. */ static MR_bool cut_trailing_integer(char *str, char **real_end, int *num) { char *end; end = *real_end; do { if (end == str) { return MR_FALSE; } end--; } while (MR_isdigit(*end)); if (sscanf(end + 1, "%d", num) != 1) { return MR_FALSE; } *++end = '\0'; *real_end = end; return MR_TRUE; } /* ** Same as cut_trailing_integer, but move end back past the underscore as well. ** If cut_trailing_underscore_integer returns MR_TRUE, the `real_end' will be ** moved back before the underscore and the integer. If it returns MR_FALSE, ** the `real_end' is unchanged. */ static MR_bool cut_trailing_underscore_integer(char *str, char **real_end, int *num) { char *end; end = *real_end; if (!cut_trailing_integer(str, &end, num)) { return MR_FALSE; } if (end == str || *(--end) != '_') { return MR_FALSE; } *end = '\0'; *real_end = end; return MR_TRUE; } /* ** Scan for `__' and return a pointer to the first `_'. ** Returns MR_TRUE if `__' was found, MR_FALSE otherwise. */ static MR_bool find_double_underscore(char **start, char *end) { char *str; str = *start; while (*str != '_' || *(str + 1) != '_') { if (str == end) { return MR_FALSE; } str++; } *start = str; return MR_TRUE; } /* ** The compiler changes all names starting with `f_' so that they start with ** `f__' instead, and uses names starting with `f_' for mangled names ** which are either descriptions (such as `f_greater_than' for `>') ** or sequences of decimal representations of ASCII codes separated by ** underscores. If the name starts with `f__', we must change it back to ** start with `f_'. Otherwise, if it starts with `f_' we must convert ** the mnemonic or list of ASCII codes back into an identifier. */ static char * fix_mangled_ascii(char *str, char **real_end) { char *end; end = *real_end; /* ** If it starts with `f__', replace that with `f_'. */ if (strncmp(str, "f__" , 3) == 0) { str++; *str = 'f'; return str; } /* ** If it starts with `f_' followed by a mnemonic description, ** then replace that with its unmangled version */ if (strncmp(str, "f_", 2) == 0 && fix_mangled_special_case(str, real_end)) { return str; } /* ** Otherwise, if it starts with `f_' we must convert the list of ** ASCII codes back into an identifier. */ if (strncmp(str, "f_", 2) == 0) { char buf[MAX_SYMBOL_LENGTH]; char *num; int count; num = str + 2; count = 0; while (num < end) { char *next_num; next_num = num; while (MR_isdigit(*next_num)) { next_num++; } if (*next_num != '_' && *next_num != '\0') { break; } *next_num = '\0'; buf[count++] = atoi(num); num = next_num + 1; } /* Copy anything after the mangled string. */ while (num < end) { buf[count++] = *num++; } buf[count] = '\0'; strcpy(str, buf); *real_end = str + count; } return str; } static MR_bool fix_mangled_special_case(char *str, char **real_end) { static const struct { const char *mangled_name; const char *unmangled_name; } translations[] = { /* ** Beware: we assume that the unmangled name is always shorter ** than the mangled name. */ { "f_not_equal", "\\=" }, { "f_greater_or_equal", ">=" }, { "f_less_or_equal", "=<" }, { "f_equal", "=" }, { "f_less_than", "<" }, { "f_greater_than", ">" }, { "f_plus", "+" }, { "f_times", "*" }, { "f_minus", "-" }, { "f_slash", "/" }, { "f_comma", "," }, { "f_semicolon", ";" }, { "f_cut", "!" }, { "f_tuple", "{}" }, { "f_cons", "[|]" }, { "f_nil", "[]" } }; const int num_translations = sizeof(translations) / sizeof(translations[0]); int i; /* ** Check for the special cases listed in the table above. */ for (i = 0; i < num_translations; i++) { const char *mangled; size_t mangled_len; mangled = translations[i].mangled_name; mangled_len = strlen(mangled); if (strncmp(str, mangled, mangled_len) == 0) { const char *unmangled; size_t unmangled_len; size_t leftover_len; unmangled = translations[i].unmangled_name; unmangled_len = strlen(unmangled); leftover_len = strlen(str) - mangled_len; assert(unmangled_len <= mangled_len); strcpy(str, unmangled); memmove(str + unmangled_len, str + mangled_len, leftover_len + 1); *real_end = str + unmangled_len + leftover_len; return MR_TRUE; } } return MR_FALSE; } static MR_bool check_for_suffix(char *start, char *position, const char *suffix, int sizeof_suffix, int *mode_num2) { const int suffix_len = sizeof_suffix - 1; return ( position - suffix_len >= start && sscanf(position + 1, "%d", mode_num2) == 1 && strncmp(position - suffix_len + 1, suffix, suffix_len) == 0 ); } /*---------------------------------------------------------------------------*/