1 /* 2 * This file and its contents are supplied under the terms of the 3 * Common Development and Distribution License ("CDDL"), version 1.0. 4 * You may only use this file in accordance with the terms of version 5 * 1.0 of the CDDL. 6 * 7 * A full copy of the text of the CDDL should have accompanied this 8 * source. A copy of the CDDL is also available via the Internet at 9 * http://www.illumos.org/license/CDDL. 10 */ 11 12 /* 13 * Copyright 2020 Joyent, Inc. 14 */ 15 16 #include <ctype.h> 17 #include <demangle-sys.h> 18 #include <err.h> 19 #include <errno.h> 20 #include <libcustr.h> 21 #include <locale.h> 22 #include <stdio.h> 23 #include <stdlib.h> 24 25 #define _(x) gettext(x) 26 27 locale_t c_locale; 28 29 static void do_symbols(sysdem_lang_t, int, char * const *); 30 static void do_input(sysdem_lang_t, FILE *restrict, FILE *restrict); 31 static void demangle_custr(custr_t *, sysdem_lang_t, FILE *); 32 static void appendc(custr_t *, char); 33 34 static void 35 usage(void) 36 { 37 (void) fprintf(stderr, _("Usage: %s [-l lang] [sym...]\n"), 38 getprogname()); 39 exit(2); 40 } 41 42 int 43 main(int argc, char * const *argv) 44 { 45 sysdem_lang_t lang = SYSDEM_LANG_AUTO; 46 int c; 47 48 (void) setlocale(LC_ALL, ""); 49 50 #if !defined(TEXT_DOMAIN) 51 #define TEXT_DOMAIN "SYS_TEST" 52 #endif 53 (void) textdomain(TEXT_DOMAIN); 54 55 /* 56 * For detecting symbol boundaries, we want to use the C locale 57 * definitions for use in isalnum_l(). 58 */ 59 if ((c_locale = newlocale(LC_CTYPE_MASK, "C", NULL)) == NULL) 60 err(EXIT_FAILURE, _("failed to construct C locale")); 61 62 while ((c = getopt(argc, argv, "hl:")) != -1) { 63 switch (c) { 64 case 'l': 65 if (sysdem_parse_lang(optarg, &lang)) 66 break; 67 68 errx(EXIT_FAILURE, _("Unsupported language '%s'\n"), 69 optarg); 70 case 'h': 71 case '?': 72 usage(); 73 } 74 } 75 76 argc -= optind; 77 argv += optind; 78 79 if (argc > 0) 80 do_symbols(lang, argc, argv); 81 else 82 do_input(lang, stdin, stdout); 83 84 return (0); 85 } 86 87 static void 88 do_symbols(sysdem_lang_t lang, int argc, char * const *argv) 89 { 90 for (int i = 0; i < argc; i++) { 91 char *demangled = NULL; 92 93 demangled = sysdemangle(argv[i], lang, NULL); 94 95 if (demangled == NULL) { 96 /* 97 * If we failed to demangle for any reason other than 98 * 'not a mangled name' (EINVAL), we print the error. 99 * For EINVAL, we just print the original value like 100 * c++filt does. 101 */ 102 if (errno != EINVAL) 103 warnx(_("failed to demangle '%s'"), argv[i]); 104 else 105 (void) printf("%s\n", argv[i]); 106 } else { 107 (void) printf("%s\n", demangled); 108 free(demangled); 109 } 110 } 111 } 112 113 static void 114 do_input(sysdem_lang_t lang, FILE *restrict in, FILE *restrict out) 115 { 116 custr_t *word = NULL; 117 int c; 118 boolean_t in_symbol = B_FALSE; 119 120 if (custr_alloc(&word) != 0) 121 err(EXIT_FAILURE, _("failed to allocate memory")); 122 123 while ((c = fgetc(in)) != EOF) { 124 if (in_symbol) { 125 /* 126 * All currently supported mangling formats only use 127 * alphanumeric characters, '.', '_', or '$' in 128 * mangled names. Once we've seen the potential start 129 * of a symbol ('_'), we accumulate subsequent 130 * charaters into 'word'. If we encounter a character 131 * that is not a part of that set ([A-Za-z0-9._$]), we 132 * treat it as a delimiter, we stop accumulating 133 * characters into word, and we attempt to demangle the 134 * accumulated string in 'word' by calling 135 * demangle_custr(). 136 * 137 * Similar utilities like c++filt behave in a similar 138 * fashion when reading from stdin to allow for 139 * demangling of symbols embedded in surrounding text. 140 */ 141 if (isalnum_l(c, c_locale) || c == '.' || c == '_' || 142 c == '$') { 143 appendc(word, c); 144 continue; 145 } 146 147 /* 148 * Hit a symbol boundary, attempt to demangle what 149 * we've accumulated in word and reset word. 150 */ 151 demangle_custr(word, lang, out); 152 in_symbol = B_FALSE; 153 } 154 155 if (c != '_') { 156 if (fputc(c, out) != c) { 157 err(EXIT_FAILURE, 158 _("failed to write to output")); 159 } 160 } else { 161 in_symbol = B_TRUE; 162 appendc(word, c); 163 } 164 } 165 166 if (ferror(in)) 167 err(EXIT_FAILURE, _("error reading input")); 168 169 /* 170 * If we were accumulating characters for a symbol and hit EOF, 171 * attempt to demangle what we accumulated. 172 */ 173 if (custr_len(word) > 0) 174 demangle_custr(word, lang, out); 175 176 custr_free(word); 177 } 178 179 /* 180 * Attempt to demangle the string in 'word' and write to out, otherwise 181 * write out the contents of word. In either case, 'word' is reset (contents 182 * cleared) prior to returning. 183 */ 184 static void 185 demangle_custr(custr_t *word, sysdem_lang_t lang, FILE *out) 186 { 187 char *demangled = NULL; 188 189 demangled = sysdemangle(custr_cstr(word), lang, NULL); 190 191 if (fprintf(out, "%s", 192 (demangled != NULL) ? demangled : custr_cstr(word)) < 0) { 193 err(EXIT_FAILURE, _("failed to write to output")); 194 } 195 196 free(demangled); 197 custr_reset(word); 198 } 199 200 static void 201 appendc(custr_t *cus, char c) 202 { 203 if (custr_appendc(cus, c) == 0) 204 return; 205 err(EXIT_FAILURE, _("failed to save character from input")); 206 }