1/* Maintenance commands for testing the options framework.
2
3   Copyright (C) 2019-2020 Free Software Foundation, Inc.
4
5   This file is part of GDB.
6
7   This program is free software; you can redistribute it and/or modify
8   it under the terms of the GNU General Public License as published by
9   the Free Software Foundation; either version 3 of the License, or
10   (at your option) any later version.
11
12   This program is distributed in the hope that it will be useful,
13   but WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   GNU General Public License for more details.
16
17   You should have received a copy of the GNU General Public License
18   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
19
20#include "defs.h"
21#include "gdbcmd.h"
22#include "cli/cli-option.h"
23
24/* This file defines three "maintenance test-options" subcommands to
25   exercise TAB-completion and option processing:
26
27    (gdb) maint test-options require-delimiter
28    (gdb) maint test-options unknown-is-error
29    (gdb) maint test-options unknown-is-operand
30
31   And a fourth one to help with TAB-completion testing.
32
33    (gdb) maint show test-options-completion-result
34
35   Each of the test-options subcommands exercise
36   gdb::option::process_options with a different enum
37   process_options_mode value.  Examples for commands they model:
38
39   - "print" and "compile print", are like "require-delimiter",
40      because they accept random expressions as argument.
41
42   - "backtrace" and "frame/thread apply" are like
43     "unknown-is-operand", because "-" is a valid command.
44
45   - "compile file" and "compile code" are like "unknown-is-error".
46
47   These commands allow exercising all aspects of option processing
48   without having to pick some existing command.  That should be more
49   stable going forward than relying on an existing user command,
50   since if we picked say "print", that command or its options could
51   change in future, and then we'd be left with having to pick some
52   other command or option to exercise some non-command-specific
53   option processing detail.  Also, actual user commands have side
54   effects that we're not interested in when we're focusing on unit
55   testing the options machinery.  BTW, a maintenance command is used
56   as a sort of unit test driver instead of actual "maint selftest"
57   unit tests, since we need to go all the way via gdb including
58   readline, for proper testing of TAB completion.
59
60   These maintenance commands support options of all the different
61   available kinds of commands (boolean, enum, flag, string, uinteger):
62
63    (gdb) maint test-options require-delimiter -[TAB]
64    -bool      -enum      -flag      -string     -uinteger   -xx1       -xx2
65
66    (gdb) maint test-options require-delimiter -bool o[TAB]
67    off  on
68    (gdb) maint test-options require-delimiter -enum [TAB]
69    xxx  yyy  zzz
70    (gdb) maint test-options require-delimiter -uinteger [TAB]
71    NUMBER     unlimited
72
73   '-xx1' and '-xx2' are flag options too.  They exist in order to
74   test ambiguous option names, like '-xx'.
75
76  Invoking the commands makes them print out the options parsed:
77
78   (gdb) maint test-options unknown-is-error -flag -enum yyy cmdarg
79   -flag 1 -xx1 0 -xx2 0 -bool 0 -enum yyy -uint 0 -zuint-unl 0 -- cmdarg
80
81   (gdb) maint test-options require-delimiter -flag -enum yyy cmdarg
82   -flag 0 -xx1 0 -xx2 0 -bool 0 -enum xxx -uint 0  -zuint-unl 0 -- -flag -enum yyy cmdarg
83   (gdb) maint test-options require-delimiter -flag -enum yyy cmdarg --
84   Unrecognized option at: cmdarg --
85   (gdb) maint test-options require-delimiter -flag -enum yyy -- cmdarg
86   -flag 1 -xx1 0 -xx2 0 -bool 0 -enum yyy -uint 0 -zuint-unl 0 -- cmdarg
87
88  The "maint show test-options-completion-result" command exists in
89  order to do something similar for completion:
90
91   (gdb) maint test-options unknown-is-error -flag -b 0 -enum yyy OPERAND[TAB]
92   (gdb) maint show test-options-completion-result
93   0 OPERAND
94
95   (gdb) maint test-options unknown-is-error -flag -b 0 -enum yyy[TAB]
96   (gdb) maint show test-options-completion-result
97   1
98
99   (gdb) maint test-options require-dash -unknown[TAB]
100   (gdb) maint show test-options-completion-result
101   1
102
103  Here, "1" means the completion function processed the whole input
104  line, and that the command shouldn't do anything with the arguments,
105  since there are no operands.  While "0" indicates that there are
106  operands after options.  The text after "0" is the operands.
107
108  This level of detail is particularly important because getting the
109  completion function's entry point to return back to the caller the
110  right pointer into the operand is quite tricky in several
111  scenarios.  */
112
113/* Enum values for the "maintenance test-options" commands.  */
114const char test_options_enum_values_xxx[] = "xxx";
115const char test_options_enum_values_yyy[] = "yyy";
116const char test_options_enum_values_zzz[] = "zzz";
117static const char *const test_options_enum_values_choices[] =
118{
119  test_options_enum_values_xxx,
120  test_options_enum_values_yyy,
121  test_options_enum_values_zzz,
122  NULL
123};
124
125/* Option data for the "maintenance test-options" commands.  */
126
127struct test_options_opts
128{
129  bool flag_opt = false;
130  bool xx1_opt = false;
131  bool xx2_opt = false;
132  bool boolean_opt = false;
133  const char *enum_opt = test_options_enum_values_xxx;
134  unsigned int uint_opt = 0;
135  int zuint_unl_opt = 0;
136  char *string_opt = nullptr;
137
138  test_options_opts () = default;
139
140  DISABLE_COPY_AND_ASSIGN (test_options_opts);
141
142  ~test_options_opts ()
143  {
144    xfree (string_opt);
145  }
146
147  /* Dump the options to FILE.  ARGS is the remainder unprocessed
148     arguments.  */
149  void dump (ui_file *file, const char *args) const
150  {
151    fprintf_unfiltered (file,
152			_("-flag %d -xx1 %d -xx2 %d -bool %d "
153			  "-enum %s -uint %s -zuint-unl %s -string '%s' -- %s\n"),
154			flag_opt,
155			xx1_opt,
156			xx2_opt,
157			boolean_opt,
158			enum_opt,
159			(uint_opt == UINT_MAX
160			 ? "unlimited"
161			 : pulongest (uint_opt)),
162			(zuint_unl_opt == -1
163			 ? "unlimited"
164			 : plongest (zuint_unl_opt)),
165			(string_opt != nullptr
166			 ? string_opt
167			 : ""),
168			args);
169  }
170};
171
172/* Option definitions for the "maintenance test-options" commands.  */
173
174static const gdb::option::option_def test_options_option_defs[] = {
175
176  /* A flag option.  */
177  gdb::option::flag_option_def<test_options_opts> {
178    "flag",
179    [] (test_options_opts *opts) { return &opts->flag_opt; },
180    N_("A flag option."),
181  },
182
183  /* A couple flags with similar names, for "ambiguous option names"
184     testing.  */
185  gdb::option::flag_option_def<test_options_opts> {
186    "xx1",
187    [] (test_options_opts *opts) { return &opts->xx1_opt; },
188    N_("A flag option."),
189  },
190  gdb::option::flag_option_def<test_options_opts> {
191    "xx2",
192    [] (test_options_opts *opts) { return &opts->xx2_opt; },
193    N_("A flag option."),
194  },
195
196  /* A boolean option.  */
197  gdb::option::boolean_option_def<test_options_opts> {
198    "bool",
199    [] (test_options_opts *opts) { return &opts->boolean_opt; },
200    nullptr, /* show_cmd_cb */
201    N_("A boolean option."),
202  },
203
204  /* An enum option.  */
205  gdb::option::enum_option_def<test_options_opts> {
206    "enum",
207    test_options_enum_values_choices,
208    [] (test_options_opts *opts) { return &opts->enum_opt; },
209    nullptr, /* show_cmd_cb */
210    N_("An enum option."),
211  },
212
213  /* A uinteger option.  */
214  gdb::option::uinteger_option_def<test_options_opts> {
215    "uinteger",
216    [] (test_options_opts *opts) { return &opts->uint_opt; },
217    nullptr, /* show_cmd_cb */
218    N_("A uinteger option."),
219    nullptr, /* show_doc */
220    N_("A help doc that spawns\nmultiple lines."),
221  },
222
223  /* A zuinteger_unlimited option.  */
224  gdb::option::zuinteger_unlimited_option_def<test_options_opts> {
225    "zuinteger-unlimited",
226    [] (test_options_opts *opts) { return &opts->zuint_unl_opt; },
227    nullptr, /* show_cmd_cb */
228    N_("A zuinteger-unlimited option."),
229    nullptr, /* show_doc */
230    nullptr, /* help_doc */
231  },
232
233  /* A string option.  */
234  gdb::option::string_option_def<test_options_opts> {
235    "string",
236    [] (test_options_opts *opts) { return &opts->string_opt; },
237    nullptr, /* show_cmd_cb */
238    N_("A string option."),
239  },
240};
241
242/* Create an option_def_group for the test_options_opts options, with
243   OPTS as context.  */
244
245static inline gdb::option::option_def_group
246make_test_options_options_def_group (test_options_opts *opts)
247{
248  return {{test_options_option_defs}, opts};
249}
250
251/* Implementation of the "maintenance test-options
252   require-delimiter/unknown-is-error/unknown-is-operand" commands.
253   Each of the commands maps to a different enum process_options_mode
254   enumerator.  The test strategy is simply processing the options in
255   a number of scenarios, and printing back the parsed result.  */
256
257static void
258maintenance_test_options_command_mode (const char *args,
259				       gdb::option::process_options_mode mode)
260{
261  test_options_opts opts;
262
263  gdb::option::process_options (&args, mode,
264				make_test_options_options_def_group (&opts));
265
266  if (args == nullptr)
267    args = "";
268  else
269    args = skip_spaces (args);
270
271  opts.dump (gdb_stdout, args);
272}
273
274/* Variable used by the "maintenance show
275   test-options-completion-result" command.  This variable is stored
276   by the completer of the "maint test-options" subcommands.
277
278   If the completer returned false, this includes the text at the word
279   point after gdb::option::complete_options returns.  If true, then
280   this includes a dump of the processed options.  */
281static std::string maintenance_test_options_command_completion_text;
282
283/* The "maintenance show test-options-completion-result" command.  */
284
285static void
286maintenance_show_test_options_completion_result (const char *args,
287						 int from_tty)
288{
289  puts_filtered (maintenance_test_options_command_completion_text.c_str ());
290}
291
292/* Save the completion result in the global variables read by the
293   "maintenance test-options require-delimiter" command.  */
294
295static void
296save_completion_result (const test_options_opts &opts, bool res,
297			const char *text)
298{
299  if (res)
300    {
301      string_file stream;
302
303      stream.puts ("1 ");
304      opts.dump (&stream, text);
305      maintenance_test_options_command_completion_text
306	= std::move (stream.string ());
307    }
308  else
309    {
310      maintenance_test_options_command_completion_text
311	= string_printf ("0 %s\n", text);
312    }
313}
314
315/* Implementation of completer for the "maintenance test-options
316   require-delimiter/unknown-is-error/unknown-is-operand" commands.
317   Each of the commands maps to a different enum process_options_mode
318   enumerator.  */
319
320static void
321maintenance_test_options_completer_mode (completion_tracker &tracker,
322					 const char *text,
323					 gdb::option::process_options_mode mode)
324{
325  test_options_opts opts;
326
327  try
328    {
329      bool res = (gdb::option::complete_options
330		  (tracker, &text, mode,
331		   make_test_options_options_def_group (&opts)));
332
333      save_completion_result (opts, res, text);
334    }
335  catch (const gdb_exception_error &ex)
336    {
337      save_completion_result (opts, true, text);
338      throw;
339    }
340}
341
342/* Implementation of the "maintenance test-options require-delimiter"
343   command.  */
344
345static void
346maintenance_test_options_require_delimiter_command (const char *args,
347						    int from_tty)
348{
349  maintenance_test_options_command_mode
350    (args, gdb::option::PROCESS_OPTIONS_REQUIRE_DELIMITER);
351}
352
353/* Implementation of the "maintenance test-options
354   unknown-is-error" command.  */
355
356static void
357maintenance_test_options_unknown_is_error_command (const char *args,
358						   int from_tty)
359{
360  maintenance_test_options_command_mode
361    (args, gdb::option::PROCESS_OPTIONS_UNKNOWN_IS_ERROR);
362}
363
364/* Implementation of the "maintenance test-options
365   unknown-is-operand" command.  */
366
367static void
368maintenance_test_options_unknown_is_operand_command (const char *args,
369						     int from_tty)
370{
371  maintenance_test_options_command_mode
372    (args, gdb::option::PROCESS_OPTIONS_UNKNOWN_IS_OPERAND);
373}
374
375/* Completer for the "maintenance test-options require-delimiter"
376   command.  */
377
378static void
379maintenance_test_options_require_delimiter_command_completer
380  (cmd_list_element *ignore, completion_tracker &tracker,
381   const char *text, const char *word)
382{
383  maintenance_test_options_completer_mode
384    (tracker, text, gdb::option::PROCESS_OPTIONS_REQUIRE_DELIMITER);
385}
386
387/* Completer for the "maintenance test-options unknown-is-error"
388   command.  */
389
390static void
391maintenance_test_options_unknown_is_error_command_completer
392  (cmd_list_element *ignore, completion_tracker &tracker,
393   const char *text, const char *word)
394{
395  maintenance_test_options_completer_mode
396    (tracker, text, gdb::option::PROCESS_OPTIONS_UNKNOWN_IS_ERROR);
397}
398
399/* Completer for the "maintenance test-options unknown-is-operand"
400   command.  */
401
402static void
403maintenance_test_options_unknown_is_operand_command_completer
404  (cmd_list_element *ignore, completion_tracker &tracker,
405   const char *text, const char *word)
406{
407  maintenance_test_options_completer_mode
408    (tracker, text, gdb::option::PROCESS_OPTIONS_UNKNOWN_IS_OPERAND);
409}
410
411/* Command list for maint test-options.  */
412struct cmd_list_element *maintenance_test_options_list;
413
414
415void _initialize_maint_test_options ();
416void
417_initialize_maint_test_options ()
418{
419  cmd_list_element *cmd;
420
421  add_basic_prefix_cmd ("test-options", no_class,
422			_("\
423Generic command for testing the options infrastructure."),
424			&maintenance_test_options_list,
425			"maintenance test-options ", 0,
426			&maintenancelist);
427
428  const auto def_group = make_test_options_options_def_group (nullptr);
429
430  static const std::string help_require_delim_str
431    = gdb::option::build_help (_("\
432Command used for testing options processing.\n\
433Usage: maint test-options require-delimiter [[OPTION]... --] [OPERAND]...\n\
434\n\
435Options:\n\
436%OPTIONS%\n\
437\n\
438If you specify any command option, you must use a double dash (\"--\")\n\
439to mark the end of option processing."),
440			       def_group);
441
442  static const std::string help_unknown_is_error_str
443    = gdb::option::build_help (_("\
444Command used for testing options processing.\n\
445Usage: maint test-options unknown-is-error [OPTION]... [OPERAND]...\n\
446\n\
447Options:\n\
448%OPTIONS%"),
449			       def_group);
450
451  static const std::string help_unknown_is_operand_str
452    = gdb::option::build_help (_("\
453Command used for testing options processing.\n\
454Usage: maint test-options unknown-is-operand [OPTION]... [OPERAND]...\n\
455\n\
456Options:\n\
457%OPTIONS%"),
458			       def_group);
459
460  cmd = add_cmd ("require-delimiter", class_maintenance,
461		 maintenance_test_options_require_delimiter_command,
462		 help_require_delim_str.c_str (),
463		 &maintenance_test_options_list);
464  set_cmd_completer_handle_brkchars
465    (cmd, maintenance_test_options_require_delimiter_command_completer);
466
467  cmd = add_cmd ("unknown-is-error", class_maintenance,
468		 maintenance_test_options_unknown_is_error_command,
469		 help_unknown_is_error_str.c_str (),
470		 &maintenance_test_options_list);
471  set_cmd_completer_handle_brkchars
472    (cmd, maintenance_test_options_unknown_is_error_command_completer);
473
474  cmd = add_cmd ("unknown-is-operand", class_maintenance,
475		 maintenance_test_options_unknown_is_operand_command,
476		 help_unknown_is_operand_str.c_str (),
477		 &maintenance_test_options_list);
478  set_cmd_completer_handle_brkchars
479    (cmd, maintenance_test_options_unknown_is_operand_command_completer);
480
481  add_cmd ("test-options-completion-result", class_maintenance,
482	   maintenance_show_test_options_completion_result,
483	   _("\
484Show maintenance test-options completion result.\n\
485Shows the results of completing\n\
486\"maint test-options require-delimiter\",\n\
487\"maint test-options unknown-is-error\", or\n\
488\"maint test-options unknown-is-operand\"."),
489	   &maintenance_show_cmdlist);
490}
491