]> git.ipfire.org Git - thirdparty/bash.git/blob - print_cmd.c
bash-5.0 distribution sources and documentation
[thirdparty/bash.git] / print_cmd.c
1 /* print_command -- A way to make readable commands from a command tree. */
2
3 /* Copyright (C) 1989-2017 Free Software Foundation, Inc.
4
5 This file is part of GNU Bash, the Bourne Again SHell.
6
7 Bash 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 Bash 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 Bash. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include "config.h"
22
23 #include <stdio.h>
24
25 #if defined (HAVE_UNISTD_H)
26 # ifdef _MINIX
27 # include <sys/types.h>
28 # endif
29 # include <unistd.h>
30 #endif
31
32 #if defined (PREFER_STDARG)
33 # include <stdarg.h>
34 #else
35 # include <varargs.h>
36 #endif
37
38 #include "bashansi.h"
39 #include "bashintl.h"
40
41 #define NEED_XTRACE_SET_DECL
42
43 #include "shell.h"
44 #include "flags.h"
45 #include <y.tab.h> /* use <...> so we pick it up from the build directory */
46 #include "input.h"
47
48 #include "shmbutil.h"
49
50 #include "builtins/common.h"
51
52 #if !HAVE_DECL_PRINTF
53 extern int printf __P((const char *, ...)); /* Yuck. Double yuck. */
54 #endif
55
56 static int indentation;
57 static int indentation_amount = 4;
58
59 #if defined (PREFER_STDARG)
60 typedef void PFUNC __P((const char *, ...));
61
62 static void cprintf __P((const char *, ...)) __attribute__((__format__ (printf, 1, 2)));
63 static void xprintf __P((const char *, ...)) __attribute__((__format__ (printf, 1, 2)));
64 #else
65 #define PFUNC VFunction
66 static void cprintf ();
67 static void xprintf ();
68 #endif
69
70 static void reset_locals __P((void));
71 static void newline __P((char *));
72 static void indent __P((int));
73 static void semicolon __P((void));
74 static void the_printed_command_resize __P((int));
75
76 static void make_command_string_internal __P((COMMAND *));
77 static void _print_word_list __P((WORD_LIST *, char *, PFUNC *));
78 static void command_print_word_list __P((WORD_LIST *, char *));
79 static void print_case_clauses __P((PATTERN_LIST *));
80 static void print_redirection_list __P((REDIRECT *));
81 static void print_redirection __P((REDIRECT *));
82 static void print_heredoc_header __P((REDIRECT *));
83 static void print_heredoc_body __P((REDIRECT *));
84 static void print_heredocs __P((REDIRECT *));
85 static void print_heredoc_bodies __P((REDIRECT *));
86 static void print_deferred_heredocs __P((const char *));
87
88 static void print_for_command __P((FOR_COM *));
89 #if defined (ARITH_FOR_COMMAND)
90 static void print_arith_for_command __P((ARITH_FOR_COM *));
91 #endif
92 #if defined (SELECT_COMMAND)
93 static void print_select_command __P((SELECT_COM *));
94 #endif
95 static void print_group_command __P((GROUP_COM *));
96 static void print_case_command __P((CASE_COM *));
97 static void print_while_command __P((WHILE_COM *));
98 static void print_until_command __P((WHILE_COM *));
99 static void print_until_or_while __P((WHILE_COM *, char *));
100 static void print_if_command __P((IF_COM *));
101 #if defined (COND_COMMAND)
102 static void print_cond_node __P((COND_COM *));
103 #endif
104 static void print_function_def __P((FUNCTION_DEF *));
105
106 #define PRINTED_COMMAND_INITIAL_SIZE 64
107 #define PRINTED_COMMAND_GROW_SIZE 128
108
109 char *the_printed_command = (char *)NULL;
110 int the_printed_command_size = 0;
111 int command_string_index = 0;
112
113 int xtrace_fd = -1;
114 FILE *xtrace_fp = 0;
115
116 #define CHECK_XTRACE_FP xtrace_fp = (xtrace_fp ? xtrace_fp : stderr)
117
118 /* shell expansion characters: used in print_redirection_list */
119 #define EXPCHAR(c) ((c) == '{' || (c) == '~' || (c) == '$' || (c) == '`')
120
121 #define PRINT_DEFERRED_HEREDOCS(x) \
122 do { \
123 if (deferred_heredocs) \
124 print_deferred_heredocs (x); \
125 } while (0)
126
127 /* Non-zero means the stuff being printed is inside of a function def. */
128 static int inside_function_def;
129 static int skip_this_indent;
130 static int was_heredoc;
131 static int printing_connection;
132 static REDIRECT *deferred_heredocs;
133
134 /* The depth of the group commands that we are currently printing. This
135 includes the group command that is a function body. */
136 static int group_command_nesting;
137
138 /* A buffer to indicate the indirection level (PS4) when set -x is enabled. */
139 static char *indirection_string = 0;
140 static int indirection_stringsiz = 0;
141
142 /* Print COMMAND (a command tree) on standard output. */
143 void
144 print_command (command)
145 COMMAND *command;
146 {
147 command_string_index = 0;
148 printf ("%s", make_command_string (command));
149 }
150
151 /* Make a string which is the printed representation of the command
152 tree in COMMAND. We return this string. However, the string is
153 not consed, so you have to do that yourself if you want it to
154 remain around. */
155 char *
156 make_command_string (command)
157 COMMAND *command;
158 {
159 command_string_index = was_heredoc = 0;
160 deferred_heredocs = 0;
161 make_command_string_internal (command);
162 return (the_printed_command);
163 }
164
165 /* The internal function. This is the real workhorse. */
166 static void
167 make_command_string_internal (command)
168 COMMAND *command;
169 {
170 char s[3];
171
172 if (command == 0)
173 cprintf ("");
174 else
175 {
176 if (skip_this_indent)
177 skip_this_indent--;
178 else
179 indent (indentation);
180
181 if (command->flags & CMD_TIME_PIPELINE)
182 {
183 cprintf ("time ");
184 if (command->flags & CMD_TIME_POSIX)
185 cprintf ("-p ");
186 }
187
188 if (command->flags & CMD_INVERT_RETURN)
189 cprintf ("! ");
190
191 switch (command->type)
192 {
193 case cm_for:
194 print_for_command (command->value.For);
195 break;
196
197 #if defined (ARITH_FOR_COMMAND)
198 case cm_arith_for:
199 print_arith_for_command (command->value.ArithFor);
200 break;
201 #endif
202
203 #if defined (SELECT_COMMAND)
204 case cm_select:
205 print_select_command (command->value.Select);
206 break;
207 #endif
208
209 case cm_case:
210 print_case_command (command->value.Case);
211 break;
212
213 case cm_while:
214 print_while_command (command->value.While);
215 break;
216
217 case cm_until:
218 print_until_command (command->value.While);
219 break;
220
221 case cm_if:
222 print_if_command (command->value.If);
223 break;
224
225 #if defined (DPAREN_ARITHMETIC)
226 case cm_arith:
227 print_arith_command (command->value.Arith->exp);
228 break;
229 #endif
230
231 #if defined (COND_COMMAND)
232 case cm_cond:
233 print_cond_command (command->value.Cond);
234 break;
235 #endif
236
237 case cm_simple:
238 print_simple_command (command->value.Simple);
239 break;
240
241 case cm_connection:
242
243 skip_this_indent++;
244 printing_connection++;
245 make_command_string_internal (command->value.Connection->first);
246
247 switch (command->value.Connection->connector)
248 {
249 case '&':
250 case '|':
251 {
252 char c = command->value.Connection->connector;
253
254 s[0] = ' ';
255 s[1] = c;
256 s[2] = '\0';
257
258 print_deferred_heredocs (s);
259
260 if (c != '&' || command->value.Connection->second)
261 {
262 cprintf (" ");
263 skip_this_indent++;
264 }
265 }
266 break;
267
268 case AND_AND:
269 print_deferred_heredocs (" && ");
270 if (command->value.Connection->second)
271 skip_this_indent++;
272 break;
273
274 case OR_OR:
275 print_deferred_heredocs (" || ");
276 if (command->value.Connection->second)
277 skip_this_indent++;
278 break;
279
280 case ';':
281 if (deferred_heredocs == 0)
282 {
283 if (was_heredoc == 0)
284 cprintf (";");
285 else
286 was_heredoc = 0;
287 }
288 else
289 print_deferred_heredocs (inside_function_def ? "" : ";");
290
291 if (inside_function_def)
292 cprintf ("\n");
293 else
294 {
295 cprintf (" ");
296 if (command->value.Connection->second)
297 skip_this_indent++;
298 }
299 break;
300
301 default:
302 cprintf (_("print_command: bad connector `%d'"),
303 command->value.Connection->connector);
304 break;
305 }
306
307 make_command_string_internal (command->value.Connection->second);
308 PRINT_DEFERRED_HEREDOCS ("");
309 printing_connection--;
310 break;
311
312 case cm_function_def:
313 print_function_def (command->value.Function_def);
314 break;
315
316 case cm_group:
317 print_group_command (command->value.Group);
318 break;
319
320 case cm_subshell:
321 cprintf ("( ");
322 skip_this_indent++;
323 make_command_string_internal (command->value.Subshell->command);
324 PRINT_DEFERRED_HEREDOCS ("");
325 cprintf (" )");
326 break;
327
328 case cm_coproc:
329 cprintf ("coproc %s ", command->value.Coproc->name);
330 skip_this_indent++;
331 make_command_string_internal (command->value.Coproc->command);
332 break;
333
334 default:
335 command_error ("print_command", CMDERR_BADTYPE, command->type, 0);
336 break;
337 }
338
339
340 if (command->redirects)
341 {
342 cprintf (" ");
343 print_redirection_list (command->redirects);
344 }
345 }
346 }
347
348 static void
349 _print_word_list (list, separator, pfunc)
350 WORD_LIST *list;
351 char *separator;
352 PFUNC *pfunc;
353 {
354 WORD_LIST *w;
355
356 for (w = list; w; w = w->next)
357 (*pfunc) ("%s%s", w->word->word, w->next ? separator : "");
358 }
359
360 void
361 print_word_list (list, separator)
362 WORD_LIST *list;
363 char *separator;
364 {
365 _print_word_list (list, separator, xprintf);
366 }
367
368 void
369 xtrace_set (fd, fp)
370 int fd;
371 FILE *fp;
372 {
373 if (fd >= 0 && sh_validfd (fd) == 0)
374 {
375 internal_error (_("xtrace_set: %d: invalid file descriptor"), fd);
376 return;
377 }
378 if (fp == 0)
379 {
380 internal_error (_("xtrace_set: NULL file pointer"));
381 return;
382 }
383 if (fd >= 0 && fileno (fp) != fd)
384 internal_warning (_("xtrace fd (%d) != fileno xtrace fp (%d)"), fd, fileno (fp));
385
386 xtrace_fd = fd;
387 xtrace_fp = fp;
388 }
389
390 void
391 xtrace_init ()
392 {
393 xtrace_set (-1, stderr);
394 }
395
396 void
397 xtrace_reset ()
398 {
399 if (xtrace_fd >= 0 && xtrace_fp)
400 {
401 fflush (xtrace_fp);
402 fclose (xtrace_fp);
403 }
404 else if (xtrace_fd >= 0)
405 close (xtrace_fd);
406
407 xtrace_fd = -1;
408 xtrace_fp = stderr;
409 }
410
411 void
412 xtrace_fdchk (fd)
413 int fd;
414 {
415 if (fd == xtrace_fd)
416 xtrace_reset ();
417 }
418
419 /* Return a string denoting what our indirection level is. */
420
421 char *
422 indirection_level_string ()
423 {
424 register int i, j;
425 char *ps4;
426 char ps4_firstc[MB_LEN_MAX+1];
427 int ps4_firstc_len, ps4_len, ineed, old;
428
429 ps4 = get_string_value ("PS4");
430 if (indirection_string == 0)
431 indirection_string = xmalloc (indirection_stringsiz = 100);
432 indirection_string[0] = '\0';
433
434 if (ps4 == 0 || *ps4 == '\0')
435 return (indirection_string);
436
437 old = change_flag ('x', FLAG_OFF);
438 ps4 = decode_prompt_string (ps4);
439 if (old)
440 change_flag ('x', FLAG_ON);
441
442 if (ps4 == 0 || *ps4 == '\0')
443 return (indirection_string);
444
445 #if defined (HANDLE_MULTIBYTE)
446 ps4_len = strnlen (ps4, MB_CUR_MAX);
447 ps4_firstc_len = MBLEN (ps4, ps4_len);
448 if (ps4_firstc_len == 1 || ps4_firstc_len == 0 || ps4_firstc_len < 0)
449 {
450 ps4_firstc[0] = ps4[0];
451 ps4_firstc[ps4_firstc_len = 1] = '\0';
452 }
453 else
454 memcpy (ps4_firstc, ps4, ps4_firstc_len);
455 #else
456 ps4_firstc[0] = ps4[0];
457 ps4_firstc[ps4_firstc_len = 1] = '\0';
458 #endif
459
460 /* Dynamically resize indirection_string so we have room for everything
461 and we don't have to truncate ps4 */
462 ineed = (ps4_firstc_len * indirection_level) + strlen (ps4);
463 if (ineed > indirection_stringsiz - 1)
464 {
465 indirection_stringsiz = ineed + 1;
466 indirection_string = xrealloc (indirection_string, indirection_stringsiz);
467 }
468
469 for (i = j = 0; ps4_firstc[0] && j < indirection_level && i < indirection_stringsiz - 1; i += ps4_firstc_len, j++)
470 {
471 if (ps4_firstc_len == 1)
472 indirection_string[i] = ps4_firstc[0];
473 else
474 memcpy (indirection_string+i, ps4_firstc, ps4_firstc_len);
475 }
476
477 for (j = ps4_firstc_len; *ps4 && ps4[j] && i < indirection_stringsiz - 1; i++, j++)
478 indirection_string[i] = ps4[j];
479
480 indirection_string[i] = '\0';
481 free (ps4);
482 return (indirection_string);
483 }
484
485 void
486 xtrace_print_assignment (name, value, assign_list, xflags)
487 char *name, *value;
488 int assign_list, xflags;
489 {
490 char *nval;
491
492 CHECK_XTRACE_FP;
493
494 if (xflags)
495 fprintf (xtrace_fp, "%s", indirection_level_string ());
496
497 /* VALUE should not be NULL when this is called. */
498 if (*value == '\0' || assign_list)
499 nval = value;
500 else if (sh_contains_shell_metas (value))
501 nval = sh_single_quote (value);
502 else if (ansic_shouldquote (value))
503 nval = ansic_quote (value, 0, (int *)0);
504 else
505 nval = value;
506
507 if (assign_list)
508 fprintf (xtrace_fp, "%s=(%s)\n", name, nval);
509 else
510 fprintf (xtrace_fp, "%s=%s\n", name, nval);
511
512 if (nval != value)
513 FREE (nval);
514
515 fflush (xtrace_fp);
516 }
517
518 /* A function to print the words of a simple command when set -x is on. Also used to
519 print the word list in a for or select command header; in that case, we suppress
520 quoting the words because they haven't been expanded yet. XTFLAGS&1 means to
521 print $PS4; XTFLAGS&2 means to suppress quoting the words in LIST. */
522 void
523 xtrace_print_word_list (list, xtflags)
524 WORD_LIST *list;
525 int xtflags;
526 {
527 WORD_LIST *w;
528 char *t, *x;
529
530 CHECK_XTRACE_FP;
531
532 if (xtflags&1)
533 fprintf (xtrace_fp, "%s", indirection_level_string ());
534
535 for (w = list; w; w = w->next)
536 {
537 t = w->word->word;
538 if (t == 0 || *t == '\0')
539 fprintf (xtrace_fp, "''%s", w->next ? " " : "");
540 else if (xtflags & 2)
541 fprintf (xtrace_fp, "%s%s", t, w->next ? " " : "");
542 else if (sh_contains_shell_metas (t))
543 {
544 x = sh_single_quote (t);
545 fprintf (xtrace_fp, "%s%s", x, w->next ? " " : "");
546 free (x);
547 }
548 else if (ansic_shouldquote (t))
549 {
550 x = ansic_quote (t, 0, (int *)0);
551 fprintf (xtrace_fp, "%s%s", x, w->next ? " " : "");
552 free (x);
553 }
554 else
555 fprintf (xtrace_fp, "%s%s", t, w->next ? " " : "");
556 }
557 fprintf (xtrace_fp, "\n");
558 fflush (xtrace_fp);
559 }
560
561 static void
562 command_print_word_list (list, separator)
563 WORD_LIST *list;
564 char *separator;
565 {
566 _print_word_list (list, separator, cprintf);
567 }
568
569 void
570 print_for_command_head (for_command)
571 FOR_COM *for_command;
572 {
573 cprintf ("for %s in ", for_command->name->word);
574 command_print_word_list (for_command->map_list, " ");
575 }
576
577 void
578 xtrace_print_for_command_head (for_command)
579 FOR_COM *for_command;
580 {
581 CHECK_XTRACE_FP;
582 fprintf (xtrace_fp, "%s", indirection_level_string ());
583 fprintf (xtrace_fp, "for %s in ", for_command->name->word);
584 xtrace_print_word_list (for_command->map_list, 2);
585 }
586
587 static void
588 print_for_command (for_command)
589 FOR_COM *for_command;
590 {
591 print_for_command_head (for_command);
592 cprintf (";");
593 newline ("do\n");
594
595 indentation += indentation_amount;
596 make_command_string_internal (for_command->action);
597 PRINT_DEFERRED_HEREDOCS ("");
598 semicolon ();
599 indentation -= indentation_amount;
600
601 newline ("done");
602 }
603
604 #if defined (ARITH_FOR_COMMAND)
605 static void
606 print_arith_for_command (arith_for_command)
607 ARITH_FOR_COM *arith_for_command;
608 {
609 cprintf ("for ((");
610 command_print_word_list (arith_for_command->init, " ");
611 cprintf ("; ");
612 command_print_word_list (arith_for_command->test, " ");
613 cprintf ("; ");
614 command_print_word_list (arith_for_command->step, " ");
615 cprintf ("))");
616 newline ("do\n");
617 indentation += indentation_amount;
618 make_command_string_internal (arith_for_command->action);
619 PRINT_DEFERRED_HEREDOCS ("");
620 semicolon ();
621 indentation -= indentation_amount;
622 newline ("done");
623 }
624 #endif /* ARITH_FOR_COMMAND */
625
626 #if defined (SELECT_COMMAND)
627 void
628 print_select_command_head (select_command)
629 SELECT_COM *select_command;
630 {
631 cprintf ("select %s in ", select_command->name->word);
632 command_print_word_list (select_command->map_list, " ");
633 }
634
635 void
636 xtrace_print_select_command_head (select_command)
637 SELECT_COM *select_command;
638 {
639 CHECK_XTRACE_FP;
640 fprintf (xtrace_fp, "%s", indirection_level_string ());
641 fprintf (xtrace_fp, "select %s in ", select_command->name->word);
642 xtrace_print_word_list (select_command->map_list, 2);
643 }
644
645 static void
646 print_select_command (select_command)
647 SELECT_COM *select_command;
648 {
649 print_select_command_head (select_command);
650
651 cprintf (";");
652 newline ("do\n");
653 indentation += indentation_amount;
654 make_command_string_internal (select_command->action);
655 PRINT_DEFERRED_HEREDOCS ("");
656 semicolon ();
657 indentation -= indentation_amount;
658 newline ("done");
659 }
660 #endif /* SELECT_COMMAND */
661
662 static void
663 print_group_command (group_command)
664 GROUP_COM *group_command;
665 {
666 group_command_nesting++;
667 cprintf ("{ ");
668
669 if (inside_function_def == 0)
670 skip_this_indent++;
671 else
672 {
673 /* This is a group command { ... } inside of a function
674 definition, and should be printed as a multiline group
675 command, using the current indentation. */
676 cprintf ("\n");
677 indentation += indentation_amount;
678 }
679
680 make_command_string_internal (group_command->command);
681 PRINT_DEFERRED_HEREDOCS ("");
682
683 if (inside_function_def)
684 {
685 cprintf ("\n");
686 indentation -= indentation_amount;
687 indent (indentation);
688 }
689 else
690 {
691 semicolon ();
692 cprintf (" ");
693 }
694
695 cprintf ("}");
696
697 group_command_nesting--;
698 }
699
700 void
701 print_case_command_head (case_command)
702 CASE_COM *case_command;
703 {
704 cprintf ("case %s in ", case_command->word->word);
705 }
706
707 void
708 xtrace_print_case_command_head (case_command)
709 CASE_COM *case_command;
710 {
711 CHECK_XTRACE_FP;
712 fprintf (xtrace_fp, "%s", indirection_level_string ());
713 fprintf (xtrace_fp, "case %s in\n", case_command->word->word);
714 }
715
716 static void
717 print_case_command (case_command)
718 CASE_COM *case_command;
719 {
720 print_case_command_head (case_command);
721
722 if (case_command->clauses)
723 print_case_clauses (case_command->clauses);
724 newline ("esac");
725 }
726
727 static void
728 print_case_clauses (clauses)
729 PATTERN_LIST *clauses;
730 {
731 indentation += indentation_amount;
732 while (clauses)
733 {
734 newline ("");
735 command_print_word_list (clauses->patterns, " | ");
736 cprintf (")\n");
737 indentation += indentation_amount;
738 make_command_string_internal (clauses->action);
739 indentation -= indentation_amount;
740 PRINT_DEFERRED_HEREDOCS ("");
741 if (clauses->flags & CASEPAT_FALLTHROUGH)
742 newline (";&");
743 else if (clauses->flags & CASEPAT_TESTNEXT)
744 newline (";;&");
745 else
746 newline (";;");
747 clauses = clauses->next;
748 }
749 indentation -= indentation_amount;
750 }
751
752 static void
753 print_while_command (while_command)
754 WHILE_COM *while_command;
755 {
756 print_until_or_while (while_command, "while");
757 }
758
759 static void
760 print_until_command (while_command)
761 WHILE_COM *while_command;
762 {
763 print_until_or_while (while_command, "until");
764 }
765
766 static void
767 print_until_or_while (while_command, which)
768 WHILE_COM *while_command;
769 char *which;
770 {
771 cprintf ("%s ", which);
772 skip_this_indent++;
773 make_command_string_internal (while_command->test);
774 PRINT_DEFERRED_HEREDOCS ("");
775 semicolon ();
776 cprintf (" do\n"); /* was newline ("do\n"); */
777 indentation += indentation_amount;
778 make_command_string_internal (while_command->action);
779 PRINT_DEFERRED_HEREDOCS ("");
780 indentation -= indentation_amount;
781 semicolon ();
782 newline ("done");
783 }
784
785 static void
786 print_if_command (if_command)
787 IF_COM *if_command;
788 {
789 cprintf ("if ");
790 skip_this_indent++;
791 make_command_string_internal (if_command->test);
792 semicolon ();
793 cprintf (" then\n");
794 indentation += indentation_amount;
795 make_command_string_internal (if_command->true_case);
796 PRINT_DEFERRED_HEREDOCS ("");
797 indentation -= indentation_amount;
798
799 if (if_command->false_case)
800 {
801 semicolon ();
802 newline ("else\n");
803 indentation += indentation_amount;
804 make_command_string_internal (if_command->false_case);
805 PRINT_DEFERRED_HEREDOCS ("");
806 indentation -= indentation_amount;
807 }
808 semicolon ();
809 newline ("fi");
810 }
811
812 #if defined (DPAREN_ARITHMETIC) || defined (ARITH_FOR_COMMAND)
813 void
814 print_arith_command (arith_cmd_list)
815 WORD_LIST *arith_cmd_list;
816 {
817 cprintf ("((");
818 command_print_word_list (arith_cmd_list, " ");
819 cprintf ("))");
820 }
821 #endif
822
823 #if defined (COND_COMMAND)
824 static void
825 print_cond_node (cond)
826 COND_COM *cond;
827 {
828 if (cond->flags & CMD_INVERT_RETURN)
829 cprintf ("! ");
830
831 if (cond->type == COND_EXPR)
832 {
833 cprintf ("( ");
834 print_cond_node (cond->left);
835 cprintf (" )");
836 }
837 else if (cond->type == COND_AND)
838 {
839 print_cond_node (cond->left);
840 cprintf (" && ");
841 print_cond_node (cond->right);
842 }
843 else if (cond->type == COND_OR)
844 {
845 print_cond_node (cond->left);
846 cprintf (" || ");
847 print_cond_node (cond->right);
848 }
849 else if (cond->type == COND_UNARY)
850 {
851 cprintf ("%s", cond->op->word);
852 cprintf (" ");
853 print_cond_node (cond->left);
854 }
855 else if (cond->type == COND_BINARY)
856 {
857 print_cond_node (cond->left);
858 cprintf (" ");
859 cprintf ("%s", cond->op->word);
860 cprintf (" ");
861 print_cond_node (cond->right);
862 }
863 else if (cond->type == COND_TERM)
864 {
865 cprintf ("%s", cond->op->word); /* need to add quoting here */
866 }
867 }
868
869 void
870 print_cond_command (cond)
871 COND_COM *cond;
872 {
873 cprintf ("[[ ");
874 print_cond_node (cond);
875 cprintf (" ]]");
876 }
877
878 #ifdef DEBUG
879 void
880 debug_print_word_list (s, list, sep)
881 char *s;
882 WORD_LIST *list;
883 char *sep;
884 {
885 WORD_LIST *w;
886
887 if (s)
888 fprintf (stderr, "%s: ", s);
889 for (w = list; w; w = w->next)
890 fprintf (stderr, "%s%s", w->word->word, w->next ? sep : "");
891 fprintf (stderr, "\n");
892 }
893
894 void
895 debug_print_cond_command (cond)
896 COND_COM *cond;
897 {
898 fprintf (stderr, "DEBUG: ");
899 command_string_index = 0;
900 print_cond_command (cond);
901 fprintf (stderr, "%s\n", the_printed_command);
902 }
903 #endif
904
905 void
906 xtrace_print_cond_term (type, invert, op, arg1, arg2)
907 int type, invert;
908 WORD_DESC *op;
909 char *arg1, *arg2;
910 {
911 CHECK_XTRACE_FP;
912 command_string_index = 0;
913 fprintf (xtrace_fp, "%s", indirection_level_string ());
914 fprintf (xtrace_fp, "[[ ");
915 if (invert)
916 fprintf (xtrace_fp, "! ");
917
918 if (type == COND_UNARY)
919 {
920 fprintf (xtrace_fp, "%s ", op->word);
921 fprintf (xtrace_fp, "%s", (arg1 && *arg1) ? arg1 : "''");
922 }
923 else if (type == COND_BINARY)
924 {
925 fprintf (xtrace_fp, "%s", (arg1 && *arg1) ? arg1 : "''");
926 fprintf (xtrace_fp, " %s ", op->word);
927 fprintf (xtrace_fp, "%s", (arg2 && *arg2) ? arg2 : "''");
928 }
929
930 fprintf (xtrace_fp, " ]]\n");
931
932 fflush (xtrace_fp);
933 }
934 #endif /* COND_COMMAND */
935
936 #if defined (DPAREN_ARITHMETIC) || defined (ARITH_FOR_COMMAND)
937 /* A function to print the words of an arithmetic command when set -x is on. */
938 void
939 xtrace_print_arith_cmd (list)
940 WORD_LIST *list;
941 {
942 WORD_LIST *w;
943
944 CHECK_XTRACE_FP;
945 fprintf (xtrace_fp, "%s", indirection_level_string ());
946 fprintf (xtrace_fp, "(( ");
947 for (w = list; w; w = w->next)
948 fprintf (xtrace_fp, "%s%s", w->word->word, w->next ? " " : "");
949 fprintf (xtrace_fp, " ))\n");
950
951 fflush (xtrace_fp);
952 }
953 #endif
954
955 void
956 print_simple_command (simple_command)
957 SIMPLE_COM *simple_command;
958 {
959 command_print_word_list (simple_command->words, " ");
960
961 if (simple_command->redirects)
962 {
963 cprintf (" ");
964 print_redirection_list (simple_command->redirects);
965 }
966 }
967
968 static void
969 print_heredocs (heredocs)
970 REDIRECT *heredocs;
971 {
972 REDIRECT *hdtail;
973
974 cprintf (" ");
975 for (hdtail = heredocs; hdtail; hdtail = hdtail->next)
976 {
977 print_redirection (hdtail);
978 cprintf ("\n");
979 }
980 was_heredoc = 1;
981 }
982
983 static void
984 print_heredoc_bodies (heredocs)
985 REDIRECT *heredocs;
986 {
987 REDIRECT *hdtail;
988
989 cprintf ("\n");
990 for (hdtail = heredocs; hdtail; hdtail = hdtail->next)
991 {
992 print_heredoc_body (hdtail);
993 cprintf ("\n");
994 }
995 was_heredoc = 1;
996 }
997
998 /* Print heredocs that are attached to the command before the connector
999 represented by CSTRING. The parsing semantics require us to print the
1000 here-doc delimiters, then the connector (CSTRING), then the here-doc
1001 bodies. We print the here-doc delimiters in print_redirection_list
1002 and print the connector and the bodies here. We don't print the connector
1003 if it's a `;', but we use it to note not to print an extra space after the
1004 last heredoc body and newline. */
1005 static void
1006 print_deferred_heredocs (cstring)
1007 const char *cstring;
1008 {
1009 /* We now print the heredoc headers in print_redirection_list */
1010 if (cstring && cstring[0] && (cstring[0] != ';' || cstring[1]))
1011 cprintf ("%s", cstring);
1012 if (deferred_heredocs)
1013 {
1014 print_heredoc_bodies (deferred_heredocs);
1015 if (cstring && cstring[0] && (cstring[0] != ';' || cstring[1]))
1016 cprintf (" "); /* make sure there's at least one space */
1017 dispose_redirects (deferred_heredocs);
1018 was_heredoc = 1;
1019 }
1020 deferred_heredocs = (REDIRECT *)NULL;
1021 }
1022
1023 static void
1024 print_redirection_list (redirects)
1025 REDIRECT *redirects;
1026 {
1027 REDIRECT *heredocs, *hdtail, *newredir;
1028 char *rw;
1029
1030 heredocs = (REDIRECT *)NULL;
1031 hdtail = heredocs;
1032
1033 was_heredoc = 0;
1034 while (redirects)
1035 {
1036 /* Defer printing the here document bodiess until we've printed the rest of the
1037 redirections, but print the headers in the order they're given. */
1038 if (redirects->instruction == r_reading_until || redirects->instruction == r_deblank_reading_until)
1039 {
1040 newredir = copy_redirect (redirects);
1041 newredir->next = (REDIRECT *)NULL;
1042
1043 print_heredoc_header (newredir);
1044
1045 if (heredocs)
1046 {
1047 hdtail->next = newredir;
1048 hdtail = newredir;
1049 }
1050 else
1051 hdtail = heredocs = newredir;
1052 }
1053 else if (redirects->instruction == r_duplicating_output_word && (redirects->flags & REDIR_VARASSIGN) == 0 && redirects->redirector.dest == 1)
1054 {
1055 /* Temporarily translate it as the execution code does. */
1056 rw = redirects->redirectee.filename->word;
1057 if (rw && *rw != '-' && DIGIT (*rw) == 0 && EXPCHAR (*rw) == 0)
1058 redirects->instruction = r_err_and_out;
1059 print_redirection (redirects);
1060 redirects->instruction = r_duplicating_output_word;
1061 }
1062 else
1063 print_redirection (redirects);
1064
1065 redirects = redirects->next;
1066 if (redirects)
1067 cprintf (" ");
1068 }
1069
1070 /* Now that we've printed all the other redirections (on one line),
1071 print the here documents. If we're printing a connection, we wait until
1072 we print the connector symbol, then we print the here document bodies */
1073 if (heredocs && printing_connection)
1074 deferred_heredocs = heredocs;
1075 else if (heredocs)
1076 {
1077 print_heredoc_bodies (heredocs);
1078 dispose_redirects (heredocs);
1079 }
1080 }
1081
1082 static void
1083 print_heredoc_header (redirect)
1084 REDIRECT *redirect;
1085 {
1086 int kill_leading;
1087 char *x;
1088
1089 kill_leading = redirect->instruction == r_deblank_reading_until;
1090
1091 /* Here doc header */
1092 if (redirect->rflags & REDIR_VARASSIGN)
1093 cprintf ("{%s}", redirect->redirector.filename->word);
1094 else if (redirect->redirector.dest != 0)
1095 cprintf ("%d", redirect->redirector.dest);
1096
1097 /* If the here document delimiter is quoted, single-quote it. */
1098 if (redirect->redirectee.filename->flags & W_QUOTED)
1099 {
1100 x = sh_single_quote (redirect->here_doc_eof);
1101 cprintf ("<<%s%s", kill_leading ? "-" : "", x);
1102 free (x);
1103 }
1104 else
1105 cprintf ("<<%s%s", kill_leading ? "-" : "", redirect->here_doc_eof);
1106 }
1107
1108 static void
1109 print_heredoc_body (redirect)
1110 REDIRECT *redirect;
1111 {
1112 /* Here doc body */
1113 cprintf ("%s%s", redirect->redirectee.filename->word, redirect->here_doc_eof);
1114 }
1115
1116 static void
1117 print_redirection (redirect)
1118 REDIRECT *redirect;
1119 {
1120 int redirector, redir_fd;
1121 WORD_DESC *redirectee, *redir_word;
1122
1123 redirectee = redirect->redirectee.filename;
1124 redir_fd = redirect->redirectee.dest;
1125
1126 redir_word = redirect->redirector.filename;
1127 redirector = redirect->redirector.dest;
1128
1129 switch (redirect->instruction)
1130 {
1131 case r_input_direction:
1132 if (redirect->rflags & REDIR_VARASSIGN)
1133 cprintf ("{%s}", redir_word->word);
1134 else if (redirector != 0)
1135 cprintf ("%d", redirector);
1136 cprintf ("< %s", redirectee->word);
1137 break;
1138
1139 case r_output_direction:
1140 if (redirect->rflags & REDIR_VARASSIGN)
1141 cprintf ("{%s}", redir_word->word);
1142 else if (redirector != 1)
1143 cprintf ("%d", redirector);
1144 cprintf ("> %s", redirectee->word);
1145 break;
1146
1147 case r_inputa_direction: /* Redirection created by the shell. */
1148 cprintf ("&");
1149 break;
1150
1151 case r_output_force:
1152 if (redirect->rflags & REDIR_VARASSIGN)
1153 cprintf ("{%s}", redir_word->word);
1154 else if (redirector != 1)
1155 cprintf ("%d", redirector);
1156 cprintf (">| %s", redirectee->word);
1157 break;
1158
1159 case r_appending_to:
1160 if (redirect->rflags & REDIR_VARASSIGN)
1161 cprintf ("{%s}", redir_word->word);
1162 else if (redirector != 1)
1163 cprintf ("%d", redirector);
1164 cprintf (">> %s", redirectee->word);
1165 break;
1166
1167 case r_input_output:
1168 if (redirect->rflags & REDIR_VARASSIGN)
1169 cprintf ("{%s}", redir_word->word);
1170 else if (redirector != 1)
1171 cprintf ("%d", redirector);
1172 cprintf ("<> %s", redirectee->word);
1173 break;
1174
1175 case r_deblank_reading_until:
1176 case r_reading_until:
1177 print_heredoc_header (redirect);
1178 cprintf ("\n");
1179 print_heredoc_body (redirect);
1180 break;
1181
1182 case r_reading_string:
1183 if (redirect->rflags & REDIR_VARASSIGN)
1184 cprintf ("{%s}", redir_word->word);
1185 else if (redirector != 0)
1186 cprintf ("%d", redirector);
1187 #if 0
1188 /* Don't need to check whether or not to requote, since original quotes
1189 are still intact. The only thing that has happened is that $'...'
1190 has been replaced with 'expanded ...'. */
1191 if (ansic_shouldquote (redirect->redirectee.filename->word))
1192 {
1193 char *x;
1194 x = ansic_quote (redirect->redirectee.filename->word, 0, (int *)0);
1195 cprintf ("<<< %s", x);
1196 free (x);
1197 }
1198 else
1199 #endif
1200 cprintf ("<<< %s", redirect->redirectee.filename->word);
1201 break;
1202
1203 case r_duplicating_input:
1204 if (redirect->rflags & REDIR_VARASSIGN)
1205 cprintf ("{%s}<&%d", redir_word->word, redir_fd);
1206 else
1207 cprintf ("%d<&%d", redirector, redir_fd);
1208 break;
1209
1210 case r_duplicating_output:
1211 if (redirect->rflags & REDIR_VARASSIGN)
1212 cprintf ("{%s}>&%d", redir_word->word, redir_fd);
1213 else
1214 cprintf ("%d>&%d", redirector, redir_fd);
1215 break;
1216
1217 case r_duplicating_input_word:
1218 if (redirect->rflags & REDIR_VARASSIGN)
1219 cprintf ("{%s}<&%s", redir_word->word, redirectee->word);
1220 else
1221 cprintf ("%d<&%s", redirector, redirectee->word);
1222 break;
1223
1224 case r_duplicating_output_word:
1225 if (redirect->rflags & REDIR_VARASSIGN)
1226 cprintf ("{%s}>&%s", redir_word->word, redirectee->word);
1227 else
1228 cprintf ("%d>&%s", redirector, redirectee->word);
1229 break;
1230
1231 case r_move_input:
1232 if (redirect->rflags & REDIR_VARASSIGN)
1233 cprintf ("{%s}<&%d-", redir_word->word, redir_fd);
1234 else
1235 cprintf ("%d<&%d-", redirector, redir_fd);
1236 break;
1237
1238 case r_move_output:
1239 if (redirect->rflags & REDIR_VARASSIGN)
1240 cprintf ("{%s}>&%d-", redir_word->word, redir_fd);
1241 else
1242 cprintf ("%d>&%d-", redirector, redir_fd);
1243 break;
1244
1245 case r_move_input_word:
1246 if (redirect->rflags & REDIR_VARASSIGN)
1247 cprintf ("{%s}<&%s-", redir_word->word, redirectee->word);
1248 else
1249 cprintf ("%d<&%s-", redirector, redirectee->word);
1250 break;
1251
1252 case r_move_output_word:
1253 if (redirect->rflags & REDIR_VARASSIGN)
1254 cprintf ("{%s}>&%s-", redir_word->word, redirectee->word);
1255 else
1256 cprintf ("%d>&%s-", redirector, redirectee->word);
1257 break;
1258
1259 case r_close_this:
1260 if (redirect->rflags & REDIR_VARASSIGN)
1261 cprintf ("{%s}>&-", redir_word->word);
1262 else
1263 cprintf ("%d>&-", redirector);
1264 break;
1265
1266 case r_err_and_out:
1267 cprintf ("&> %s", redirectee->word);
1268 break;
1269
1270 case r_append_err_and_out:
1271 cprintf ("&>> %s", redirectee->word);
1272 break;
1273 }
1274 }
1275
1276 static void
1277 reset_locals ()
1278 {
1279 inside_function_def = 0;
1280 indentation = 0;
1281 printing_connection = 0;
1282 deferred_heredocs = 0;
1283 }
1284
1285 static void
1286 print_function_def (func)
1287 FUNCTION_DEF *func;
1288 {
1289 COMMAND *cmdcopy;
1290 REDIRECT *func_redirects;
1291
1292 func_redirects = NULL;
1293 /* When in posix mode, print functions as posix specifies them. */
1294 if (posixly_correct == 0)
1295 cprintf ("function %s () \n", func->name->word);
1296 else
1297 cprintf ("%s () \n", func->name->word);
1298 add_unwind_protect (reset_locals, 0);
1299
1300 indent (indentation);
1301 cprintf ("{ \n");
1302
1303 inside_function_def++;
1304 indentation += indentation_amount;
1305
1306 cmdcopy = copy_command (func->command);
1307 if (cmdcopy->type == cm_group)
1308 {
1309 func_redirects = cmdcopy->redirects;
1310 cmdcopy->redirects = (REDIRECT *)NULL;
1311 }
1312 make_command_string_internal (cmdcopy->type == cm_group
1313 ? cmdcopy->value.Group->command
1314 : cmdcopy);
1315 /* XXX - PRINT_DEFERRED_HEREDOCS (""); ? */
1316
1317 remove_unwind_protect ();
1318 indentation -= indentation_amount;
1319 inside_function_def--;
1320
1321 if (func_redirects)
1322 { /* { */
1323 newline ("} ");
1324 print_redirection_list (func_redirects);
1325 cmdcopy->redirects = func_redirects;
1326 }
1327 else
1328 newline ("}");
1329
1330 dispose_command (cmdcopy);
1331 }
1332
1333 /* Return the string representation of the named function.
1334 NAME is the name of the function.
1335 COMMAND is the function body. It should be a GROUP_COM.
1336 flags&FUNC_MULTILINE is non-zero to pretty-print, or zero for all on one line.
1337 flags&FUNC_EXTERNAL means convert from internal to external form
1338 */
1339 char *
1340 named_function_string (name, command, flags)
1341 char *name;
1342 COMMAND *command;
1343 int flags;
1344 {
1345 char *result;
1346 int old_indent, old_amount;
1347 COMMAND *cmdcopy;
1348 REDIRECT *func_redirects;
1349
1350 old_indent = indentation;
1351 old_amount = indentation_amount;
1352 command_string_index = was_heredoc = 0;
1353 deferred_heredocs = 0;
1354
1355 if (name && *name)
1356 {
1357 if (find_reserved_word (name) >= 0)
1358 cprintf ("function ");
1359 cprintf ("%s ", name);
1360 }
1361
1362 cprintf ("() ");
1363
1364 if ((flags & FUNC_MULTILINE) == 0)
1365 {
1366 indentation = 1;
1367 indentation_amount = 0;
1368 }
1369 else
1370 {
1371 cprintf ("\n");
1372 indentation += indentation_amount;
1373 }
1374
1375 inside_function_def++;
1376
1377 cprintf ((flags & FUNC_MULTILINE) ? "{ \n" : "{ ");
1378
1379 cmdcopy = copy_command (command);
1380 /* Take any redirections specified in the function definition (which should
1381 apply to the function as a whole) and save them for printing later. */
1382 func_redirects = (REDIRECT *)NULL;
1383 if (cmdcopy->type == cm_group)
1384 {
1385 func_redirects = cmdcopy->redirects;
1386 cmdcopy->redirects = (REDIRECT *)NULL;
1387 }
1388 make_command_string_internal (cmdcopy->type == cm_group
1389 ? cmdcopy->value.Group->command
1390 : cmdcopy);
1391 /* XXX - PRINT_DEFERRED_HEREDOCS (""); ? */
1392
1393 indentation = old_indent;
1394 indentation_amount = old_amount;
1395 inside_function_def--;
1396
1397 if (func_redirects)
1398 { /* { */
1399 newline ("} ");
1400 print_redirection_list (func_redirects);
1401 cmdcopy->redirects = func_redirects;
1402 }
1403 else
1404 newline ("}");
1405
1406 result = the_printed_command;
1407
1408 if ((flags & FUNC_MULTILINE) == 0)
1409 {
1410 #if 0
1411 register int i;
1412 for (i = 0; result[i]; i++)
1413 if (result[i] == '\n')
1414 {
1415 strcpy (result + i, result + i + 1);
1416 --i;
1417 }
1418 #else
1419 if (result[2] == '\n') /* XXX -- experimental */
1420 memmove (result + 2, result + 3, strlen (result) - 2);
1421 #endif
1422 }
1423
1424 dispose_command (cmdcopy);
1425
1426 if (flags & FUNC_EXTERNAL)
1427 result = remove_quoted_escapes (result);
1428
1429 return (result);
1430 }
1431
1432 static void
1433 newline (string)
1434 char *string;
1435 {
1436 cprintf ("\n");
1437 indent (indentation);
1438 if (string && *string)
1439 cprintf ("%s", string);
1440 }
1441
1442 static char *indentation_string;
1443 static int indentation_size;
1444
1445 static void
1446 indent (amount)
1447 int amount;
1448 {
1449 register int i;
1450
1451 RESIZE_MALLOCED_BUFFER (indentation_string, 0, amount, indentation_size, 16);
1452
1453 for (i = 0; amount > 0; amount--)
1454 indentation_string[i++] = ' ';
1455 indentation_string[i] = '\0';
1456 cprintf ("%s", indentation_string);
1457 }
1458
1459 static void
1460 semicolon ()
1461 {
1462 if (command_string_index > 0 &&
1463 (the_printed_command[command_string_index - 1] == '&' ||
1464 the_printed_command[command_string_index - 1] == '\n'))
1465 return;
1466 cprintf (";");
1467 }
1468
1469 /* How to make the string. */
1470 static void
1471 #if defined (PREFER_STDARG)
1472 cprintf (const char *control, ...)
1473 #else
1474 cprintf (control, va_alist)
1475 const char *control;
1476 va_dcl
1477 #endif
1478 {
1479 register const char *s;
1480 char char_arg[2], *argp, intbuf[INT_STRLEN_BOUND (unsigned int) + 1];
1481 int digit_arg, arg_len, c;
1482 va_list args;
1483
1484 SH_VA_START (args, control);
1485
1486 arg_len = strlen (control);
1487 the_printed_command_resize (arg_len + 1);
1488
1489 char_arg[1] = '\0';
1490 s = control;
1491 while (s && *s)
1492 {
1493 c = *s++;
1494 argp = (char *)NULL;
1495 if (c != '%' || !*s)
1496 {
1497 char_arg[0] = c;
1498 argp = char_arg;
1499 arg_len = 1;
1500 }
1501 else
1502 {
1503 c = *s++;
1504 switch (c)
1505 {
1506 case '%':
1507 char_arg[0] = c;
1508 argp = char_arg;
1509 arg_len = 1;
1510 break;
1511
1512 case 's':
1513 argp = va_arg (args, char *);
1514 arg_len = strlen (argp);
1515 break;
1516
1517 case 'd':
1518 /* Represent an out-of-range file descriptor with an out-of-range
1519 integer value. We can do this because the only use of `%d' in
1520 the calls to cprintf is to output a file descriptor number for
1521 a redirection. */
1522 digit_arg = va_arg (args, int);
1523 if (digit_arg < 0)
1524 {
1525 sprintf (intbuf, "%u", (unsigned int)-1);
1526 argp = intbuf;
1527 }
1528 else
1529 argp = inttostr (digit_arg, intbuf, sizeof (intbuf));
1530 arg_len = strlen (argp);
1531 break;
1532
1533 case 'c':
1534 char_arg[0] = va_arg (args, int);
1535 argp = char_arg;
1536 arg_len = 1;
1537 break;
1538
1539 default:
1540 programming_error (_("cprintf: `%c': invalid format character"), c);
1541 /*NOTREACHED*/
1542 }
1543 }
1544
1545 if (argp && arg_len)
1546 {
1547 the_printed_command_resize (arg_len + 1);
1548 FASTCOPY (argp, the_printed_command + command_string_index, arg_len);
1549 command_string_index += arg_len;
1550 }
1551 }
1552
1553 va_end (args);
1554
1555 the_printed_command[command_string_index] = '\0';
1556 }
1557
1558 /* Ensure that there is enough space to stuff LENGTH characters into
1559 THE_PRINTED_COMMAND. */
1560 static void
1561 the_printed_command_resize (length)
1562 int length;
1563 {
1564 if (the_printed_command == 0)
1565 {
1566 the_printed_command_size = (length + PRINTED_COMMAND_INITIAL_SIZE - 1) & ~(PRINTED_COMMAND_INITIAL_SIZE - 1);
1567 the_printed_command = (char *)xmalloc (the_printed_command_size);
1568 command_string_index = 0;
1569 }
1570 else if ((command_string_index + length) >= the_printed_command_size)
1571 {
1572 int new;
1573 new = command_string_index + length + 1;
1574
1575 /* Round up to the next multiple of PRINTED_COMMAND_GROW_SIZE. */
1576 new = (new + PRINTED_COMMAND_GROW_SIZE - 1) & ~(PRINTED_COMMAND_GROW_SIZE - 1);
1577 the_printed_command_size = new;
1578
1579 the_printed_command = (char *)xrealloc (the_printed_command, the_printed_command_size);
1580 }
1581 }
1582
1583 #if defined (HAVE_VPRINTF)
1584 /* ``If vprintf is available, you may assume that vfprintf and vsprintf are
1585 also available.'' */
1586
1587 static void
1588 #if defined (PREFER_STDARG)
1589 xprintf (const char *format, ...)
1590 #else
1591 xprintf (format, va_alist)
1592 const char *format;
1593 va_dcl
1594 #endif
1595 {
1596 va_list args;
1597
1598 SH_VA_START (args, format);
1599
1600 vfprintf (stdout, format, args);
1601 va_end (args);
1602 }
1603
1604 #else
1605
1606 static void
1607 xprintf (format, arg1, arg2, arg3, arg4, arg5)
1608 const char *format;
1609 {
1610 printf (format, arg1, arg2, arg3, arg4, arg5);
1611 }
1612
1613 #endif /* !HAVE_VPRINTF */