/* * This file and its contents are supplied under the terms of the * Common Development and Distribution License ("CDDL"), version 1.0. * You may only use this file in accordance with the terms of version * 1.0 of the CDDL. * * A full copy of the text of the CDDL should have accompanied this * source. A copy of the CDDL is also available via the Internet at * http://www.illumos.org/license/CDDL. */ /* * Copyright 2020 Joyent, Inc. */ #include #include #include #include #include #include #include #include #define _(x) gettext(x) locale_t c_locale; static int do_symbols(sysdem_lang_t, int, char * const *); static int do_input(sysdem_lang_t, FILE *restrict, FILE *restrict); static int do_demangle(const char *, sysdem_lang_t, FILE *); static void appendc(custr_t *, char); static void xputc(int, FILE *); static void usage(void) { (void) fprintf(stderr, _("Usage: %s [-l lang] [sym...]\n"), getprogname()); exit(2); } int main(int argc, char * const *argv) { sysdem_lang_t lang = SYSDEM_LANG_AUTO; int c; int ret; (void) setlocale(LC_ALL, ""); #if !defined(TEXT_DOMAIN) #define TEXT_DOMAIN "SYS_TEST" #endif (void) textdomain(TEXT_DOMAIN); /* * For detecting symbol boundaries, we want to use the C locale * definitions for use in isalnum_l(). */ if ((c_locale = newlocale(LC_CTYPE_MASK, "C", NULL)) == NULL) err(EXIT_FAILURE, _("failed to construct C locale")); while ((c = getopt(argc, argv, "hl:")) != -1) { switch (c) { case 'l': if (sysdem_parse_lang(optarg, &lang)) break; errx(EXIT_FAILURE, _("Unsupported language '%s'\n"), optarg); case 'h': case '?': usage(); } } argc -= optind; argv += optind; if (argc > 0) ret = do_symbols(lang, argc, argv); else ret = do_input(lang, stdin, stdout); return ((ret < 0) ? EXIT_FAILURE : EXIT_SUCCESS); } static int do_symbols(sysdem_lang_t lang, int argc, char * const *argv) { int ret = 0; for (int i = 0; i < argc; i++) { if (do_demangle(argv[i], lang, stdout) < 0) ret = -1; else xputc('\n', stdout); } return (ret); } static int do_input(sysdem_lang_t lang, FILE *restrict in, FILE *restrict out) { custr_t *word = NULL; int c; int ret = 0; boolean_t in_symbol = B_FALSE; if (custr_alloc(&word) != 0) err(EXIT_FAILURE, _("failed to allocate memory")); while ((c = fgetc(in)) != EOF) { if (in_symbol) { /* * All currently supported mangling formats only use * alphanumeric characters, '.', '_', or '$' in * mangled names. Once we've seen the potential start * of a symbol ('_'), we accumulate subsequent * charaters into 'word'. If we encounter a character * that is not a part of that set ([A-Za-z0-9._$]), we * treat it as a delimiter, we stop accumulating * characters into word, and we attempt to demangle the * accumulated string in 'word' by calling * demangle_custr(). * * Similar utilities like c++filt behave in a similar * fashion when reading from stdin to allow for * demangling of symbols embedded in surrounding text. */ if (isalnum_l(c, c_locale) || c == '.' || c == '_' || c == '$') { appendc(word, c); continue; } /* * Hit a symbol boundary, attempt to demangle what * we've accumulated in word and reset word. */ if (do_demangle(custr_cstr(word), lang, out) < 0) ret = -1; custr_reset(word); in_symbol = B_FALSE; } if (c != '_') { xputc(c, out); } else { in_symbol = B_TRUE; appendc(word, c); } } if (ferror(in)) err(EXIT_FAILURE, _("error reading input")); /* * If we were accumulating characters for a symbol and hit EOF, * attempt to demangle what we accumulated. */ if (custr_len(word) > 0 && do_demangle(custr_cstr(word), lang, out) < 0) ret = -1; custr_free(word); return (ret); } /* * Attempt to demangle 'sym' as a symbol for 'lang' and write the result * to 'out'. If 'sym' could not be demangled as 'lang' symbol, the original * string is output instead. * * If an error other than 'not a mangled symbol' is encountered (e.g. ENOMEM), * a warning is sent to stderr and -1 is returned. Otherwise, 0 is returned * (including when 'sym' is merely not a mangled symbol of 'lang'). */ static int do_demangle(const char *sym, sysdem_lang_t lang, FILE *out) { char *demangled = sysdemangle(sym, lang, NULL); if (demangled == NULL && errno != EINVAL && errno != ENOTSUP) { warn(_("error while demangling '%s'"), sym); return (-1); } if (fprintf(out, "%s", (demangled != NULL) ? demangled : sym) < 0) err(EXIT_FAILURE, _("failed to write to output")); free(demangled); return (0); } static void appendc(custr_t *cus, char c) { if (custr_appendc(cus, c) == 0) return; err(EXIT_FAILURE, _("failed to save character from input")); } static void xputc(int c, FILE *out) { if (fputc(c, out) < 0) err(EXIT_FAILURE, _("failed to write output")); }