constify error_no_arg
[deliverable/binutils-gdb.git] / gdb / cli / cli-setshow.c
CommitLineData
d318976c 1/* Handle set and show GDB commands.
8926118c 2
ecd75fc8 3 Copyright (C) 2000-2014 Free Software Foundation, Inc.
d318976c
FN
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
a9762ec7 7 the Free Software Foundation; either version 3 of the License, or
d318976c
FN
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
a9762ec7 16 along with this program. If not, see <http://www.gnu.org/licenses/>. */
d318976c
FN
17
18#include "defs.h"
dbda9972 19#include "readline/tilde.h"
d318976c
FN
20#include "value.h"
21#include <ctype.h>
0e9f083f 22#include <string.h>
f870a310 23#include "arch-utils.h"
5b9afe8a 24#include "observer.h"
d318976c 25
d318976c 26#include "ui-out.h"
d318976c
FN
27
28#include "cli/cli-decode.h"
29#include "cli/cli-cmds.h"
30#include "cli/cli-setshow.h"
f81d1120 31#include "cli/cli-utils.h"
d318976c 32
5b9afe8a
YQ
33/* Return true if the change of command parameter should be notified. */
34
35static int
36notify_command_param_changed_p (int param_changed, struct cmd_list_element *c)
37{
38 if (param_changed == 0)
39 return 0;
40
41 if (c->class == class_maintenance || c->class == class_deprecated
42 || c->class == class_obscure)
43 return 0;
44
45 return 1;
46}
47
d318976c 48\f
7f19b9a2 49static enum auto_boolean
d318976c
FN
50parse_auto_binary_operation (const char *arg)
51{
52 if (arg != NULL && *arg != '\0')
53 {
54 int length = strlen (arg);
cdb27c12 55
d318976c
FN
56 while (isspace (arg[length - 1]) && length > 0)
57 length--;
58 if (strncmp (arg, "on", length) == 0
59 || strncmp (arg, "1", length) == 0
60 || strncmp (arg, "yes", length) == 0
61 || strncmp (arg, "enable", length) == 0)
7f19b9a2 62 return AUTO_BOOLEAN_TRUE;
d318976c
FN
63 else if (strncmp (arg, "off", length) == 0
64 || strncmp (arg, "0", length) == 0
65 || strncmp (arg, "no", length) == 0
66 || strncmp (arg, "disable", length) == 0)
7f19b9a2 67 return AUTO_BOOLEAN_FALSE;
d318976c
FN
68 else if (strncmp (arg, "auto", length) == 0
69 || (strncmp (arg, "-1", length) == 0 && length > 1))
7f19b9a2 70 return AUTO_BOOLEAN_AUTO;
d318976c 71 }
8a3fe4f8 72 error (_("\"on\", \"off\" or \"auto\" expected."));
ebcd3b23 73 return AUTO_BOOLEAN_AUTO; /* Pacify GCC. */
d318976c
FN
74}
75
bd712aed
DE
76/* See cli-setshow.h. */
77
78int
5bc98e52 79parse_cli_boolean_value (const char *arg)
d318976c
FN
80{
81 int length;
82
83 if (!arg || !*arg)
84 return 1;
85
86 length = strlen (arg);
87
88 while (arg[length - 1] == ' ' || arg[length - 1] == '\t')
89 length--;
90
91 if (strncmp (arg, "on", length) == 0
92 || strncmp (arg, "1", length) == 0
93 || strncmp (arg, "yes", length) == 0
94 || strncmp (arg, "enable", length) == 0)
95 return 1;
96 else if (strncmp (arg, "off", length) == 0
97 || strncmp (arg, "0", length) == 0
98 || strncmp (arg, "no", length) == 0
99 || strncmp (arg, "disable", length) == 0)
100 return 0;
101 else
bd712aed 102 return -1;
d318976c
FN
103}
104\f
08546159
AC
105void
106deprecated_show_value_hack (struct ui_file *ignore_file,
107 int ignore_from_tty,
108 struct cmd_list_element *c,
109 const char *value)
110{
4d28ad1e
AC
111 /* If there's no command or value, don't try to print it out. */
112 if (c == NULL || value == NULL)
113 return;
08546159
AC
114 /* Print doc minus "show" at start. */
115 print_doc_line (gdb_stdout, c->doc + 5);
116 switch (c->var_type)
117 {
118 case var_string:
119 case var_string_noescape:
b4b4ac0b 120 case var_optional_filename:
08546159
AC
121 case var_filename:
122 case var_enum:
123 printf_filtered ((" is \"%s\".\n"), value);
124 break;
125 default:
126 printf_filtered ((" is %s.\n"), value);
127 break;
128 }
129}
130
f81d1120
PA
131/* Returns true if ARG is "unlimited". */
132
133static int
134is_unlimited_literal (const char *arg)
135{
136 size_t len = sizeof ("unlimited") - 1;
137
138 arg = skip_spaces_const (arg);
139
140 return (strncmp (arg, "unlimited", len) == 0
141 && (isspace (arg[len]) || arg[len] == '\0'));
142}
143
144
5b9afe8a 145/* Do a "set" command. ARG is NULL if no argument, or the
ebcd3b23
MS
146 text of the argument, and FROM_TTY is nonzero if this command is
147 being entered directly by the user (i.e. these are just like any
148 other command). C is the command list element for the command. */
d318976c
FN
149
150void
06900326 151do_set_command (const char *arg, int from_tty, struct cmd_list_element *c)
d318976c 152{
5b9afe8a
YQ
153 /* A flag to indicate the option is changed or not. */
154 int option_changed = 0;
155
156 gdb_assert (c->type == set_cmd);
79a45e25 157
5b9afe8a 158 switch (c->var_type)
d318976c 159 {
5b9afe8a
YQ
160 case var_string:
161 {
162 char *new;
d7561cbb 163 const char *p;
5b9afe8a
YQ
164 char *q;
165 int ch;
166
167 if (arg == NULL)
168 arg = "";
169 new = (char *) xmalloc (strlen (arg) + 2);
170 p = arg;
171 q = new;
172 while ((ch = *p++) != '\000')
d318976c 173 {
5b9afe8a 174 if (ch == '\\')
d318976c 175 {
5b9afe8a
YQ
176 /* \ at end of argument is used after spaces
177 so they won't be lost. */
178 /* This is obsolete now that we no longer strip
179 trailing whitespace and actually, the backslash
180 didn't get here in my test, readline or
181 something did something funky with a backslash
182 right before a newline. */
183 if (*p == 0)
184 break;
185 ch = parse_escape (get_current_arch (), &p);
186 if (ch == 0)
187 break; /* C loses */
188 else if (ch > 0)
d318976c
FN
189 *q++ = ch;
190 }
5b9afe8a
YQ
191 else
192 *q++ = ch;
193 }
d318976c 194#if 0
5b9afe8a
YQ
195 if (*(p - 1) != '\\')
196 *q++ = ' ';
d318976c 197#endif
5b9afe8a
YQ
198 *q++ = '\0';
199 new = (char *) xrealloc (new, q - new);
200
201 if (*(char **) c->var == NULL
202 || strcmp (*(char **) c->var, new) != 0)
203 {
c24343e2 204 xfree (*(char **) c->var);
d318976c 205 *(char **) c->var = new;
5b9afe8a
YQ
206
207 option_changed = 1;
d318976c 208 }
5b9afe8a
YQ
209 else
210 xfree (new);
211 }
212 break;
213 case var_string_noescape:
214 if (arg == NULL)
215 arg = "";
216
217 if (*(char **) c->var == NULL || strcmp (*(char **) c->var, arg) != 0)
218 {
c24343e2 219 xfree (*(char **) c->var);
1b36a34b 220 *(char **) c->var = xstrdup (arg);
cdb27c12 221
5b9afe8a
YQ
222 option_changed = 1;
223 }
224 break;
225 case var_filename:
226 if (arg == NULL)
227 error_no_arg (_("filename to set it to."));
228 /* FALLTHROUGH */
229 case var_optional_filename:
230 {
231 char *val = NULL;
6ace3df1 232
5b9afe8a
YQ
233 if (arg != NULL)
234 {
235 /* Clear trailing whitespace of filename. */
06900326
TT
236 const char *ptr = arg + strlen (arg) - 1;
237 char *copy;
6ace3df1 238
5b9afe8a
YQ
239 while (ptr >= arg && (*ptr == ' ' || *ptr == '\t'))
240 ptr--;
06900326 241 copy = xstrndup (arg, ptr + 1 - arg);
5b9afe8a 242
06900326
TT
243 val = tilde_expand (copy);
244 xfree (copy);
5b9afe8a
YQ
245 }
246 else
247 val = xstrdup ("");
248
249 if (*(char **) c->var == NULL
250 || strcmp (*(char **) c->var, val) != 0)
d318976c 251 {
5b9afe8a
YQ
252 xfree (*(char **) c->var);
253 *(char **) c->var = val;
254
255 option_changed = 1;
d318976c 256 }
5b9afe8a
YQ
257 else
258 xfree (val);
259 }
260 break;
261 case var_boolean:
262 {
bd712aed 263 int val = parse_cli_boolean_value (arg);
5b9afe8a 264
bd712aed
DE
265 if (val < 0)
266 error (_("\"on\" or \"off\" expected."));
5b9afe8a
YQ
267 if (val != *(int *) c->var)
268 {
269 *(int *) c->var = val;
270
271 option_changed = 1;
272 }
273 }
274 break;
275 case var_auto_boolean:
276 {
277 enum auto_boolean val = parse_auto_binary_operation (arg);
278
279 if (*(enum auto_boolean *) c->var != val)
280 {
281 *(enum auto_boolean *) c->var = val;
282
283 option_changed = 1;
284 }
285 }
286 break;
287 case var_uinteger:
288 case var_zuinteger:
5b9afe8a 289 {
ef0026f0
PA
290 LONGEST val;
291
292 if (arg == NULL)
f81d1120
PA
293 {
294 if (c->var_type == var_uinteger)
295 error_no_arg (_("integer to set it to, or \"unlimited\"."));
296 else
297 error_no_arg (_("integer to set it to."));
298 }
299
300 if (c->var_type == var_uinteger && is_unlimited_literal (arg))
301 val = 0;
302 else
303 val = parse_and_eval_long (arg);
5b9afe8a
YQ
304
305 if (c->var_type == var_uinteger && val == 0)
306 val = UINT_MAX;
82b821e9
PA
307 else if (val < 0
308 /* For var_uinteger, don't let the user set the value
309 to UINT_MAX directly, as that exposes an
310 implementation detail to the user interface. */
311 || (c->var_type == var_uinteger && val >= UINT_MAX)
312 || (c->var_type == var_zuinteger && val > UINT_MAX))
ef0026f0 313 error (_("integer %s out of range"), plongest (val));
5b9afe8a
YQ
314
315 if (*(unsigned int *) c->var != val)
316 {
317 *(unsigned int *) c->var = val;
318
319 option_changed = 1;
320 }
321 }
322 break;
323 case var_integer:
324 case var_zinteger:
325 {
ef0026f0 326 LONGEST val;
5b9afe8a
YQ
327
328 if (arg == NULL)
f81d1120
PA
329 {
330 if (c->var_type == var_integer)
331 error_no_arg (_("integer to set it to, or \"unlimited\"."));
332 else
333 error_no_arg (_("integer to set it to."));
334 }
335
336 if (c->var_type == var_integer && is_unlimited_literal (arg))
337 val = 0;
338 else
339 val = parse_and_eval_long (arg);
ef0026f0 340
5b9afe8a
YQ
341 if (val == 0 && c->var_type == var_integer)
342 val = INT_MAX;
82b821e9
PA
343 else if (val < INT_MIN
344 /* For var_integer, don't let the user set the value
345 to INT_MAX directly, as that exposes an
346 implementation detail to the user interface. */
347 || (c->var_type == var_integer && val >= INT_MAX)
348 || (c->var_type == var_zinteger && val > INT_MAX))
ef0026f0 349 error (_("integer %s out of range"), plongest (val));
5b9afe8a
YQ
350
351 if (*(int *) c->var != val)
d318976c 352 {
5b9afe8a
YQ
353 *(int *) c->var = val;
354
355 option_changed = 1;
356 }
357 break;
358 }
359 case var_enum:
360 {
361 int i;
362 int len;
363 int nmatches;
364 const char *match = NULL;
365 char *p;
366
367 /* If no argument was supplied, print an informative error
368 message. */
369 if (arg == NULL)
370 {
371 char *msg;
372 int msg_len = 0;
373
374 for (i = 0; c->enums[i]; i++)
375 msg_len += strlen (c->enums[i]) + 2;
376
377 msg = xmalloc (msg_len);
378 *msg = '\0';
379 make_cleanup (xfree, msg);
380
381 for (i = 0; c->enums[i]; i++)
d318976c 382 {
5b9afe8a
YQ
383 if (i != 0)
384 strcat (msg, ", ");
385 strcat (msg, c->enums[i]);
d318976c 386 }
5b9afe8a
YQ
387 error (_("Requires an argument. Valid arguments are %s."),
388 msg);
389 }
d318976c 390
5b9afe8a 391 p = strchr (arg, ' ');
d318976c 392
5b9afe8a
YQ
393 if (p)
394 len = p - arg;
395 else
396 len = strlen (arg);
d318976c 397
5b9afe8a
YQ
398 nmatches = 0;
399 for (i = 0; c->enums[i]; i++)
400 if (strncmp (arg, c->enums[i], len) == 0)
401 {
402 if (c->enums[i][len] == '\0')
403 {
404 match = c->enums[i];
405 nmatches = 1;
406 break; /* Exact match. */
407 }
408 else
d318976c 409 {
5b9afe8a
YQ
410 match = c->enums[i];
411 nmatches++;
d318976c 412 }
5b9afe8a 413 }
d318976c 414
5b9afe8a
YQ
415 if (nmatches <= 0)
416 error (_("Undefined item: \"%s\"."), arg);
d318976c 417
5b9afe8a
YQ
418 if (nmatches > 1)
419 error (_("Ambiguous item \"%s\"."), arg);
d318976c 420
5b9afe8a
YQ
421 if (*(const char **) c->var != match)
422 {
d318976c 423 *(const char **) c->var = match;
5b9afe8a
YQ
424
425 option_changed = 1;
d318976c 426 }
5b9afe8a
YQ
427 }
428 break;
6fc1c773
YQ
429 case var_zuinteger_unlimited:
430 {
431 LONGEST val;
432
433 if (arg == NULL)
f81d1120
PA
434 error_no_arg (_("integer to set it to, or \"unlimited\"."));
435
436 if (is_unlimited_literal (arg))
437 val = -1;
438 else
439 val = parse_and_eval_long (arg);
6fc1c773 440
ef0026f0 441 if (val > INT_MAX)
6fc1c773
YQ
442 error (_("integer %s out of range"), plongest (val));
443 else if (val < -1)
444 error (_("only -1 is allowed to set as unlimited"));
445
446 if (*(int *) c->var != val)
447 {
448 *(int *) c->var = val;
449 option_changed = 1;
450 }
451 }
452 break;
5b9afe8a
YQ
453 default:
454 error (_("gdb internal error: bad var_type in do_setshow_command"));
d318976c 455 }
5b9afe8a 456 c->func (c, NULL, from_tty);
5b9afe8a
YQ
457
458 if (notify_command_param_changed_p (option_changed, c))
d318976c 459 {
5b9afe8a
YQ
460 char *name, *cp;
461 struct cmd_list_element **cmds;
462 struct cmd_list_element *p;
463 int i;
464 int length = 0;
465
466 /* Compute the whole multi-word command options. If user types command
467 'set foo bar baz on', c->name is 'baz', and GDB can't pass "bar" to
468 command option change notification, because it is confusing. We can
469 trace back through field 'prefix' to compute the whole options,
470 and pass "foo bar baz" to notification. */
471
472 for (i = 0, p = c; p != NULL; i++)
473 {
474 length += strlen (p->name);
475 length++;
476
477 p = p->prefix;
478 }
479 cp = name = xmalloc (length);
480 cmds = xmalloc (sizeof (struct cmd_list_element *) * i);
481
482 /* Track back through filed 'prefix' and cache them in CMDS. */
483 for (i = 0, p = c; p != NULL; i++)
484 {
485 cmds[i] = p;
486 p = p->prefix;
487 }
488
489 /* Don't trigger any observer notification if prefixlist is not
490 setlist. */
491 i--;
492 if (cmds[i]->prefixlist != &setlist)
493 {
494 xfree (cmds);
495 xfree (name);
496
497 return;
498 }
499 /* Traverse them in the reversed order, and copy their names into
500 NAME. */
501 for (i--; i >= 0; i--)
502 {
503 memcpy (cp, cmds[i]->name, strlen (cmds[i]->name));
504 cp += strlen (cmds[i]->name);
d318976c 505
5b9afe8a
YQ
506 if (i != 0)
507 {
508 cp[0] = ' ';
509 cp++;
510 }
511 }
512 cp[0] = 0;
d318976c 513
5b9afe8a 514 xfree (cmds);
552c04a7 515
d318976c
FN
516 switch (c->var_type)
517 {
518 case var_string:
d318976c
FN
519 case var_string_noescape:
520 case var_filename:
5b9afe8a 521 case var_optional_filename:
d318976c 522 case var_enum:
5b9afe8a 523 observer_notify_command_param_changed (name, *(char **) c->var);
d318976c
FN
524 break;
525 case var_boolean:
5b9afe8a
YQ
526 {
527 char *opt = *(int *) c->var ? "on" : "off";
528
529 observer_notify_command_param_changed (name, opt);
530 }
d318976c
FN
531 break;
532 case var_auto_boolean:
5b9afe8a
YQ
533 {
534 const char *s = auto_boolean_enums[*(enum auto_boolean *) c->var];
535
536 observer_notify_command_param_changed (name, s);
537 }
d318976c
FN
538 break;
539 case var_uinteger:
1e8fb976 540 case var_zuinteger:
5b9afe8a
YQ
541 {
542 char s[64];
543
544 xsnprintf (s, sizeof s, "%u", *(unsigned int *) c->var);
545 observer_notify_command_param_changed (name, s);
546 }
d318976c
FN
547 break;
548 case var_integer:
a40a111f 549 case var_zinteger:
6fc1c773 550 case var_zuinteger_unlimited:
5b9afe8a
YQ
551 {
552 char s[64];
d318976c 553
5b9afe8a
YQ
554 xsnprintf (s, sizeof s, "%d", *(int *) c->var);
555 observer_notify_command_param_changed (name, s);
556 }
557 break;
d318976c 558 }
5b9afe8a
YQ
559 xfree (name);
560 }
561}
899506a8 562
5b9afe8a
YQ
563/* Do a "show" command. ARG is NULL if no argument, or the
564 text of the argument, and FROM_TTY is nonzero if this command is
565 being entered directly by the user (i.e. these are just like any
566 other command). C is the command list element for the command. */
899506a8 567
5b9afe8a 568void
06900326 569do_show_command (const char *arg, int from_tty, struct cmd_list_element *c)
5b9afe8a
YQ
570{
571 struct ui_out *uiout = current_uiout;
572 struct cleanup *old_chain;
573 struct ui_file *stb;
899506a8 574
5b9afe8a
YQ
575 gdb_assert (c->type == show_cmd);
576
577 stb = mem_fileopen ();
578 old_chain = make_cleanup_ui_file_delete (stb);
cdb27c12 579
5b9afe8a
YQ
580 /* Possibly call the pre hook. */
581 if (c->pre_show_hook)
582 (c->pre_show_hook) (c);
583
584 switch (c->var_type)
585 {
586 case var_string:
587 if (*(char **) c->var)
588 fputstr_filtered (*(char **) c->var, '"', stb);
589 break;
590 case var_string_noescape:
591 case var_optional_filename:
592 case var_filename:
593 case var_enum:
594 if (*(char **) c->var)
595 fputs_filtered (*(char **) c->var, stb);
596 break;
597 case var_boolean:
598 fputs_filtered (*(int *) c->var ? "on" : "off", stb);
599 break;
600 case var_auto_boolean:
601 switch (*(enum auto_boolean*) c->var)
602 {
603 case AUTO_BOOLEAN_TRUE:
604 fputs_filtered ("on", stb);
605 break;
606 case AUTO_BOOLEAN_FALSE:
607 fputs_filtered ("off", stb);
608 break;
609 case AUTO_BOOLEAN_AUTO:
610 fputs_filtered ("auto", stb);
611 break;
612 default:
613 internal_error (__FILE__, __LINE__,
614 _("do_show_command: "
615 "invalid var_auto_boolean"));
616 break;
899506a8 617 }
5b9afe8a
YQ
618 break;
619 case var_uinteger:
620 case var_zuinteger:
621 if (c->var_type == var_uinteger
622 && *(unsigned int *) c->var == UINT_MAX)
623 fputs_filtered ("unlimited", stb);
624 else
625 fprintf_filtered (stb, "%u", *(unsigned int *) c->var);
626 break;
627 case var_integer:
628 case var_zinteger:
629 if (c->var_type == var_integer
630 && *(int *) c->var == INT_MAX)
631 fputs_filtered ("unlimited", stb);
632 else
633 fprintf_filtered (stb, "%d", *(int *) c->var);
634 break;
6fc1c773
YQ
635 case var_zuinteger_unlimited:
636 {
637 if (*(int *) c->var == -1)
638 fputs_filtered ("unlimited", stb);
639 else
ef0026f0 640 fprintf_filtered (stb, "%d", *(int *) c->var);
6fc1c773
YQ
641 }
642 break;
5b9afe8a
YQ
643 default:
644 error (_("gdb internal error: bad var_type in do_show_command"));
d318976c 645 }
5b9afe8a
YQ
646
647
648 /* FIXME: cagney/2005-02-10: Need to split this in half: code to
649 convert the value into a string (esentially the above); and
650 code to print the value out. For the latter there should be
651 MI and CLI specific versions. */
652
653 if (ui_out_is_mi_like_p (uiout))
654 ui_out_field_stream (uiout, "value", stb);
d318976c 655 else
5b9afe8a
YQ
656 {
657 char *value = ui_file_xstrdup (stb, NULL);
658
659 make_cleanup (xfree, value);
660 if (c->show_value_func != NULL)
661 c->show_value_func (gdb_stdout, from_tty, c, value);
662 else
663 deprecated_show_value_hack (gdb_stdout, from_tty, c, value);
664 }
665 do_cleanups (old_chain);
666
9f60d481 667 c->func (c, NULL, from_tty);
d318976c
FN
668}
669
670/* Show all the settings in a list of show commands. */
671
672void
673cmd_show_list (struct cmd_list_element *list, int from_tty, char *prefix)
674{
3b31d625 675 struct cleanup *showlist_chain;
79a45e25 676 struct ui_out *uiout = current_uiout;
3b31d625
EZ
677
678 showlist_chain = make_cleanup_ui_out_tuple_begin_end (uiout, "showlist");
d318976c
FN
679 for (; list != NULL; list = list->next)
680 {
681 /* If we find a prefix, run its list, prefixing our output by its
682 prefix (with "show " skipped). */
d318976c
FN
683 if (list->prefixlist && !list->abbrev_flag)
684 {
3b31d625
EZ
685 struct cleanup *optionlist_chain
686 = make_cleanup_ui_out_tuple_begin_end (uiout, "optionlist");
37fc812e 687 char *new_prefix = strstr (list->prefixname, "show ") + 5;
cdb27c12 688
37fc812e
DJ
689 if (ui_out_is_mi_like_p (uiout))
690 ui_out_field_string (uiout, "prefix", new_prefix);
691 cmd_show_list (*list->prefixlist, from_tty, new_prefix);
3b31d625
EZ
692 /* Close the tuple. */
693 do_cleanups (optionlist_chain);
d318976c 694 }
427c3a89 695 else
d318976c 696 {
db5f229b
MS
697 if (list->class != no_set_class)
698 {
699 struct cleanup *option_chain
700 = make_cleanup_ui_out_tuple_begin_end (uiout, "option");
701
702 ui_out_text (uiout, prefix);
703 ui_out_field_string (uiout, "name", list->name);
704 ui_out_text (uiout, ": ");
705 if (list->type == show_cmd)
5b9afe8a 706 do_show_command ((char *) NULL, from_tty, list);
db5f229b
MS
707 else
708 cmd_func (list, NULL, from_tty);
709 /* Close the tuple. */
710 do_cleanups (option_chain);
711 }
d318976c 712 }
d318976c 713 }
3b31d625
EZ
714 /* Close the tuple. */
715 do_cleanups (showlist_chain);
d318976c
FN
716}
717
This page took 0.844773 seconds and 4 git commands to generate.