1 // For clippy.
2 #![allow(unknown_lints)]
3 
4 extern crate cpp_demangle;
5 
6 // For command line integration
7 #[macro_use]
8 extern crate clap;
9 
10 use clap::{App, Arg};
11 use cpp_demangle::{BorrowedSymbol, DemangleOptions};
12 use std::io::{self, BufRead, Cursor, Write};
13 use std::process;
14 
15 /// Find the index of the first (potential) occurrence of a mangled C++ symbol
16 /// in the given `haystack`.
find_mangled(haystack: &[u8]) -> Option<usize>17 fn find_mangled(haystack: &[u8]) -> Option<usize> {
18     if haystack.is_empty() {
19         return None;
20     }
21 
22     for i in 0..haystack.len() - 1 {
23         if haystack[i] == b'_' {
24             match (
25                 haystack[i + 1],
26                 haystack.get(i + 2),
27                 haystack.get(i + 3),
28                 haystack.get(i + 4),
29             ) {
30                 (b'Z', _, _, _) | (b'_', Some(b'Z'), _, _) | (b'_', Some(b'_'), Some(b'Z'), _) => {
31                     return Some(i)
32                 }
33                 (b'_', Some(b'_'), Some(b'_'), Some(b'Z')) => return Some(i),
34                 _ => (),
35             }
36         }
37     }
38 
39     None
40 }
41 
42 /// Print the given `line` to `out`, with all mangled C++ symbols replaced with
43 /// their demangled form.
demangle_line<W>(out: &mut W, line: &[u8], options: DemangleOptions) -> io::Result<()> where W: Write,44 fn demangle_line<W>(out: &mut W, line: &[u8], options: DemangleOptions) -> io::Result<()>
45 where
46     W: Write,
47 {
48     let mut line = line;
49 
50     while let Some(idx) = find_mangled(line) {
51         write!(out, "{}", String::from_utf8_lossy(&line[..idx]))?;
52 
53         if let Ok((sym, tail)) = BorrowedSymbol::with_tail(&line[idx..]) {
54             let demangled = sym
55                 .demangle(&options)
56                 .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
57             write!(out, "{}", demangled)?;
58             line = tail;
59         } else {
60             write!(out, "_Z")?;
61             line = &line[2..];
62         }
63     }
64 
65     write!(out, "{}", String::from_utf8_lossy(line))
66 }
67 
68 /// Print all the lines from the given `input` to `out`, with all mangled C++
69 /// symbols replaced with their demangled form.
demangle_all<R, W>(input: &mut R, out: &mut W, options: DemangleOptions) -> io::Result<()> where R: BufRead, W: Write,70 fn demangle_all<R, W>(input: &mut R, out: &mut W, options: DemangleOptions) -> io::Result<()>
71 where
72     R: BufRead,
73     W: Write,
74 {
75     let mut buf = vec![];
76 
77     while input.read_until(b'\n', &mut buf)? > 0 {
78         let nl = buf.ends_with(&[b'\n']);
79         if nl {
80             buf.pop();
81         }
82         demangle_line(out, &buf[..], options)?;
83         if nl {
84             write!(out, "\n")?;
85         }
86         buf.clear();
87     }
88 
89     Ok(())
90 }
91 
main()92 fn main() {
93     let matches = App::new("cppfilt")
94         .version(crate_version!())
95         .author(crate_authors!())
96         .about("A c++filt clone as an example of how to use the cpp_demangle crate!")
97         .arg(
98             Arg::with_name("noparams")
99                 .short("p")
100                 .long("no-params")
101                 .help("Do not display function arguments"),
102         )
103         .arg(
104             Arg::with_name("mangled_names")
105                 .multiple(true)
106                 .value_delimiter(" "),
107         )
108         .get_matches();
109 
110     let stdin = io::stdin();
111     let mut stdin = stdin.lock();
112 
113     let stdout = io::stdout();
114     let mut stdout = stdout.lock();
115 
116     let stderr = io::stderr();
117     let mut stderr = stderr.lock();
118 
119     let mut options = DemangleOptions::new();
120     if matches.is_present("noparams") {
121         options = options.no_params();
122     }
123     if matches.is_present("noreturntype") {
124         options = options.no_return_type();
125     }
126 
127     let demangle_result = if let Some(names) = matches.values_of("mangled_names") {
128         let mut input = Cursor::new(names.fold(String::new(), |mut accumulated, name| {
129             accumulated.push_str(&name);
130             accumulated.push_str("\n");
131             accumulated
132         }));
133         demangle_all(&mut input, &mut stdout, options)
134     } else {
135         demangle_all(&mut stdin, &mut stdout, options)
136     };
137 
138     let code = match demangle_result {
139         Ok(_) => 0,
140         Err(e) => {
141             let _ = writeln!(&mut stderr, "error: {}", e);
142             1
143         }
144     };
145 
146     process::exit(code);
147 }
148