File: | tools/lldb/source/Interpreter/Args.cpp |
Location: | line 692, column 42 |
Description: | Access to field 'validator' results in a dereference of a null pointer (loaded from variable 'def') |
1 | //===-- Args.cpp ------------------------------------------------*- C++ -*-===// | |||
2 | // | |||
3 | // The LLVM Compiler Infrastructure | |||
4 | // | |||
5 | // This file is distributed under the University of Illinois Open Source | |||
6 | // License. See LICENSE.TXT for details. | |||
7 | // | |||
8 | //===----------------------------------------------------------------------===// | |||
9 | ||||
10 | #include "lldb/lldb-python.h" | |||
11 | ||||
12 | // C Includes | |||
13 | #include <cstdlib> | |||
14 | // C++ Includes | |||
15 | // Other libraries and framework includes | |||
16 | // Project includes | |||
17 | #include "lldb/Interpreter/Args.h" | |||
18 | #include "lldb/Core/Stream.h" | |||
19 | #include "lldb/Core/StreamFile.h" | |||
20 | #include "lldb/Core/StreamString.h" | |||
21 | #include "lldb/DataFormatters/FormatManager.h" | |||
22 | #include "lldb/Interpreter/Options.h" | |||
23 | #include "lldb/Interpreter/CommandInterpreter.h" | |||
24 | #include "lldb/Interpreter/CommandReturnObject.h" | |||
25 | #include "lldb/Target/Process.h" | |||
26 | //#include "lldb/Target/RegisterContext.h" | |||
27 | #include "lldb/Target/StackFrame.h" | |||
28 | #include "lldb/Target/Target.h" | |||
29 | //#include "lldb/Target/Thread.h" | |||
30 | ||||
31 | using namespace lldb; | |||
32 | using namespace lldb_private; | |||
33 | ||||
34 | //---------------------------------------------------------------------- | |||
35 | // Args constructor | |||
36 | //---------------------------------------------------------------------- | |||
37 | Args::Args (const char *command) : | |||
38 | m_args(), | |||
39 | m_argv(), | |||
40 | m_args_quote_char() | |||
41 | { | |||
42 | if (command) | |||
43 | SetCommandString (command); | |||
44 | } | |||
45 | ||||
46 | ||||
47 | Args::Args (const char *command, size_t len) : | |||
48 | m_args(), | |||
49 | m_argv(), | |||
50 | m_args_quote_char() | |||
51 | { | |||
52 | if (command && len) | |||
53 | SetCommandString (command, len); | |||
54 | } | |||
55 | ||||
56 | //---------------------------------------------------------------------- | |||
57 | // We have to be very careful on the copy constructor of this class | |||
58 | // to make sure we copy all of the string values, but we can't copy the | |||
59 | // rhs.m_argv into m_argv since it will point to the "const char *" c | |||
60 | // strings in rhs.m_args. We need to copy the string list and update our | |||
61 | // own m_argv appropriately. | |||
62 | //---------------------------------------------------------------------- | |||
63 | Args::Args (const Args &rhs) : | |||
64 | m_args (rhs.m_args), | |||
65 | m_argv (), | |||
66 | m_args_quote_char(rhs.m_args_quote_char) | |||
67 | { | |||
68 | UpdateArgvFromArgs(); | |||
69 | } | |||
70 | ||||
71 | //---------------------------------------------------------------------- | |||
72 | // We have to be very careful on the copy constructor of this class | |||
73 | // to make sure we copy all of the string values, but we can't copy the | |||
74 | // rhs.m_argv into m_argv since it will point to the "const char *" c | |||
75 | // strings in rhs.m_args. We need to copy the string list and update our | |||
76 | // own m_argv appropriately. | |||
77 | //---------------------------------------------------------------------- | |||
78 | const Args & | |||
79 | Args::operator= (const Args &rhs) | |||
80 | { | |||
81 | // Make sure we aren't assigning to self | |||
82 | if (this != &rhs) | |||
83 | { | |||
84 | m_args = rhs.m_args; | |||
85 | m_args_quote_char = rhs.m_args_quote_char; | |||
86 | UpdateArgvFromArgs(); | |||
87 | } | |||
88 | return *this; | |||
89 | } | |||
90 | ||||
91 | //---------------------------------------------------------------------- | |||
92 | // Destructor | |||
93 | //---------------------------------------------------------------------- | |||
94 | Args::~Args () | |||
95 | { | |||
96 | } | |||
97 | ||||
98 | void | |||
99 | Args::Dump (Stream *s) | |||
100 | { | |||
101 | const size_t argc = m_argv.size(); | |||
102 | for (size_t i=0; i<argc; ++i) | |||
103 | { | |||
104 | s->Indent(); | |||
105 | const char *arg_cstr = m_argv[i]; | |||
106 | if (arg_cstr) | |||
107 | s->Printf("argv[%zi]=\"%s\"\n", i, arg_cstr); | |||
108 | else | |||
109 | s->Printf("argv[%zi]=NULL\n", i); | |||
110 | } | |||
111 | s->EOL(); | |||
112 | } | |||
113 | ||||
114 | bool | |||
115 | Args::GetCommandString (std::string &command) const | |||
116 | { | |||
117 | command.clear(); | |||
118 | const size_t argc = GetArgumentCount(); | |||
119 | for (size_t i=0; i<argc; ++i) | |||
120 | { | |||
121 | if (i > 0) | |||
122 | command += ' '; | |||
123 | command += m_argv[i]; | |||
124 | } | |||
125 | return argc > 0; | |||
126 | } | |||
127 | ||||
128 | bool | |||
129 | Args::GetQuotedCommandString (std::string &command) const | |||
130 | { | |||
131 | command.clear (); | |||
132 | const size_t argc = GetArgumentCount(); | |||
133 | for (size_t i = 0; i < argc; ++i) | |||
134 | { | |||
135 | if (i > 0) | |||
136 | command.append (1, ' '); | |||
137 | char quote_char = GetArgumentQuoteCharAtIndex(i); | |||
138 | if (quote_char) | |||
139 | { | |||
140 | command.append (1, quote_char); | |||
141 | command.append (m_argv[i]); | |||
142 | command.append (1, quote_char); | |||
143 | } | |||
144 | else | |||
145 | command.append (m_argv[i]); | |||
146 | } | |||
147 | return argc > 0; | |||
148 | } | |||
149 | ||||
150 | void | |||
151 | Args::SetCommandString (const char *command, size_t len) | |||
152 | { | |||
153 | // Use std::string to make sure we get a NULL terminated string we can use | |||
154 | // as "command" could point to a string within a large string.... | |||
155 | std::string null_terminated_command(command, len); | |||
156 | SetCommandString(null_terminated_command.c_str()); | |||
157 | } | |||
158 | ||||
159 | void | |||
160 | Args::SetCommandString (const char *command) | |||
161 | { | |||
162 | m_args.clear(); | |||
163 | m_argv.clear(); | |||
164 | m_args_quote_char.clear(); | |||
165 | ||||
166 | if (command && command[0]) | |||
167 | { | |||
168 | static const char *k_space_separators = " \t"; | |||
169 | static const char *k_space_separators_with_slash_and_quotes = " \t \\'\""; | |||
170 | const char *arg_end = nullptr; | |||
171 | const char *arg_pos; | |||
172 | for (arg_pos = command; | |||
173 | arg_pos && arg_pos[0]; | |||
174 | arg_pos = arg_end) | |||
175 | { | |||
176 | // Skip any leading space separators | |||
177 | const char *arg_start = ::strspn (arg_pos, k_space_separators) + arg_pos; | |||
178 | ||||
179 | // If there were only space separators to the end of the line, then | |||
180 | // we're done. | |||
181 | if (*arg_start == '\0') | |||
182 | break; | |||
183 | ||||
184 | // Arguments can be split into multiple discontiguous pieces, | |||
185 | // for example: | |||
186 | // "Hello ""World" | |||
187 | // this would result in a single argument "Hello World" (without/ | |||
188 | // the quotes) since the quotes would be removed and there is | |||
189 | // not space between the strings. So we need to keep track of the | |||
190 | // current start of each argument piece in "arg_piece_start" | |||
191 | const char *arg_piece_start = arg_start; | |||
192 | arg_pos = arg_piece_start; | |||
193 | ||||
194 | std::string arg; | |||
195 | // Since we can have multiple quotes that form a single command | |||
196 | // in a command like: "Hello "world'!' (which will make a single | |||
197 | // argument "Hello world!") we remember the first quote character | |||
198 | // we encounter and use that for the quote character. | |||
199 | char first_quote_char = '\0'; | |||
200 | char quote_char = '\0'; | |||
201 | bool arg_complete = false; | |||
202 | ||||
203 | do | |||
204 | { | |||
205 | arg_end = ::strcspn (arg_pos, k_space_separators_with_slash_and_quotes) + arg_pos; | |||
206 | ||||
207 | switch (arg_end[0]) | |||
208 | { | |||
209 | default: | |||
210 | assert (!"Unhandled case statement, we must handle this...")((!"Unhandled case statement, we must handle this...") ? static_cast <void> (0) : __assert_fail ("!\"Unhandled case statement, we must handle this...\"" , "/tmp/buildd/llvm-toolchain-snapshot-3.6~svn219601/tools/lldb/source/Interpreter/Args.cpp" , 210, __PRETTY_FUNCTION__)); | |||
211 | break; | |||
212 | ||||
213 | case '\0': | |||
214 | // End of C string | |||
215 | if (arg_piece_start && arg_piece_start[0]) | |||
216 | arg.append (arg_piece_start); | |||
217 | arg_complete = true; | |||
218 | break; | |||
219 | ||||
220 | case '\\': | |||
221 | // Backslash character | |||
222 | switch (arg_end[1]) | |||
223 | { | |||
224 | case '\0': | |||
225 | arg.append (arg_piece_start); | |||
226 | ++arg_end; | |||
227 | arg_complete = true; | |||
228 | break; | |||
229 | ||||
230 | default: | |||
231 | if (quote_char == '\0') | |||
232 | { | |||
233 | arg.append (arg_piece_start, arg_end - arg_piece_start); | |||
234 | if (arg_end[1] != '\0') | |||
235 | { | |||
236 | arg.append (arg_end + 1, 1); | |||
237 | arg_pos = arg_end + 2; | |||
238 | arg_piece_start = arg_pos; | |||
239 | } | |||
240 | } | |||
241 | else | |||
242 | arg_pos = arg_end + 2; | |||
243 | break; | |||
244 | } | |||
245 | break; | |||
246 | ||||
247 | case '"': | |||
248 | case '\'': | |||
249 | case '`': | |||
250 | // Quote characters | |||
251 | if (quote_char) | |||
252 | { | |||
253 | // We found a quote character while inside a quoted | |||
254 | // character argument. If it matches our current quote | |||
255 | // character, this ends the effect of the quotes. If it | |||
256 | // doesn't we ignore it. | |||
257 | if (quote_char == arg_end[0]) | |||
258 | { | |||
259 | arg.append (arg_piece_start, arg_end - arg_piece_start); | |||
260 | // Clear the quote character and let parsing | |||
261 | // continue (we need to watch for things like: | |||
262 | // "Hello ""World" | |||
263 | // "Hello "World | |||
264 | // "Hello "'World' | |||
265 | // All of which will result in a single argument "Hello World" | |||
266 | quote_char = '\0'; // Note that we are no longer inside quotes | |||
267 | arg_pos = arg_end + 1; // Skip the quote character | |||
268 | arg_piece_start = arg_pos; // Note we are starting from later in the string | |||
269 | } | |||
270 | else | |||
271 | { | |||
272 | // different quote, skip it and keep going | |||
273 | arg_pos = arg_end + 1; | |||
274 | } | |||
275 | } | |||
276 | else | |||
277 | { | |||
278 | // We found the start of a quote scope. | |||
279 | // Make sure there isn't a string that precedes | |||
280 | // the start of a quote scope like: | |||
281 | // Hello" World" | |||
282 | // If so, then add the "Hello" to the arg | |||
283 | if (arg_end > arg_piece_start) | |||
284 | arg.append (arg_piece_start, arg_end - arg_piece_start); | |||
285 | ||||
286 | // Enter into a quote scope | |||
287 | quote_char = arg_end[0]; | |||
288 | ||||
289 | if (first_quote_char == '\0') | |||
290 | first_quote_char = quote_char; | |||
291 | ||||
292 | arg_pos = arg_end; | |||
293 | ++arg_pos; // Skip the quote character | |||
294 | arg_piece_start = arg_pos; // Note we are starting from later in the string | |||
295 | ||||
296 | // Skip till the next quote character | |||
297 | const char *end_quote = ::strchr (arg_piece_start, quote_char); | |||
298 | while (end_quote && end_quote[-1] == '\\') | |||
299 | { | |||
300 | // Don't skip the quote character if it is | |||
301 | // preceded by a '\' character | |||
302 | end_quote = ::strchr (end_quote + 1, quote_char); | |||
303 | } | |||
304 | ||||
305 | if (end_quote) | |||
306 | { | |||
307 | if (end_quote > arg_piece_start) | |||
308 | arg.append (arg_piece_start, end_quote - arg_piece_start); | |||
309 | ||||
310 | // If the next character is a space or the end of | |||
311 | // string, this argument is complete... | |||
312 | if (end_quote[1] == ' ' || end_quote[1] == '\t' || end_quote[1] == '\0') | |||
313 | { | |||
314 | arg_complete = true; | |||
315 | arg_end = end_quote + 1; | |||
316 | } | |||
317 | else | |||
318 | { | |||
319 | arg_pos = end_quote + 1; | |||
320 | arg_piece_start = arg_pos; | |||
321 | } | |||
322 | quote_char = '\0'; | |||
323 | } | |||
324 | else | |||
325 | { | |||
326 | // Consume the rest of the string as there was no terminating quote | |||
327 | arg.append(arg_piece_start); | |||
328 | arg_end = arg_piece_start + strlen(arg_piece_start); | |||
329 | arg_complete = true; | |||
330 | } | |||
331 | } | |||
332 | break; | |||
333 | ||||
334 | case ' ': | |||
335 | case '\t': | |||
336 | if (quote_char) | |||
337 | { | |||
338 | // We are currently processing a quoted character and found | |||
339 | // a space character, skip any spaces and keep trying to find | |||
340 | // the end of the argument. | |||
341 | arg_pos = ::strspn (arg_end, k_space_separators) + arg_end; | |||
342 | } | |||
343 | else | |||
344 | { | |||
345 | // We are not inside any quotes, we just found a space after an | |||
346 | // argument | |||
347 | if (arg_end > arg_piece_start) | |||
348 | arg.append (arg_piece_start, arg_end - arg_piece_start); | |||
349 | arg_complete = true; | |||
350 | } | |||
351 | break; | |||
352 | } | |||
353 | } while (!arg_complete); | |||
354 | ||||
355 | m_args.push_back(arg); | |||
356 | m_args_quote_char.push_back (first_quote_char); | |||
357 | } | |||
358 | UpdateArgvFromArgs(); | |||
359 | } | |||
360 | } | |||
361 | ||||
362 | void | |||
363 | Args::UpdateArgsAfterOptionParsing() | |||
364 | { | |||
365 | // Now m_argv might be out of date with m_args, so we need to fix that | |||
366 | arg_cstr_collection::const_iterator argv_pos, argv_end = m_argv.end(); | |||
367 | arg_sstr_collection::iterator args_pos; | |||
368 | arg_quote_char_collection::iterator quotes_pos; | |||
369 | ||||
370 | for (argv_pos = m_argv.begin(), args_pos = m_args.begin(), quotes_pos = m_args_quote_char.begin(); | |||
371 | argv_pos != argv_end && args_pos != m_args.end(); | |||
372 | ++argv_pos) | |||
373 | { | |||
374 | const char *argv_cstr = *argv_pos; | |||
375 | if (argv_cstr == nullptr) | |||
376 | break; | |||
377 | ||||
378 | while (args_pos != m_args.end()) | |||
379 | { | |||
380 | const char *args_cstr = args_pos->c_str(); | |||
381 | if (args_cstr == argv_cstr) | |||
382 | { | |||
383 | // We found the argument that matches the C string in the | |||
384 | // vector, so we can now look for the next one | |||
385 | ++args_pos; | |||
386 | ++quotes_pos; | |||
387 | break; | |||
388 | } | |||
389 | else | |||
390 | { | |||
391 | quotes_pos = m_args_quote_char.erase (quotes_pos); | |||
392 | args_pos = m_args.erase (args_pos); | |||
393 | } | |||
394 | } | |||
395 | } | |||
396 | ||||
397 | if (args_pos != m_args.end()) | |||
398 | m_args.erase (args_pos, m_args.end()); | |||
399 | ||||
400 | if (quotes_pos != m_args_quote_char.end()) | |||
401 | m_args_quote_char.erase (quotes_pos, m_args_quote_char.end()); | |||
402 | } | |||
403 | ||||
404 | void | |||
405 | Args::UpdateArgvFromArgs() | |||
406 | { | |||
407 | m_argv.clear(); | |||
408 | arg_sstr_collection::const_iterator pos, end = m_args.end(); | |||
409 | for (pos = m_args.begin(); pos != end; ++pos) | |||
410 | m_argv.push_back(pos->c_str()); | |||
411 | m_argv.push_back(nullptr); | |||
412 | // Make sure we have enough arg quote chars in the array | |||
413 | if (m_args_quote_char.size() < m_args.size()) | |||
414 | m_args_quote_char.resize (m_argv.size()); | |||
415 | } | |||
416 | ||||
417 | size_t | |||
418 | Args::GetArgumentCount() const | |||
419 | { | |||
420 | if (m_argv.empty()) | |||
421 | return 0; | |||
422 | return m_argv.size() - 1; | |||
423 | } | |||
424 | ||||
425 | const char * | |||
426 | Args::GetArgumentAtIndex (size_t idx) const | |||
427 | { | |||
428 | if (idx < m_argv.size()) | |||
429 | return m_argv[idx]; | |||
430 | return nullptr; | |||
431 | } | |||
432 | ||||
433 | char | |||
434 | Args::GetArgumentQuoteCharAtIndex (size_t idx) const | |||
435 | { | |||
436 | if (idx < m_args_quote_char.size()) | |||
437 | return m_args_quote_char[idx]; | |||
438 | return '\0'; | |||
439 | } | |||
440 | ||||
441 | char ** | |||
442 | Args::GetArgumentVector() | |||
443 | { | |||
444 | if (!m_argv.empty()) | |||
445 | return (char **)&m_argv[0]; | |||
446 | return nullptr; | |||
447 | } | |||
448 | ||||
449 | const char ** | |||
450 | Args::GetConstArgumentVector() const | |||
451 | { | |||
452 | if (!m_argv.empty()) | |||
453 | return (const char **)&m_argv[0]; | |||
454 | return nullptr; | |||
455 | } | |||
456 | ||||
457 | void | |||
458 | Args::Shift () | |||
459 | { | |||
460 | // Don't pop the last NULL terminator from the argv array | |||
461 | if (m_argv.size() > 1) | |||
462 | { | |||
463 | m_argv.erase(m_argv.begin()); | |||
464 | m_args.pop_front(); | |||
465 | if (!m_args_quote_char.empty()) | |||
466 | m_args_quote_char.erase(m_args_quote_char.begin()); | |||
467 | } | |||
468 | } | |||
469 | ||||
470 | const char * | |||
471 | Args::Unshift (const char *arg_cstr, char quote_char) | |||
472 | { | |||
473 | m_args.push_front(arg_cstr); | |||
474 | m_argv.insert(m_argv.begin(), m_args.front().c_str()); | |||
475 | m_args_quote_char.insert(m_args_quote_char.begin(), quote_char); | |||
476 | return GetArgumentAtIndex (0); | |||
477 | } | |||
478 | ||||
479 | void | |||
480 | Args::AppendArguments (const Args &rhs) | |||
481 | { | |||
482 | const size_t rhs_argc = rhs.GetArgumentCount(); | |||
483 | for (size_t i=0; i<rhs_argc; ++i) | |||
484 | AppendArgument(rhs.GetArgumentAtIndex(i)); | |||
485 | } | |||
486 | ||||
487 | void | |||
488 | Args::AppendArguments (const char **argv) | |||
489 | { | |||
490 | if (argv) | |||
491 | { | |||
492 | for (uint32_t i=0; argv[i]; ++i) | |||
493 | AppendArgument(argv[i]); | |||
494 | } | |||
495 | } | |||
496 | ||||
497 | const char * | |||
498 | Args::AppendArgument (const char *arg_cstr, char quote_char) | |||
499 | { | |||
500 | return InsertArgumentAtIndex (GetArgumentCount(), arg_cstr, quote_char); | |||
501 | } | |||
502 | ||||
503 | const char * | |||
504 | Args::InsertArgumentAtIndex (size_t idx, const char *arg_cstr, char quote_char) | |||
505 | { | |||
506 | // Since we are using a std::list to hold onto the copied C string and | |||
507 | // we don't have direct access to the elements, we have to iterate to | |||
508 | // find the value. | |||
509 | arg_sstr_collection::iterator pos, end = m_args.end(); | |||
510 | size_t i = idx; | |||
511 | for (pos = m_args.begin(); i > 0 && pos != end; ++pos) | |||
512 | --i; | |||
513 | ||||
514 | pos = m_args.insert(pos, arg_cstr); | |||
515 | ||||
516 | if (idx >= m_args_quote_char.size()) | |||
517 | { | |||
518 | m_args_quote_char.resize(idx + 1); | |||
519 | m_args_quote_char[idx] = quote_char; | |||
520 | } | |||
521 | else | |||
522 | m_args_quote_char.insert(m_args_quote_char.begin() + idx, quote_char); | |||
523 | ||||
524 | UpdateArgvFromArgs(); | |||
525 | return GetArgumentAtIndex(idx); | |||
526 | } | |||
527 | ||||
528 | const char * | |||
529 | Args::ReplaceArgumentAtIndex (size_t idx, const char *arg_cstr, char quote_char) | |||
530 | { | |||
531 | // Since we are using a std::list to hold onto the copied C string and | |||
532 | // we don't have direct access to the elements, we have to iterate to | |||
533 | // find the value. | |||
534 | arg_sstr_collection::iterator pos, end = m_args.end(); | |||
535 | size_t i = idx; | |||
536 | for (pos = m_args.begin(); i > 0 && pos != end; ++pos) | |||
537 | --i; | |||
538 | ||||
539 | if (pos != end) | |||
540 | { | |||
541 | pos->assign(arg_cstr); | |||
542 | assert(idx < m_argv.size() - 1)((idx < m_argv.size() - 1) ? static_cast<void> (0) : __assert_fail ("idx < m_argv.size() - 1", "/tmp/buildd/llvm-toolchain-snapshot-3.6~svn219601/tools/lldb/source/Interpreter/Args.cpp" , 542, __PRETTY_FUNCTION__)); | |||
543 | m_argv[idx] = pos->c_str(); | |||
544 | if (idx >= m_args_quote_char.size()) | |||
545 | m_args_quote_char.resize(idx + 1); | |||
546 | m_args_quote_char[idx] = quote_char; | |||
547 | return GetArgumentAtIndex(idx); | |||
548 | } | |||
549 | return nullptr; | |||
550 | } | |||
551 | ||||
552 | void | |||
553 | Args::DeleteArgumentAtIndex (size_t idx) | |||
554 | { | |||
555 | // Since we are using a std::list to hold onto the copied C string and | |||
556 | // we don't have direct access to the elements, we have to iterate to | |||
557 | // find the value. | |||
558 | arg_sstr_collection::iterator pos, end = m_args.end(); | |||
559 | size_t i = idx; | |||
560 | for (pos = m_args.begin(); i > 0 && pos != end; ++pos) | |||
561 | --i; | |||
562 | ||||
563 | if (pos != end) | |||
564 | { | |||
565 | m_args.erase (pos); | |||
566 | assert(idx < m_argv.size() - 1)((idx < m_argv.size() - 1) ? static_cast<void> (0) : __assert_fail ("idx < m_argv.size() - 1", "/tmp/buildd/llvm-toolchain-snapshot-3.6~svn219601/tools/lldb/source/Interpreter/Args.cpp" , 566, __PRETTY_FUNCTION__)); | |||
567 | m_argv.erase(m_argv.begin() + idx); | |||
568 | if (idx < m_args_quote_char.size()) | |||
569 | m_args_quote_char.erase(m_args_quote_char.begin() + idx); | |||
570 | } | |||
571 | } | |||
572 | ||||
573 | void | |||
574 | Args::SetArguments (size_t argc, const char **argv) | |||
575 | { | |||
576 | // m_argv will be rebuilt in UpdateArgvFromArgs() below, so there is | |||
577 | // no need to clear it here. | |||
578 | m_args.clear(); | |||
579 | m_args_quote_char.clear(); | |||
580 | ||||
581 | // First copy each string | |||
582 | for (size_t i=0; i<argc; ++i) | |||
583 | { | |||
584 | m_args.push_back (argv[i]); | |||
585 | if ((argv[i][0] == '\'') || (argv[i][0] == '"') || (argv[i][0] == '`')) | |||
586 | m_args_quote_char.push_back (argv[i][0]); | |||
587 | else | |||
588 | m_args_quote_char.push_back ('\0'); | |||
589 | } | |||
590 | ||||
591 | UpdateArgvFromArgs(); | |||
592 | } | |||
593 | ||||
594 | void | |||
595 | Args::SetArguments (const char **argv) | |||
596 | { | |||
597 | // m_argv will be rebuilt in UpdateArgvFromArgs() below, so there is | |||
598 | // no need to clear it here. | |||
599 | m_args.clear(); | |||
600 | m_args_quote_char.clear(); | |||
601 | ||||
602 | if (argv) | |||
603 | { | |||
604 | // First copy each string | |||
605 | for (size_t i=0; argv[i]; ++i) | |||
606 | { | |||
607 | m_args.push_back (argv[i]); | |||
608 | if ((argv[i][0] == '\'') || (argv[i][0] == '"') || (argv[i][0] == '`')) | |||
609 | m_args_quote_char.push_back (argv[i][0]); | |||
610 | else | |||
611 | m_args_quote_char.push_back ('\0'); | |||
612 | } | |||
613 | } | |||
614 | ||||
615 | UpdateArgvFromArgs(); | |||
616 | } | |||
617 | ||||
618 | ||||
619 | Error | |||
620 | Args::ParseOptions (Options &options) | |||
621 | { | |||
622 | StreamString sstr; | |||
623 | Error error; | |||
624 | Option *long_options = options.GetLongOptions(); | |||
625 | if (long_options == nullptr) | |||
| ||||
626 | { | |||
627 | error.SetErrorStringWithFormat("invalid long options"); | |||
628 | return error; | |||
629 | } | |||
630 | ||||
631 | for (int i=0; long_options[i].definition != nullptr; ++i) | |||
632 | { | |||
633 | if (long_options[i].flag == nullptr) | |||
634 | { | |||
635 | if (isprint8(long_options[i].val)) | |||
636 | { | |||
637 | sstr << (char)long_options[i].val; | |||
638 | switch (long_options[i].definition->option_has_arg) | |||
639 | { | |||
640 | default: | |||
641 | case OptionParser::eNoArgument: break; | |||
642 | case OptionParser::eRequiredArgument: sstr << ':'; break; | |||
643 | case OptionParser::eOptionalArgument: sstr << "::"; break; | |||
644 | } | |||
645 | } | |||
646 | } | |||
647 | } | |||
648 | OptionParser::Prepare(); | |||
649 | int val; | |||
650 | while (1) | |||
651 | { | |||
652 | int long_options_index = -1; | |||
653 | val = OptionParser::Parse(GetArgumentCount(), | |||
654 | GetArgumentVector(), | |||
655 | sstr.GetData(), | |||
656 | long_options, | |||
657 | &long_options_index); | |||
658 | if (val == -1) | |||
659 | break; | |||
660 | ||||
661 | // Did we get an error? | |||
662 | if (val == '?') | |||
663 | { | |||
664 | error.SetErrorStringWithFormat("unknown or ambiguous option"); | |||
665 | break; | |||
666 | } | |||
667 | // The option auto-set itself | |||
668 | if (val == 0) | |||
669 | continue; | |||
670 | ||||
671 | ((Options *) &options)->OptionSeen (val); | |||
672 | ||||
673 | // Lookup the long option index | |||
674 | if (long_options_index == -1) | |||
675 | { | |||
676 | for (int i=0; | |||
677 | long_options[i].definition || long_options[i].flag || long_options[i].val; | |||
678 | ++i) | |||
679 | { | |||
680 | if (long_options[i].val == val) | |||
681 | { | |||
682 | long_options_index = i; | |||
683 | break; | |||
684 | } | |||
685 | } | |||
686 | } | |||
687 | // Call the callback with the option | |||
688 | if (long_options_index >= 0) | |||
689 | { | |||
690 | const OptionDefinition *def = long_options[long_options_index].definition; | |||
691 | CommandInterpreter &interpreter = options.GetInterpreter(); | |||
692 | OptionValidator *validator = def->validator; | |||
| ||||
693 | if (validator && !validator->IsValid(*interpreter.GetPlatform(true), interpreter.GetExecutionContext())) | |||
694 | { | |||
695 | error.SetErrorStringWithFormat("Option \"%s\" invalid. %s", def->long_option, def->validator->LongConditionString()); | |||
696 | } | |||
697 | else | |||
698 | { | |||
699 | error = options.SetOptionValue(long_options_index, | |||
700 | (def->option_has_arg == OptionParser::eNoArgument) ? nullptr : OptionParser::GetOptionArgument()); | |||
701 | } | |||
702 | } | |||
703 | else | |||
704 | { | |||
705 | error.SetErrorStringWithFormat("invalid option with value '%i'", val); | |||
706 | } | |||
707 | if (error.Fail()) | |||
708 | break; | |||
709 | } | |||
710 | ||||
711 | // Update our ARGV now that get options has consumed all the options | |||
712 | m_argv.erase(m_argv.begin(), m_argv.begin() + OptionParser::GetOptionIndex()); | |||
713 | UpdateArgsAfterOptionParsing (); | |||
714 | return error; | |||
715 | } | |||
716 | ||||
717 | void | |||
718 | Args::Clear () | |||
719 | { | |||
720 | m_args.clear (); | |||
721 | m_argv.clear (); | |||
722 | m_args_quote_char.clear(); | |||
723 | } | |||
724 | ||||
725 | int32_t | |||
726 | Args::StringToSInt32 (const char *s, int32_t fail_value, int base, bool *success_ptr) | |||
727 | { | |||
728 | if (s && s[0]) | |||
729 | { | |||
730 | char *end = nullptr; | |||
731 | const long sval = ::strtol (s, &end, base); | |||
732 | if (*end == '\0') | |||
733 | { | |||
734 | if (success_ptr) | |||
735 | *success_ptr = ((sval <= INT32_MAX(2147483647)) && (sval >= INT32_MIN(-2147483647-1))); | |||
736 | return (int32_t)sval; // All characters were used, return the result | |||
737 | } | |||
738 | } | |||
739 | if (success_ptr) *success_ptr = false; | |||
740 | return fail_value; | |||
741 | } | |||
742 | ||||
743 | uint32_t | |||
744 | Args::StringToUInt32 (const char *s, uint32_t fail_value, int base, bool *success_ptr) | |||
745 | { | |||
746 | if (s && s[0]) | |||
747 | { | |||
748 | char *end = nullptr; | |||
749 | const unsigned long uval = ::strtoul (s, &end, base); | |||
750 | if (*end == '\0') | |||
751 | { | |||
752 | if (success_ptr) | |||
753 | *success_ptr = (uval <= UINT32_MAX(4294967295U)); | |||
754 | return (uint32_t)uval; // All characters were used, return the result | |||
755 | } | |||
756 | } | |||
757 | if (success_ptr) *success_ptr = false; | |||
758 | return fail_value; | |||
759 | } | |||
760 | ||||
761 | ||||
762 | int64_t | |||
763 | Args::StringToSInt64 (const char *s, int64_t fail_value, int base, bool *success_ptr) | |||
764 | { | |||
765 | if (s && s[0]) | |||
766 | { | |||
767 | char *end = nullptr; | |||
768 | int64_t uval = ::strtoll (s, &end, base); | |||
769 | if (*end == '\0') | |||
770 | { | |||
771 | if (success_ptr) *success_ptr = true; | |||
772 | return uval; // All characters were used, return the result | |||
773 | } | |||
774 | } | |||
775 | if (success_ptr) *success_ptr = false; | |||
776 | return fail_value; | |||
777 | } | |||
778 | ||||
779 | uint64_t | |||
780 | Args::StringToUInt64 (const char *s, uint64_t fail_value, int base, bool *success_ptr) | |||
781 | { | |||
782 | if (s && s[0]) | |||
783 | { | |||
784 | char *end = nullptr; | |||
785 | uint64_t uval = ::strtoull (s, &end, base); | |||
786 | if (*end == '\0') | |||
787 | { | |||
788 | if (success_ptr) *success_ptr = true; | |||
789 | return uval; // All characters were used, return the result | |||
790 | } | |||
791 | } | |||
792 | if (success_ptr) *success_ptr = false; | |||
793 | return fail_value; | |||
794 | } | |||
795 | ||||
796 | lldb::addr_t | |||
797 | Args::StringToAddress (const ExecutionContext *exe_ctx, const char *s, lldb::addr_t fail_value, Error *error_ptr) | |||
798 | { | |||
799 | bool error_set = false; | |||
800 | if (s && s[0]) | |||
801 | { | |||
802 | char *end = nullptr; | |||
803 | lldb::addr_t addr = ::strtoull (s, &end, 0); | |||
804 | if (*end == '\0') | |||
805 | { | |||
806 | if (error_ptr) | |||
807 | error_ptr->Clear(); | |||
808 | return addr; // All characters were used, return the result | |||
809 | } | |||
810 | // Try base 16 with no prefix... | |||
811 | addr = ::strtoull (s, &end, 16); | |||
812 | if (*end == '\0') | |||
813 | { | |||
814 | if (error_ptr) | |||
815 | error_ptr->Clear(); | |||
816 | return addr; // All characters were used, return the result | |||
817 | } | |||
818 | ||||
819 | if (exe_ctx) | |||
820 | { | |||
821 | Target *target = exe_ctx->GetTargetPtr(); | |||
822 | if (target) | |||
823 | { | |||
824 | lldb::ValueObjectSP valobj_sp; | |||
825 | EvaluateExpressionOptions options; | |||
826 | options.SetCoerceToId(false); | |||
827 | options.SetUnwindOnError(true); | |||
828 | options.SetKeepInMemory(false); | |||
829 | options.SetTryAllThreads(true); | |||
830 | ||||
831 | ExpressionResults expr_result = target->EvaluateExpression(s, | |||
832 | exe_ctx->GetFramePtr(), | |||
833 | valobj_sp, | |||
834 | options); | |||
835 | ||||
836 | bool success = false; | |||
837 | if (expr_result == eExpressionCompleted) | |||
838 | { | |||
839 | if (valobj_sp) | |||
840 | valobj_sp = valobj_sp->GetQualifiedRepresentationIfAvailable(valobj_sp->GetDynamicValueType(), true); | |||
841 | // Get the address to watch. | |||
842 | if (valobj_sp) | |||
843 | addr = valobj_sp->GetValueAsUnsigned(fail_value, &success); | |||
844 | if (success) | |||
845 | { | |||
846 | if (error_ptr) | |||
847 | error_ptr->Clear(); | |||
848 | return addr; | |||
849 | } | |||
850 | else | |||
851 | { | |||
852 | if (error_ptr) | |||
853 | { | |||
854 | error_set = true; | |||
855 | error_ptr->SetErrorStringWithFormat("address expression \"%s\" resulted in a value whose type can't be converted to an address: %s", s, valobj_sp->GetTypeName().GetCString()); | |||
856 | } | |||
857 | } | |||
858 | ||||
859 | } | |||
860 | else | |||
861 | { | |||
862 | // Since the compiler can't handle things like "main + 12" we should | |||
863 | // try to do this for now. The compliler doesn't like adding offsets | |||
864 | // to function pointer types. | |||
865 | static RegularExpression g_symbol_plus_offset_regex("^(.*)([-\\+])[[:space:]]*(0x[0-9A-Fa-f]+|[0-9]+)[[:space:]]*$"); | |||
866 | RegularExpression::Match regex_match(3); | |||
867 | if (g_symbol_plus_offset_regex.Execute(s, ®ex_match)) | |||
868 | { | |||
869 | uint64_t offset = 0; | |||
870 | bool add = true; | |||
871 | std::string name; | |||
872 | std::string str; | |||
873 | if (regex_match.GetMatchAtIndex(s, 1, name)) | |||
874 | { | |||
875 | if (regex_match.GetMatchAtIndex(s, 2, str)) | |||
876 | { | |||
877 | add = str[0] == '+'; | |||
878 | ||||
879 | if (regex_match.GetMatchAtIndex(s, 3, str)) | |||
880 | { | |||
881 | offset = Args::StringToUInt64(str.c_str(), 0, 0, &success); | |||
882 | ||||
883 | if (success) | |||
884 | { | |||
885 | Error error; | |||
886 | addr = StringToAddress (exe_ctx, name.c_str(), LLDB_INVALID_ADDRESS(18446744073709551615UL), &error); | |||
887 | if (addr != LLDB_INVALID_ADDRESS(18446744073709551615UL)) | |||
888 | { | |||
889 | if (add) | |||
890 | return addr + offset; | |||
891 | else | |||
892 | return addr - offset; | |||
893 | } | |||
894 | } | |||
895 | } | |||
896 | } | |||
897 | } | |||
898 | } | |||
899 | ||||
900 | if (error_ptr) | |||
901 | { | |||
902 | error_set = true; | |||
903 | error_ptr->SetErrorStringWithFormat("address expression \"%s\" evaluation failed", s); | |||
904 | } | |||
905 | } | |||
906 | } | |||
907 | } | |||
908 | } | |||
909 | if (error_ptr) | |||
910 | { | |||
911 | if (!error_set) | |||
912 | error_ptr->SetErrorStringWithFormat("invalid address expression \"%s\"", s); | |||
913 | } | |||
914 | return fail_value; | |||
915 | } | |||
916 | ||||
917 | const char * | |||
918 | Args::StripSpaces (std::string &s, bool leading, bool trailing, bool return_null_if_empty) | |||
919 | { | |||
920 | static const char *k_white_space = " \t\v"; | |||
921 | if (!s.empty()) | |||
922 | { | |||
923 | if (leading) | |||
924 | { | |||
925 | size_t pos = s.find_first_not_of (k_white_space); | |||
926 | if (pos == std::string::npos) | |||
927 | s.clear(); | |||
928 | else if (pos > 0) | |||
929 | s.erase(0, pos); | |||
930 | } | |||
931 | ||||
932 | if (trailing) | |||
933 | { | |||
934 | size_t rpos = s.find_last_not_of(k_white_space); | |||
935 | if (rpos != std::string::npos && rpos + 1 < s.size()) | |||
936 | s.erase(rpos + 1); | |||
937 | } | |||
938 | } | |||
939 | if (return_null_if_empty && s.empty()) | |||
940 | return nullptr; | |||
941 | return s.c_str(); | |||
942 | } | |||
943 | ||||
944 | bool | |||
945 | Args::StringToBoolean (const char *s, bool fail_value, bool *success_ptr) | |||
946 | { | |||
947 | if (s && s[0]) | |||
948 | { | |||
949 | if (::strcasecmp (s, "false") == 0 || | |||
950 | ::strcasecmp (s, "off") == 0 || | |||
951 | ::strcasecmp (s, "no") == 0 || | |||
952 | ::strcmp (s, "0") == 0) | |||
953 | { | |||
954 | if (success_ptr) | |||
955 | *success_ptr = true; | |||
956 | return false; | |||
957 | } | |||
958 | else | |||
959 | if (::strcasecmp (s, "true") == 0 || | |||
960 | ::strcasecmp (s, "on") == 0 || | |||
961 | ::strcasecmp (s, "yes") == 0 || | |||
962 | ::strcmp (s, "1") == 0) | |||
963 | { | |||
964 | if (success_ptr) *success_ptr = true; | |||
965 | return true; | |||
966 | } | |||
967 | } | |||
968 | if (success_ptr) *success_ptr = false; | |||
969 | return fail_value; | |||
970 | } | |||
971 | ||||
972 | const char * | |||
973 | Args::StringToVersion (const char *s, uint32_t &major, uint32_t &minor, uint32_t &update) | |||
974 | { | |||
975 | major = UINT32_MAX(4294967295U); | |||
976 | minor = UINT32_MAX(4294967295U); | |||
977 | update = UINT32_MAX(4294967295U); | |||
978 | ||||
979 | if (s && s[0]) | |||
980 | { | |||
981 | char *pos = nullptr; | |||
982 | unsigned long uval32 = ::strtoul (s, &pos, 0); | |||
983 | if (pos == s) | |||
984 | return s; | |||
985 | major = uval32; | |||
986 | if (*pos == '\0') | |||
987 | { | |||
988 | return pos; // Decoded major and got end of string | |||
989 | } | |||
990 | else if (*pos == '.') | |||
991 | { | |||
992 | const char *minor_cstr = pos + 1; | |||
993 | uval32 = ::strtoul (minor_cstr, &pos, 0); | |||
994 | if (pos == minor_cstr) | |||
995 | return pos; // Didn't get any digits for the minor version... | |||
996 | minor = uval32; | |||
997 | if (*pos == '.') | |||
998 | { | |||
999 | const char *update_cstr = pos + 1; | |||
1000 | uval32 = ::strtoul (update_cstr, &pos, 0); | |||
1001 | if (pos == update_cstr) | |||
1002 | return pos; | |||
1003 | update = uval32; | |||
1004 | } | |||
1005 | return pos; | |||
1006 | } | |||
1007 | } | |||
1008 | return nullptr; | |||
1009 | } | |||
1010 | ||||
1011 | const char * | |||
1012 | Args::GetShellSafeArgument (const char *unsafe_arg, std::string &safe_arg) | |||
1013 | { | |||
1014 | safe_arg.assign (unsafe_arg); | |||
1015 | size_t prev_pos = 0; | |||
1016 | while (prev_pos < safe_arg.size()) | |||
1017 | { | |||
1018 | // Escape spaces and quotes | |||
1019 | size_t pos = safe_arg.find_first_of(" '\"", prev_pos); | |||
1020 | if (pos != std::string::npos) | |||
1021 | { | |||
1022 | safe_arg.insert (pos, 1, '\\'); | |||
1023 | prev_pos = pos + 2; | |||
1024 | } | |||
1025 | else | |||
1026 | break; | |||
1027 | } | |||
1028 | return safe_arg.c_str(); | |||
1029 | } | |||
1030 | ||||
1031 | ||||
1032 | int64_t | |||
1033 | Args::StringToOptionEnum (const char *s, OptionEnumValueElement *enum_values, int32_t fail_value, Error &error) | |||
1034 | { | |||
1035 | if (enum_values) | |||
1036 | { | |||
1037 | if (s && s[0]) | |||
1038 | { | |||
1039 | for (int i = 0; enum_values[i].string_value != nullptr ; i++) | |||
1040 | { | |||
1041 | if (strstr(enum_values[i].string_value, s) == enum_values[i].string_value) | |||
1042 | { | |||
1043 | error.Clear(); | |||
1044 | return enum_values[i].value; | |||
1045 | } | |||
1046 | } | |||
1047 | } | |||
1048 | ||||
1049 | StreamString strm; | |||
1050 | strm.PutCString ("invalid enumeration value, valid values are: "); | |||
1051 | for (int i = 0; enum_values[i].string_value != nullptr; i++) | |||
1052 | { | |||
1053 | strm.Printf ("%s\"%s\"", | |||
1054 | i > 0 ? ", " : "", | |||
1055 | enum_values[i].string_value); | |||
1056 | } | |||
1057 | error.SetErrorString(strm.GetData()); | |||
1058 | } | |||
1059 | else | |||
1060 | { | |||
1061 | error.SetErrorString ("invalid enumeration argument"); | |||
1062 | } | |||
1063 | return fail_value; | |||
1064 | } | |||
1065 | ||||
1066 | ScriptLanguage | |||
1067 | Args::StringToScriptLanguage (const char *s, ScriptLanguage fail_value, bool *success_ptr) | |||
1068 | { | |||
1069 | if (s && s[0]) | |||
1070 | { | |||
1071 | if ((::strcasecmp (s, "python") == 0) || | |||
1072 | (::strcasecmp (s, "default") == 0 && eScriptLanguagePython == eScriptLanguageDefault)) | |||
1073 | { | |||
1074 | if (success_ptr) *success_ptr = true; | |||
1075 | return eScriptLanguagePython; | |||
1076 | } | |||
1077 | if (::strcasecmp (s, "none")) | |||
1078 | { | |||
1079 | if (success_ptr) *success_ptr = true; | |||
1080 | return eScriptLanguageNone; | |||
1081 | } | |||
1082 | } | |||
1083 | if (success_ptr) *success_ptr = false; | |||
1084 | return fail_value; | |||
1085 | } | |||
1086 | ||||
1087 | Error | |||
1088 | Args::StringToFormat | |||
1089 | ( | |||
1090 | const char *s, | |||
1091 | lldb::Format &format, | |||
1092 | size_t *byte_size_ptr | |||
1093 | ) | |||
1094 | { | |||
1095 | format = eFormatInvalid; | |||
1096 | Error error; | |||
1097 | ||||
1098 | if (s && s[0]) | |||
1099 | { | |||
1100 | if (byte_size_ptr) | |||
1101 | { | |||
1102 | if (isdigit (s[0])) | |||
1103 | { | |||
1104 | char *format_char = nullptr; | |||
1105 | unsigned long byte_size = ::strtoul (s, &format_char, 0); | |||
1106 | if (byte_size != ULONG_MAX(9223372036854775807L *2UL+1UL)) | |||
1107 | *byte_size_ptr = byte_size; | |||
1108 | s = format_char; | |||
1109 | } | |||
1110 | else | |||
1111 | *byte_size_ptr = 0; | |||
1112 | } | |||
1113 | ||||
1114 | const bool partial_match_ok = true; | |||
1115 | if (!FormatManager::GetFormatFromCString (s, partial_match_ok, format)) | |||
1116 | { | |||
1117 | StreamString error_strm; | |||
1118 | error_strm.Printf ("Invalid format character or name '%s'. Valid values are:\n", s); | |||
1119 | for (Format f = eFormatDefault; f < kNumFormats; f = Format(f+1)) | |||
1120 | { | |||
1121 | char format_char = FormatManager::GetFormatAsFormatChar(f); | |||
1122 | if (format_char) | |||
1123 | error_strm.Printf ("'%c' or ", format_char); | |||
1124 | ||||
1125 | error_strm.Printf ("\"%s\"", FormatManager::GetFormatAsCString(f)); | |||
1126 | error_strm.EOL(); | |||
1127 | } | |||
1128 | ||||
1129 | if (byte_size_ptr) | |||
1130 | error_strm.PutCString ("An optional byte size can precede the format character.\n"); | |||
1131 | error.SetErrorString(error_strm.GetString().c_str()); | |||
1132 | } | |||
1133 | ||||
1134 | if (error.Fail()) | |||
1135 | return error; | |||
1136 | } | |||
1137 | else | |||
1138 | { | |||
1139 | error.SetErrorStringWithFormat("%s option string", s ? "empty" : "invalid"); | |||
1140 | } | |||
1141 | return error; | |||
1142 | } | |||
1143 | ||||
1144 | lldb::Encoding | |||
1145 | Args::StringToEncoding (const char *s, lldb::Encoding fail_value) | |||
1146 | { | |||
1147 | if (s && s[0]) | |||
1148 | { | |||
1149 | if (strcmp(s, "uint") == 0) | |||
1150 | return eEncodingUint; | |||
1151 | else if (strcmp(s, "sint") == 0) | |||
1152 | return eEncodingSint; | |||
1153 | else if (strcmp(s, "ieee754") == 0) | |||
1154 | return eEncodingIEEE754; | |||
1155 | else if (strcmp(s, "vector") == 0) | |||
1156 | return eEncodingVector; | |||
1157 | } | |||
1158 | return fail_value; | |||
1159 | } | |||
1160 | ||||
1161 | uint32_t | |||
1162 | Args::StringToGenericRegister (const char *s) | |||
1163 | { | |||
1164 | if (s && s[0]) | |||
1165 | { | |||
1166 | if (strcmp(s, "pc") == 0) | |||
1167 | return LLDB_REGNUM_GENERIC_PC0; | |||
1168 | else if (strcmp(s, "sp") == 0) | |||
1169 | return LLDB_REGNUM_GENERIC_SP1; | |||
1170 | else if (strcmp(s, "fp") == 0) | |||
1171 | return LLDB_REGNUM_GENERIC_FP2; | |||
1172 | else if (strcmp(s, "ra") == 0 || strcmp(s, "lr") == 0) | |||
1173 | return LLDB_REGNUM_GENERIC_RA3; | |||
1174 | else if (strcmp(s, "flags") == 0) | |||
1175 | return LLDB_REGNUM_GENERIC_FLAGS4; | |||
1176 | else if (strncmp(s, "arg", 3) == 0) | |||
1177 | { | |||
1178 | if (s[3] && s[4] == '\0') | |||
1179 | { | |||
1180 | switch (s[3]) | |||
1181 | { | |||
1182 | case '1': return LLDB_REGNUM_GENERIC_ARG15; | |||
1183 | case '2': return LLDB_REGNUM_GENERIC_ARG26; | |||
1184 | case '3': return LLDB_REGNUM_GENERIC_ARG37; | |||
1185 | case '4': return LLDB_REGNUM_GENERIC_ARG48; | |||
1186 | case '5': return LLDB_REGNUM_GENERIC_ARG59; | |||
1187 | case '6': return LLDB_REGNUM_GENERIC_ARG610; | |||
1188 | case '7': return LLDB_REGNUM_GENERIC_ARG711; | |||
1189 | case '8': return LLDB_REGNUM_GENERIC_ARG812; | |||
1190 | } | |||
1191 | } | |||
1192 | } | |||
1193 | } | |||
1194 | return LLDB_INVALID_REGNUM(4294967295U); | |||
1195 | } | |||
1196 | ||||
1197 | ||||
1198 | void | |||
1199 | Args::LongestCommonPrefix (std::string &common_prefix) | |||
1200 | { | |||
1201 | arg_sstr_collection::iterator pos, end = m_args.end(); | |||
1202 | pos = m_args.begin(); | |||
1203 | if (pos == end) | |||
1204 | common_prefix.clear(); | |||
1205 | else | |||
1206 | common_prefix = (*pos); | |||
1207 | ||||
1208 | for (++pos; pos != end; ++pos) | |||
1209 | { | |||
1210 | size_t new_size = (*pos).size(); | |||
1211 | ||||
1212 | // First trim common_prefix if it is longer than the current element: | |||
1213 | if (common_prefix.size() > new_size) | |||
1214 | common_prefix.erase (new_size); | |||
1215 | ||||
1216 | // Then trim it at the first disparity: | |||
1217 | ||||
1218 | for (size_t i = 0; i < common_prefix.size(); i++) | |||
1219 | { | |||
1220 | if ((*pos)[i] != common_prefix[i]) | |||
1221 | { | |||
1222 | common_prefix.erase(i); | |||
1223 | break; | |||
1224 | } | |||
1225 | } | |||
1226 | ||||
1227 | // If we've emptied the common prefix, we're done. | |||
1228 | if (common_prefix.empty()) | |||
1229 | break; | |||
1230 | } | |||
1231 | } | |||
1232 | ||||
1233 | size_t | |||
1234 | Args::FindArgumentIndexForOption (Option *long_options, int long_options_index) | |||
1235 | { | |||
1236 | char short_buffer[3]; | |||
1237 | char long_buffer[255]; | |||
1238 | ::snprintf (short_buffer, sizeof (short_buffer), "-%c", long_options[long_options_index].val); | |||
1239 | ::snprintf (long_buffer, sizeof (long_buffer), "--%s", long_options[long_options_index].definition->long_option); | |||
1240 | size_t end = GetArgumentCount (); | |||
1241 | size_t idx = 0; | |||
1242 | while (idx < end) | |||
1243 | { | |||
1244 | if ((::strncmp (GetArgumentAtIndex (idx), short_buffer, strlen (short_buffer)) == 0) | |||
1245 | || (::strncmp (GetArgumentAtIndex (idx), long_buffer, strlen (long_buffer)) == 0)) | |||
1246 | { | |||
1247 | return idx; | |||
1248 | } | |||
1249 | ++idx; | |||
1250 | } | |||
1251 | ||||
1252 | return end; | |||
1253 | } | |||
1254 | ||||
1255 | bool | |||
1256 | Args::IsPositionalArgument (const char *arg) | |||
1257 | { | |||
1258 | if (arg == nullptr) | |||
1259 | return false; | |||
1260 | ||||
1261 | bool is_positional = true; | |||
1262 | char *cptr = (char *) arg; | |||
1263 | ||||
1264 | if (cptr[0] == '%') | |||
1265 | { | |||
1266 | ++cptr; | |||
1267 | while (isdigit (cptr[0])) | |||
1268 | ++cptr; | |||
1269 | if (cptr[0] != '\0') | |||
1270 | is_positional = false; | |||
1271 | } | |||
1272 | else | |||
1273 | is_positional = false; | |||
1274 | ||||
1275 | return is_positional; | |||
1276 | } | |||
1277 | ||||
1278 | void | |||
1279 | Args::ParseAliasOptions (Options &options, | |||
1280 | CommandReturnObject &result, | |||
1281 | OptionArgVector *option_arg_vector, | |||
1282 | std::string &raw_input_string) | |||
1283 | { | |||
1284 | StreamString sstr; | |||
1285 | int i; | |||
1286 | Option *long_options = options.GetLongOptions(); | |||
1287 | ||||
1288 | if (long_options == nullptr) | |||
1289 | { | |||
1290 | result.AppendError ("invalid long options"); | |||
1291 | result.SetStatus (eReturnStatusFailed); | |||
1292 | return; | |||
1293 | } | |||
1294 | ||||
1295 | for (i = 0; long_options[i].definition != nullptr; ++i) | |||
1296 | { | |||
1297 | if (long_options[i].flag == nullptr) | |||
1298 | { | |||
1299 | sstr << (char) long_options[i].val; | |||
1300 | switch (long_options[i].definition->option_has_arg) | |||
1301 | { | |||
1302 | default: | |||
1303 | case OptionParser::eNoArgument: | |||
1304 | break; | |||
1305 | case OptionParser::eRequiredArgument: | |||
1306 | sstr << ":"; | |||
1307 | break; | |||
1308 | case OptionParser::eOptionalArgument: | |||
1309 | sstr << "::"; | |||
1310 | break; | |||
1311 | } | |||
1312 | } | |||
1313 | } | |||
1314 | ||||
1315 | OptionParser::Prepare(); | |||
1316 | int val; | |||
1317 | while (1) | |||
1318 | { | |||
1319 | int long_options_index = -1; | |||
1320 | val = OptionParser::Parse (GetArgumentCount(), | |||
1321 | GetArgumentVector(), | |||
1322 | sstr.GetData(), | |||
1323 | long_options, | |||
1324 | &long_options_index); | |||
1325 | ||||
1326 | if (val == -1) | |||
1327 | break; | |||
1328 | ||||
1329 | if (val == '?') | |||
1330 | { | |||
1331 | result.AppendError ("unknown or ambiguous option"); | |||
1332 | result.SetStatus (eReturnStatusFailed); | |||
1333 | break; | |||
1334 | } | |||
1335 | ||||
1336 | if (val == 0) | |||
1337 | continue; | |||
1338 | ||||
1339 | options.OptionSeen (val); | |||
1340 | ||||
1341 | // Look up the long option index | |||
1342 | if (long_options_index == -1) | |||
1343 | { | |||
1344 | for (int j = 0; | |||
1345 | long_options[j].definition || long_options[j].flag || long_options[j].val; | |||
1346 | ++j) | |||
1347 | { | |||
1348 | if (long_options[j].val == val) | |||
1349 | { | |||
1350 | long_options_index = j; | |||
1351 | break; | |||
1352 | } | |||
1353 | } | |||
1354 | } | |||
1355 | ||||
1356 | // See if the option takes an argument, and see if one was supplied. | |||
1357 | if (long_options_index >= 0) | |||
1358 | { | |||
1359 | StreamString option_str; | |||
1360 | option_str.Printf ("-%c", val); | |||
1361 | const OptionDefinition *def = long_options[long_options_index].definition; | |||
1362 | int has_arg = (def == nullptr) ? OptionParser::eNoArgument : def->option_has_arg; | |||
1363 | ||||
1364 | switch (has_arg) | |||
1365 | { | |||
1366 | case OptionParser::eNoArgument: | |||
1367 | option_arg_vector->push_back (OptionArgPair (std::string (option_str.GetData()), | |||
1368 | OptionArgValue (OptionParser::eNoArgument, "<no-argument>"))); | |||
1369 | result.SetStatus (eReturnStatusSuccessFinishNoResult); | |||
1370 | break; | |||
1371 | case OptionParser::eRequiredArgument: | |||
1372 | if (OptionParser::GetOptionArgument() != nullptr) | |||
1373 | { | |||
1374 | option_arg_vector->push_back (OptionArgPair (std::string (option_str.GetData()), | |||
1375 | OptionArgValue (OptionParser::eRequiredArgument, | |||
1376 | std::string (OptionParser::GetOptionArgument())))); | |||
1377 | result.SetStatus (eReturnStatusSuccessFinishNoResult); | |||
1378 | } | |||
1379 | else | |||
1380 | { | |||
1381 | result.AppendErrorWithFormat ("Option '%s' is missing argument specifier.\n", | |||
1382 | option_str.GetData()); | |||
1383 | result.SetStatus (eReturnStatusFailed); | |||
1384 | } | |||
1385 | break; | |||
1386 | case OptionParser::eOptionalArgument: | |||
1387 | if (OptionParser::GetOptionArgument() != nullptr) | |||
1388 | { | |||
1389 | option_arg_vector->push_back (OptionArgPair (std::string (option_str.GetData()), | |||
1390 | OptionArgValue (OptionParser::eOptionalArgument, | |||
1391 | std::string (OptionParser::GetOptionArgument())))); | |||
1392 | result.SetStatus (eReturnStatusSuccessFinishNoResult); | |||
1393 | } | |||
1394 | else | |||
1395 | { | |||
1396 | option_arg_vector->push_back (OptionArgPair (std::string (option_str.GetData()), | |||
1397 | OptionArgValue (OptionParser::eOptionalArgument, "<no-argument>"))); | |||
1398 | result.SetStatus (eReturnStatusSuccessFinishNoResult); | |||
1399 | } | |||
1400 | break; | |||
1401 | default: | |||
1402 | result.AppendErrorWithFormat ("error with options table; invalid value in has_arg field for option '%c'.\n", val); | |||
1403 | result.SetStatus (eReturnStatusFailed); | |||
1404 | break; | |||
1405 | } | |||
1406 | } | |||
1407 | else | |||
1408 | { | |||
1409 | result.AppendErrorWithFormat ("Invalid option with value '%c'.\n", val); | |||
1410 | result.SetStatus (eReturnStatusFailed); | |||
1411 | } | |||
1412 | ||||
1413 | if (long_options_index >= 0) | |||
1414 | { | |||
1415 | // Find option in the argument list; also see if it was supposed to take an argument and if one was | |||
1416 | // supplied. Remove option (and argument, if given) from the argument list. Also remove them from | |||
1417 | // the raw_input_string, if one was passed in. | |||
1418 | size_t idx = FindArgumentIndexForOption (long_options, long_options_index); | |||
1419 | if (idx < GetArgumentCount()) | |||
1420 | { | |||
1421 | if (raw_input_string.size() > 0) | |||
1422 | { | |||
1423 | const char *tmp_arg = GetArgumentAtIndex (idx); | |||
1424 | size_t pos = raw_input_string.find (tmp_arg); | |||
1425 | if (pos != std::string::npos) | |||
1426 | raw_input_string.erase (pos, strlen (tmp_arg)); | |||
1427 | } | |||
1428 | ReplaceArgumentAtIndex (idx, ""); | |||
1429 | if ((long_options[long_options_index].definition->option_has_arg != OptionParser::eNoArgument) | |||
1430 | && (OptionParser::GetOptionArgument() != nullptr) | |||
1431 | && (idx+1 < GetArgumentCount()) | |||
1432 | && (strcmp (OptionParser::GetOptionArgument(), GetArgumentAtIndex(idx+1)) == 0)) | |||
1433 | { | |||
1434 | if (raw_input_string.size() > 0) | |||
1435 | { | |||
1436 | const char *tmp_arg = GetArgumentAtIndex (idx+1); | |||
1437 | size_t pos = raw_input_string.find (tmp_arg); | |||
1438 | if (pos != std::string::npos) | |||
1439 | raw_input_string.erase (pos, strlen (tmp_arg)); | |||
1440 | } | |||
1441 | ReplaceArgumentAtIndex (idx+1, ""); | |||
1442 | } | |||
1443 | } | |||
1444 | } | |||
1445 | ||||
1446 | if (!result.Succeeded()) | |||
1447 | break; | |||
1448 | } | |||
1449 | } | |||
1450 | ||||
1451 | void | |||
1452 | Args::ParseArgsForCompletion | |||
1453 | ( | |||
1454 | Options &options, | |||
1455 | OptionElementVector &option_element_vector, | |||
1456 | uint32_t cursor_index | |||
1457 | ) | |||
1458 | { | |||
1459 | StreamString sstr; | |||
1460 | Option *long_options = options.GetLongOptions(); | |||
1461 | option_element_vector.clear(); | |||
1462 | ||||
1463 | if (long_options == nullptr) | |||
1464 | { | |||
1465 | return; | |||
1466 | } | |||
1467 | ||||
1468 | // Leading : tells getopt to return a : for a missing option argument AND | |||
1469 | // to suppress error messages. | |||
1470 | ||||
1471 | sstr << ":"; | |||
1472 | for (int i = 0; long_options[i].definition != nullptr; ++i) | |||
1473 | { | |||
1474 | if (long_options[i].flag == nullptr) | |||
1475 | { | |||
1476 | sstr << (char) long_options[i].val; | |||
1477 | switch (long_options[i].definition->option_has_arg) | |||
1478 | { | |||
1479 | default: | |||
1480 | case OptionParser::eNoArgument: | |||
1481 | break; | |||
1482 | case OptionParser::eRequiredArgument: | |||
1483 | sstr << ":"; | |||
1484 | break; | |||
1485 | case OptionParser::eOptionalArgument: | |||
1486 | sstr << "::"; | |||
1487 | break; | |||
1488 | } | |||
1489 | } | |||
1490 | } | |||
1491 | ||||
1492 | OptionParser::Prepare(); | |||
1493 | OptionParser::EnableError(false); | |||
1494 | ||||
1495 | int val; | |||
1496 | const OptionDefinition *opt_defs = options.GetDefinitions(); | |||
1497 | ||||
1498 | // Fooey... OptionParser::Parse permutes the GetArgumentVector to move the options to the front. | |||
1499 | // So we have to build another Arg and pass that to OptionParser::Parse so it doesn't | |||
1500 | // change the one we have. | |||
1501 | ||||
1502 | std::vector<const char *> dummy_vec (GetArgumentVector(), GetArgumentVector() + GetArgumentCount() + 1); | |||
1503 | ||||
1504 | bool failed_once = false; | |||
1505 | uint32_t dash_dash_pos = -1; | |||
1506 | ||||
1507 | while (1) | |||
1508 | { | |||
1509 | bool missing_argument = false; | |||
1510 | int long_options_index = -1; | |||
1511 | ||||
1512 | val = OptionParser::Parse (dummy_vec.size() - 1, | |||
1513 | (char *const *) &dummy_vec.front(), | |||
1514 | sstr.GetData(), | |||
1515 | long_options, | |||
1516 | &long_options_index); | |||
1517 | ||||
1518 | if (val == -1) | |||
1519 | { | |||
1520 | // When we're completing a "--" which is the last option on line, | |||
1521 | if (failed_once) | |||
1522 | break; | |||
1523 | ||||
1524 | failed_once = true; | |||
1525 | ||||
1526 | // If this is a bare "--" we mark it as such so we can complete it successfully later. | |||
1527 | // Handling the "--" is a little tricky, since that may mean end of options or arguments, or the | |||
1528 | // user might want to complete options by long name. I make this work by checking whether the | |||
1529 | // cursor is in the "--" argument, and if so I assume we're completing the long option, otherwise | |||
1530 | // I let it pass to OptionParser::Parse which will terminate the option parsing. | |||
1531 | // Note, in either case we continue parsing the line so we can figure out what other options | |||
1532 | // were passed. This will be useful when we come to restricting completions based on what other | |||
1533 | // options we've seen on the line. | |||
1534 | ||||
1535 | if (static_cast<size_t>(OptionParser::GetOptionIndex()) < dummy_vec.size() - 1 | |||
1536 | && (strcmp (dummy_vec[OptionParser::GetOptionIndex()-1], "--") == 0)) | |||
1537 | { | |||
1538 | dash_dash_pos = OptionParser::GetOptionIndex() - 1; | |||
1539 | if (static_cast<size_t>(OptionParser::GetOptionIndex() - 1) == cursor_index) | |||
1540 | { | |||
1541 | option_element_vector.push_back (OptionArgElement (OptionArgElement::eBareDoubleDash, OptionParser::GetOptionIndex() - 1, | |||
1542 | OptionArgElement::eBareDoubleDash)); | |||
1543 | continue; | |||
1544 | } | |||
1545 | else | |||
1546 | break; | |||
1547 | } | |||
1548 | else | |||
1549 | break; | |||
1550 | } | |||
1551 | else if (val == '?') | |||
1552 | { | |||
1553 | option_element_vector.push_back (OptionArgElement (OptionArgElement::eUnrecognizedArg, OptionParser::GetOptionIndex() - 1, | |||
1554 | OptionArgElement::eUnrecognizedArg)); | |||
1555 | continue; | |||
1556 | } | |||
1557 | else if (val == 0) | |||
1558 | { | |||
1559 | continue; | |||
1560 | } | |||
1561 | else if (val == ':') | |||
1562 | { | |||
1563 | // This is a missing argument. | |||
1564 | val = OptionParser::GetOptionErrorCause(); | |||
1565 | missing_argument = true; | |||
1566 | } | |||
1567 | ||||
1568 | ((Options *) &options)->OptionSeen (val); | |||
1569 | ||||
1570 | // Look up the long option index | |||
1571 | if (long_options_index == -1) | |||
1572 | { | |||
1573 | for (int j = 0; | |||
1574 | long_options[j].definition || long_options[j].flag || long_options[j].val; | |||
1575 | ++j) | |||
1576 | { | |||
1577 | if (long_options[j].val == val) | |||
1578 | { | |||
1579 | long_options_index = j; | |||
1580 | break; | |||
1581 | } | |||
1582 | } | |||
1583 | } | |||
1584 | ||||
1585 | // See if the option takes an argument, and see if one was supplied. | |||
1586 | if (long_options_index >= 0) | |||
1587 | { | |||
1588 | int opt_defs_index = -1; | |||
1589 | for (int i = 0; ; i++) | |||
1590 | { | |||
1591 | if (opt_defs[i].short_option == 0) | |||
1592 | break; | |||
1593 | else if (opt_defs[i].short_option == val) | |||
1594 | { | |||
1595 | opt_defs_index = i; | |||
1596 | break; | |||
1597 | } | |||
1598 | } | |||
1599 | ||||
1600 | const OptionDefinition *def = long_options[long_options_index].definition; | |||
1601 | int has_arg = (def == nullptr) ? OptionParser::eNoArgument : def->option_has_arg; | |||
1602 | switch (has_arg) | |||
1603 | { | |||
1604 | case OptionParser::eNoArgument: | |||
1605 | option_element_vector.push_back (OptionArgElement (opt_defs_index, OptionParser::GetOptionIndex() - 1, 0)); | |||
1606 | break; | |||
1607 | case OptionParser::eRequiredArgument: | |||
1608 | if (OptionParser::GetOptionArgument() != nullptr) | |||
1609 | { | |||
1610 | int arg_index; | |||
1611 | if (missing_argument) | |||
1612 | arg_index = -1; | |||
1613 | else | |||
1614 | arg_index = OptionParser::GetOptionIndex() - 1; | |||
1615 | ||||
1616 | option_element_vector.push_back (OptionArgElement (opt_defs_index, OptionParser::GetOptionIndex() - 2, arg_index)); | |||
1617 | } | |||
1618 | else | |||
1619 | { | |||
1620 | option_element_vector.push_back (OptionArgElement (opt_defs_index, OptionParser::GetOptionIndex() - 1, -1)); | |||
1621 | } | |||
1622 | break; | |||
1623 | case OptionParser::eOptionalArgument: | |||
1624 | if (OptionParser::GetOptionArgument() != nullptr) | |||
1625 | { | |||
1626 | option_element_vector.push_back (OptionArgElement (opt_defs_index, OptionParser::GetOptionIndex() - 2, OptionParser::GetOptionIndex() - 1)); | |||
1627 | } | |||
1628 | else | |||
1629 | { | |||
1630 | option_element_vector.push_back (OptionArgElement (opt_defs_index, OptionParser::GetOptionIndex() - 2, OptionParser::GetOptionIndex() - 1)); | |||
1631 | } | |||
1632 | break; | |||
1633 | default: | |||
1634 | // The options table is messed up. Here we'll just continue | |||
1635 | option_element_vector.push_back (OptionArgElement (OptionArgElement::eUnrecognizedArg, OptionParser::GetOptionIndex() - 1, | |||
1636 | OptionArgElement::eUnrecognizedArg)); | |||
1637 | break; | |||
1638 | } | |||
1639 | } | |||
1640 | else | |||
1641 | { | |||
1642 | option_element_vector.push_back (OptionArgElement (OptionArgElement::eUnrecognizedArg, OptionParser::GetOptionIndex() - 1, | |||
1643 | OptionArgElement::eUnrecognizedArg)); | |||
1644 | } | |||
1645 | } | |||
1646 | ||||
1647 | // Finally we have to handle the case where the cursor index points at a single "-". We want to mark that in | |||
1648 | // the option_element_vector, but only if it is not after the "--". But it turns out that OptionParser::Parse just ignores | |||
1649 | // an isolated "-". So we have to look it up by hand here. We only care if it is AT the cursor position. | |||
1650 | // Note, a single quoted dash is not the same as a single dash... | |||
1651 | ||||
1652 | if ((static_cast<int32_t>(dash_dash_pos) == -1 || cursor_index < dash_dash_pos) | |||
1653 | && m_args_quote_char[cursor_index] == '\0' | |||
1654 | && strcmp (GetArgumentAtIndex(cursor_index), "-") == 0) | |||
1655 | { | |||
1656 | option_element_vector.push_back (OptionArgElement (OptionArgElement::eBareDash, cursor_index, | |||
1657 | OptionArgElement::eBareDash)); | |||
1658 | ||||
1659 | } | |||
1660 | } | |||
1661 | ||||
1662 | void | |||
1663 | Args::EncodeEscapeSequences (const char *src, std::string &dst) | |||
1664 | { | |||
1665 | dst.clear(); | |||
1666 | if (src) | |||
1667 | { | |||
1668 | for (const char *p = src; *p != '\0'; ++p) | |||
1669 | { | |||
1670 | size_t non_special_chars = ::strcspn (p, "\\"); | |||
1671 | if (non_special_chars > 0) | |||
1672 | { | |||
1673 | dst.append(p, non_special_chars); | |||
1674 | p += non_special_chars; | |||
1675 | if (*p == '\0') | |||
1676 | break; | |||
1677 | } | |||
1678 | ||||
1679 | if (*p == '\\') | |||
1680 | { | |||
1681 | ++p; // skip the slash | |||
1682 | switch (*p) | |||
1683 | { | |||
1684 | case 'a' : dst.append(1, '\a'); break; | |||
1685 | case 'b' : dst.append(1, '\b'); break; | |||
1686 | case 'f' : dst.append(1, '\f'); break; | |||
1687 | case 'n' : dst.append(1, '\n'); break; | |||
1688 | case 'r' : dst.append(1, '\r'); break; | |||
1689 | case 't' : dst.append(1, '\t'); break; | |||
1690 | case 'v' : dst.append(1, '\v'); break; | |||
1691 | case '\\': dst.append(1, '\\'); break; | |||
1692 | case '\'': dst.append(1, '\''); break; | |||
1693 | case '"' : dst.append(1, '"'); break; | |||
1694 | case '0' : | |||
1695 | // 1 to 3 octal chars | |||
1696 | { | |||
1697 | // Make a string that can hold onto the initial zero char, | |||
1698 | // up to 3 octal digits, and a terminating NULL. | |||
1699 | char oct_str[5] = { '\0', '\0', '\0', '\0', '\0' }; | |||
1700 | ||||
1701 | int i; | |||
1702 | for (i=0; (p[i] >= '0' && p[i] <= '7') && i<4; ++i) | |||
1703 | oct_str[i] = p[i]; | |||
1704 | ||||
1705 | // We don't want to consume the last octal character since | |||
1706 | // the main for loop will do this for us, so we advance p by | |||
1707 | // one less than i (even if i is zero) | |||
1708 | p += i - 1; | |||
1709 | unsigned long octal_value = ::strtoul (oct_str, nullptr, 8); | |||
1710 | if (octal_value <= UINT8_MAX(255)) | |||
1711 | { | |||
1712 | dst.append(1, (char)octal_value); | |||
1713 | } | |||
1714 | } | |||
1715 | break; | |||
1716 | ||||
1717 | case 'x': | |||
1718 | // hex number in the format | |||
1719 | if (isxdigit(p[1])) | |||
1720 | { | |||
1721 | ++p; // Skip the 'x' | |||
1722 | ||||
1723 | // Make a string that can hold onto two hex chars plus a | |||
1724 | // NULL terminator | |||
1725 | char hex_str[3] = { *p, '\0', '\0' }; | |||
1726 | if (isxdigit(p[1])) | |||
1727 | { | |||
1728 | ++p; // Skip the first of the two hex chars | |||
1729 | hex_str[1] = *p; | |||
1730 | } | |||
1731 | ||||
1732 | unsigned long hex_value = strtoul (hex_str, nullptr, 16); | |||
1733 | if (hex_value <= UINT8_MAX(255)) | |||
1734 | dst.append (1, (char)hex_value); | |||
1735 | } | |||
1736 | else | |||
1737 | { | |||
1738 | dst.append(1, 'x'); | |||
1739 | } | |||
1740 | break; | |||
1741 | ||||
1742 | default: | |||
1743 | // Just desensitize any other character by just printing what | |||
1744 | // came after the '\' | |||
1745 | dst.append(1, *p); | |||
1746 | break; | |||
1747 | ||||
1748 | } | |||
1749 | } | |||
1750 | } | |||
1751 | } | |||
1752 | } | |||
1753 | ||||
1754 | ||||
1755 | void | |||
1756 | Args::ExpandEscapedCharacters (const char *src, std::string &dst) | |||
1757 | { | |||
1758 | dst.clear(); | |||
1759 | if (src) | |||
1760 | { | |||
1761 | for (const char *p = src; *p != '\0'; ++p) | |||
1762 | { | |||
1763 | if (isprint8(*p)) | |||
1764 | dst.append(1, *p); | |||
1765 | else | |||
1766 | { | |||
1767 | switch (*p) | |||
1768 | { | |||
1769 | case '\a': dst.append("\\a"); break; | |||
1770 | case '\b': dst.append("\\b"); break; | |||
1771 | case '\f': dst.append("\\f"); break; | |||
1772 | case '\n': dst.append("\\n"); break; | |||
1773 | case '\r': dst.append("\\r"); break; | |||
1774 | case '\t': dst.append("\\t"); break; | |||
1775 | case '\v': dst.append("\\v"); break; | |||
1776 | case '\'': dst.append("\\'"); break; | |||
1777 | case '"': dst.append("\\\""); break; | |||
1778 | case '\\': dst.append("\\\\"); break; | |||
1779 | default: | |||
1780 | { | |||
1781 | // Just encode as octal | |||
1782 | dst.append("\\0"); | |||
1783 | char octal_str[32]; | |||
1784 | snprintf(octal_str, sizeof(octal_str), "%o", *p); | |||
1785 | dst.append(octal_str); | |||
1786 | } | |||
1787 | break; | |||
1788 | } | |||
1789 | } | |||
1790 | } | |||
1791 | } | |||
1792 | } | |||
1793 |