]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blame - gdb/cli/cli-setshow.c
Update Copyright year range in all files maintained by GDB.
[thirdparty/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
79parse_cli_boolean_value (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
5b9afe8a 151do_set_command (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. */
236 char *ptr = arg + strlen (arg) - 1;
6ace3df1 237
5b9afe8a
YQ
238 while (ptr >= arg && (*ptr == ' ' || *ptr == '\t'))
239 ptr--;
240 *(ptr + 1) = '\0';
241
242 val = tilde_expand (arg);
243 }
244 else
245 val = xstrdup ("");
246
247 if (*(char **) c->var == NULL
248 || strcmp (*(char **) c->var, val) != 0)
d318976c 249 {
5b9afe8a
YQ
250 xfree (*(char **) c->var);
251 *(char **) c->var = val;
252
253 option_changed = 1;
d318976c 254 }
5b9afe8a
YQ
255 else
256 xfree (val);
257 }
258 break;
259 case var_boolean:
260 {
bd712aed 261 int val = parse_cli_boolean_value (arg);
5b9afe8a 262
bd712aed
DE
263 if (val < 0)
264 error (_("\"on\" or \"off\" expected."));
5b9afe8a
YQ
265 if (val != *(int *) c->var)
266 {
267 *(int *) c->var = val;
268
269 option_changed = 1;
270 }
271 }
272 break;
273 case var_auto_boolean:
274 {
275 enum auto_boolean val = parse_auto_binary_operation (arg);
276
277 if (*(enum auto_boolean *) c->var != val)
278 {
279 *(enum auto_boolean *) c->var = val;
280
281 option_changed = 1;
282 }
283 }
284 break;
285 case var_uinteger:
286 case var_zuinteger:
5b9afe8a 287 {
ef0026f0
PA
288 LONGEST val;
289
290 if (arg == NULL)
f81d1120
PA
291 {
292 if (c->var_type == var_uinteger)
293 error_no_arg (_("integer to set it to, or \"unlimited\"."));
294 else
295 error_no_arg (_("integer to set it to."));
296 }
297
298 if (c->var_type == var_uinteger && is_unlimited_literal (arg))
299 val = 0;
300 else
301 val = parse_and_eval_long (arg);
5b9afe8a
YQ
302
303 if (c->var_type == var_uinteger && val == 0)
304 val = UINT_MAX;
82b821e9
PA
305 else if (val < 0
306 /* For var_uinteger, don't let the user set the value
307 to UINT_MAX directly, as that exposes an
308 implementation detail to the user interface. */
309 || (c->var_type == var_uinteger && val >= UINT_MAX)
310 || (c->var_type == var_zuinteger && val > UINT_MAX))
ef0026f0 311 error (_("integer %s out of range"), plongest (val));
5b9afe8a
YQ
312
313 if (*(unsigned int *) c->var != val)
314 {
315 *(unsigned int *) c->var = val;
316
317 option_changed = 1;
318 }
319 }
320 break;
321 case var_integer:
322 case var_zinteger:
323 {
ef0026f0 324 LONGEST val;
5b9afe8a
YQ
325
326 if (arg == NULL)
f81d1120
PA
327 {
328 if (c->var_type == var_integer)
329 error_no_arg (_("integer to set it to, or \"unlimited\"."));
330 else
331 error_no_arg (_("integer to set it to."));
332 }
333
334 if (c->var_type == var_integer && is_unlimited_literal (arg))
335 val = 0;
336 else
337 val = parse_and_eval_long (arg);
ef0026f0 338
5b9afe8a
YQ
339 if (val == 0 && c->var_type == var_integer)
340 val = INT_MAX;
82b821e9
PA
341 else if (val < INT_MIN
342 /* For var_integer, don't let the user set the value
343 to INT_MAX directly, as that exposes an
344 implementation detail to the user interface. */
345 || (c->var_type == var_integer && val >= INT_MAX)
346 || (c->var_type == var_zinteger && val > INT_MAX))
ef0026f0 347 error (_("integer %s out of range"), plongest (val));
5b9afe8a
YQ
348
349 if (*(int *) c->var != val)
d318976c 350 {
5b9afe8a
YQ
351 *(int *) c->var = val;
352
353 option_changed = 1;
354 }
355 break;
356 }
357 case var_enum:
358 {
359 int i;
360 int len;
361 int nmatches;
362 const char *match = NULL;
363 char *p;
364
365 /* If no argument was supplied, print an informative error
366 message. */
367 if (arg == NULL)
368 {
369 char *msg;
370 int msg_len = 0;
371
372 for (i = 0; c->enums[i]; i++)
373 msg_len += strlen (c->enums[i]) + 2;
374
375 msg = xmalloc (msg_len);
376 *msg = '\0';
377 make_cleanup (xfree, msg);
378
379 for (i = 0; c->enums[i]; i++)
d318976c 380 {
5b9afe8a
YQ
381 if (i != 0)
382 strcat (msg, ", ");
383 strcat (msg, c->enums[i]);
d318976c 384 }
5b9afe8a
YQ
385 error (_("Requires an argument. Valid arguments are %s."),
386 msg);
387 }
d318976c 388
5b9afe8a 389 p = strchr (arg, ' ');
d318976c 390
5b9afe8a
YQ
391 if (p)
392 len = p - arg;
393 else
394 len = strlen (arg);
d318976c 395
5b9afe8a
YQ
396 nmatches = 0;
397 for (i = 0; c->enums[i]; i++)
398 if (strncmp (arg, c->enums[i], len) == 0)
399 {
400 if (c->enums[i][len] == '\0')
401 {
402 match = c->enums[i];
403 nmatches = 1;
404 break; /* Exact match. */
405 }
406 else
d318976c 407 {
5b9afe8a
YQ
408 match = c->enums[i];
409 nmatches++;
d318976c 410 }
5b9afe8a 411 }
d318976c 412
5b9afe8a
YQ
413 if (nmatches <= 0)
414 error (_("Undefined item: \"%s\"."), arg);
d318976c 415
5b9afe8a
YQ
416 if (nmatches > 1)
417 error (_("Ambiguous item \"%s\"."), arg);
d318976c 418
5b9afe8a
YQ
419 if (*(const char **) c->var != match)
420 {
d318976c 421 *(const char **) c->var = match;
5b9afe8a
YQ
422
423 option_changed = 1;
d318976c 424 }
5b9afe8a
YQ
425 }
426 break;
6fc1c773
YQ
427 case var_zuinteger_unlimited:
428 {
429 LONGEST val;
430
431 if (arg == NULL)
f81d1120
PA
432 error_no_arg (_("integer to set it to, or \"unlimited\"."));
433
434 if (is_unlimited_literal (arg))
435 val = -1;
436 else
437 val = parse_and_eval_long (arg);
6fc1c773 438
ef0026f0 439 if (val > INT_MAX)
6fc1c773
YQ
440 error (_("integer %s out of range"), plongest (val));
441 else if (val < -1)
442 error (_("only -1 is allowed to set as unlimited"));
443
444 if (*(int *) c->var != val)
445 {
446 *(int *) c->var = val;
447 option_changed = 1;
448 }
449 }
450 break;
5b9afe8a
YQ
451 default:
452 error (_("gdb internal error: bad var_type in do_setshow_command"));
d318976c 453 }
5b9afe8a
YQ
454 c->func (c, NULL, from_tty);
455 if (deprecated_set_hook)
456 deprecated_set_hook (c);
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
YQ
568void
569do_show_command (char *arg, int from_tty, struct cmd_list_element *c)
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