1/*
2 * Copyright 2018, Haiku, Inc. All rights reserved.
3 * Based on Demumble; Copyright 2016-2018, Nico Weber.
4 * 		https://github.com/nico/demumble/
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 *   http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18
19#include <stddef.h>
20#include <stdio.h>
21#include <stdlib.h>
22#include <string.h>
23#include <algorithm>
24
25#include "Demangler.h"
26
27
28static void print_help(FILE* out)
29{
30	fprintf(out,
31		"usage: haikuc++filt [options] [symbols...]\n"
32		"\n"
33		"if symbols are unspecified, reads from stdin.\n"
34		"\n"
35		"options:\n"
36		"  -m         only print mangled names that were demangled,"
37			"omit other output\n"
38		"  -u         use unbuffered output\n"
39		"  --no-gcc2	ignore GCC 2-style symbols\n");
40}
41
42
43static bool starts_with(const char* s, const char* prefix)
44{
45	return strncmp(s, prefix, strlen(prefix)) == 0;
46}
47
48
49static void print_demangled(const char* s)
50{
51	const char* cxa_in = s;
52	if (starts_with(s, "__Z") || starts_with(s, "____Z"))
53		cxa_in += 1;
54	printf("%s", Demangler::Demangle(cxa_in).String());
55}
56
57
58static bool is_mangle_char_posix(char c)
59{
60	return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
61		(c >= '0' && c <= '9') || c == '_';
62}
63
64
65static bool look_for_itanium_prefix(char** str, char* end)
66{
67	char* s = *str;
68	s += strcspn(s, "_?");
69	if (s == end)
70		return false;
71
72	// Itanium symbols start with 1-4 underscores followed by Z.
73	// strnstr() is BSD, so use a small local buffer and strstr().
74	const int N = 5;  // == strlen("____Z")
75	char prefix[N + 1];
76	strncpy(prefix, s, N);
77		prefix[N] = '\0';
78	if (strstr(prefix, "_Z")) {
79		*str = s;
80		return true;
81	}
82	return false;
83}
84
85
86static bool look_for_gcc2_symbol(char** str, char* end)
87{
88	// search '__' starting from the end, don't accept them at the start
89	char* s = *str;
90	size_t pos = (end - s) - 1;
91	char* mangled = NULL;
92
93	while (pos > 1) {
94		if (s[pos] == '_') {
95			if (s[pos - 1] == '_') {
96				mangled = s + pos + 1;
97				break;
98			} else
99				pos--;
100		}
101		pos--;
102	}
103
104	// if we've found a symbol, go backwards to its beginning
105	while (mangled != NULL && mangled > (s + 1)
106			&& is_mangle_char_posix(mangled[-1])) {
107		mangled--;
108	}
109
110	if (mangled != NULL)
111		*str = mangled;
112
113	return mangled != NULL;
114}
115
116
117static char buf[8192];
118int main(int argc, char* argv[])
119{
120	enum { kPrintAll, kPrintMatching } print_mode = kPrintAll;
121	bool noGCC2 = false;
122
123	while (argc > 1 && argv[1][0] == '-') {
124		if (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0) {
125			print_help(stdout);
126			return 0;
127		} else if (strcmp(argv[1], "-m") == 0) {
128			print_mode = kPrintMatching;
129		} else if (strcmp(argv[1], "--no-gcc2") == 0) {
130			noGCC2 = true;
131		} else if (strcmp(argv[1], "-u") == 0) {
132			setbuf(stdout, NULL);
133		} else if (strcmp(argv[1], "--") == 0) {
134			--argc;
135			++argv;
136			break;
137		} else {
138			fprintf(stderr, "c++filt: unrecognized option `%s'\n", argv[1]);
139			print_help(stderr);
140			return 1;
141		}
142		--argc;
143		++argv;
144	}
145	for (int i = 1; i < argc; ++i) {
146		print_demangled(argv[i]);
147		printf("\n");
148	}
149	if (argc != 1)
150		return 0;
151	// Read stdin instead.
152	// By default, don't demangle types.  Mangled function names are unlikely
153	// to appear in text for since they start with _Z (or ___Z) or ?? / ?$ / ?@.
154	// But type manglings can be regular words ("Pi" is "int*").
155	// (For command-line args, do try to demangle types though.)
156	while (fgets(buf, sizeof(buf), stdin)) {
157		bool need_separator = false;
158		char* cur = buf;
159		char* end = cur + strlen(cur);
160
161		while (cur != end) {
162			if (print_mode == kPrintMatching && need_separator)
163				printf("\n");
164			need_separator = false;
165
166			// Check if we have a symbol, and then for how long it is.
167			size_t n_sym = 0;
168			char* real_cur = cur;
169			if (look_for_itanium_prefix(&real_cur, end) ||
170					(!noGCC2 && look_for_gcc2_symbol(&real_cur, end))) {
171				// Print all the stuff before the symbol.
172				if (print_mode == kPrintAll)
173					printf("%.*s", static_cast<int>(real_cur - cur), cur);
174				cur = real_cur;
175				while (cur + n_sym != end && is_mangle_char_posix(cur[n_sym]))
176					++n_sym;
177			} else {
178				// No symbols found in this block; skip it.
179				printf("%s", cur);
180				cur = end;
181				continue;
182			}
183			if (n_sym == 0) {
184				++cur;
185				continue;
186			}
187
188			char tmp = cur[n_sym];
189			cur[n_sym] = '\0';
190			print_demangled(cur);
191			need_separator = true;
192			cur[n_sym] = tmp;
193
194			cur += n_sym;
195		}
196	}
197}
198