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 }