xref: /netbsd-src/external/gpl3/gdb.old/dist/gdb/skip.c (revision bdc22b2e01993381dcefeff2bc9b56ca75a4235c)
1 /* Skipping uninteresting files and functions while stepping.
2 
3    Copyright (C) 2011-2016 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 "gdb_regex.h"
37 
38 struct skiplist_entry
39 {
40   int number;
41 
42   /* Non-zero if FILE is a glob-style pattern.
43      Otherewise it is the plain file name (possibly with directories).  */
44   int file_is_glob;
45 
46   /* The name of the file or NULL.
47      The skiplist entry owns this pointer.  */
48   char *file;
49 
50   /* Non-zero if FUNCTION is a regexp.
51      Otherwise it is a plain function name (possibly with arguments,
52      for C++).  */
53   int function_is_regexp;
54 
55   /* The name of the function or NULL.
56      The skiplist entry owns this pointer.  */
57   char *function;
58 
59   /* If this is a function regexp, the compiled form.  */
60   regex_t compiled_function_regexp;
61 
62   /* Non-zero if the function regexp has been compiled.  */
63   int compiled_function_regexp_is_valid;
64 
65   int enabled;
66 
67   struct skiplist_entry *next;
68 };
69 
70 static void add_skiplist_entry (struct skiplist_entry *e);
71 
72 static struct skiplist_entry *skiplist_entry_chain;
73 static int skiplist_entry_count;
74 
75 #define ALL_SKIPLIST_ENTRIES(E) \
76   for (E = skiplist_entry_chain; E; E = E->next)
77 
78 #define ALL_SKIPLIST_ENTRIES_SAFE(E,TMP) \
79   for (E = skiplist_entry_chain;         \
80        E ? (TMP = E->next, 1) : 0;       \
81        E = TMP)
82 
83 /* Create a skip object.  */
84 
85 static struct skiplist_entry *
86 make_skip_entry (int file_is_glob, const char *file,
87 		 int function_is_regexp, const char *function)
88 {
89   struct skiplist_entry *e = XCNEW (struct skiplist_entry);
90 
91   gdb_assert (file != NULL || function != NULL);
92   if (file_is_glob)
93     gdb_assert (file != NULL);
94   if (function_is_regexp)
95     gdb_assert (function != NULL);
96 
97   if (file != NULL)
98     e->file = xstrdup (file);
99   if (function != NULL)
100     e->function = xstrdup (function);
101   e->file_is_glob = file_is_glob;
102   e->function_is_regexp = function_is_regexp;
103   e->enabled = 1;
104 
105   return e;
106 }
107 
108 /* Free a skiplist entry.  */
109 
110 static void
111 free_skiplist_entry (struct skiplist_entry *e)
112 {
113   xfree (e->file);
114   xfree (e->function);
115   if (e->function_is_regexp && e->compiled_function_regexp_is_valid)
116     regfree (&e->compiled_function_regexp);
117   xfree (e);
118 }
119 
120 /* Wrapper to free_skiplist_entry for use as a cleanup.  */
121 
122 static void
123 free_skiplist_entry_cleanup (void *e)
124 {
125   free_skiplist_entry ((struct skiplist_entry *) e);
126 }
127 
128 /* Create a cleanup to free skiplist entry E.  */
129 
130 static struct cleanup *
131 make_free_skiplist_entry_cleanup (struct skiplist_entry *e)
132 {
133   return make_cleanup (free_skiplist_entry_cleanup, e);
134 }
135 
136 static void
137 skip_file_command (char *arg, int from_tty)
138 {
139   struct symtab *symtab;
140   const char *filename = NULL;
141 
142   /* If no argument was given, try to default to the last
143      displayed codepoint.  */
144   if (arg == NULL)
145     {
146       symtab = get_last_displayed_symtab ();
147       if (symtab == NULL)
148 	error (_("No default file now."));
149 
150       /* It is not a typo, symtab_to_filename_for_display woule be needlessly
151 	 ambiguous.  */
152       filename = symtab_to_fullname (symtab);
153     }
154   else
155     filename = arg;
156 
157   add_skiplist_entry (make_skip_entry (0, filename, 0, NULL));
158 
159   printf_filtered (_("File %s will be skipped when stepping.\n"), filename);
160 }
161 
162 /* Create a skiplist entry for the given function NAME and add it to the
163    list.  */
164 
165 static void
166 skip_function (const char *name)
167 {
168   add_skiplist_entry (make_skip_entry (0, NULL, 0, name));
169 
170   printf_filtered (_("Function %s will be skipped when stepping.\n"), name);
171 }
172 
173 static void
174 skip_function_command (char *arg, int from_tty)
175 {
176   /* Default to the current function if no argument is given.  */
177   if (arg == NULL)
178     {
179       const char *name = NULL;
180       CORE_ADDR pc;
181 
182       if (!last_displayed_sal_is_valid ())
183 	error (_("No default function now."));
184 
185       pc = get_last_displayed_addr ();
186       if (!find_pc_partial_function (pc, &name, NULL, NULL))
187 	{
188 	  error (_("No function found containing current program point %s."),
189 		  paddress (get_current_arch (), pc));
190 	}
191       skip_function (name);
192       return;
193     }
194 
195   skip_function (arg);
196 }
197 
198 /* Compile the regexp in E.
199    An error is thrown if there's an error.
200    MESSAGE is used as a prefix of the error message.  */
201 
202 static void
203 compile_skip_regexp (struct skiplist_entry *e, const char *message)
204 {
205   int code;
206   int flags = REG_NOSUB;
207 
208 #ifdef REG_EXTENDED
209   flags |= REG_EXTENDED;
210 #endif
211 
212   gdb_assert (e->function_is_regexp && e->function != NULL);
213 
214   code = regcomp (&e->compiled_function_regexp, e->function, flags);
215   if (code != 0)
216     {
217       char *err = get_regcomp_error (code, &e->compiled_function_regexp);
218 
219       make_cleanup (xfree, err);
220       error (_("%s: %s"), message, err);
221     }
222   e->compiled_function_regexp_is_valid = 1;
223 }
224 
225 /* Process "skip ..." that does not match "skip file" or "skip function".  */
226 
227 static void
228 skip_command (char *arg, int from_tty)
229 {
230   const char *file = NULL;
231   const char *gfile = NULL;
232   const char *function = NULL;
233   const char *rfunction = NULL;
234   char **argv;
235   struct cleanup *cleanups;
236   struct skiplist_entry *e;
237   int i;
238 
239   if (arg == NULL)
240     {
241       skip_function_command (arg, from_tty);
242       return;
243     }
244 
245   argv = buildargv (arg);
246   cleanups = make_cleanup_freeargv (argv);
247 
248   for (i = 0; argv[i] != NULL; ++i)
249     {
250       const char *p = argv[i];
251       const char *value = argv[i + 1];
252 
253       if (strcmp (p, "-fi") == 0
254 	  || strcmp (p, "-file") == 0)
255 	{
256 	  if (value == NULL)
257 	    error (_("Missing value for %s option."), p);
258 	  file = value;
259 	  ++i;
260 	}
261       else if (strcmp (p, "-gfi") == 0
262 	       || strcmp (p, "-gfile") == 0)
263 	{
264 	  if (value == NULL)
265 	    error (_("Missing value for %s option."), p);
266 	  gfile = value;
267 	  ++i;
268 	}
269       else if (strcmp (p, "-fu") == 0
270 	       || strcmp (p, "-function") == 0)
271 	{
272 	  if (value == NULL)
273 	    error (_("Missing value for %s option."), p);
274 	  function = value;
275 	  ++i;
276 	}
277       else if (strcmp (p, "-rfu") == 0
278 	       || strcmp (p, "-rfunction") == 0)
279 	{
280 	  if (value == NULL)
281 	    error (_("Missing value for %s option."), p);
282 	  rfunction = value;
283 	  ++i;
284 	}
285       else if (*p == '-')
286 	error (_("Invalid skip option: %s"), p);
287       else if (i == 0)
288 	{
289 	  /* Assume the user entered "skip FUNCTION-NAME".
290 	     FUNCTION-NAME may be `foo (int)', and therefore we pass the
291 	     complete original arg to skip_function command as if the user
292 	     typed "skip function arg".  */
293 	  do_cleanups (cleanups);
294 	  skip_function_command (arg, from_tty);
295 	  return;
296 	}
297       else
298 	error (_("Invalid argument: %s"), p);
299     }
300 
301   if (file != NULL && gfile != NULL)
302     error (_("Cannot specify both -file and -gfile."));
303 
304   if (function != NULL && rfunction != NULL)
305     error (_("Cannot specify both -function and -rfunction."));
306 
307   /* This shouldn't happen as "skip" by itself gets punted to
308      skip_function_command.  */
309   gdb_assert (file != NULL || gfile != NULL
310 	      || function != NULL || rfunction != NULL);
311 
312   e = make_skip_entry (gfile != NULL, file ? file : gfile,
313 		       rfunction != NULL, function ? function : rfunction);
314   if (rfunction != NULL)
315     {
316       struct cleanup *rf_cleanups = make_free_skiplist_entry_cleanup (e);
317 
318       compile_skip_regexp (e, _("regexp"));
319       discard_cleanups (rf_cleanups);
320     }
321   add_skiplist_entry (e);
322 
323   /* I18N concerns drive some of the choices here (we can't piece together
324      the output too much).  OTOH we want to keep this simple.  Therefore the
325      only polish we add to the output is to append "(s)" to "File" or
326      "Function" if they're a glob/regexp.  */
327   {
328     const char *file_to_print = file != NULL ? file : gfile;
329     const char *function_to_print = function != NULL ? function : rfunction;
330     const char *file_text = gfile != NULL ? _("File(s)") : _("File");
331     const char *lower_file_text = gfile != NULL ? _("file(s)") : _("file");
332     const char *function_text
333       = rfunction != NULL ? _("Function(s)") : _("Function");
334 
335     if (function_to_print == NULL)
336       {
337 	printf_filtered (_("%s %s will be skipped when stepping.\n"),
338 			 file_text, file_to_print);
339       }
340     else if (file_to_print == NULL)
341       {
342 	printf_filtered (_("%s %s will be skipped when stepping.\n"),
343 			 function_text, function_to_print);
344       }
345     else
346       {
347 	printf_filtered (_("%s %s in %s %s will be skipped"
348 			   " when stepping.\n"),
349 			 function_text, function_to_print,
350 			 lower_file_text, file_to_print);
351       }
352   }
353 
354   do_cleanups (cleanups);
355 }
356 
357 static void
358 skip_info (char *arg, int from_tty)
359 {
360   struct skiplist_entry *e;
361   int num_printable_entries = 0;
362   struct value_print_options opts;
363   struct cleanup *tbl_chain;
364 
365   get_user_print_options (&opts);
366 
367   /* Count the number of rows in the table and see if we need space for a
368      64-bit address anywhere.  */
369   ALL_SKIPLIST_ENTRIES (e)
370     if (arg == NULL || number_is_in_list (arg, e->number))
371       num_printable_entries++;
372 
373   if (num_printable_entries == 0)
374     {
375       if (arg == NULL)
376 	ui_out_message (current_uiout, 0, _("\
377 Not skipping any files or functions.\n"));
378       else
379 	ui_out_message (current_uiout, 0,
380 			_("No skiplist entries found with number %s.\n"), arg);
381 
382       return;
383     }
384 
385   tbl_chain = make_cleanup_ui_out_table_begin_end (current_uiout, 6,
386 						   num_printable_entries,
387 						   "SkiplistTable");
388 
389   ui_out_table_header (current_uiout, 5, ui_left, "number", "Num");   /* 1 */
390   ui_out_table_header (current_uiout, 3, ui_left, "enabled", "Enb");  /* 2 */
391   ui_out_table_header (current_uiout, 4, ui_right, "regexp", "Glob"); /* 3 */
392   ui_out_table_header (current_uiout, 20, ui_left, "file", "File");   /* 4 */
393   ui_out_table_header (current_uiout, 2, ui_right, "regexp", "RE");   /* 5 */
394   ui_out_table_header (current_uiout, 40, ui_noalign,
395 		       "function", "Function"); /* 6 */
396   ui_out_table_body (current_uiout);
397 
398   ALL_SKIPLIST_ENTRIES (e)
399     {
400       struct cleanup *entry_chain;
401 
402       QUIT;
403       if (arg != NULL && !number_is_in_list (arg, e->number))
404 	continue;
405 
406       entry_chain = make_cleanup_ui_out_tuple_begin_end (current_uiout,
407 							 "blklst-entry");
408       ui_out_field_int (current_uiout, "number", e->number); /* 1 */
409 
410       if (e->enabled)
411 	ui_out_field_string (current_uiout, "enabled", "y"); /* 2 */
412       else
413 	ui_out_field_string (current_uiout, "enabled", "n"); /* 2 */
414 
415       if (e->file_is_glob)
416 	ui_out_field_string (current_uiout, "regexp", "y"); /* 3 */
417       else
418 	ui_out_field_string (current_uiout, "regexp", "n"); /* 3 */
419 
420       ui_out_field_string (current_uiout, "file",
421 			   e->file ? e->file : "<none>"); /* 4 */
422       if (e->function_is_regexp)
423 	ui_out_field_string (current_uiout, "regexp", "y"); /* 5 */
424       else
425 	ui_out_field_string (current_uiout, "regexp", "n"); /* 5 */
426 
427       ui_out_field_string (current_uiout, "function",
428 			   e->function ? e->function : "<none>"); /* 6 */
429 
430       ui_out_text (current_uiout, "\n");
431       do_cleanups (entry_chain);
432     }
433 
434   do_cleanups (tbl_chain);
435 }
436 
437 static void
438 skip_enable_command (char *arg, int from_tty)
439 {
440   struct skiplist_entry *e;
441   int found = 0;
442 
443   ALL_SKIPLIST_ENTRIES (e)
444     if (arg == NULL || number_is_in_list (arg, e->number))
445       {
446         e->enabled = 1;
447         found = 1;
448       }
449 
450   if (!found)
451     error (_("No skiplist entries found with number %s."), arg);
452 }
453 
454 static void
455 skip_disable_command (char *arg, int from_tty)
456 {
457   struct skiplist_entry *e;
458   int found = 0;
459 
460   ALL_SKIPLIST_ENTRIES (e)
461     if (arg == NULL || number_is_in_list (arg, e->number))
462       {
463 	e->enabled = 0;
464         found = 1;
465       }
466 
467   if (!found)
468     error (_("No skiplist entries found with number %s."), arg);
469 }
470 
471 static void
472 skip_delete_command (char *arg, int from_tty)
473 {
474   struct skiplist_entry *e, *temp, *b_prev;
475   int found = 0;
476 
477   b_prev = 0;
478   ALL_SKIPLIST_ENTRIES_SAFE (e, temp)
479     if (arg == NULL || number_is_in_list (arg, e->number))
480       {
481 	if (b_prev != NULL)
482 	  b_prev->next = e->next;
483 	else
484 	  skiplist_entry_chain = e->next;
485 
486 	free_skiplist_entry (e);
487         found = 1;
488       }
489     else
490       {
491 	b_prev = e;
492       }
493 
494   if (!found)
495     error (_("No skiplist entries found with number %s."), arg);
496 }
497 
498 /* Add the given skiplist entry to our list, and set the entry's number.  */
499 
500 static void
501 add_skiplist_entry (struct skiplist_entry *e)
502 {
503   struct skiplist_entry *e1;
504 
505   e->number = ++skiplist_entry_count;
506 
507   /* Add to the end of the chain so that the list of
508      skiplist entries will be in numerical order.  */
509 
510   e1 = skiplist_entry_chain;
511   if (e1 == NULL)
512     skiplist_entry_chain = e;
513   else
514     {
515       while (e1->next)
516 	e1 = e1->next;
517       e1->next = e;
518     }
519 }
520 
521 /* Return non-zero if we're stopped at a file to be skipped.  */
522 
523 static int
524 skip_file_p (struct skiplist_entry *e,
525 	     const struct symtab_and_line *function_sal)
526 {
527   gdb_assert (e->file != NULL && !e->file_is_glob);
528 
529   if (function_sal->symtab == NULL)
530     return 0;
531 
532   /* Check first sole SYMTAB->FILENAME.  It may not be a substring of
533      symtab_to_fullname as it may contain "./" etc.  */
534   if (compare_filenames_for_search (function_sal->symtab->filename, e->file))
535     return 1;
536 
537   /* Before we invoke realpath, which can get expensive when many
538      files are involved, do a quick comparison of the basenames.  */
539   if (!basenames_may_differ
540       && filename_cmp (lbasename (function_sal->symtab->filename),
541 		       lbasename (e->file)) != 0)
542     return 0;
543 
544   /* Note: symtab_to_fullname caches its result, thus we don't have to.  */
545   {
546     const char *fullname = symtab_to_fullname (function_sal->symtab);
547 
548     if (compare_filenames_for_search (fullname, e->file))
549       return 1;
550   }
551 
552   return 0;
553 }
554 
555 /* Return non-zero if we're stopped at a globbed file to be skipped.  */
556 
557 static int
558 skip_gfile_p (struct skiplist_entry *e,
559 	      const struct symtab_and_line *function_sal)
560 {
561   gdb_assert (e->file != NULL && e->file_is_glob);
562 
563   if (function_sal->symtab == NULL)
564     return 0;
565 
566   /* Check first sole SYMTAB->FILENAME.  It may not be a substring of
567      symtab_to_fullname as it may contain "./" etc.  */
568   if (gdb_filename_fnmatch (e->file, function_sal->symtab->filename,
569 			    FNM_FILE_NAME | FNM_NOESCAPE) == 0)
570     return 1;
571 
572   /* Before we invoke symtab_to_fullname, which is expensive, do a quick
573      comparison of the basenames.
574      Note that we assume that lbasename works with glob-style patterns.
575      If the basename of the glob pattern is something like "*.c" then this
576      isn't much of a win.  Oh well.  */
577   if (!basenames_may_differ
578       && gdb_filename_fnmatch (lbasename (e->file),
579 			       lbasename (function_sal->symtab->filename),
580 			       FNM_FILE_NAME | FNM_NOESCAPE) != 0)
581     return 0;
582 
583   /* Note: symtab_to_fullname caches its result, thus we don't have to.  */
584   {
585     const char *fullname = symtab_to_fullname (function_sal->symtab);
586 
587     if (compare_glob_filenames_for_search (fullname, e->file))
588       return 1;
589   }
590 
591   return 0;
592 }
593 
594 /* Return non-zero if we're stopped at a function to be skipped.  */
595 
596 static int
597 skip_function_p (struct skiplist_entry *e, const char *function_name)
598 {
599   gdb_assert (e->function != NULL && !e->function_is_regexp);
600   return strcmp_iw (function_name, e->function) == 0;
601 }
602 
603 /* Return non-zero if we're stopped at a function regexp to be skipped.  */
604 
605 static int
606 skip_rfunction_p (struct skiplist_entry *e, const char *function_name)
607 {
608   gdb_assert (e->function != NULL && e->function_is_regexp
609 	      && e->compiled_function_regexp_is_valid);
610   return (regexec (&e->compiled_function_regexp, function_name, 0, NULL, 0)
611 	  == 0);
612 }
613 
614 /* See skip.h.  */
615 
616 int
617 function_name_is_marked_for_skip (const char *function_name,
618 				  const struct symtab_and_line *function_sal)
619 {
620   struct skiplist_entry *e;
621 
622   if (function_name == NULL)
623     return 0;
624 
625   ALL_SKIPLIST_ENTRIES (e)
626     {
627       int skip_by_file = 0;
628       int skip_by_function = 0;
629 
630       if (!e->enabled)
631 	continue;
632 
633       if (e->file != NULL)
634 	{
635 	  if (e->file_is_glob)
636 	    {
637 	      if (skip_gfile_p (e, function_sal))
638 		skip_by_file = 1;
639 	    }
640 	  else
641 	    {
642 	      if (skip_file_p (e, function_sal))
643 		skip_by_file = 1;
644 	    }
645 	}
646       if (e->function != NULL)
647 	{
648 	  if (e->function_is_regexp)
649 	    {
650 	      if (skip_rfunction_p (e, function_name))
651 		skip_by_function = 1;
652 	    }
653 	  else
654 	    {
655 	      if (skip_function_p (e, function_name))
656 		skip_by_function = 1;
657 	    }
658 	}
659 
660       /* If both file and function must match, make sure we don't errantly
661 	 exit if only one of them match.  */
662       if (e->file != NULL && e->function != NULL)
663 	{
664 	  if (skip_by_file && skip_by_function)
665 	    return 1;
666 	}
667       /* Only one of file/function is specified.  */
668       else if (skip_by_file || skip_by_function)
669 	return 1;
670     }
671 
672   return 0;
673 }
674 
675 /* Provide a prototype to silence -Wmissing-prototypes.  */
676 extern initialize_file_ftype _initialize_step_skip;
677 
678 void
679 _initialize_step_skip (void)
680 {
681   static struct cmd_list_element *skiplist = NULL;
682   struct cmd_list_element *c;
683 
684   skiplist_entry_chain = 0;
685   skiplist_entry_count = 0;
686 
687   add_prefix_cmd ("skip", class_breakpoint, skip_command, _("\
688 Ignore a function while stepping.\n\
689 \n\
690 Usage: skip [FUNCTION-NAME]\n\
691        skip [<file-spec>] [<function-spec>]\n\
692 If no arguments are given, ignore the current function.\n\
693 \n\
694 <file-spec> is one of:\n\
695        -fi|-file FILE-NAME\n\
696        -gfi|-gfile GLOB-FILE-PATTERN\n\
697 <function-spec> is one of:\n\
698        -fu|-function FUNCTION-NAME\n\
699        -rfu|-rfunction FUNCTION-NAME-REGULAR-EXPRESSION"),
700                   &skiplist, "skip ", 1, &cmdlist);
701 
702   c = add_cmd ("file", class_breakpoint, skip_file_command, _("\
703 Ignore a file while stepping.\n\
704 Usage: skip file [FILE-NAME]\n\
705 If no filename is given, ignore the current file."),
706 	       &skiplist);
707   set_cmd_completer (c, filename_completer);
708 
709   c = add_cmd ("function", class_breakpoint, skip_function_command, _("\
710 Ignore a function while stepping.\n\
711 Usage: skip function [FUNCTION-NAME]\n\
712 If no function name is given, skip the current function."),
713 	       &skiplist);
714   set_cmd_completer (c, location_completer);
715 
716   add_cmd ("enable", class_breakpoint, skip_enable_command, _("\
717 Enable skip entries.  You can specify numbers (e.g. \"skip enable 1 3\"), \
718 ranges (e.g. \"skip enable 4-8\"), or both (e.g. \"skip enable 1 3 4-8\").\n\n\
719 If you don't specify any numbers or ranges, we'll enable all skip entries.\n\n\
720 Usage: skip enable [NUMBERS AND/OR RANGES]"),
721 	   &skiplist);
722 
723   add_cmd ("disable", class_breakpoint, skip_disable_command, _("\
724 Disable skip entries.  You can specify numbers (e.g. \"skip disable 1 3\"), \
725 ranges (e.g. \"skip disable 4-8\"), or both (e.g. \"skip disable 1 3 4-8\").\n\n\
726 If you don't specify any numbers or ranges, we'll disable all skip entries.\n\n\
727 Usage: skip disable [NUMBERS AND/OR RANGES]"),
728 	   &skiplist);
729 
730   add_cmd ("delete", class_breakpoint, skip_delete_command, _("\
731 Delete skip entries.  You can specify numbers (e.g. \"skip delete 1 3\"), \
732 ranges (e.g. \"skip delete 4-8\"), or both (e.g. \"skip delete 1 3 4-8\").\n\n\
733 If you don't specify any numbers or ranges, we'll delete all skip entries.\n\n\
734 Usage: skip delete [NUMBERS AND/OR RANGES]"),
735            &skiplist);
736 
737   add_info ("skip", skip_info, _("\
738 Display the status of skips.  You can specify numbers (e.g. \"skip info 1 3\"), \
739 ranges (e.g. \"skip info 4-8\"), or both (e.g. \"skip info 1 3 4-8\").\n\n\
740 If you don't specify any numbers or ranges, we'll show all skips.\n\n\
741 Usage: skip info [NUMBERS AND/OR RANGES]\n\
742 The \"Type\" column indicates one of:\n\
743 \tfile        - ignored file\n\
744 \tfunction    - ignored function"));
745 }
746