Imported from ../bash-2.04.tar.gz.
[platform/upstream/bash.git] / builtins / common.c
1 /* Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
2
3    This file is part of GNU Bash, the Bourne Again SHell.
4
5    Bash is free software; you can redistribute it and/or modify it under
6    the terms of the GNU General Public License as published by the Free
7    Software Foundation; either version 2, or (at your option) any later
8    version.
9
10    Bash is distributed in the hope that it will be useful, but WITHOUT ANY
11    WARRANTY; without even the implied warranty of MERCHANTABILITY or
12    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
13    for more details.
14    
15    You should have received a copy of the GNU General Public License along
16    with Bash; see the file COPYING.  If not, write to the Free Software
17    Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
18
19 #include <config.h>
20
21 #if defined (HAVE_UNISTD_H)
22 #  ifdef _MINIX
23 #    include <sys/types.h>
24 #  endif
25 #  include <unistd.h>
26 #endif
27
28 #include <stdio.h>
29 #include "../bashtypes.h"
30 #include "posixstat.h"
31 #include <signal.h>
32
33 #include <errno.h>
34
35 #if defined (PREFER_STDARG)
36 #  include <stdarg.h>
37 #else
38 #  if defined (PREFER_VARARGS)
39 #    include <varargs.h>
40 #  endif
41 #endif
42
43 #include "../bashansi.h"
44
45 #include "../shell.h"
46 #include "maxpath.h"
47 #include "../flags.h"
48 #include "../jobs.h"
49 #include "../builtins.h"
50 #include "../input.h"
51 #include "../execute_cmd.h"
52 #include "../trap.h"
53 #include "bashgetopt.h"
54 #include "common.h"
55 #include "builtext.h"
56 #include <tilde/tilde.h>
57
58 #if defined (HISTORY)
59 #  include "../bashhist.h"
60 #endif
61
62 #if !defined (errno)
63 extern int errno;   
64 #endif /* !errno */
65
66 #ifdef __STDC__
67 typedef int QSFUNC (const void *, const void *);
68 #else
69 typedef int QSFUNC ();
70 #endif 
71
72 extern int no_symbolic_links, interactive, interactive_shell;
73 extern int indirection_level, startup_state, subshell_environment;
74 extern int line_number;
75 extern int last_command_exit_value;
76 extern int running_trap;
77 extern int variable_context;
78 extern int posixly_correct;
79 extern char *this_command_name, *shell_name;
80 extern COMMAND *global_command;
81 extern char *bash_getcwd_errstr;
82
83 /* Used by some builtins and the mainline code. */
84 Function *last_shell_builtin = (Function *)NULL;
85 Function *this_shell_builtin = (Function *)NULL;
86
87 /* **************************************************************** */
88 /*                                                                  */
89 /*           Error reporting, usage, and option processing          */
90 /*                                                                  */
91 /* **************************************************************** */
92
93 /* This is a lot like report_error (), but it is for shell builtins
94    instead of shell control structures, and it won't ever exit the
95    shell. */
96 #if defined (USE_VARARGS)
97 void
98 #if defined (PREFER_STDARG)
99 builtin_error (const char *format, ...)
100 #else
101 builtin_error (format, va_alist)
102      const char *format;
103      va_dcl
104 #endif
105 {
106   va_list args;
107   char *name;
108
109   name = get_name_for_error ();
110   fprintf (stderr, "%s: ", name);
111
112   if (this_command_name && *this_command_name)
113     fprintf (stderr, "%s: ", this_command_name);
114
115 #if defined (PREFER_STDARG)
116   va_start (args, format);
117 #else
118   va_start (args);
119 #endif
120
121   vfprintf (stderr, format, args);
122   va_end (args);
123   fprintf (stderr, "\n");
124 }
125 #else /* !USE_VARARGS */
126 void
127 builtin_error (format, arg1, arg2, arg3, arg4, arg5)
128      char *format, *arg1, *arg2, *arg3, *arg4, *arg5;
129 {
130   if (this_command_name && *this_command_name)
131     fprintf (stderr, "%s: ", this_command_name);
132
133   fprintf (stderr, format, arg1, arg2, arg3, arg4, arg5);
134   fprintf (stderr, "\n");
135   fflush (stderr);
136 }
137 #endif /* !USE_VARARGS */
138
139 /* Print a usage summary for the currently-executing builtin command. */
140 void
141 builtin_usage ()
142 {
143   if (this_command_name && *this_command_name)
144     fprintf (stderr, "%s: usage: ", this_command_name);
145   fprintf (stderr, "%s\n", current_builtin->short_doc);
146   fflush (stderr);
147 }
148
149 /* Return if LIST is NULL else barf and jump to top_level.  Used by some
150    builtins that do not accept arguments. */
151 void
152 no_args (list)
153      WORD_LIST *list;
154 {
155   if (list)
156     {
157       builtin_error ("too many arguments");
158       jump_to_top_level (DISCARD);
159     }
160 }
161
162 /* Function called when one of the builtin commands detects a bad
163    option. */
164 void
165 bad_option (s)
166      char *s;
167 {
168   builtin_error ("unknown option: %s", s);
169 }
170
171 /* Check that no options were given to the currently-executing builtin,
172    and return 0 if there were options. */
173 int
174 no_options (list)
175      WORD_LIST *list;
176 {
177   reset_internal_getopt ();
178   if (internal_getopt (list, "") != -1)
179     {
180       builtin_usage ();
181       return (1);
182     }
183   return (0);
184 }
185
186 /* **************************************************************** */
187 /*                                                                  */
188 /*           Shell positional parameter manipulation                */
189 /*                                                                  */
190 /* **************************************************************** */
191
192 /* Convert a WORD_LIST into a C-style argv.  Return the number of elements
193    in the list in *IP, if IP is non-null.  A convenience function for
194    loadable builtins; also used by `test'. */
195 char **
196 make_builtin_argv (list, ip)
197      WORD_LIST *list;
198      int *ip;
199 {
200   char **argv;
201
202   argv = word_list_to_argv (list, 0, 1, ip);
203   argv[0] = this_command_name;
204   return argv;
205 }
206
207 /* Remember LIST in $0 ... $9, and REST_OF_ARGS.  If DESTRUCTIVE is
208    non-zero, then discard whatever the existing arguments are, else
209    only discard the ones that are to be replaced. */
210 void
211 remember_args (list, destructive)
212      WORD_LIST *list;
213      int destructive;
214 {
215   register int i;
216
217   for (i = 1; i < 10; i++)
218     {
219       if ((destructive || list) && dollar_vars[i])
220         {
221           free (dollar_vars[i]);
222           dollar_vars[i] = (char *)NULL;
223         }
224
225       if (list)
226         {
227           dollar_vars[i] = savestring (list->word->word);
228           list = list->next;
229         }
230     }
231
232   /* If arguments remain, assign them to REST_OF_ARGS.
233      Note that copy_word_list (NULL) returns NULL, and
234      that dispose_words (NULL) does nothing. */
235   if (destructive || list)
236     {
237       dispose_words (rest_of_args);
238       rest_of_args = copy_word_list (list);
239     }
240
241   if (destructive)
242     set_dollar_vars_changed ();
243 }
244
245 /* **************************************************************** */
246 /*                                                                  */
247 /*               Pushing and Popping variable contexts              */
248 /*                                                                  */
249 /* **************************************************************** */
250
251 static WORD_LIST **dollar_arg_stack = (WORD_LIST **)NULL;
252 static int dollar_arg_stack_slots;
253 static int dollar_arg_stack_index;
254
255 void
256 push_context ()
257 {
258   push_dollar_vars ();
259   variable_context++;
260 }
261
262 void
263 pop_context ()
264 {
265   pop_dollar_vars ();
266   kill_all_local_variables ();
267   variable_context--;
268 }
269
270 /* Save the existing positional parameters on a stack. */
271 void
272 push_dollar_vars ()
273 {
274   if (dollar_arg_stack_index + 2 > dollar_arg_stack_slots)
275     {
276       dollar_arg_stack = (WORD_LIST **)
277         xrealloc (dollar_arg_stack, (dollar_arg_stack_slots += 10)
278                   * sizeof (WORD_LIST **));
279     }
280   dollar_arg_stack[dollar_arg_stack_index++] = list_rest_of_args ();
281   dollar_arg_stack[dollar_arg_stack_index] = (WORD_LIST *)NULL;
282 }
283
284 /* Restore the positional parameters from our stack. */
285 void
286 pop_dollar_vars ()
287 {
288   if (!dollar_arg_stack || dollar_arg_stack_index == 0)
289     return;
290
291   remember_args (dollar_arg_stack[--dollar_arg_stack_index], 1);
292   dispose_words (dollar_arg_stack[dollar_arg_stack_index]);
293   dollar_arg_stack[dollar_arg_stack_index] = (WORD_LIST *)NULL;
294 }
295
296 void
297 dispose_saved_dollar_vars ()
298 {
299   if (!dollar_arg_stack || dollar_arg_stack_index == 0)
300     return;
301
302   dispose_words (dollar_arg_stack[dollar_arg_stack_index]);
303   dollar_arg_stack[dollar_arg_stack_index] = (WORD_LIST *)NULL;
304 }
305
306 static int changed_dollar_vars;
307
308 /* Have the dollar variables been reset to new values since we last
309    checked? */
310 int
311 dollar_vars_changed ()
312 {
313   return (changed_dollar_vars);
314 }
315
316 void
317 set_dollar_vars_unchanged ()
318 {
319   changed_dollar_vars = 0;
320 }
321
322 void
323 set_dollar_vars_changed ()
324 {
325   changed_dollar_vars = 1;
326 }
327
328 /* **************************************************************** */
329 /*                                                                  */
330 /*              Validating numeric input and arguments              */
331 /*                                                                  */
332 /* **************************************************************** */
333
334 /* Read a numeric arg for this_command_name, the name of the shell builtin
335    that wants it.  LIST is the word list that the arg is to come from.
336    Accept only the numeric argument; report an error if other arguments
337    follow.  If FATAL is true, call throw_to_top_level, which exits the
338    shell; if not, call jump_to_top_level (DISCARD), which aborts the
339    current command. */
340 int
341 get_numeric_arg (list, fatal)
342      WORD_LIST *list;
343      int fatal;
344 {
345   long count = 1;
346
347   if (list)
348     {
349       register char *arg;
350
351       arg = list->word->word;
352       if (!arg || (legal_number (arg, &count) == 0))
353         {
354           builtin_error ("bad non-numeric arg `%s'", list->word->word);
355           if (fatal)
356             throw_to_top_level ();
357           else
358             jump_to_top_level (DISCARD);
359         }
360       no_args (list->next);
361     }
362   return (count);
363 }
364
365 /* Return the octal number parsed from STRING, or -1 to indicate
366    that the string contained a bad number. */
367 int
368 read_octal (string)
369      char *string;
370 {
371   int result, digits;
372
373   result = digits = 0;
374   while (*string && *string >= '0' && *string < '8')
375     {
376       digits++;
377       result = (result * 8) + *string++ - '0';
378     }
379
380   if (!digits || result > 0777 || *string)
381     result = -1;
382
383   return (result);
384 }
385
386 /* **************************************************************** */
387 /*                                                                  */
388 /*           Manipulating the current working directory             */
389 /*                                                                  */
390 /* **************************************************************** */
391
392 /* Return a consed string which is the current working directory.
393    FOR_WHOM is the name of the caller for error printing.  */
394 char *the_current_working_directory = (char *)NULL;
395
396 char *
397 get_working_directory (for_whom)
398      char *for_whom;
399 {
400   char *directory;
401
402   if (no_symbolic_links)
403     {
404       if (the_current_working_directory)
405         free (the_current_working_directory);
406
407       the_current_working_directory = (char *)NULL;
408     }
409
410   if (the_current_working_directory == 0)
411     {
412       the_current_working_directory = xmalloc (PATH_MAX);
413       the_current_working_directory[0] = '\0';
414       directory = getcwd (the_current_working_directory, PATH_MAX);
415       if (directory == 0)
416         {
417           fprintf (stderr, "%s: could not get current directory: %s: %s\n",
418                    (for_whom && *for_whom) ? for_whom : get_name_for_error (),
419                    the_current_working_directory[0]
420                         ? the_current_working_directory
421                         : bash_getcwd_errstr,
422                    strerror (errno));
423
424           free (the_current_working_directory);
425           the_current_working_directory = (char *)NULL;
426           return (char *)NULL;
427         }
428     }
429
430   return (savestring (the_current_working_directory));
431 }
432
433 /* Make NAME our internal idea of the current working directory. */
434 void
435 set_working_directory (name)
436      char *name;
437 {
438   FREE (the_current_working_directory);
439   the_current_working_directory = savestring (name);
440 }
441
442 /* **************************************************************** */
443 /*                                                                  */
444 /*              Job control support functions                       */
445 /*                                                                  */
446 /* **************************************************************** */
447
448 #if defined (JOB_CONTROL)
449 /* Return the job spec found in LIST. */
450 int
451 get_job_spec (list)
452      WORD_LIST *list;
453 {
454   register char *word;
455   int job, substring;
456
457   if (list == 0)
458     return (current_job);
459
460   word = list->word->word;
461
462   if (*word == '\0')
463     return (current_job);
464
465   if (*word == '%')
466     word++;
467
468   if (digit (*word) && all_digits (word))
469     {
470       job = atoi (word);
471       return (job >= job_slots ? NO_JOB : job - 1);
472     }
473
474   substring = 0;
475   switch (*word)
476     {
477     case 0:
478     case '%':
479     case '+':
480       return (current_job);
481
482     case '-':
483       return (previous_job);
484
485     case '?':                   /* Substring search requested. */
486       substring++;
487       word++;
488       /* FALLTHROUGH */
489
490     default:
491       {
492         register int i, wl;
493
494         job = NO_JOB;
495         wl = strlen (word);
496         for (i = 0; i < job_slots; i++)
497           {
498             if (jobs[i])
499               {
500                 register PROCESS *p;
501                 p = jobs[i]->pipe;
502                 do
503                   {
504                     if ((substring && strindex (p->command, word)) ||
505                         (STREQN (p->command, word, wl)))
506                       if (job != NO_JOB)
507                         {
508                           builtin_error ("ambigious job spec: %s", word);
509                           return (DUP_JOB);
510                         }
511                       else
512                         job = i;
513
514                     p = p->next;
515                   }
516                 while (p != jobs[i]->pipe);
517               }
518           }
519         return (job);
520       }
521     }
522 }
523 #endif /* JOB_CONTROL */
524
525 int
526 display_signal_list (list, forcecols)
527      WORD_LIST *list;
528      int forcecols;
529 {
530   register int i, column;
531   char *name;
532   int result;
533   long signum;
534
535   result = EXECUTION_SUCCESS;
536   if (!list)
537     {
538       for (i = 1, column = 0; i < NSIG; i++)
539         {
540           name = signal_name (i);
541           if (STREQN (name, "SIGJUNK", 7) || STREQN (name, "Unknown", 7))
542             continue;
543
544           if (posixly_correct && !forcecols)
545             printf ("%s%s", name, (i == NSIG - 1) ? "" : " ");
546           else
547             {
548               printf ("%2d) %s", i, name);
549
550               if (++column < 4)
551                 printf ("\t");
552               else
553                 {
554                   printf ("\n");
555                   column = 0;
556                 }
557             }
558         }
559
560       if ((posixly_correct && !forcecols) || column != 0)
561         printf ("\n");
562       return result;
563     }
564
565   /* List individual signal names or numbers. */
566   while (list)
567     {
568       if (legal_number (list->word->word, &signum))
569         {
570           /* This is specified by Posix.2 so that exit statuses can be
571              mapped into signal numbers. */
572           if (signum > 128)
573             signum -= 128;
574           if (signum < 0 || signum >= NSIG)
575             {
576               builtin_error ("bad signal number: %s", list->word->word);
577               result = EXECUTION_FAILURE;
578               list = list->next;
579               continue;
580             }
581
582           name = signal_name (signum);
583           if (STREQN (name, "SIGJUNK", 7) || STREQN (name, "Unknown", 7))
584             {
585               list = list->next;
586               continue;
587             }
588 #if defined (JOB_CONTROL)
589           /* POSIX.2 says that `kill -l signum' prints the signal name without
590              the `SIG' prefix. */
591           printf ("%s\n", (this_shell_builtin == kill_builtin) ? name + 3 : name);
592 #else
593           printf ("%s\n", name);
594 #endif
595         }
596       else
597         {
598           signum = decode_signal (list->word->word);
599           if (signum == NO_SIG)
600             {
601               builtin_error ("%s: not a signal specification", list->word->word);
602               result = EXECUTION_FAILURE;
603               list = list->next;
604               continue;
605             }
606           printf ("%ld\n", signum);
607         }
608       list = list->next;
609     }
610   return (result);
611 }
612
613 /* **************************************************************** */
614 /*                                                                  */
615 /*          Finding builtin commands and their functions            */
616 /*                                                                  */
617 /* **************************************************************** */
618
619 /* Perform a binary search and return the address of the builtin function
620    whose name is NAME.  If the function couldn't be found, or the builtin
621    is disabled or has no function associated with it, return NULL.
622    Return the address of the builtin.
623    DISABLED_OKAY means find it even if the builtin is disabled. */
624 struct builtin *
625 builtin_address_internal (name, disabled_okay)
626      char *name;
627      int disabled_okay;
628 {
629   int hi, lo, mid, j;
630
631   hi = num_shell_builtins - 1;
632   lo = 0;
633
634   while (lo <= hi)
635     {
636       mid = (lo + hi) / 2;
637
638       j = shell_builtins[mid].name[0] - name[0];
639
640       if (j == 0)
641         j = strcmp (shell_builtins[mid].name, name);
642
643       if (j == 0)
644         {
645           /* It must have a function pointer.  It must be enabled, or we
646              must have explicitly allowed disabled functions to be found,
647              and it must not have been deleted. */
648           if (shell_builtins[mid].function &&
649               ((shell_builtins[mid].flags & BUILTIN_DELETED) == 0) &&
650               ((shell_builtins[mid].flags & BUILTIN_ENABLED) || disabled_okay))
651             return (&shell_builtins[mid]);
652           else
653             return ((struct builtin *)NULL);
654         }
655       if (j > 0)
656         hi = mid - 1;
657       else
658         lo = mid + 1;
659     }
660   return ((struct builtin *)NULL);
661 }
662
663 /* Return the pointer to the function implementing builtin command NAME. */
664 Function *
665 find_shell_builtin (name)
666      char *name;
667 {
668   current_builtin = builtin_address_internal (name, 0);
669   return (current_builtin ? current_builtin->function : (Function *)NULL);
670 }
671
672 /* Return the address of builtin with NAME, whether it is enabled or not. */
673 Function *
674 builtin_address (name)
675      char *name;
676 {
677   current_builtin = builtin_address_internal (name, 1);
678   return (current_builtin ? current_builtin->function : (Function *)NULL);
679 }
680
681 /* Return the function implementing the builtin NAME, but only if it is a
682    POSIX.2 special builtin. */
683 Function *
684 find_special_builtin (name)
685      char *name;
686 {
687   current_builtin = builtin_address_internal (name, 0);
688   return ((current_builtin && (current_builtin->flags & SPECIAL_BUILTIN)) ?
689                         current_builtin->function :
690                         (Function *)NULL);
691 }
692   
693 static int
694 shell_builtin_compare (sbp1, sbp2)
695      struct builtin *sbp1, *sbp2;
696 {
697   int result;
698
699   if ((result = sbp1->name[0] - sbp2->name[0]) == 0)
700     result = strcmp (sbp1->name, sbp2->name);
701
702   return (result);
703 }
704
705 /* Sort the table of shell builtins so that the binary search will work
706    in find_shell_builtin. */
707 void
708 initialize_shell_builtins ()
709 {
710   qsort (shell_builtins, num_shell_builtins, sizeof (struct builtin),
711     (QSFUNC *)shell_builtin_compare);
712 }
713
714 /* **************************************************************** */
715 /*                                                                  */
716 /*       Functions for quoting strings to be re-read as input       */
717 /*                                                                  */
718 /* **************************************************************** */
719
720 /* Return a new string which is the single-quoted version of STRING.
721    Used by alias and trap, among others. */
722 char *
723 single_quote (string)
724      char *string;
725 {
726   register int c;
727   char *result, *r, *s;
728
729   result = (char *)xmalloc (3 + (4 * strlen (string)));
730   r = result;
731   *r++ = '\'';
732
733   for (s = string; s && (c = *s); s++)
734     {
735       *r++ = c;
736
737       if (c == '\'')
738         {
739           *r++ = '\\';  /* insert escaped single quote */
740           *r++ = '\'';
741           *r++ = '\'';  /* start new quoted string */
742         }
743     }
744
745   *r++ = '\'';
746   *r = '\0';
747
748   return (result);
749 }
750
751 /* Quote STRING using double quotes.  Return a new string. */
752 char *
753 double_quote (string)
754      char *string;
755 {
756   register int c;
757   char *result, *r, *s;
758
759   result = (char *)xmalloc (3 + (2 * strlen (string)));
760   r = result;
761   *r++ = '"';
762
763   for (s = string; s && (c = *s); s++)
764     {
765       switch (c)
766         {
767         case '"':
768         case '$':
769         case '`':
770         case '\\':
771         case '\n':              /* XXX */
772           *r++ = '\\';
773         default:
774           *r++ = c;
775           break;
776         }
777     }
778
779   *r++ = '"';
780   *r = '\0';
781
782   return (result);
783 }
784
785 /* Remove backslashes that are quoting characters that are special between
786    double quotes.  Return a new string. */
787 char *
788 un_double_quote (string)
789      char *string;
790 {
791   register int c, pass_next;
792   char *result, *r, *s;
793
794   r = result = xmalloc (strlen (string) + 1);
795
796   for (pass_next = 0, s = string; s && (c = *s); s++)
797     {
798       if (pass_next)
799         {
800           *r++ = c;
801           pass_next = 0;
802           continue;
803         }
804       if (c == '\\' && strchr (slashify_in_quotes, s[1]))
805         {
806           pass_next = 1;
807           continue;
808         }
809       *r++ = c;
810     }
811
812   *r = '\0';
813   return result;
814 }
815
816 /* Quote special characters in STRING using backslashes.  Return a new
817    string. */
818 char *
819 backslash_quote (string)
820      char *string;
821 {
822   int c;
823   char *result, *r, *s;
824
825   result = xmalloc (2 * strlen (string) + 1);
826
827   for (r = result, s = string; s && (c = *s); s++)
828     {
829       switch (c)
830         {
831         case ' ': case '\t': case '\n':         /* IFS white space */
832         case '\'': case '"': case '\\':         /* quoting chars */
833         case '|': case '&': case ';':           /* shell metacharacters */
834         case '(': case ')': case '<': case '>':
835         case '!': case '{': case '}':           /* reserved words */
836         case '*': case '[': case '?': case ']': /* globbing chars */
837         case '^':
838         case '$': case '`':                     /* expansion chars */
839           *r++ = '\\';
840           *r++ = c;
841           break;
842 #if 0
843         case '~':                               /* tilde expansion */
844           if (s == string || s[-1] == '=' || s[-1] == ':')
845             *r++ = '\\';
846           *r++ = c;
847           break;
848 #endif
849         case '#':                               /* comment char */
850           if (s == string)
851             *r++ = '\\';
852           /* FALLTHROUGH */
853         default:
854           *r++ = c;
855           break;
856         }
857     }
858
859   *r = '\0';
860   return (result);
861 }
862
863 #if defined (PROMPT_STRING_DECODE)
864 /* Quote characters that get special treatment when in double quotes in STRING
865    using backslashes.  Return a new string. */
866 char *
867 backslash_quote_for_double_quotes (string)
868      char *string;
869 {
870   int c;
871   char *result, *r, *s;
872
873   result = xmalloc (2 * strlen (string) + 1);
874
875   for (r = result, s = string; s && (c = *s); s++)
876     {
877       switch (c)
878         {
879         case '"':
880         case '$':
881         case '`':
882         case '\\':
883         case '\n':
884           *r++ = '\\';
885           *r++ = c;
886           break;
887         default:
888           *r++ = c;
889           break;
890         }
891     }
892
893   *r = '\0';
894   return (result);
895 }
896 #endif /* PROMPT_STRING_DECODE */
897
898 int
899 contains_shell_metas (string)
900      char *string;
901 {
902   char *s;
903
904   for (s = string; s && *s; s++)
905     {
906       switch (*s)
907         {
908         case ' ': case '\t': case '\n':         /* IFS white space */
909         case '\'': case '"': case '\\':         /* quoting chars */
910         case '|': case '&': case ';':           /* shell metacharacters */
911         case '(': case ')': case '<': case '>':
912         case '!': case '{': case '}':           /* reserved words */
913         case '*': case '[': case '?': case ']': /* globbing chars */
914         case '^':
915         case '$': case '`':                     /* expansion chars */
916           return (1);
917         case '~':                               /* tilde expansion */
918           if (s == string || s[-1] == '=' || s[-1] == ':')
919             return (1);
920         case '#':
921           if (s == string)                      /* comment char */
922             return (1);
923           /* FALLTHROUGH */
924         default:
925           break;
926         }
927     }
928
929   return (0);
930 }