1 //===-- CommandObjectSource.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 "CommandObjectCommands.h"
11 
12 // C Includes
13 // C++ Includes
14 // Other libraries and framework includes
15 // Project includes
16 #include "lldb/Interpreter/Args.h"
17 #include "lldb/Core/Debugger.h"
18 #include "lldb/Interpreter/CommandInterpreter.h"
19 #include "lldb/Interpreter/CommandReturnObject.h"
20 #include "lldb/Interpreter/Options.h"
21 
22 using namespace lldb;
23 using namespace lldb_private;
24 
25 const char *k_space_characters = "\t\n\v\f\r ";
26 
27 //-------------------------------------------------------------------------
28 // CommandObjectCommandsSource
29 //-------------------------------------------------------------------------
30 
31 class CommandObjectCommandsSource : public CommandObject
32 {
33 public:
34     CommandObjectCommandsSource(CommandInterpreter &interpreter) :
35         CommandObject (interpreter,
36                        "commands source",
37                        "Read in debugger commands from the file <filename> and execute them.",
38                        NULL)
39     {
40         CommandArgumentEntry arg;
41         CommandArgumentData file_arg;
42 
43         // Define the first (and only) variant of this arg.
44         file_arg.arg_type = eArgTypeFilename;
45         file_arg.arg_repetition = eArgRepeatPlain;
46 
47         // There is only one variant this argument could be; put it into the argument entry.
48         arg.push_back (file_arg);
49 
50         // Push the data for the first argument into the m_arguments vector.
51         m_arguments.push_back (arg);
52     }
53 
54     ~CommandObjectCommandsSource ()
55     {
56     }
57 
58     bool
59     Execute
60     (
61         Args& args,
62         CommandReturnObject &result
63     )
64     {
65         const int argc = args.GetArgumentCount();
66         if (argc == 1)
67         {
68             const char *filename = args.GetArgumentAtIndex(0);
69             bool success = true;
70 
71             result.AppendMessageWithFormat ("Executing commands in '%s'.\n", filename);
72 
73             FileSpec cmd_file (filename, true);
74             if (cmd_file.Exists())
75             {
76                 STLStringArray commands;
77                 success = cmd_file.ReadFileLines (commands);
78 
79                 STLStringArray::iterator pos = commands.begin();
80 
81                 // Trim out any empty lines or lines that start with the comment
82                 // char '#'
83                 while (pos != commands.end())
84                 {
85                     size_t non_space = pos->find_first_not_of (k_space_characters);
86                     // Check for empty line or comment line (lines whose first
87                     // non-space character is a '#')
88                     if (non_space == std::string::npos || (*pos)[non_space] == '#')
89                         pos = commands.erase(pos);
90                     else
91                         ++pos;
92                 }
93 
94                 if (commands.size() > 0)
95                 {
96                     const size_t num_commands = commands.size();
97                     size_t i;
98                     for (i = 0; i<num_commands; ++i)
99                     {
100                         result.GetOutputStream().Printf ("%s %s\n",
101                                                          m_interpreter.GetPrompt(),
102                                                          commands[i].c_str());
103                         if (!m_interpreter.HandleCommand(commands[i].c_str(), false, result))
104                             break;
105                     }
106 
107                     if (i < num_commands)
108                     {
109                         result.AppendErrorWithFormat("Aborting source of '%s' after command '%s' failed.\n",
110                                                      filename, commands[i].c_str());
111                         result.SetStatus (eReturnStatusSuccessFinishResult);
112                     }
113                     else
114                     {
115                         success = true;
116                         result.SetStatus (eReturnStatusFailed);
117                     }
118                 }
119             }
120             else
121             {
122                 result.AppendErrorWithFormat ("File '%s' does not exist.\n", filename);
123                 result.SetStatus (eReturnStatusFailed);
124                 success = false;
125             }
126 
127             if (success)
128             {
129                 result.SetStatus (eReturnStatusSuccessFinishNoResult);
130             }
131         }
132         else
133         {
134             result.AppendErrorWithFormat("'%s' takes exactly one executable filename argument.\n", GetCommandName());
135             result.SetStatus (eReturnStatusFailed);
136         }
137         return result.Succeeded();
138 
139     }
140 };
141 
142 #pragma mark CommandObjectCommandsAlias
143 //-------------------------------------------------------------------------
144 // CommandObjectCommandsAlias
145 //-------------------------------------------------------------------------
146 
147 class CommandObjectCommandsAlias : public CommandObject
148 {
149 public:
150     CommandObjectCommandsAlias (CommandInterpreter &interpreter) :
151         CommandObject (interpreter,
152                        "commands alias",
153                        "Allow users to define their own debugger command abbreviations.",
154                        NULL)
155     {
156         SetHelpLong(
157     "'alias' allows the user to create a short-cut or abbreviation for long \n\
158     commands, multi-word commands, and commands that take particular options. \n\
159     Below are some simple examples of how one might use the 'alias' command: \n\
160     \n    'commands alias sc script'           // Creates the abbreviation 'sc' for the 'script' \n\
161                                          // command. \n\
162     'commands alias bp breakpoint'       // Creates the abbreviation 'bp' for the 'breakpoint' \n\
163                                          // command.  Since breakpoint commands are two-word \n\
164                                          // commands, the user will still need to enter the \n\
165                                          // second word after 'bp', e.g. 'bp enable' or \n\
166                                          // 'bp delete'. \n\
167     'commands alias bpl breakpoint list' // Creates the abbreviation 'bpl' for the \n\
168                                          // two-word command 'breakpoint list'. \n\
169     \nAn alias can include some options for the command, with the values either \n\
170     filled in at the time the alias is created, or specified as positional \n\
171     arguments, to be filled in when the alias is invoked.  The following example \n\
172     shows how to create aliases with options: \n\
173     \n\
174     'commands alias bfl breakpoint set -f %1 -l %2' \n\
175     \nThis creates the abbreviation 'bfl' (for break-file-line), with the -f and -l \n\
176     options already part of the alias.  So if the user wants to set a breakpoint \n\
177     by file and line without explicitly having to use the -f and -l options, the \n\
178     user can now use 'bfl' instead.  The '%1' and '%2' are positional placeholders \n\
179     for the actual arguments that will be passed when the alias command is used. \n\
180     The number in the placeholder refers to the position/order the actual value \n\
181     occupies when the alias is used.  So all the occurrences of '%1' in the alias \n\
182     will be replaced with the first argument, all the occurrences of '%2' in the \n\
183     alias will be replaced with the second argument, and so on.  This also allows \n\
184     actual arguments to be used multiple times within an alias (see 'process \n\
185     launch' example below).  So in the 'bfl' case, the actual file value will be \n\
186     filled in with the first argument following 'bfl' and the actual line number \n\
187     value will be filled in with the second argument.  The user would use this \n\
188     alias as follows: \n\
189     \n    (lldb)  commands alias bfl breakpoint set -f %1 -l %2 \n\
190     <... some time later ...> \n\
191     (lldb)  bfl my-file.c 137 \n\
192     \nThis would be the same as if the user had entered \n\
193     'breakpoint set -f my-file.c -l 137'. \n\
194     \nAnother example: \n\
195     \n    (lldb)  commands alias pltty  process launch -s -o %1 -e %1 \n\
196     (lldb)  pltty /dev/tty0 \n\
197            // becomes 'process launch -s -o /dev/tty0 -e /dev/tty0' \n\
198     \nIf the user always wanted to pass the same value to a particular option, the \n\
199     alias could be defined with that value directly in the alias as a constant, \n\
200     rather than using a positional placeholder: \n\
201     \n    commands alias bl3  breakpoint set -f %1 -l 3  // Always sets a breakpoint on line \n\
202                                                    // 3 of whatever file is indicated. \n");
203 
204         CommandArgumentEntry arg1;
205         CommandArgumentEntry arg2;
206         CommandArgumentEntry arg3;
207         CommandArgumentData alias_arg;
208         CommandArgumentData cmd_arg;
209         CommandArgumentData options_arg;
210 
211         // Define the first (and only) variant of this arg.
212         alias_arg.arg_type = eArgTypeAliasName;
213         alias_arg.arg_repetition = eArgRepeatPlain;
214 
215         // There is only one variant this argument could be; put it into the argument entry.
216         arg1.push_back (alias_arg);
217 
218         // Define the first (and only) variant of this arg.
219         cmd_arg.arg_type = eArgTypeCommandName;
220         cmd_arg.arg_repetition = eArgRepeatPlain;
221 
222         // There is only one variant this argument could be; put it into the argument entry.
223         arg2.push_back (cmd_arg);
224 
225         // Define the first (and only) variant of this arg.
226         options_arg.arg_type = eArgTypeAliasOptions;
227         options_arg.arg_repetition = eArgRepeatOptional;
228 
229         // There is only one variant this argument could be; put it into the argument entry.
230         arg3.push_back (options_arg);
231 
232         // Push the data for the first argument into the m_arguments vector.
233         m_arguments.push_back (arg1);
234         m_arguments.push_back (arg2);
235         m_arguments.push_back (arg3);
236     }
237 
238     ~CommandObjectCommandsAlias ()
239     {
240     }
241 
242 
243     bool
244     Execute
245     (
246         Args& args,
247         CommandReturnObject &result
248     )
249     {
250         size_t argc = args.GetArgumentCount();
251 
252         if (argc < 2)
253         {
254             result.AppendError ("'alias' requires at least two arguments");
255             result.SetStatus (eReturnStatusFailed);
256             return false;
257         }
258 
259         const std::string alias_command = args.GetArgumentAtIndex(0);
260         const std::string actual_command = args.GetArgumentAtIndex(1);
261 
262         args.Shift();  // Shift the alias command word off the argument vector.
263         args.Shift();  // Shift the old command word off the argument vector.
264 
265         // Verify that the command is alias'able, and get the appropriate command object.
266 
267         if (m_interpreter.CommandExists (alias_command.c_str()))
268         {
269             result.AppendErrorWithFormat ("'%s' is a permanent debugger command and cannot be redefined.\n",
270                                          alias_command.c_str());
271             result.SetStatus (eReturnStatusFailed);
272         }
273         else
274         {
275              CommandObjectSP command_obj_sp(m_interpreter.GetCommandSPExact (actual_command.c_str(), true));
276              CommandObjectSP subcommand_obj_sp;
277              bool use_subcommand = false;
278              if (command_obj_sp.get())
279              {
280                  CommandObject *cmd_obj = command_obj_sp.get();
281                  CommandObject *sub_cmd_obj = NULL;
282                  OptionArgVectorSP option_arg_vector_sp = OptionArgVectorSP (new OptionArgVector);
283                  OptionArgVector *option_arg_vector = option_arg_vector_sp.get();
284 
285                  if (cmd_obj->IsMultiwordObject())
286                  {
287                      if (argc >= 3)
288                      {
289                          const std::string sub_command = args.GetArgumentAtIndex(0);
290                          assert (sub_command.length() != 0);
291                          subcommand_obj_sp =
292                                            (((CommandObjectMultiword *) cmd_obj)->GetSubcommandSP (sub_command.c_str()));
293                          if (subcommand_obj_sp.get())
294                          {
295                              sub_cmd_obj = subcommand_obj_sp.get();
296                              use_subcommand = true;
297                              args.Shift();  // Shift the sub_command word off the argument vector.
298                          }
299                          else
300                          {
301                              result.AppendErrorWithFormat ("Error occurred while attempting to look up command '%s %s'.\n",
302                                                           alias_command.c_str(), sub_command.c_str());
303                              result.SetStatus (eReturnStatusFailed);
304                              return false;
305                          }
306                      }
307                  }
308 
309                  // Verify & handle any options/arguments passed to the alias command
310 
311                  if (args.GetArgumentCount () > 0)
312                  {
313                      //if ((!use_subcommand && (cmd_obj->WantsRawCommandString()))
314                      //    || (use_subcommand && (sub_cmd_obj->WantsRawCommandString())))
315                      //{
316                      //    result.AppendErrorWithFormat ("'%s' cannot be aliased with any options or arguments.\n",
317                      //                                 (use_subcommand ? sub_cmd_obj->GetCommandName()
318                      //                                                 : cmd_obj->GetCommandName()));
319                      //    result.SetStatus (eReturnStatusFailed);
320                      //    return false;
321                      //}
322 
323                      // options or arguments have been passed to the alias command, and must be
324                      // verified & processed here.
325                      if ((!use_subcommand && (cmd_obj->GetOptions() != NULL))
326                          || (use_subcommand && (sub_cmd_obj->GetOptions() != NULL)))
327                      {
328                          Options *options;
329                          if (use_subcommand)
330                              options = sub_cmd_obj->GetOptions();
331                          else
332                              options = cmd_obj->GetOptions();
333                          options->ResetOptionValues ();
334                          args.Unshift ("dummy_arg");
335                          args.ParseAliasOptions (*options, result, option_arg_vector);
336                          args.Shift ();
337                          if (result.Succeeded())
338                              options->VerifyPartialOptions (result);
339                          if (!result.Succeeded() && result.GetStatus() != lldb::eReturnStatusStarted)
340                         {
341                             result.AppendError ("Unable to create requested command alias.\n");
342                         }
343                      }
344 
345                      // Anything remaining in args must be a plain argument.
346 
347                      argc = args.GetArgumentCount();
348                      for (size_t i = 0; i < argc; ++i)
349                         if (strcmp (args.GetArgumentAtIndex (i), "") != 0)
350                              option_arg_vector->push_back (OptionArgPair ("<argument>",
351                                                                           std::string (args.GetArgumentAtIndex (i))));
352                  }
353 
354                  // Create the alias.
355 
356                  if (m_interpreter.AliasExists (alias_command.c_str())
357                      || m_interpreter.UserCommandExists (alias_command.c_str()))
358                  {
359                      OptionArgVectorSP tmp_option_arg_sp (m_interpreter.GetAliasOptions (alias_command.c_str()));
360                      if (tmp_option_arg_sp.get())
361                      {
362                          if (option_arg_vector->size() == 0)
363                              m_interpreter.RemoveAliasOptions (alias_command.c_str());
364                      }
365                      result.AppendWarningWithFormat ("Overwriting existing definition for '%s'.\n",
366                                                      alias_command.c_str());
367                  }
368 
369                  if (use_subcommand)
370                      m_interpreter.AddAlias (alias_command.c_str(), subcommand_obj_sp);
371                  else
372                      m_interpreter.AddAlias (alias_command.c_str(), command_obj_sp);
373                  if (option_arg_vector->size() > 0)
374                      m_interpreter.AddOrReplaceAliasOptions (alias_command.c_str(), option_arg_vector_sp);
375                  result.SetStatus (eReturnStatusSuccessFinishNoResult);
376              }
377              else
378              {
379                  result.AppendErrorWithFormat ("'%s' is not an existing command.\n", actual_command.c_str());
380                  result.SetStatus (eReturnStatusFailed);
381              }
382         }
383 
384         return result.Succeeded();
385     }
386 };
387 
388 #pragma mark CommandObjectCommandsUnalias
389 //-------------------------------------------------------------------------
390 // CommandObjectCommandsUnalias
391 //-------------------------------------------------------------------------
392 
393 class CommandObjectCommandsUnalias : public CommandObject
394 {
395 public:
396     CommandObjectCommandsUnalias (CommandInterpreter &interpreter) :
397         CommandObject (interpreter,
398                        "commands unalias",
399                        "Allow the user to remove/delete a user-defined command abbreviation.",
400                        NULL)
401     {
402         CommandArgumentEntry arg;
403         CommandArgumentData alias_arg;
404 
405         // Define the first (and only) variant of this arg.
406         alias_arg.arg_type = eArgTypeAliasName;
407         alias_arg.arg_repetition = eArgRepeatPlain;
408 
409         // There is only one variant this argument could be; put it into the argument entry.
410         arg.push_back (alias_arg);
411 
412         // Push the data for the first argument into the m_arguments vector.
413         m_arguments.push_back (arg);
414     }
415 
416     ~CommandObjectCommandsUnalias()
417     {
418     }
419 
420 
421     bool
422     Execute
423     (
424         Args& args,
425         CommandReturnObject &result
426     )
427     {
428         CommandObject::CommandMap::iterator pos;
429         CommandObject *cmd_obj;
430 
431         if (args.GetArgumentCount() != 0)
432         {
433             const char *command_name = args.GetArgumentAtIndex(0);
434             cmd_obj = m_interpreter.GetCommandObject(command_name);
435             if (cmd_obj)
436             {
437                 if (m_interpreter.CommandExists (command_name))
438                 {
439                     result.AppendErrorWithFormat ("'%s' is a permanent debugger command and cannot be removed.\n",
440                                                   command_name);
441                     result.SetStatus (eReturnStatusFailed);
442                 }
443                 else
444                 {
445 
446                     if (m_interpreter.RemoveAlias (command_name) == false)
447                     {
448                         if (m_interpreter.AliasExists (command_name))
449                             result.AppendErrorWithFormat ("Error occurred while attempting to unalias '%s'.\n",
450                                                           command_name);
451                         else
452                             result.AppendErrorWithFormat ("'%s' is not an existing alias.\n", command_name);
453                         result.SetStatus (eReturnStatusFailed);
454                     }
455                     else
456                         result.SetStatus (eReturnStatusSuccessFinishNoResult);
457                 }
458             }
459             else
460             {
461                 result.AppendErrorWithFormat ("'%s' is not a known command.\nTry 'help' to see a "
462                                               "current list of commands.\n",
463                                              command_name);
464                 result.SetStatus (eReturnStatusFailed);
465             }
466         }
467         else
468         {
469             result.AppendError ("must call 'unalias' with a valid alias");
470             result.SetStatus (eReturnStatusFailed);
471         }
472 
473         return result.Succeeded();
474     }
475 };
476 
477 #pragma mark CommandObjectMultiwordCommands
478 
479 //-------------------------------------------------------------------------
480 // CommandObjectMultiwordCommands
481 //-------------------------------------------------------------------------
482 
483 CommandObjectMultiwordCommands::CommandObjectMultiwordCommands (CommandInterpreter &interpreter) :
484     CommandObjectMultiword (interpreter,
485                             "commands",
486                             "A set of commands for managing or customizing the debugger commands.",
487                             "commands <subcommand> [<subcommand-options>]")
488 {
489     LoadSubCommand ("source",  CommandObjectSP (new CommandObjectCommandsSource (interpreter)));
490     LoadSubCommand ("alias",   CommandObjectSP (new CommandObjectCommandsAlias (interpreter)));
491     LoadSubCommand ("unalias", CommandObjectSP (new CommandObjectCommandsUnalias (interpreter)));
492 }
493 
494 CommandObjectMultiwordCommands::~CommandObjectMultiwordCommands ()
495 {
496 }
497 
498