xref: /netbsd-src/external/gpl3/gdb.old/dist/gdb/skip.c (revision 8b657b0747480f8989760d71343d6dd33f8d4cf9)
1 /* Skipping uninteresting files and functions while stepping.
2 
3    Copyright (C) 2011-2023 Free Software Foundation, Inc.
4 
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 3 of the License, or
8    (at your option) any later version.
9 
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14 
15    You should have received a copy of the GNU General Public License
16    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
17 
18 #include "defs.h"
19 #include "skip.h"
20 #include "value.h"
21 #include "valprint.h"
22 #include "ui-out.h"
23 #include "symtab.h"
24 #include "gdbcmd.h"
25 #include "command.h"
26 #include "completer.h"
27 #include "stack.h"
28 #include "cli/cli-utils.h"
29 #include "arch-utils.h"
30 #include "linespec.h"
31 #include "objfiles.h"
32 #include "breakpoint.h" /* for get_sal_arch () */
33 #include "source.h"
34 #include "filenames.h"
35 #include "fnmatch.h"
36 #include "gdbsupport/gdb_regex.h"
37 #include "gdbsupport/gdb_optional.h"
38 #include <list>
39 #include "cli/cli-style.h"
40 #include "gdbsupport/buildargv.h"
41 
42 /* True if we want to print debug printouts related to file/function
43    skipping. */
44 static bool debug_skip = false;
45 
46 class skiplist_entry
47 {
48 public:
49   /* Create a skiplist_entry object and add it to the chain.  */
50   static void add_entry (bool file_is_glob,
51 			 std::string &&file,
52 			 bool function_is_regexp,
53 			 std::string &&function);
54 
55   /* Return true if the skip entry has a file or glob-style file
56      pattern that matches FUNCTION_SAL.  */
57   bool skip_file_p (const symtab_and_line &function_sal) const;
58 
59   /* Return true if the skip entry has a function or function regexp
60      that matches FUNCTION_NAME.  */
61   bool skip_function_p (const char *function_name) const;
62 
63   /* Getters.  */
64   int number () const { return m_number; };
65   bool enabled () const { return m_enabled; };
66   bool file_is_glob () const { return m_file_is_glob; }
67   const std::string &file () const { return m_file; }
68   const std::string &function () const { return m_function; }
69   bool function_is_regexp () const { return m_function_is_regexp; }
70 
71   /* Setters.  */
72   void enable () { m_enabled = true; };
73   void disable () { m_enabled = false; };
74 
75   /* Disable copy.  */
76   skiplist_entry (const skiplist_entry &) = delete;
77   void operator= (const skiplist_entry &) = delete;
78 
79 private:
80   /* Key that grants access to the constructor.  */
81   struct private_key {};
82 public:
83   /* Public so we can construct with container::emplace_back.  Since
84      it requires a private class key, it can't be called from outside.
85      Use the add_entry static factory method to construct instead.  */
86   skiplist_entry (bool file_is_glob, std::string &&file,
87 		  bool function_is_regexp, std::string &&function,
88 		  private_key);
89 
90 private:
91   /* Return true if we're stopped at a file to be skipped.  */
92   bool do_skip_file_p (const symtab_and_line &function_sal) const;
93 
94   /* Return true if we're stopped at a globbed file to be skipped.  */
95   bool do_skip_gfile_p (const symtab_and_line &function_sal) const;
96 
97 private: /* data */
98   int m_number = -1;
99 
100   /* True if FILE is a glob-style pattern.
101      Otherwise it is the plain file name (possibly with directories).  */
102   bool m_file_is_glob;
103 
104   /* The name of the file or empty if no name.  */
105   std::string m_file;
106 
107   /* True if FUNCTION is a regexp.
108      Otherwise it is a plain function name (possibly with arguments,
109      for C++).  */
110   bool m_function_is_regexp;
111 
112   /* The name of the function or empty if no name.  */
113   std::string m_function;
114 
115   /* If this is a function regexp, the compiled form.  */
116   gdb::optional<compiled_regex> m_compiled_function_regexp;
117 
118   /* Enabled/disabled state.  */
119   bool m_enabled = true;
120 };
121 
122 static std::list<skiplist_entry> skiplist_entries;
123 static int highest_skiplist_entry_num = 0;
124 
125 skiplist_entry::skiplist_entry (bool file_is_glob,
126 				std::string &&file,
127 				bool function_is_regexp,
128 				std::string &&function,
129 				private_key)
130   : m_file_is_glob (file_is_glob),
131     m_file (std::move (file)),
132     m_function_is_regexp (function_is_regexp),
133     m_function (std::move (function))
134 {
135   gdb_assert (!m_file.empty () || !m_function.empty ());
136 
137   if (m_file_is_glob)
138     gdb_assert (!m_file.empty ());
139 
140   if (m_function_is_regexp)
141     {
142       gdb_assert (!m_function.empty ());
143       m_compiled_function_regexp.emplace (m_function.c_str (),
144 					  REG_NOSUB | REG_EXTENDED,
145 					  _("regexp"));
146     }
147 }
148 
149 void
150 skiplist_entry::add_entry (bool file_is_glob, std::string &&file,
151 			   bool function_is_regexp, std::string &&function)
152 {
153   skiplist_entries.emplace_back (file_is_glob,
154 				 std::move (file),
155 				 function_is_regexp,
156 				 std::move (function),
157 				 private_key {});
158 
159   /* Incremented after push_back, in case push_back throws.  */
160   skiplist_entries.back ().m_number = ++highest_skiplist_entry_num;
161 }
162 
163 static void
164 skip_file_command (const char *arg, int from_tty)
165 {
166   struct symtab *symtab;
167   const char *filename = NULL;
168 
169   /* If no argument was given, try to default to the last
170      displayed codepoint.  */
171   if (arg == NULL)
172     {
173       symtab = get_last_displayed_symtab ();
174       if (symtab == NULL)
175 	error (_("No default file now."));
176 
177       /* It is not a typo, symtab_to_filename_for_display would be needlessly
178 	 ambiguous.  */
179       filename = symtab_to_fullname (symtab);
180     }
181   else
182     filename = arg;
183 
184   skiplist_entry::add_entry (false, std::string (filename),
185 			     false, std::string ());
186 
187   gdb_printf (_("File %s will be skipped when stepping.\n"), filename);
188 }
189 
190 /* Create a skiplist entry for the given function NAME and add it to the
191    list.  */
192 
193 static void
194 skip_function (const char *name)
195 {
196   skiplist_entry::add_entry (false, std::string (), false, std::string (name));
197 
198   gdb_printf (_("Function %s will be skipped when stepping.\n"), name);
199 }
200 
201 static void
202 skip_function_command (const char *arg, int from_tty)
203 {
204   /* Default to the current function if no argument is given.  */
205   if (arg == NULL)
206     {
207       frame_info_ptr fi = get_selected_frame (_("No default function now."));
208       struct symbol *sym = get_frame_function (fi);
209       const char *name = NULL;
210 
211       if (sym != NULL)
212 	name = sym->print_name ();
213       else
214 	error (_("No function found containing current program point %s."),
215 	       paddress (get_current_arch (), get_frame_pc (fi)));
216       skip_function (name);
217       return;
218     }
219 
220   skip_function (arg);
221 }
222 
223 /* Process "skip ..." that does not match "skip file" or "skip function".  */
224 
225 static void
226 skip_command (const char *arg, int from_tty)
227 {
228   const char *file = NULL;
229   const char *gfile = NULL;
230   const char *function = NULL;
231   const char *rfunction = NULL;
232   int i;
233 
234   if (arg == NULL)
235     {
236       skip_function_command (arg, from_tty);
237       return;
238     }
239 
240   gdb_argv argv (arg);
241 
242   for (i = 0; argv[i] != NULL; ++i)
243     {
244       const char *p = argv[i];
245       const char *value = argv[i + 1];
246 
247       if (strcmp (p, "-fi") == 0
248 	  || strcmp (p, "-file") == 0)
249 	{
250 	  if (value == NULL)
251 	    error (_("Missing value for %s option."), p);
252 	  file = value;
253 	  ++i;
254 	}
255       else if (strcmp (p, "-gfi") == 0
256 	       || strcmp (p, "-gfile") == 0)
257 	{
258 	  if (value == NULL)
259 	    error (_("Missing value for %s option."), p);
260 	  gfile = value;
261 	  ++i;
262 	}
263       else if (strcmp (p, "-fu") == 0
264 	       || strcmp (p, "-function") == 0)
265 	{
266 	  if (value == NULL)
267 	    error (_("Missing value for %s option."), p);
268 	  function = value;
269 	  ++i;
270 	}
271       else if (strcmp (p, "-rfu") == 0
272 	       || strcmp (p, "-rfunction") == 0)
273 	{
274 	  if (value == NULL)
275 	    error (_("Missing value for %s option."), p);
276 	  rfunction = value;
277 	  ++i;
278 	}
279       else if (*p == '-')
280 	error (_("Invalid skip option: %s"), p);
281       else if (i == 0)
282 	{
283 	  /* Assume the user entered "skip FUNCTION-NAME".
284 	     FUNCTION-NAME may be `foo (int)', and therefore we pass the
285 	     complete original arg to skip_function command as if the user
286 	     typed "skip function arg".  */
287 	  skip_function_command (arg, from_tty);
288 	  return;
289 	}
290       else
291 	error (_("Invalid argument: %s"), p);
292     }
293 
294   if (file != NULL && gfile != NULL)
295     error (_("Cannot specify both -file and -gfile."));
296 
297   if (function != NULL && rfunction != NULL)
298     error (_("Cannot specify both -function and -rfunction."));
299 
300   /* This shouldn't happen as "skip" by itself gets punted to
301      skip_function_command.  */
302   gdb_assert (file != NULL || gfile != NULL
303 	      || function != NULL || rfunction != NULL);
304 
305   std::string entry_file;
306   if (file != NULL)
307     entry_file = file;
308   else if (gfile != NULL)
309     entry_file = gfile;
310 
311   std::string entry_function;
312   if (function != NULL)
313     entry_function = function;
314   else if (rfunction != NULL)
315     entry_function = rfunction;
316 
317   skiplist_entry::add_entry (gfile != NULL, std::move (entry_file),
318 			     rfunction != NULL, std::move (entry_function));
319 
320   /* I18N concerns drive some of the choices here (we can't piece together
321      the output too much).  OTOH we want to keep this simple.  Therefore the
322      only polish we add to the output is to append "(s)" to "File" or
323      "Function" if they're a glob/regexp.  */
324   {
325     const char *file_to_print = file != NULL ? file : gfile;
326     const char *function_to_print = function != NULL ? function : rfunction;
327     const char *file_text = gfile != NULL ? _("File(s)") : _("File");
328     const char *lower_file_text = gfile != NULL ? _("file(s)") : _("file");
329     const char *function_text
330       = rfunction != NULL ? _("Function(s)") : _("Function");
331 
332     if (function_to_print == NULL)
333       {
334 	gdb_printf (_("%s %s will be skipped when stepping.\n"),
335 		    file_text, file_to_print);
336       }
337     else if (file_to_print == NULL)
338       {
339 	gdb_printf (_("%s %s will be skipped when stepping.\n"),
340 		    function_text, function_to_print);
341       }
342     else
343       {
344 	gdb_printf (_("%s %s in %s %s will be skipped"
345 		      " when stepping.\n"),
346 		    function_text, function_to_print,
347 		    lower_file_text, file_to_print);
348       }
349   }
350 }
351 
352 static void
353 info_skip_command (const char *arg, int from_tty)
354 {
355   int num_printable_entries = 0;
356   struct value_print_options opts;
357 
358   get_user_print_options (&opts);
359 
360   /* Count the number of rows in the table and see if we need space for a
361      64-bit address anywhere.  */
362   for (const skiplist_entry &e : skiplist_entries)
363     if (arg == NULL || number_is_in_list (arg, e.number ()))
364       num_printable_entries++;
365 
366   if (num_printable_entries == 0)
367     {
368       if (arg == NULL)
369 	current_uiout->message (_("Not skipping any files or functions.\n"));
370       else
371 	current_uiout->message (
372 	  _("No skiplist entries found with number %s.\n"), arg);
373 
374       return;
375     }
376 
377   ui_out_emit_table table_emitter (current_uiout, 6, num_printable_entries,
378 				   "SkiplistTable");
379 
380   current_uiout->table_header (5, ui_left, "number", "Num");   /* 1 */
381   current_uiout->table_header (3, ui_left, "enabled", "Enb");  /* 2 */
382   current_uiout->table_header (4, ui_right, "regexp", "Glob"); /* 3 */
383   current_uiout->table_header (20, ui_left, "file", "File");   /* 4 */
384   current_uiout->table_header (2, ui_right, "regexp", "RE");   /* 5 */
385   current_uiout->table_header (40, ui_noalign, "function", "Function"); /* 6 */
386   current_uiout->table_body ();
387 
388   for (const skiplist_entry &e : skiplist_entries)
389     {
390       QUIT;
391       if (arg != NULL && !number_is_in_list (arg, e.number ()))
392 	continue;
393 
394       ui_out_emit_tuple tuple_emitter (current_uiout, "blklst-entry");
395       current_uiout->field_signed ("number", e.number ()); /* 1 */
396 
397       if (e.enabled ())
398 	current_uiout->field_string ("enabled", "y"); /* 2 */
399       else
400 	current_uiout->field_string ("enabled", "n"); /* 2 */
401 
402       if (e.file_is_glob ())
403 	current_uiout->field_string ("regexp", "y"); /* 3 */
404       else
405 	current_uiout->field_string ("regexp", "n"); /* 3 */
406 
407       current_uiout->field_string ("file",
408 				   e.file ().empty () ? "<none>"
409 				   : e.file ().c_str (),
410 				   e.file ().empty ()
411 				   ? metadata_style.style ()
412 				   : file_name_style.style ()); /* 4 */
413       if (e.function_is_regexp ())
414 	current_uiout->field_string ("regexp", "y"); /* 5 */
415       else
416 	current_uiout->field_string ("regexp", "n"); /* 5 */
417 
418       current_uiout->field_string ("function",
419 				   e.function ().empty () ? "<none>"
420 				   : e.function ().c_str (),
421 				   e.function ().empty ()
422 				   ? metadata_style.style ()
423 				   : function_name_style.style ()); /* 6 */
424 
425       current_uiout->text ("\n");
426     }
427 }
428 
429 static void
430 skip_enable_command (const char *arg, int from_tty)
431 {
432   bool found = false;
433 
434   for (skiplist_entry &e : skiplist_entries)
435     if (arg == NULL || number_is_in_list (arg, e.number ()))
436       {
437 	e.enable ();
438 	found = true;
439       }
440 
441   if (!found)
442     error (_("No skiplist entries found with number %s."), arg);
443 }
444 
445 static void
446 skip_disable_command (const char *arg, int from_tty)
447 {
448   bool found = false;
449 
450   for (skiplist_entry &e : skiplist_entries)
451     if (arg == NULL || number_is_in_list (arg, e.number ()))
452       {
453 	e.disable ();
454 	found = true;
455       }
456 
457   if (!found)
458     error (_("No skiplist entries found with number %s."), arg);
459 }
460 
461 static void
462 skip_delete_command (const char *arg, int from_tty)
463 {
464   bool found = false;
465 
466   for (auto it = skiplist_entries.begin (),
467 	 end = skiplist_entries.end ();
468        it != end;)
469     {
470       const skiplist_entry &e = *it;
471 
472       if (arg == NULL || number_is_in_list (arg, e.number ()))
473 	{
474 	  it = skiplist_entries.erase (it);
475 	  found = true;
476 	}
477       else
478 	++it;
479     }
480 
481   if (!found)
482     error (_("No skiplist entries found with number %s."), arg);
483 }
484 
485 bool
486 skiplist_entry::do_skip_file_p (const symtab_and_line &function_sal) const
487 {
488   if (debug_skip)
489     gdb_printf (gdb_stdlog,
490 		"skip: checking if file %s matches non-glob %s...",
491 		function_sal.symtab->filename, m_file.c_str ());
492 
493   bool result;
494 
495   /* Check first sole SYMTAB->FILENAME.  It may not be a substring of
496      symtab_to_fullname as it may contain "./" etc.  */
497   if (compare_filenames_for_search (function_sal.symtab->filename,
498 				    m_file.c_str ()))
499     result = true;
500 
501   /* Before we invoke realpath, which can get expensive when many
502      files are involved, do a quick comparison of the basenames.  */
503   else if (!basenames_may_differ
504 	   && filename_cmp (lbasename (function_sal.symtab->filename),
505 			    lbasename (m_file.c_str ())) != 0)
506     result = false;
507   else
508     {
509       /* Note: symtab_to_fullname caches its result, thus we don't have to.  */
510       const char *fullname = symtab_to_fullname (function_sal.symtab);
511 
512       result = compare_filenames_for_search (fullname, m_file.c_str ());
513     }
514 
515   if (debug_skip)
516     gdb_printf (gdb_stdlog, result ? "yes.\n" : "no.\n");
517 
518   return result;
519 }
520 
521 bool
522 skiplist_entry::do_skip_gfile_p (const symtab_and_line &function_sal) const
523 {
524   if (debug_skip)
525     gdb_printf (gdb_stdlog,
526 		"skip: checking if file %s matches glob %s...",
527 		function_sal.symtab->filename, m_file.c_str ());
528 
529   bool result;
530 
531   /* Check first sole SYMTAB->FILENAME.  It may not be a substring of
532      symtab_to_fullname as it may contain "./" etc.  */
533   if (gdb_filename_fnmatch (m_file.c_str (), function_sal.symtab->filename,
534 			    FNM_FILE_NAME | FNM_NOESCAPE) == 0)
535     result = true;
536 
537   /* Before we invoke symtab_to_fullname, which is expensive, do a quick
538      comparison of the basenames.
539      Note that we assume that lbasename works with glob-style patterns.
540      If the basename of the glob pattern is something like "*.c" then this
541      isn't much of a win.  Oh well.  */
542   else if (!basenames_may_differ
543       && gdb_filename_fnmatch (lbasename (m_file.c_str ()),
544 			       lbasename (function_sal.symtab->filename),
545 			       FNM_FILE_NAME | FNM_NOESCAPE) != 0)
546     result = false;
547   else
548     {
549       /* Note: symtab_to_fullname caches its result, thus we don't have to.  */
550       const char *fullname = symtab_to_fullname (function_sal.symtab);
551 
552       result = compare_glob_filenames_for_search (fullname, m_file.c_str ());
553     }
554 
555   if (debug_skip)
556     gdb_printf (gdb_stdlog, result ? "yes.\n" : "no.\n");
557 
558   return result;
559 }
560 
561 bool
562 skiplist_entry::skip_file_p (const symtab_and_line &function_sal) const
563 {
564   if (m_file.empty ())
565     return false;
566 
567   if (function_sal.symtab == NULL)
568     return false;
569 
570   if (m_file_is_glob)
571     return do_skip_gfile_p (function_sal);
572   else
573     return do_skip_file_p (function_sal);
574 }
575 
576 bool
577 skiplist_entry::skip_function_p (const char *function_name) const
578 {
579   if (m_function.empty ())
580     return false;
581 
582   bool result;
583 
584   if (m_function_is_regexp)
585     {
586       if (debug_skip)
587 	gdb_printf (gdb_stdlog,
588 		    "skip: checking if function %s matches regex %s...",
589 		    function_name, m_function.c_str ());
590 
591       gdb_assert (m_compiled_function_regexp);
592       result
593 	= (m_compiled_function_regexp->exec (function_name, 0, NULL, 0) == 0);
594     }
595   else
596     {
597       if (debug_skip)
598 	gdb_printf (gdb_stdlog,
599 		    ("skip: checking if function %s matches non-regex "
600 		     "%s..."),
601 		    function_name, m_function.c_str ());
602       result = (strcmp_iw (function_name, m_function.c_str ()) == 0);
603     }
604 
605   if (debug_skip)
606     gdb_printf (gdb_stdlog, result ? "yes.\n" : "no.\n");
607 
608   return result;
609 }
610 
611 /* See skip.h.  */
612 
613 bool
614 function_name_is_marked_for_skip (const char *function_name,
615 				  const symtab_and_line &function_sal)
616 {
617   if (function_name == NULL)
618     return false;
619 
620   for (const skiplist_entry &e : skiplist_entries)
621     {
622       if (!e.enabled ())
623 	continue;
624 
625       bool skip_by_file = e.skip_file_p (function_sal);
626       bool skip_by_function = e.skip_function_p (function_name);
627 
628       /* If both file and function must match, make sure we don't errantly
629 	 exit if only one of them match.  */
630       if (!e.file ().empty () && !e.function ().empty ())
631 	{
632 	  if (skip_by_file && skip_by_function)
633 	    return true;
634 	}
635       /* Only one of file/function is specified.  */
636       else if (skip_by_file || skip_by_function)
637 	return true;
638     }
639 
640   return false;
641 }
642 
643 /* Completer for skip numbers.  */
644 
645 static void
646 complete_skip_number (cmd_list_element *cmd,
647 		      completion_tracker &completer,
648 		      const char *text, const char *word)
649 {
650   size_t word_len = strlen (word);
651 
652   for (const skiplist_entry &entry : skiplist_entries)
653     {
654       gdb::unique_xmalloc_ptr<char> name = xstrprintf ("%d", entry.number ());
655       if (strncmp (word, name.get (), word_len) == 0)
656 	completer.add_completion (std::move (name));
657     }
658 }
659 
660 void _initialize_step_skip ();
661 void
662 _initialize_step_skip ()
663 {
664   static struct cmd_list_element *skiplist = NULL;
665   struct cmd_list_element *c;
666 
667   add_prefix_cmd ("skip", class_breakpoint, skip_command, _("\
668 Ignore a function while stepping.\n\
669 \n\
670 Usage: skip [FUNCTION-NAME]\n\
671        skip [FILE-SPEC] [FUNCTION-SPEC]\n\
672 If no arguments are given, ignore the current function.\n\
673 \n\
674 FILE-SPEC is one of:\n\
675        -fi|-file FILE-NAME\n\
676        -gfi|-gfile GLOB-FILE-PATTERN\n\
677 FUNCTION-SPEC is one of:\n\
678        -fu|-function FUNCTION-NAME\n\
679        -rfu|-rfunction FUNCTION-NAME-REGULAR-EXPRESSION"),
680 		  &skiplist, 1, &cmdlist);
681 
682   c = add_cmd ("file", class_breakpoint, skip_file_command, _("\
683 Ignore a file while stepping.\n\
684 Usage: skip file [FILE-NAME]\n\
685 If no filename is given, ignore the current file."),
686 	       &skiplist);
687   set_cmd_completer (c, filename_completer);
688 
689   c = add_cmd ("function", class_breakpoint, skip_function_command, _("\
690 Ignore a function while stepping.\n\
691 Usage: skip function [FUNCTION-NAME]\n\
692 If no function name is given, skip the current function."),
693 	       &skiplist);
694   set_cmd_completer (c, location_completer);
695 
696   c = add_cmd ("enable", class_breakpoint, skip_enable_command, _("\
697 Enable skip entries.\n\
698 Usage: skip enable [NUMBER | RANGE]...\n\
699 You can specify numbers (e.g. \"skip enable 1 3\"),\n\
700 ranges (e.g. \"skip enable 4-8\"), or both (e.g. \"skip enable 1 3 4-8\").\n\n\
701 If you don't specify any numbers or ranges, we'll enable all skip entries."),
702 	       &skiplist);
703   set_cmd_completer (c, complete_skip_number);
704 
705   c = add_cmd ("disable", class_breakpoint, skip_disable_command, _("\
706 Disable skip entries.\n\
707 Usage: skip disable [NUMBER | RANGE]...\n\
708 You can specify numbers (e.g. \"skip disable 1 3\"),\n\
709 ranges (e.g. \"skip disable 4-8\"), or both (e.g. \"skip disable 1 3 4-8\").\n\n\
710 If you don't specify any numbers or ranges, we'll disable all skip entries."),
711 	       &skiplist);
712   set_cmd_completer (c, complete_skip_number);
713 
714   c = add_cmd ("delete", class_breakpoint, skip_delete_command, _("\
715 Delete skip entries.\n\
716 Usage: skip delete [NUMBER | RANGES]...\n\
717 You can specify numbers (e.g. \"skip delete 1 3\"),\n\
718 ranges (e.g. \"skip delete 4-8\"), or both (e.g. \"skip delete 1 3 4-8\").\n\n\
719 If you don't specify any numbers or ranges, we'll delete all skip entries."),
720 	       &skiplist);
721   set_cmd_completer (c, complete_skip_number);
722 
723   add_info ("skip", info_skip_command, _("\
724 Display the status of skips.\n\
725 Usage: info skip [NUMBER | RANGES]...\n\
726 You can specify numbers (e.g. \"info skip 1 3\"), \n\
727 ranges (e.g. \"info skip 4-8\"), or both (e.g. \"info skip 1 3 4-8\").\n\n\
728 If you don't specify any numbers or ranges, we'll show all skips."));
729   set_cmd_completer (c, complete_skip_number);
730 
731   add_setshow_boolean_cmd ("skip", class_maintenance,
732 			   &debug_skip, _("\
733 Set whether to print the debug output about skipping files and functions."),
734 			   _("\
735 Show whether the debug output about skipping files and functions is printed."),
736 			   _("\
737 When non-zero, debug output about skipping files and functions is displayed."),
738 			   NULL, NULL,
739 			   &setdebuglist, &showdebuglist);
740 }
741