home *** CD-ROM | disk | FTP | other *** search
/ The Atari Compendium / The Atari Compendium (Toad Computers) (1994).iso / files / prgtools / mint / shells / bashsrc.zoo / subst.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-06-05  |  48.9 KB  |  2,024 lines

  1. /* substitutions.c -- The part of the shell that does parameter,
  2.    command, and globbing substitutions. */
  3.  
  4. /* Copyright (C) 1987,1989 Free Software Foundation, Inc.
  5.  
  6. This file is part of GNU Bash, the Bourne Again SHell.
  7.  
  8. Bash is free software; you can redistribute it and/or modify it under
  9. the terms of the GNU General Public License as published by the Free
  10. Software Foundation; either version 1, or (at your option) any later
  11. version.
  12.  
  13. Bash is distributed in the hope that it will be useful, but WITHOUT ANY
  14. WARRANTY; without even the implied warranty of MERCHANTABILITY or
  15. FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  16. for more details.
  17.  
  18. You should have received a copy of the GNU General Public License along
  19. with Bash; see the file COPYING.  If not, write to the Free Software
  20. Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
  21.  
  22. #include <stdio.h>
  23. #include <pwd.h>
  24. #include <fcntl.h>
  25. #include "shell.h"
  26. #include "flags.h"
  27. #include "alias.h"
  28. #include <readline/history.h>
  29.  
  30. /* The size that strings change by. */
  31. #define DEFAULT_ARRAY_SIZE 512
  32.  
  33. /* Some forward declarations. */
  34.  
  35. extern WORD_LIST *expand_string (), *expand_word (), *list_string ();
  36. extern char *string_list ();
  37. extern WORD_DESC *make_word ();
  38.  
  39.  
  40. /* **************************************************************** */
  41. /*                                    */
  42. /*            Utility Functions                */
  43. /*                                    */
  44. /* **************************************************************** */
  45.  
  46.  
  47. /* Cons a new string from STRING starting at START and ending at END,
  48.    not including END. */
  49. char *
  50. substring (string, start, end)
  51.      char *string;
  52.      int start, end;
  53. {
  54.   register int len = end - start;
  55.   register char *result = (char *)xmalloc (len + 1);
  56.  
  57.   strncpy (result, string + start, len);
  58.   result[len] = '\0';
  59.   return (result);
  60. }
  61.  
  62. /* Just like string_extract, but doesn't hack backslashes or any of
  63.    that other stuff. */
  64. char *
  65. string_extract_verbatim (string, sindex, charlist)
  66.      char *string, *charlist;
  67.      int *sindex;
  68. {
  69.   register int i = *sindex;
  70.   int c;
  71.   char *temp;
  72.  
  73.   while ((c = string[i]) && (!member (c, charlist))) i++;
  74.   temp = (char *)xmalloc (1 + (i - *sindex));
  75.   strncpy (temp, string + (*sindex), i - (*sindex));
  76.   temp[i - (*sindex)] = '\0';
  77.   *sindex = i;
  78.   return (temp);
  79. }
  80.  
  81. /* Extract a substring from STRING, starting at INDEX and ending with
  82.    one of the characters in CHARLIST.  Don't make the ending character
  83.    part of the string.  Leave INDEX pointing at the ending character.
  84.    Understand about backslashes in the string. */
  85. char *
  86. string_extract (string, sindex, charlist)
  87.      char *string, *charlist;
  88.      int *sindex;
  89. {
  90.   register int c, i = *sindex;
  91.   char *temp;
  92.  
  93.   while (c = string[i]) {
  94.     if (c == '\\')
  95.       if (string[i + 1])
  96.     i++;
  97.       else
  98.     break;
  99.     else
  100.       if (member (c, charlist))
  101.     break;
  102.     i++;
  103.   }
  104.   temp = (char *)xmalloc (1 + (i - *sindex));
  105.   strncpy (temp, string + (*sindex), i - (*sindex));
  106.   temp[i - (*sindex)] = '\0';
  107.   *sindex = i;
  108.   return (temp);
  109. }
  110.  
  111. /* Remove backslashes which are quoting backquotes from STRING.  Modifies
  112.    STRING, and returns a pointer to it. */
  113. char *
  114. de_backslash (string)
  115.      char *string;
  116. {
  117.   register int i, l = strlen (string);
  118.  
  119.   for (i = 0; i < l; i++)
  120.     if (string[i] == '\\' && (string[i + 1] == '`' || string[i] == '\\'))
  121.       strcpy (&string[i], &string[i + 1]);
  122.   return (string);
  123. }
  124.  
  125. /* Remove instances of \! from a string. */
  126. void
  127. unquote_bang (string)
  128.      char *string;
  129. {
  130.   register int i, j;
  131.   register char *temp = (char *)alloca (1 + strlen (string));
  132.  
  133.   for (i = 0, j = 0; (temp[j] = string[i]); i++, j++)
  134.     {
  135.       if (string[i] == '\\' && string[i + 1] == '!')
  136.     {
  137.       temp[j] = '!';
  138.       i++;
  139.     }
  140.     }
  141.   strcpy (string, temp);
  142. }
  143.  
  144. /* Extract the $( construct in STRING, and return a new string.
  145.    Start extracting at (SINDEX) as if we had just seen "$(".
  146.    Make (SINDEX) get the position just after the matching ")". */
  147. char *
  148. extract_command_subst (string, sindex)
  149.      char *string;
  150.      int *sindex;
  151. {
  152.   register int i, c, l;
  153.   int pass_character, paren_level;
  154.   int delimiter, delimited_paren_level;
  155.   char *result;
  156.  
  157.   pass_character = delimiter = delimited_paren_level = 0;
  158.   paren_level = 1;
  159.  
  160.   for (i = *sindex; c = string[i]; i++)
  161.     {
  162.       if (pass_character)
  163.     {
  164.       pass_character = 0;
  165.       continue;
  166.     }
  167.  
  168.       if (c == '\\')
  169.     {
  170.       if ((delimiter == '"') &&
  171.           (member (string[i + 1], slashify_in_quotes)))
  172.         {
  173.         pass_next_character:
  174.           pass_character++;
  175.           continue;
  176.         }
  177.     }
  178.  
  179.       if (!delimiter || delimiter == '"')
  180.     {
  181.       if (c == '$' && (string[i + 1] == '('))
  182.         {
  183.           if (!delimiter)
  184.         paren_level++;
  185.           else
  186.         delimited_paren_level++;
  187.           goto pass_next_character;
  188.         }
  189.  
  190.       if (c == ')')
  191.         {
  192.           if (delimiter && delimited_paren_level)
  193.         delimited_paren_level--;
  194.  
  195.           if (!delimiter)
  196.         {
  197.           paren_level--;
  198.           if (paren_level == 0)
  199.             break;
  200.         }
  201.         }
  202.     }
  203.  
  204.       if (delimiter)
  205.     {
  206.       if (c == delimiter)
  207.         delimiter = 0;
  208.       continue;
  209.     }
  210.       else
  211.     {
  212.       if (c == '"' || c == '\'' || c == '\\')
  213.         delimiter = c;
  214.     }
  215.     }
  216.  
  217.   l = i - *sindex;
  218.   result = (char *)xmalloc (1 + l);
  219.   strncpy (result, &string[*sindex], l);
  220.   result[l] = '\0';
  221.   *sindex = i;
  222.  
  223.   if (!c && (delimiter || paren_level))
  224.     {
  225.       report_error ("Bad command substitution: `$(%s'", result);
  226.       free (result);
  227.       longjmp (top_level, DISCARD);
  228.     }
  229.  
  230.   return (result);
  231. }
  232.  
  233. /* An artifact for extracting the contents of a quoted string.  Since the
  234.    string is about to be evaluated, we pass everything through, and only
  235.    strip backslash before backslash or quote. */
  236. char *
  237. string_extract_double_quoted (string, sindex)
  238.      char *string;
  239.      int *sindex;
  240. {
  241.   register int c, j, i;
  242.   char *temp = (char *)xmalloc (1 + strlen (string + (*sindex)));
  243.  
  244.   for (j = 0, i = *sindex; ((c = string[i]) && (c != '"')); i++)
  245.     {
  246.       if (c == '\\')
  247.     {
  248.       c = string[++i];
  249.       if (c != '\\' && c != '"')
  250.         temp[j++] = '\\';
  251.     }
  252.       temp[j++] = c;
  253.     }
  254.   temp[j] = '\0';
  255.   *sindex = i;
  256.   return (temp);
  257. }
  258.  
  259. /* Extract the name of the variable to bind to from the assignment string. */
  260. char *
  261. assignment_name (string)
  262.      char *string;
  263. {
  264.   int offset = assignment (string);
  265.   char *temp;
  266.   if (!offset) return (char *)NULL;
  267.   temp = (char *)xmalloc (offset + 1);
  268.   strncpy (temp, string, offset);
  269.   temp[offset] = '\0';
  270.   return (temp);
  271. }
  272.  
  273. /* Return a single string of all the words in LIST. */
  274. char *
  275. string_list (list)
  276.      WORD_LIST *list;
  277. {
  278.   char *result = (char *)NULL;
  279.  
  280.   while (list)
  281.     {
  282.       if (!result)
  283.     result = savestring ("");
  284.  
  285.       result =
  286.     (char *)xrealloc (result, 3 + strlen (result) + strlen (list->word->word));
  287.       strcat (result, list->word->word);
  288.       if (list->next) strcat (result, " ");
  289.       list = list->next;
  290.     }
  291.   return (result);
  292. }
  293.  
  294. /* Return the list of words present in STRING.  Separate the string into
  295.    words at any of the characters found in SEPARATORS.  If QUOTED is
  296.    non-zero then word in the list will have its quoted flag set, otherwise
  297.    the quoted flag is left as make_word () deemed fit. */
  298. WORD_LIST *
  299. list_string (string, separators, quoted)
  300.      register char *string, *separators;
  301.      int quoted;
  302. {
  303.   WORD_LIST *result = (WORD_LIST *)NULL;
  304.   char *current_word = (char *)NULL;
  305.   int sindex =0;
  306.  
  307.   while (string[sindex] &&
  308.      (current_word =
  309.       string_extract_verbatim (string, &sindex, separators)))
  310.     {
  311.       if (strlen (current_word))
  312.     {
  313.       register char *temp_string;
  314.  
  315.        for (temp_string = current_word; *temp_string; temp_string++)
  316.         if ((unsigned char)(*temp_string) == (unsigned char)0x80)
  317.           {
  318.         strcpy (temp_string, temp_string + 1);
  319.         temp_string--;
  320.           }
  321.  
  322.       result = make_word_list (make_word (current_word), result);
  323.       if (quoted)
  324.         result->word->quoted++;
  325.     }
  326.       free (current_word);
  327.       if (string[sindex]) sindex++;
  328.     }
  329.   return (WORD_LIST *)reverse_list (result);
  330. }
  331.   
  332. /* Given STRING, an assignment string, get the value of the right side
  333.    of the `=', and bind it to the left side. */
  334. do_assignment (string)
  335.      char *string;
  336. {
  337.   int offset = assignment (string);
  338.   char *name = savestring (string);
  339.   char *value = (char *)NULL;
  340.   SHELL_VAR *entry = (SHELL_VAR *)NULL;
  341.  
  342.   if (name[offset] == '=')
  343.     {
  344.       char *temp, *tilde_expand (), *string_list ();
  345.       WORD_LIST *list, *expand_string ();
  346.       extern int disallow_filename_globbing;
  347.  
  348.       name[offset] = 0;
  349.       temp = name + offset + 1;
  350.       if (!disallow_filename_globbing)
  351.     temp = tilde_expand (temp);
  352.       else
  353.     temp = savestring (temp);
  354.  
  355.       list = expand_string (temp, 0);
  356.       if (list)
  357.     {
  358.       value = string_list (list);
  359.       dispose_words (list);
  360.     }
  361.       free (temp);
  362.     }
  363.  
  364.   if (!value) value = savestring ("");
  365.   entry = bind_variable (name, value);
  366.  
  367.   /* Yes, here is where the special shell variables get tested for.
  368.      Don't ask me, I just work here.  This is really stupid.  I would
  369.      swear, but I've decided that that is an impolite thing to do in
  370.      source that is to be distributed around the net, even if this code
  371.      is totally brain-damaged. */
  372.  
  373.   /* if (strcmp (name, "PATH") == 0) Yeeecchhh!!!*/
  374.   stupidly_hack_special_variables (name);
  375.  
  376.   if (entry)
  377.     entry->attributes &= ~att_invisible;
  378.   if (value) free (value);
  379.   free (name);
  380. }
  381.  
  382. /* Most of the substitutions must be done in parallel.  In order
  383.    to avoid using tons of unclear goto's, I have some functions
  384.    for manipulating malloc'ed strings.  They all take INDEX, a
  385.    pointer to an integer which is the offset into the string
  386.    where manipulation is taking place.  They also take SIZE, a
  387.    pointer to an integer which is the current length of the
  388.    character array for this string. */
  389.  
  390. /* Append SOURCE to TARGET at INDEX.  SIZE is the current amount
  391.    of space allocated to TARGET.  SOURCE can be NULL, in which
  392.    case nothing happens.  Gets rid of SOURCE by free ()ing it.
  393.    Returns TARGET in case the location has changed. */
  394. char *
  395. sub_append_string (source, target, index, size)
  396.      char *source, *target;
  397.      int *index, *size;
  398. {
  399.   if (source)
  400.     {
  401.       if (!target)
  402.     target = (char *)xmalloc (*size = DEFAULT_ARRAY_SIZE);
  403.  
  404.       while (strlen (source) >= (*size - *index))
  405.     target = (char *)xrealloc (target, *size += DEFAULT_ARRAY_SIZE);
  406.  
  407.       strcat (target, source);
  408.       *index += strlen (source);
  409.  
  410.       free (source);
  411.     }
  412.   return (target);
  413. }
  414.  
  415. /* Append the textual representation of NUMBER to TARGET.
  416.    INDEX and SIZE are as in SUB_APPEND_STRING. */
  417. char *
  418. sub_append_number (number, target, index, size)
  419.      int number, *index, *size;
  420.      char *target;
  421. {
  422.   char *temp = (char *)xmalloc (10);
  423.   sprintf (temp, "%d", number);
  424.   return (sub_append_string (temp, target, index, size));
  425. }
  426.  
  427. /* Return the word list that corresponds to `$*'. */
  428. WORD_LIST *
  429. list_rest_of_args ()
  430. {
  431.   register WORD_LIST *list = (WORD_LIST *)NULL;
  432.   register WORD_LIST *args = rest_of_args;
  433.   int i;
  434.  
  435.   for (i = 1; i < 10; i++)
  436.     if (dollar_vars[i])
  437.       list = make_word_list (make_word (dollar_vars[i]), list);
  438.   while (args)
  439.     {
  440.       list = make_word_list (make_word (args->word->word), list);
  441.       args = args->next;
  442.     }
  443.   return ((WORD_LIST *)reverse_list (list));
  444. }
  445.  
  446. /* Make a single large string out of the dollar digit variables,
  447.    and the rest_of_args. */
  448. char *
  449. string_rest_of_args ()
  450. {
  451.   register WORD_LIST *list = list_rest_of_args ();
  452.   char *string = string_list (list);
  453.  
  454.   dispose_words (list);
  455.   return (string);
  456. }
  457.  
  458. /* Expand STRING just as if you were expanding a word.  This also returns
  459.    a list of words.  Note that filename globbing is *NOT* done for word
  460.    or string expansion, just when the shell is expanding a command. */
  461. WORD_LIST *
  462. expand_string (string, quoted)
  463.      char *string;
  464.      int quoted;
  465. {
  466.   WORD_DESC *make_word (), *temp = make_word (string);
  467.   WORD_LIST *expand_word ();
  468.   WORD_LIST *value = expand_word (temp, quoted);
  469.  
  470.   dispose_word (temp);
  471.   dequote (value);
  472.   return (value);
  473. }
  474.  
  475. /* I'm going to have to rewrite expansion because filename globbing is
  476.    beginning to make the entire arrangement ugly.  I'll do this soon. */
  477. dequote (list)
  478.      register WORD_LIST *list;
  479. {
  480.   register char *s;
  481.  
  482.   while (list)
  483.     {
  484.       s = list->word->word;
  485.       while (*s)
  486.     {
  487.       *s &= 0x7f;
  488.       s++;
  489.     }
  490.       list = list->next;
  491.     }
  492. }
  493.  
  494. /* How to quote and dequote the character C. */
  495. #define QUOTE_CHAR(c)    ((unsigned char)(c) | 0x80)
  496. #define DEQUOTE_CHAR(c) ((unsigned char)(c) & 0x7f)
  497.  
  498. /* Quote the string s. */
  499. quote_string (s)
  500.      char *s;
  501. {
  502.   register unsigned char *t = (unsigned char *) s;
  503.  
  504.   for ( ; t && *t ; t++)
  505.     *t |= 0x80;
  506. }
  507.  
  508. /* Quote the entire WORD_LIST list. */
  509. quote_list (list)
  510.      WORD_LIST *list;
  511. {
  512.   register WORD_LIST *w;
  513.  
  514.   for (w = list; w; w = w->next)
  515.     {
  516.       quote_string (w->word->word);
  517.       w->word->quoted = 1;
  518.     }
  519. }
  520.  
  521. /* Remove the portion of PARAM matched by PATTERN according to OP, where OP
  522.    can have one of 4 values:
  523.     RP_LONG_LEFT    remove longest matching portion at start of PARAM
  524.     RP_SHORT_LEFT    remove shortest matching portion at start of PARAM
  525.     RP_LONG_RIGHT    remove longest matching portion at end of PARAM
  526.     RP_SHORT_RIGHT    remove shortest matching portion at end of PARAM
  527. */
  528.  
  529. #define RP_LONG_LEFT    1
  530. #define RP_SHORT_LEFT    2
  531. #define RP_LONG_RIGHT    3
  532. #define RP_SHORT_RIGHT    4
  533.  
  534. static char *
  535. remove_pattern (param, pattern, op)
  536.      char *param, *pattern;
  537.      int op;
  538. {
  539.   register int len = param ? strlen (param) : 0;
  540.   register char *end = param + len;
  541.   register char *p, *ret, c;
  542.  
  543.   if (pattern == NULL || *pattern == '\0')    /* minor optimization */
  544.     return (savestring (param));
  545.  
  546.   if (param == NULL || *param == '\0')
  547.     return (param);
  548.  
  549.   switch (op)
  550.     {
  551.       case RP_LONG_LEFT:    /* remove longest match at start */
  552.     for (p = end; p >= param; p--)
  553.       {
  554.         c = *p; *p = '\0';
  555.         if (glob_match (pattern, param, 0))
  556.           {
  557.         *p = c;
  558.         return (savestring (p));
  559.           }
  560.         *p = c;
  561.       }
  562.     break;
  563.  
  564.       case RP_SHORT_LEFT:    /* remove shortest match at start */
  565.     for (p = param; p <= end; p++)
  566.       {
  567.         c = *p; *p = '\0';
  568.         if (glob_match (pattern, param, 0))
  569.           {
  570.         *p = c;
  571.         return (savestring (p));
  572.           }
  573.         *p = c;
  574.       }
  575.     break;
  576.  
  577.       case RP_LONG_RIGHT:    /* remove longest match at end */
  578.     for (p = param; p <= end; p++)
  579.       {
  580.         if (glob_match (pattern, p, 0))
  581.           {
  582.         c = *p;
  583.         *p = '\0';
  584.         ret = savestring (param);
  585.         *p = c;
  586.         return (ret);
  587.           }
  588.       }
  589.     break;
  590.  
  591.       case RP_SHORT_RIGHT:    /* remove shortest match at end */
  592.     for (p = end; p >= param; p--)
  593.       {
  594.         if (glob_match (pattern, p, 0))
  595.           {
  596.         c = *p;
  597.         *p = '\0';
  598.         ret = savestring (param);
  599.         *p = c;
  600.         return (ret);
  601.           }
  602.       }
  603.     break;
  604.     }
  605.   return (savestring (param));    /* no match, return original string */
  606. }
  607.     
  608. WORD_LIST *
  609. expand_word (word, quoted)
  610.      WORD_DESC *word;
  611.      int quoted;
  612. {
  613.   WORD_LIST *expand_word_internal ();
  614.   WORD_LIST *result = expand_word_internal (word, quoted);
  615.   if (result)
  616.     dequote (result);
  617.   return (result);
  618. }
  619.  
  620. /* Make a word list which is the expansion of the word passed in WORD.
  621.    This returns a WORD_LIST * if the expansion expands to something,
  622.    else it returns NULL.  If QUOTED is non-zero, then the text of WORD is
  623.    treated as if it was surrounded by double-quotes.  */
  624. WORD_LIST *
  625. expand_word_internal (word, quoted)
  626.      WORD_DESC *word;
  627.      int quoted;
  628. {
  629.   extern int last_command_exit_value;
  630.  
  631.   /* The thing that we finally output. */
  632.   WORD_LIST *result = (WORD_LIST *)NULL;
  633.  
  634.   /* The intermediate string that we build while expanding. */
  635.   char *istring = (char *)xmalloc (DEFAULT_ARRAY_SIZE);
  636.  
  637.   /* The current size of the above object. */
  638.   int istring_size = DEFAULT_ARRAY_SIZE;
  639.  
  640.   /* Index into ISTRING. */
  641.   int istring_index = 0;
  642.  
  643.   /* Temporary string storage. */
  644.   char *temp = (char *)NULL;
  645.  
  646.   /* The text of WORD. */
  647.   register char *string = word->word;
  648.  
  649.   /* The index into STRING. */
  650.   register int sindex = 0;
  651.  
  652.   register int c;        /* Current character. */
  653.   int number;            /* Temporary number value. */
  654.   int t_index;            /* For calls to string_extract_xxx. */
  655.   extern int interactive;
  656.  
  657.   istring[0] = '\0';
  658.  
  659.   if (!string) goto final_exit;
  660.  
  661.   /* Begin the expansion. */
  662.  
  663.   for (;;) {
  664.  
  665.     c = string[sindex];
  666.  
  667.     switch (c) {        /* Case on toplevel character. */
  668.  
  669.     case '\0':
  670.       goto finished_with_string;
  671.  
  672.     case '$':
  673.  
  674.       c = string[++sindex];
  675.  
  676.       /* Do simple cases first... */
  677.  
  678.       switch (c) {        /* Case on what follows '$'. */
  679.  
  680.       case '0':            /* $0 .. $9? */
  681.       case '1':
  682.       case '2':
  683.       case '3':
  684.       case '4':
  685.       case '5':
  686.       case '6':
  687.       case '7':
  688.       case '8':
  689.       case '9':
  690. #define SINGLE_DIGIT_COMPATIBILITY /* I'm beginning to hate Bourne. */
  691. #ifdef SINGLE_DIGIT_COMPATIBILITY
  692.     if (dollar_vars[digit_value (c)])
  693.       temp = savestring (dollar_vars[digit_value (c)]);
  694.     else
  695.       temp = (char *)NULL;
  696. #else /* We read the whole number, not just the first digit. */
  697. #endif
  698.     goto dollar_add_string;
  699.  
  700.       case '$':            /* $$ -- pid of the invoking shell. */
  701.     {
  702.       extern int dollar_dollar_pid;
  703.       number = dollar_dollar_pid;
  704.     }
  705. add_number:
  706.     temp = (char *)xmalloc (10);
  707.     sprintf (temp, "%d", number);
  708. dollar_add_string:
  709.     if (string[sindex]) sindex++;
  710.  
  711.     /* Add TEMP to ISTRING. */
  712. add_string:
  713.     istring = sub_append_string (temp, istring,
  714.                      &istring_index, &istring_size);
  715.     break;
  716.     
  717.       case '#':            /* $# -- number of positional parameters. */
  718.     {
  719.       WORD_LIST *list = list_rest_of_args ();
  720.       number = list_length (list);
  721.       dispose_words (list);
  722.       goto add_number;
  723.     }
  724.     break;
  725.       
  726.       case '?':            /* $? -- return value of the last synchronous command. */
  727.     {
  728.       number = last_command_exit_value;
  729.       goto add_number;
  730.     }
  731.     break;
  732.       
  733.       case '-':            /* $- -- flags supplied to the shell on
  734.                    invocation or by the `set' builtin. */
  735.     temp = (char *)which_set_flags ();
  736.     goto dollar_add_string;
  737.     break;
  738.       
  739.       case '!':            /* $! -- Pid of the last asynchronous command. */
  740.     {
  741.       extern int last_asynchronous_pid;
  742.       number = last_asynchronous_pid;
  743.       goto add_number;
  744.     }
  745.     break;
  746.       
  747.     /* The only difference between this and $@ is when the arg is
  748.        quoted. */
  749.       case '*':            /* `$*' */
  750.     temp = string_rest_of_args ();
  751.  
  752.     /* In the case of a quoted string, quote the entire arg-list.
  753.        "$1 $2 $3". */
  754.     if (quoted && temp)
  755.       quote_string (temp);
  756.     goto dollar_add_string;
  757.     break;
  758.     
  759.     /* When we have "$@" what we want is "$1" "$2" "$3" ... This 
  760.        means that we have to turn quoting off after we split into
  761.        the individually quoted arguments so that the final split
  762.        on the first character of $IFS is still done.  */
  763.       case '@':            /* `$@' */
  764.     {
  765.       WORD_LIST *tlist = list_rest_of_args ();
  766.       if (quoted && tlist)
  767.         {
  768.           quote_list (tlist);
  769.           quoted = 0;    /* This *should* be safe */
  770.         }
  771.       temp = string_list (tlist);
  772.       goto dollar_add_string;
  773.       break;
  774.     }
  775.  
  776.       case '{':            /* ${[#]name[[:]#[#]%[%]-=?+[word]]} */
  777.     {
  778.       int check_nullness = 0;
  779.       int var_is_set = 0;
  780.       int var_is_null = 0;
  781.       int var_is_special = 0;
  782.       char *name, *value, *string_extract ();
  783.  
  784.       sindex++;
  785.       t_index = sindex;
  786.       name = string_extract (string, &t_index, "#%:-=?+}");
  787.  
  788.       /* If the name really consists of a special variable, then
  789.          make sure that we have the entire name. */
  790.       if (sindex == t_index &&
  791.           (string[sindex] == '-' ||
  792.            string[sindex] == '?' ||
  793.            string[sindex] == '#'))
  794.         {
  795.           char *tt;
  796.           t_index++;
  797.           free (name);
  798.           tt = (string_extract (string, &t_index, "#%:-=?+}"));
  799.           name = (char *)xmalloc (2 + (strlen (tt)));
  800.           *name = string[sindex];
  801.           strcpy (name + 1, tt);
  802.           free (tt);
  803.         }
  804.       sindex = t_index;
  805.  
  806.       /* Find out what character ended the variable name.  Then
  807.          do the appropriate thing. */
  808.       
  809.       if (c = string[sindex])
  810.         sindex++;
  811.  
  812.       if (c == ':')
  813.         {
  814.           check_nullness++;
  815.           if (c = string[sindex])
  816.         sindex++;
  817.         }
  818.       
  819.       /* Determine the value of this variable. */
  820.       if (strlen (name) == 1 && (digit(*name) || member(*name, "#-?$!@*")))
  821.         var_is_special++;
  822.         
  823.       /* Check for special expansion things. */
  824.       if (*name == '#')
  825.         {
  826.           if (name[1] != '*' && name[1] != '@')
  827.         {
  828.           char *tt = get_string_value (&name[1]);
  829.           if (tt)
  830.             number = strlen (tt);
  831.           else
  832.             number = 0;
  833.           goto add_number;
  834.         }
  835.           else
  836.         name[1] = '\0';    /* ${#@} is the same as $#. */
  837.         }
  838.       
  839.       if (var_is_special)
  840.         {
  841.           char *tt = (char *)alloca (2 + strlen (name));
  842.           WORD_LIST *l;
  843.           tt[0] = '$'; tt[1] = '\0';
  844.           strcat (tt, name);
  845.           l = expand_string (tt, 0);
  846.           temp = string_list (l);
  847.           dispose_words (l);
  848.  
  849.           if (temp)
  850.         var_is_set++;
  851.         }
  852.       else
  853.         {
  854.           char *get_string_value ();
  855.           SHELL_VAR *f = find_variable (name);
  856.  
  857.           if (f && !invisible_p (f) &&
  858.           ((temp = get_string_value (name)) != (char *)NULL))
  859.         {
  860.           temp = savestring (temp);
  861.           var_is_set++;
  862.         }
  863.           else
  864.         temp = (char *)NULL;
  865.         }
  866.  
  867.       if (!var_is_set || !temp || !*temp)
  868.         var_is_null++;
  869.       
  870.       if (!check_nullness)
  871.         var_is_null = 0;
  872.  
  873.       /* Get the rest of the stuff inside the braces. */
  874.       if (c && c != '}')
  875.         {
  876.           /* Scan forward searching for last `{'.  This is a hack,
  877.          it will always be a hack, and it always has been a hack. */
  878.           {
  879.         int braces_found = 0;
  880.  
  881.         for (t_index = sindex; string[t_index]; t_index++)
  882.           {
  883.             if (string[t_index] == '{')
  884.               braces_found++;
  885.             else
  886.               if (string[t_index] == '}')
  887.             if (!braces_found)
  888.               break;
  889.             else
  890.               braces_found--;
  891.           }
  892.  
  893.         value = (char *)xmalloc (1 + (t_index - sindex));
  894.         strncpy (value, &string[sindex], t_index - sindex);
  895.         value[t_index - sindex] = '\0';
  896.         sindex = t_index;
  897.           }
  898.  
  899.           if (string[sindex] == '}') sindex++;
  900.           else goto bad_substitution;
  901.         
  902.         }
  903.       else
  904.         {
  905.           value = (char *)NULL;
  906.         }
  907.       
  908.       /* Do the right thing based on which character ended the variable
  909.          name. */
  910.       switch (c)
  911.         {
  912.         case '\0':
  913.         bad_substitution:
  914.           report_error ("%s: bad substitution", name ? name : "??");
  915.           longjmp (top_level, DISCARD);
  916.  
  917.         case '}':
  918.           break;
  919.         
  920.         case '#':    /* ${param#[#]pattern} */
  921.           if (!value || !*value || !temp || !*temp)
  922.         break;
  923.  
  924.           if (*value == '#')
  925.         {
  926.           WORD_LIST *l = expand_string (++value, 0);
  927.           char *pattern = (char *)string_list (l);
  928.           char *t = temp;
  929.           dispose_words (l);
  930.           temp = remove_pattern (t, pattern, RP_LONG_LEFT);
  931.           free (t);
  932.           free (pattern);
  933.         }
  934.           else
  935.         {
  936.           WORD_LIST *l = expand_string (value, 0);
  937.           char *pattern = string_list (l);
  938.           char *t = temp;
  939.           dispose_words (l);
  940.           temp = remove_pattern (t, pattern, RP_SHORT_LEFT);
  941.           free (t);
  942.           free (pattern);
  943.         }
  944.           break;
  945.  
  946.         case '%':    /* ${param%[%]pattern} */
  947.           if (!value || !*value || !temp || !*temp)
  948.         break;
  949.  
  950.           if (*value == '%')
  951.         {
  952.           WORD_LIST *l = expand_string (++value, 0);
  953.           char *pattern = string_list (l);
  954.           char *t = temp;
  955.           dispose_words (l);
  956.           temp = remove_pattern (t, pattern, RP_LONG_RIGHT);
  957.           free (t);
  958.           free (pattern);
  959.         }
  960.           else
  961.         {
  962.           WORD_LIST *l = expand_string (value, 0);
  963.           char *pattern = string_list (l);
  964.           char *t = temp;
  965.           dispose_words (l);
  966.           temp = remove_pattern (t, pattern, RP_SHORT_RIGHT);
  967.           free (t);
  968.           free (pattern);
  969.         }
  970.           break;
  971.  
  972.         case '-':
  973.           if (var_is_set && !var_is_null)
  974.         {
  975.           /* Do nothing.  Just use the value in temp. */
  976.         }
  977.           else
  978.         {
  979.           WORD_LIST *l = expand_string (value, 0);
  980.           if (temp)
  981.             free (temp);
  982.           temp = (char *)string_list (l);
  983.           dispose_words (l);
  984.           free (value);
  985.         }
  986.           break;
  987.         
  988.         case '=':
  989.           if (var_is_set && !var_is_null)
  990.         {
  991.           /* Do nothing.  The value of temp is desired. */
  992.         }
  993.           else
  994.         {
  995.           if (var_is_special)
  996.             {
  997.               report_error ("$%s: cannot assign in this way", name);
  998.               if (temp)
  999.             free (temp);
  1000.               temp = (char *)NULL;
  1001.             }
  1002.           else
  1003.             {
  1004.               WORD_LIST *l = expand_string (value, 0);
  1005.               if (temp)
  1006.             free (temp);
  1007.               temp = (char *)string_list (l);
  1008.               dispose_words (l);
  1009.               bind_variable (name, temp);
  1010.             }
  1011.         }
  1012.           break;
  1013.         
  1014.         case '?':
  1015.           if (var_is_set && !var_is_null)
  1016.         {
  1017.           /* Do nothing.  The value in temp is desired. */
  1018.         }
  1019.           else
  1020.         {
  1021.           /* The spec says to "print `word' [the right hand
  1022.              side], and exit from the shell", but I think that
  1023.              is brain-damaged beyond all belief.  I also haven't
  1024.              found another shell that does that yet.  I don't
  1025.              think that I ever will find one.  The spec goes on
  1026.              to then say "If `word' is omitted, the message
  1027.              ``parameter null or not set'' is printed", but that
  1028.              is so stupid it is clearly to be ignored.  I just
  1029.              print whatever was found on the right-hand of the
  1030.              statement.  If nothing is found on the right hand
  1031.              side, I am told that I must print a default error
  1032.              message and exit from the shell.  Remember, this
  1033.              isn't my idea.  */
  1034.  
  1035.           if (value && *value)
  1036.             {
  1037.               WORD_LIST *l = expand_string (value, 0);
  1038.               char *temp1 =  string_list (l);
  1039.               fprintf (stderr, "%s: %s\n", temp, temp1 ? temp1 : "");
  1040.               if (temp1)
  1041.             free (temp1);
  1042.               dispose_words (l);
  1043.             }
  1044.           else
  1045.             {
  1046.               if (var_is_special)
  1047.             report_error ("%s: Too few arguments", dollar_vars[0]);
  1048.               else
  1049.             report_error ("%s: parameter not set", name);
  1050.             }
  1051.  
  1052.           if (temp)
  1053.             free (temp);
  1054.  
  1055.           if (!interactive)
  1056.             longjmp (top_level, FORCE_EOF);
  1057.           else
  1058.             longjmp (top_level, DISCARD);
  1059.         }
  1060.           break;
  1061.         
  1062.         case '+':
  1063.           /* We don't want the value of the named variable for anything. */
  1064.           if (temp)
  1065.         free (temp);
  1066.           temp = (char *)NULL;
  1067.         
  1068.           if (var_is_set && !var_is_null)
  1069.         {
  1070.           /* Use the right-hand side.  Pretty weird. */
  1071.           if (value)
  1072.             {
  1073.               WORD_LIST *l = expand_string (value, 0);
  1074.               temp = (char *)string_list (l);
  1075.               dispose_words (l);
  1076.             }
  1077.         }
  1078.           break;
  1079.         }            /* end case on closing character. */
  1080.       free (name);
  1081.       goto add_string;
  1082.     }            /* end case '{' */
  1083.       break;
  1084.     
  1085.       case '(':            /* Do command substitution. */
  1086.     /* We have to extract the contents of this paren substitution. */
  1087.     {
  1088.       char *extract_command_subst ();
  1089.       int old_index = ++sindex;
  1090.  
  1091.       temp = extract_command_subst (string, &old_index);
  1092.       sindex = old_index;
  1093.  
  1094.       goto command_substitution;
  1095.     }
  1096.  
  1097.       default:
  1098.     {
  1099.       /* Find the variable in VARIABLE_LIST. */
  1100.       int old_index = sindex;
  1101.       char *name;
  1102.       SHELL_VAR *var;
  1103.  
  1104.       temp = (char *)NULL;
  1105.  
  1106.       for (;
  1107.            (c = string[sindex]) && (isletter (c) || digit (c) || c == '_');
  1108.            sindex++);
  1109.       name = (char *)substring (string, old_index, sindex);
  1110.  
  1111.       /* If this isn't a variable name, then just output the `$'. */
  1112.       if (!name || !*name) {
  1113.         free (name);
  1114.         temp = savestring ("$");
  1115.         goto add_string;
  1116.       }
  1117.  
  1118.       /* If the variable exists, return its value cell. */
  1119.       var = find_variable (name);
  1120.  
  1121.       if (var && value_cell (var))
  1122.         {
  1123.           temp = savestring (value_cell (var));
  1124.           free (name);
  1125.           goto add_string;
  1126.         }
  1127.       else
  1128.         temp = (char *)NULL;
  1129.  
  1130.       if (var && !temp && function_cell (var))
  1131.         {
  1132.           report_error ("%s: cannot substitute function", name);
  1133.         }
  1134.       else
  1135.         {
  1136.           if (unbound_vars_is_error)
  1137.         {
  1138.           report_error ("%s: unbound variable", name);
  1139.         }
  1140.           else
  1141.         goto add_string;
  1142.         }
  1143.       free (name);
  1144.       longjmp (top_level, DISCARD);
  1145.     }
  1146.       }
  1147.       break;            /* End case '$': */
  1148.  
  1149.     case '`':
  1150.       {
  1151.     sindex++;
  1152.  
  1153.     t_index = sindex;
  1154.     temp = string_extract (string, &t_index, "`");
  1155.     sindex = t_index;
  1156.     de_backslash (temp);
  1157.     
  1158.       command_substitution:
  1159.     /* Pipe the output of executing TEMP into the current shell.
  1160.        Make any and all whitespace (including newlines) be just
  1161.        one space character. */
  1162.     {
  1163.       extern int last_made_pid;
  1164.       int pid, old_pid, fildes[2];
  1165.       
  1166.       if (pipe (fildes) < 0) {
  1167.         report_error ("Can't make pipes for backquote substitution!");
  1168.         goto error_exit;
  1169.       }
  1170.       
  1171.       old_pid = last_made_pid;
  1172. #ifdef JOB_CONTROL
  1173.       {
  1174.         extern int pipeline_pgrp, shell_pgrp;
  1175.         pipeline_pgrp = shell_pgrp;
  1176.         pid = make_child (savestring ("backquote"), 0);
  1177.  
  1178.         stop_making_children ();
  1179.         pipeline_pgrp = 0;
  1180.       }
  1181. #else   /* JOB_CONTROL */
  1182.       pid = make_child (savestring ("backquote"), 0);
  1183. #endif  /* JOB_CONTROL */
  1184.  
  1185.       if (pid < 0) {
  1186.         report_error ("Can't make a child for backquote substitution!");
  1187.       error_exit:
  1188.         if (istring)
  1189.           free (istring);
  1190.         dispose_words (result);
  1191.         return ((WORD_LIST *)NULL);
  1192.       }
  1193.  
  1194.       if (pid == 0)
  1195.         {
  1196. #ifdef JOB_CONTROL
  1197.           set_job_control (0);
  1198. #endif
  1199.           dup2 (fildes[1], 1);
  1200.           close (fildes[1]);
  1201.           close (fildes[0]);
  1202.  
  1203.           exit (parse_and_execute (temp, "backquote"));
  1204.         }
  1205.       else
  1206.         {
  1207.           FILE *istream = fdopen (fildes[0], "r");
  1208.           int start_index = istring_index;
  1209.           int lastc;
  1210.         
  1211.           close (fildes[1]);
  1212.         
  1213.           if (!istream)
  1214.         {
  1215.           report_error ("Can't reopen pipe to command substitution");
  1216.           goto error_exit;
  1217.         }
  1218.  
  1219.           while ((lastc = c) &&
  1220. #ifdef SYSV
  1221.              ((c = sysv_getc (istream)) != EOF)
  1222. #else
  1223.              ((c = getc (istream)) != EOF)
  1224. #endif
  1225.              )
  1226.         {
  1227.  
  1228.           /* Duplicated from `add_character:' */
  1229.           while (istring_index + 1 >= istring_size)
  1230.             istring =
  1231.               (char *)xrealloc (istring,
  1232.                     istring_size += DEFAULT_ARRAY_SIZE);
  1233.  
  1234.           if (quoted)
  1235.             {
  1236.               istring[istring_index++] = QUOTE_CHAR (c);
  1237.             }
  1238.           else if (whitespace (c) || c == '\n')
  1239.             {
  1240.               if (!whitespace (lastc) && lastc != '\n')
  1241.             istring[istring_index++] = ' ';
  1242.             }
  1243.           else
  1244.             {
  1245.               istring[istring_index++] = c;
  1246.             }
  1247.           istring[istring_index] = '\0';
  1248.         }
  1249.           fclose (istream);
  1250.           close (fildes[0]);
  1251.  
  1252.           last_command_exit_value = wait_for (pid);
  1253.           last_made_pid = old_pid;
  1254.  
  1255.           if (temp)
  1256.         free (temp);
  1257.  
  1258.           if (quoted)
  1259.         {
  1260.           if (istring_index > 0 &&
  1261.               DEQUOTE_CHAR (istring[istring_index - 1]) == '\n')
  1262.             istring[--istring_index] = '\0';
  1263.         }
  1264.           else
  1265.         {
  1266.           strip_leading (istring + start_index);
  1267.           strip_trailing (istring + start_index);
  1268.           istring_index = strlen (istring);
  1269.         }
  1270.           if (string[sindex])
  1271.         goto next_character;
  1272.           else
  1273.         continue;
  1274.         }
  1275.     }
  1276.       }
  1277.       
  1278.     case '\\':
  1279.       if (string[sindex + 1] == '\n')
  1280.     {
  1281.       sindex += 2;
  1282.       continue;
  1283.     }
  1284.       else
  1285.     {
  1286.       c = (string[++sindex]);
  1287.       if (quoted && !member (c, slashify_in_quotes))
  1288.         {
  1289.           temp = (char *)xmalloc (3);
  1290.           temp[0] = '\\'; temp[1] = c; temp[2] = '\0';
  1291.           if (c)
  1292.         sindex++;
  1293.           goto add_string;
  1294.         }
  1295.       else
  1296.         {
  1297.           /* This character is quoted, so add it it in quoted mode. */
  1298.           c = QUOTE_CHAR (c);
  1299.           goto add_character;
  1300.         }
  1301.     }
  1302.       
  1303.     case '"':
  1304.       if (quoted)
  1305.     goto add_character;
  1306.       sindex++;
  1307.       {
  1308.     WORD_LIST *tresult = (WORD_LIST *)NULL;
  1309.  
  1310.     t_index = sindex;
  1311.     temp = string_extract_double_quoted (string, &t_index);
  1312.     sindex = t_index;
  1313.  
  1314.     if (string[sindex])
  1315.       sindex++;
  1316.  
  1317.     tresult = expand_string (temp, 1);
  1318.     free (temp);
  1319.  
  1320.     /* expand_string *might* return a list (consider the case of "$@",
  1321.        where it returns "$1", "$2", and so on).  We can't throw away
  1322.        the rest of the list, and we have to make sure each word gets
  1323.        added as quoted.  We test on tresult->next:  if it is non-NULL,
  1324.        we  quote the whole list, save it to a string with string_list,
  1325.        and add that string. We don't need to quote the results of this
  1326.        (and it would be wrong, since that would quote the separators
  1327.        as well), so we go directly to add_string. */
  1328.  
  1329.     if (tresult)
  1330.       {
  1331.         if (tresult->next)
  1332.           {
  1333.         quote_list (tresult);
  1334.         temp = string_list (tresult);
  1335.         dispose_words (tresult);
  1336.         goto add_string;
  1337.           }
  1338.         else
  1339.           {
  1340.         temp = savestring (tresult->word->word);
  1341.         dispose_words (tresult);
  1342.           }
  1343.       }
  1344.     else
  1345.       temp = (char *)NULL;
  1346.  
  1347.       add_quoted_string:
  1348.  
  1349.     if (temp)
  1350.       quote_string (temp);
  1351.     else
  1352.       {
  1353.       add_null_arg:
  1354.         temp = savestring (" ");
  1355.         temp[0] = (unsigned char)QUOTE_CHAR ('\0');
  1356.       }
  1357.     goto add_string;
  1358.       }
  1359.       break;
  1360.       
  1361.     case '\'':
  1362.       if (!quoted)
  1363.     {
  1364.       sindex++;
  1365.  
  1366.       t_index = sindex;
  1367.       temp = string_extract_verbatim (string, &t_index, "'");
  1368.       if (history_expansion)
  1369.         unquote_bang (temp);
  1370.       sindex = t_index;
  1371.  
  1372.       if (string[sindex]) sindex++;
  1373.  
  1374.       if (!*temp)
  1375.         {
  1376.           free (temp);
  1377.           temp = (char *)NULL;
  1378.         }
  1379.  
  1380.       goto add_quoted_string;
  1381.     }
  1382.       else
  1383.     {
  1384.       goto add_character;
  1385.     }
  1386.       break;
  1387.  
  1388.     default:
  1389.  
  1390. add_character:
  1391.       while (istring_index + 1 >= istring_size)
  1392.     istring = (char *)xrealloc (istring,
  1393.                     istring_size += DEFAULT_ARRAY_SIZE);
  1394.       istring[istring_index++] = c;
  1395.       istring[istring_index] = '\0';
  1396.  
  1397. next_character:
  1398.       sindex++;
  1399.     }
  1400.   }
  1401.  
  1402. finished_with_string:
  1403. final_exit:
  1404.   if (istring)
  1405.     {
  1406.       WORD_LIST *temp_list;
  1407.       char *ifs_chars = get_string_value ("IFS");
  1408.  
  1409.       if (quoted || !ifs_chars) ifs_chars = "";
  1410.  
  1411.       temp_list = list_string (istring, ifs_chars, quoted);
  1412.       free (istring);
  1413.       result = (WORD_LIST *)list_append (reverse_list (result), temp_list);
  1414.     }
  1415.   else
  1416.     result = (WORD_LIST *)NULL;
  1417.   return (result);
  1418. }
  1419.  
  1420. /* Do all of the assignments in LIST upto a word which isn't an
  1421.    assignment. */
  1422. WORD_LIST *
  1423. get_rid_of_variable_assignments (list)
  1424.      WORD_LIST *list;
  1425. {
  1426.   WORD_LIST *copy_word_list ();
  1427.   WORD_LIST *orig = list;
  1428.  
  1429.   while (list)
  1430.     if (!list->word->assignment)
  1431.       {
  1432.     WORD_LIST *new_list = copy_word_list (list);
  1433.     dispose_words (orig);
  1434.     return (new_list);
  1435.       }
  1436.     else
  1437.       {
  1438.     do_assignment (list->word->word);
  1439.     list = list->next;
  1440.       }
  1441.   dispose_words (orig);
  1442.   return ((WORD_LIST *)NULL);
  1443. }
  1444.  
  1445. /* Check and handle the case where there are some variable assignments
  1446.    in LIST which go into the environment for this command. */
  1447. WORD_LIST *
  1448. get_rid_of_environment_assignments (list)
  1449.      WORD_LIST *list;
  1450. {
  1451.   register WORD_LIST *tlist = list;
  1452.   register WORD_LIST *new_list;
  1453.   WORD_LIST *copy_word_list (), *copy_word ();
  1454.  
  1455.   while (tlist)
  1456.     {
  1457.       if (!tlist->word->assignment) goto make_assignments;
  1458.       tlist = tlist->next;
  1459.     }
  1460.   /* Since all of the assignments are variable assignments. */
  1461.   return (list);
  1462.  
  1463. make_assignments:
  1464.   tlist = list;
  1465.   while (tlist)
  1466.     {
  1467.       if (tlist->word->assignment)
  1468.     assign_in_env (tlist->word->word);
  1469.       else
  1470.     {
  1471.       if (!place_keywords_in_env)
  1472.         {
  1473.           new_list = copy_word_list (tlist);
  1474.           dispose_words (list);
  1475.           return (new_list);
  1476.         }
  1477.     }
  1478.       tlist = tlist->next;
  1479.     }
  1480.  
  1481.   /* We got all of the keywords assigned.  Now return the remainder
  1482.      of the words. */
  1483.   {
  1484.     register WORD_LIST *new_list = (WORD_LIST *)NULL;
  1485.  
  1486.     tlist = list;
  1487.  
  1488.     /* Skip the ones at the start. */
  1489.     while (tlist && tlist->word->assignment)
  1490.       tlist = tlist->next;
  1491.  
  1492.     /* If we placed all the keywords in the list into the environment,
  1493.        then remove them from the output list. */
  1494.     if (place_keywords_in_env)
  1495.       {
  1496.     while (tlist)
  1497.       {
  1498.         if (!tlist->word->assignment)
  1499.           new_list = make_word_list (copy_word (tlist->word), new_list);
  1500.         tlist = tlist->next;
  1501.       }
  1502.     new_list = (WORD_LIST *)reverse_list (new_list);
  1503.       }
  1504.     else
  1505.       {
  1506.     /* Just copy the list. */
  1507.     new_list = copy_word_list (tlist);
  1508.       }
  1509.     dispose_words (list);
  1510.     return (new_list);
  1511.   }
  1512. }
  1513.  
  1514. /* Take the list of words in LIST and do the various substitutions.  Return
  1515.    a new list of words which is the expanded list, and without things like
  1516.    variable assignments. */
  1517. WORD_LIST *
  1518. expand_words (list)
  1519.      WORD_LIST *list;
  1520. {
  1521.   WORD_LIST *expand_words_1 ();
  1522.   return (expand_words_1 (list, 1));
  1523. }
  1524.  
  1525. /* Same as expand_words (), but doesn't hack variable or environment
  1526.    variables. */
  1527. WORD_LIST *
  1528. expand_words_no_vars (list)
  1529.      WORD_LIST *list;
  1530. {
  1531.   WORD_LIST *expand_words_1 ();
  1532.   return (expand_words_1 (list, 0));
  1533. }
  1534.  
  1535. /* Non-zero means to allow unmatched globbed filenames to expand to
  1536.    a null file. */
  1537. static int allow_null_glob_expansion = 0;
  1538.  
  1539. /* The workhorse for expand_words () and expand_words_no_var ().
  1540.    First arg is LIST, a WORD_LIST of words.
  1541.    Second arg DO_VARS is non-zero if you want to do environment and
  1542.    variable assignments, else zero. */
  1543. WORD_LIST *
  1544. expand_words_1 (list, do_vars)
  1545.      WORD_LIST *list;
  1546.      int do_vars;
  1547. {
  1548.   register WORD_LIST *tlist, *new_list = (WORD_LIST *)NULL;
  1549.   WORD_LIST *orig_list;
  1550.   extern int no_brace_expansion;
  1551.  
  1552.   tlist = (WORD_LIST *)copy_word_list (list);
  1553.  
  1554.   if (do_vars)
  1555.     {
  1556.       /* Handle the case where the arguments are assignments for
  1557.      the environment of this command. */
  1558.       tlist = get_rid_of_environment_assignments (tlist);
  1559.  
  1560.       /* Handle the case where the arguments are all variable assignments. */
  1561.       tlist = get_rid_of_variable_assignments (tlist);
  1562.     }
  1563.  
  1564.   /* Begin expanding the words that remain.  The expansions take place on
  1565.      things that aren't really variable assignments. */
  1566.  
  1567.   if (!tlist)
  1568.     return ((WORD_LIST *)NULL);
  1569.  
  1570.   /* Do brace expansion on this word if there are any brace characters
  1571.      in the string. */
  1572.   if (!no_brace_expansion)
  1573.     {
  1574.       extern char **brace_expand ();
  1575.       register char **expansions;
  1576.       WORD_LIST *braces = (WORD_LIST *)NULL;
  1577.       int index;
  1578.     
  1579.       orig_list = tlist;
  1580.  
  1581.       while (tlist)
  1582.     {
  1583.       expansions = brace_expand (tlist->word->word);
  1584.  
  1585.       for (index = 0; expansions[index]; index++)
  1586.         {
  1587.           braces = make_word_list (make_word (expansions[index]), braces);
  1588.           free (expansions[index]);
  1589.         }
  1590.       free (expansions);
  1591.  
  1592.       tlist = tlist->next;
  1593.     }
  1594.       dispose_words (orig_list);
  1595.       tlist = (WORD_LIST *)reverse_list (braces);
  1596.     }
  1597.  
  1598.   orig_list = tlist;
  1599.  
  1600.   /* We do tilde expansion, but only if globbing is enabled.
  1601.      I guess this is the right thing. */
  1602.   while (tlist)
  1603.     {
  1604.       if (!disallow_filename_globbing && !tlist->word->quoted &&
  1605.       (*(tlist->word->word) == '~'))
  1606.     {
  1607.       char *tilde_expand (), *tt = tlist->word->word;
  1608.       tlist->word->word = tilde_expand (tlist->word->word);
  1609.       free (tt);
  1610.     }
  1611.  
  1612.       new_list =
  1613.     (WORD_LIST *)list_append
  1614.       (reverse_list (expand_word_internal (tlist->word, 0)), new_list);
  1615.  
  1616.       tlist = tlist->next;
  1617.     }
  1618.  
  1619.   new_list = (WORD_LIST *)reverse_list (new_list);
  1620.  
  1621.   dispose_words (orig_list);
  1622.  
  1623.   /* Okay, we're almost done.  Now let's just do some filename
  1624.      globbing. */
  1625.   {
  1626.     char **shell_glob_filename (), **temp_list = (char **)NULL;
  1627.     register int list_index;
  1628.     WORD_LIST *glob_list;
  1629.  
  1630.     orig_list = (WORD_LIST *)NULL;
  1631.     tlist = new_list;
  1632.  
  1633.     if (!disallow_filename_globbing)
  1634.       {
  1635.     while (tlist)
  1636.       {
  1637.         /* If the word isn't quoted, then glob it. */
  1638.         if (!tlist->word->quoted && glob_pattern_p (tlist->word->word))
  1639.           {
  1640.         temp_list = shell_glob_filename (tlist->word->word);
  1641.  
  1642.         /* Fix the hi-bits. (This is how we quoted
  1643.            special characters.) */
  1644.         {
  1645.           register char *t = tlist->word->word;
  1646.           while (*t) *t++ &= 0x7f;
  1647.         }
  1648.  
  1649.         /* Handle error cases.
  1650.            I don't think we should report errors like "No such file
  1651.            or directory".  However, I would like to report errors
  1652.            like "Read failed". */
  1653.          
  1654.         if (temp_list == (char **)-1)
  1655.           {
  1656.             /* file_error (tlist->word->word); */
  1657.             return (new_list);
  1658.           }
  1659.  
  1660.         if (!temp_list) abort ();
  1661.  
  1662.         /* Sort the returned names.  Maybe this should be done in
  1663.            glob_filename (). */
  1664.         {
  1665.           int qsort_string_compare ();
  1666.           qsort (temp_list, array_len (temp_list),
  1667.              sizeof (char *), qsort_string_compare);
  1668.         }
  1669.  
  1670.         /* Make the array into a word list. */
  1671.         glob_list = (WORD_LIST *)NULL;
  1672.         for (list_index = 0; temp_list[list_index]; list_index++)
  1673.           glob_list =
  1674.             make_word_list (make_word (temp_list[list_index]), glob_list);
  1675.       
  1676.         if (glob_list)
  1677.           orig_list = (WORD_LIST *)list_append (glob_list, orig_list);
  1678.         else
  1679.           if (!allow_null_glob_expansion)
  1680.             orig_list =
  1681.               make_word_list (copy_word (tlist->word), orig_list);
  1682.           }
  1683.         else
  1684.           {
  1685.         /* Fix the hi-bits. (This is how we quoted special
  1686.            characters.) */
  1687.         register char *t = tlist->word->word;
  1688.         while (*t) *t++ &= 0x7f;
  1689.         orig_list = make_word_list (copy_word (tlist->word), orig_list);
  1690.           }
  1691.  
  1692.         free_array (temp_list);
  1693.         temp_list = (char **)NULL;
  1694.  
  1695.         tlist = tlist->next;
  1696.       }
  1697.     dispose_words (new_list);
  1698.     new_list = orig_list;
  1699.       }
  1700.     else
  1701.       {
  1702.     /* Fix the hi-bits. (This is how we quoted special characters.) */
  1703.     register WORD_LIST *wl = new_list;
  1704.     register char *wp;
  1705.     while (wl) {
  1706.       wp = wl->word->word;
  1707.       while (*wp) *wp++ &= 0x7f;
  1708.       wl = wl->next;
  1709.     }
  1710.     return (new_list);
  1711.       }
  1712.   }
  1713.   return (WORD_LIST *)(reverse_list (new_list));
  1714. }
  1715.  
  1716. /* Call the glob library to do globbing on PATHNAME.
  1717.    PATHNAME can contain characters with the hi bit set; this indicates
  1718.    that the character is to be quoted.  We quote it here. */
  1719. char **
  1720. shell_glob_filename (pathname)
  1721.      char *pathname;
  1722. {
  1723.   extern char **glob_filename ();
  1724.   register int i, j;
  1725.   char *temp = (char *)alloca (2 * strlen (pathname) + 1);
  1726.  
  1727.   for (i = j = 0; pathname[i]; i++, j++)
  1728.     {
  1729.       if ((unsigned char)pathname[i] > 0x7f)
  1730.     temp[j++] = '\\';
  1731.   
  1732.       temp[j] = (unsigned char)pathname[i] & 0x7f;
  1733.     }
  1734.   temp[j] = '\0';
  1735.  
  1736.   return (glob_filename (temp));
  1737. }
  1738.  
  1739. /* An alist of name.function for each special variable.  Most of the functions
  1740.    don't do much, and in fact, this would be faster with a switch statement,
  1741.    but by the end of this file, I am sick of switch statements. */
  1742.  
  1743. /* The functions that get called. */
  1744. int sv_path (), sv_mail (), sv_terminal (), sv_histsize (), sv_uids (),
  1745.   sv_ignoreeof (), sv_glob_dot_filenames (), sv_histchars (), sv_nolinks (),
  1746.   sv_hostname_completion_file (), sv_allow_null_glob_expansion (),
  1747.   sv_history_control (), sv_noclobber ();
  1748.  
  1749. #ifdef JOB_CONTROL
  1750. extern int sv_notify ();
  1751. #endif
  1752.  
  1753.  
  1754. struct name_and_function {
  1755.   char *name;
  1756.   Function *function;
  1757. } special_vars[] = {
  1758.   { "PATH", sv_path },
  1759.   { "MAIL", sv_mail },
  1760.   { "MAILPATH", sv_mail },
  1761.   { "MAILCHECK", sv_mail },
  1762.   { "TERMCAP", sv_terminal },
  1763.   { "TERM", sv_terminal },
  1764.   { "HISTSIZE", sv_histsize },
  1765.   { "EUID", sv_uids},
  1766.   { "UID", sv_uids},
  1767.   { "ignoreeof", sv_ignoreeof },
  1768. #ifdef JOB_CONTROL
  1769.   { "notify", sv_notify },
  1770. #endif  /* JOB_CONTROL */
  1771.   { "glob_dot_filenames", sv_glob_dot_filenames },
  1772.   { "allow_null_glob_expansion", sv_allow_null_glob_expansion },
  1773.   { "histchars", sv_histchars },
  1774.   { "nolinks", sv_nolinks },
  1775.   { "hostname_completion_file", sv_hostname_completion_file },
  1776.   { "history_control", sv_history_control },
  1777.   { "noclobber", sv_noclobber },
  1778.   { (char *)0x00, (Function *)0x00 }
  1779. };
  1780.  
  1781. /* The variable in NAME has just had its state changed.  Check to see if it
  1782.    is one of the special ones where something special happens. */
  1783. stupidly_hack_special_variables (name)
  1784.      char *name;
  1785. {
  1786.   int i = 0;
  1787.  
  1788.   while (special_vars[i].name)
  1789.     {
  1790.       if (strcmp (special_vars[i].name, name) == 0)
  1791.     {
  1792.       (*(special_vars[i].function)) (name);
  1793.       return;
  1794.     }
  1795.       i++;
  1796.     }
  1797. }
  1798.  
  1799. /* Set/unset noclobber. */
  1800. sv_noclobber (name)
  1801.      char *name;
  1802. {
  1803.   extern int noclobber;
  1804.  
  1805.   if (find_variable (name))
  1806.     noclobber = 1;
  1807.   else
  1808.     noclobber = 0;
  1809. }
  1810.  
  1811. /* What to do just after the PATH variable has changed. */
  1812. sv_path ()
  1813. {
  1814.   /* hash -r */
  1815.   WORD_LIST *args;
  1816.  
  1817.   args = make_word_list (make_word ("-r"), NULL);
  1818.   hash_builtin (args);
  1819.   dispose_words (args);
  1820. }
  1821.  
  1822. /* What to do just after one of the MAILxxxx variables has changed.  NAME
  1823.    is the name of the variable.  */
  1824. sv_mail (name)
  1825.      char *name;
  1826. {
  1827.   /* If the time interval for checking the files has changed, then
  1828.      reset the mail timer.  Otherwise, one of the pathname vars
  1829.      to the users mailbox has changed, so rebuild the array of
  1830.      filenames. */
  1831.   if (strcmp (name, "MAILCHECK") == 0)
  1832.     reset_mail_timer ();
  1833.   else
  1834.     {
  1835.       if ((strcmp (name, "MAIL") == 0) || (strcmp (name, "MAILPATH") == 0))
  1836.     {
  1837.       free_mail_files ();
  1838.       remember_mail_dates ();
  1839.     }
  1840.     }
  1841. }
  1842.  
  1843. /* What to do just after one of the TERMxxx variables has changed.
  1844.    If we are an interactive shell, then try to reset the terminal
  1845.    information in readline. */
  1846. sv_terminal (name)
  1847.      char *name;
  1848. {
  1849.   extern int interactive;
  1850.  
  1851.   if (interactive)
  1852.     rl_reset_terminal (get_string_value ("TERM"));
  1853. }
  1854.  
  1855. /* What to do after the HISTSIZE variable changes.
  1856.    If there is a value for this variable (and it is numeric), then stifle
  1857.    the history.  Otherwise, if there is NO value for this variable,
  1858.    unstifle the history. */
  1859. sv_histsize (name)
  1860.      char *name;
  1861. {
  1862.   char *temp = get_string_value (name);
  1863.   
  1864.   if (temp)
  1865.     {
  1866.       int num;
  1867.       if (sscanf (temp, "%d", &num) == 1)
  1868.     stifle_history (num);
  1869.     }
  1870.   else
  1871.     unstifle_history ();
  1872. }
  1873.  
  1874. /* A nit for picking at history saving.
  1875.    Value of 0 means save all lines parsed by the shell on the history.
  1876.    Value of 1 means save all lines that do not start with a space.
  1877.    Value of 2 means save all lines that do not match the last line saved. */
  1878. int history_control = 0;
  1879.  
  1880. /* What to do after the HISTORY_CONTROL variable changes. */
  1881. sv_history_control (name)
  1882.      char *name;
  1883. {
  1884.   char *temp = get_string_value (name);
  1885.  
  1886.   history_control = 0;
  1887.  
  1888.   if (temp && *temp)
  1889.     {
  1890.       if (strcmp (temp, "ignorespace") == 0)
  1891.     history_control = 1;
  1892.       else if (strcmp (temp, "ignoredups") == 0)
  1893.     history_control = 2;
  1894.     }
  1895. }
  1896.  
  1897. /* If the variable exists, then the value of it can be the number
  1898.    of times we actually ignore the EOF.  The default is small,
  1899.    (smaller than csh, anyway). */
  1900. sv_ignoreeof (name)
  1901.      char *name;
  1902. {
  1903.   extern int eof_encountered, eof_encountered_limit;
  1904.   char *temp = get_string_value (name);
  1905.   int new_limit;
  1906.  
  1907.   eof_encountered = 0;
  1908.  
  1909.   if (temp && (sscanf (temp, "%d", &new_limit) == 1))
  1910.     eof_encountered_limit = new_limit;
  1911.   else
  1912.     eof_encountered_limit = 10; /* csh uses 26. */
  1913. }
  1914.  
  1915. /* Control whether * matches .files in globbing.  Yechh. */
  1916.  
  1917. sv_glob_dot_filenames (name)
  1918.      char *name;
  1919. {
  1920.   extern int noglob_dot_filenames;
  1921.  
  1922.   noglob_dot_filenames = !(find_variable (name));
  1923. }
  1924.  
  1925. /* Setting/unsetting of the history expansion character. */
  1926. char old_history_expansion_char = '!';
  1927. char old_history_comment_char = '#';
  1928. char old_history_subst_char = '^';
  1929.  
  1930. sv_histchars (name)
  1931.      char *name;
  1932. {
  1933.   extern int history_expansion_char;
  1934.   extern int history_comment_char;
  1935.   extern int history_subst_char;
  1936.   char *temp = get_string_value (name);
  1937.  
  1938.   if (temp)
  1939.     {
  1940.       old_history_expansion_char = history_expansion_char;
  1941.       history_expansion_char = *temp;
  1942.  
  1943.       if (temp[1])
  1944.     {
  1945.       old_history_subst_char = history_subst_char;
  1946.       history_subst_char = temp[1];
  1947.  
  1948.       if (temp[2])
  1949.         {
  1950.           old_history_comment_char = history_comment_char;
  1951.           history_comment_char = temp[2];
  1952.         }
  1953.     }
  1954.     }
  1955.   else
  1956.     {
  1957.       history_expansion_char = '!';
  1958.       history_subst_char = '^';
  1959.       history_comment_char = '#';
  1960.     }
  1961. }
  1962.  
  1963. #ifdef JOB_CONTROL
  1964. /* Job notification feature desired? */
  1965. sv_notify (name)
  1966.      char *name;
  1967. {
  1968.   extern int asynchronous_notification;
  1969.  
  1970.   if (get_string_value (name))
  1971.     asynchronous_notification = 1;
  1972.   else
  1973.     asynchronous_notification = 0;
  1974. }
  1975. #endif  /* JOB_CONTROL */
  1976.  
  1977. /* If the variable `nolinks' exists, it specifies that symbolic links are
  1978.    not to be followed in `cd' commands. */
  1979. sv_nolinks (name)
  1980.      char *name;
  1981. {
  1982.   extern int follow_symbolic_links;
  1983.  
  1984.   follow_symbolic_links = !find_variable (name);
  1985. }
  1986.  
  1987. /* Don't let users hack the user id variables. */
  1988. sv_uids ()
  1989. {
  1990.   int uid = getuid ();
  1991.   int euid = geteuid ();
  1992.   char buff[10];
  1993.   register SHELL_VAR *v;
  1994.  
  1995.   sprintf (buff, "%d", uid);
  1996.   v = find_variable ("UID");
  1997.   if (v)
  1998.     v->attributes &= ~att_readonly;
  1999.  
  2000.   v = bind_variable ("UID", buff);
  2001.   v->attributes |= att_readonly;
  2002.  
  2003.   sprintf (buff, "%d", euid);
  2004.   v = find_variable ("EUID");
  2005.   if (v)
  2006.     v->attributes &= ~att_readonly;
  2007.  
  2008.   v = bind_variable ("EUID", buff);
  2009.   v->attributes |= att_readonly;
  2010. }
  2011.  
  2012. sv_hostname_completion_file ()
  2013. {
  2014.   extern int hostname_list_initialized;
  2015.  
  2016.   hostname_list_initialized = 0;
  2017. }
  2018.  
  2019. sv_allow_null_glob_expansion (name)
  2020. {
  2021.   allow_null_glob_expansion = (int)find_variable (name);
  2022. }
  2023.  
  2024.