当前位置: 首页 > 工具软件 > tty-prompt > 使用案例 >

bash源代码分析----yylex函数分析和decode_prompt_string函数负责分析并返回提示字符串

单于淇
2023-12-01

 yylex函数调用位置:y.tab.c中

if (yychar == YYEMPTY)
    {
      YYDPRINTF ((stderr, "Reading a token: "));
      yychar = YYLEX;
    }


/* Function for yyparse to call.  yylex keeps track of
   the last two tokens read, and calls read_token.  */
static int
yylex ()
{
  if (interactive && (current_token == 0 || current_token == '\n'))
    {
      /* Before we print a prompt, we might have to check mailboxes.
     We do this only if it is time to do so. Notice that only here
     is the mail alarm reset; nothing takes place in check_mail ()
     except the checking of mail.  Please don't change this. */
      if (prompt_is_ps1 && parse_and_execute_level == 0 && time_to_check_mail ())
    {
      check_mail ();
      reset_mail_timer ();
    }

      /* Avoid printing a prompt if we're not going to read anything, e.g.
     after resetting the parser with read_token (RESET). */
      if (token_to_read == 0 && SHOULD_PROMPT ())
    prompt_again ();
    }

  two_tokens_ago = token_before_that;
  token_before_that = last_read_token;
  last_read_token = current_token;
  current_token = read_token (READ);

  if ((parser_state & PST_EOFTOKEN) && current_token == shell_eof_token)
    {
      current_token = yacc_EOF;
      if (bash_input.type == st_string)
    rewind_input_string ();
    }
  parser_state &= ~PST_EOFTOKEN;

  return (current_token);
}

 

 


/* Issue a prompt, or prepare to issue a prompt when the next character
   is read. */
static void
prompt_again ()
{
  char *temp_prompt;

  if (interactive == 0 || expanding_alias ())    /* XXX */
    return;

  ps1_prompt = get_string_value ("PS1");
  ps2_prompt = get_string_value ("PS2");

  if (!prompt_string_pointer)
    prompt_string_pointer = &ps1_prompt;

  temp_prompt = *prompt_string_pointer
            ? decode_prompt_string (*prompt_string_pointer)
            : (char *)NULL;

  if (temp_prompt == 0)
    {
      temp_prompt = (char *)xmalloc (1);
      temp_prompt[0] = '\0';
    }

  current_prompt_string = *prompt_string_pointer;
  prompt_string_pointer = &ps2_prompt;

#if defined (READLINE)
  if (!no_line_editing)
    {
      FREE (current_readline_prompt);
      current_readline_prompt = temp_prompt;
    }
  else
#endif    /* READLINE */
    {
      FREE (current_decoded_prompt);
      current_decoded_prompt = temp_prompt;
    }
}

Value returned is $32 = 0xa9c608 "[root@localhost bash-4.2.53]# "  

上面是gdb调试信息,decode_prompt_string函数finish后返回值。

temp_prompt = 0x6faf88 "[root@localhost bash-4.2.53]# "


/* Return a string which will be printed as a prompt.  The string
   may contain special characters which are decoded as follows:

    \a    bell (ascii 07)
    \d    the date in Day Mon Date format
    \e    escape (ascii 033)
    \h    the hostname up to the first `.'
    \H    the hostname
    \j    the number of active jobs
    \l    the basename of the shell's tty device name
    \n    CRLF
    \r    CR
    \s    the name of the shell
    \t    the time in 24-hour hh:mm:ss format
    \T    the time in 12-hour hh:mm:ss format
    \@    the time in 12-hour hh:mm am/pm format
    \A    the time in 24-hour hh:mm format
    \D{fmt}    the result of passing FMT to strftime(3)
    \u    your username
    \v    the version of bash (e.g., 2.00)
    \V    the release of bash, version + patchlevel (e.g., 2.00.0)
    \w    the current working directory
    \W    the last element of $PWD
    \!    the history number of this command
    \#    the command number of this command
    \$    a $ or a # if you are root
    \nnn    character code nnn in octal
    \\    a backslash
    \[    begin a sequence of non-printing chars
    \]    end a sequence of non-printing chars
*/
#define PROMPT_GROWTH 48
char *
decode_prompt_string (string)
     char *string;
{
  WORD_LIST *list;
  char *result, *t;
  struct dstack save_dstack;
  int last_exit_value, last_comsub_pid;
#if defined (PROMPT_STRING_DECODE)
  int result_size, result_index;
  int c, n, i;
  char *temp, octal_string[4];
  struct tm *tm;  
  time_t the_time;
  char timebuf[128];
  char *timefmt;

  result = (char *)xmalloc (result_size = PROMPT_GROWTH);
  result[result_index = 0] = 0;
  temp = (char *)NULL;

  while (c = *string++)
    {
      if (posixly_correct && c == '!')
    {
      if (*string == '!')
        {
          temp = savestring ("!");
          goto add_string;
        }
      else
        {
#if !defined (HISTORY)
        temp = savestring ("1");
#else /* HISTORY */
        temp = itos (history_number ());
#endif /* HISTORY */
        string--;    /* add_string increments string again. */
        goto add_string;
        }
    }
      if (c == '\\')
    {
      c = *string;

      switch (c)
        {
        case '0':
        case '1':
        case '2':
        case '3':
        case '4':
        case '5':
        case '6':
        case '7':
          strncpy (octal_string, string, 3);
          octal_string[3] = '\0';

          n = read_octal (octal_string);
          temp = (char *)xmalloc (3);

          if (n == CTLESC || n == CTLNUL)
        {
          temp[0] = CTLESC;
          temp[1] = n;
          temp[2] = '\0';
        }
          else if (n == -1)
        {
          temp[0] = '\\';
          temp[1] = '\0';
        }
          else
        {
          temp[0] = n;
          temp[1] = '\0';
        }

          for (c = 0; n != -1 && c < 3 && ISOCTAL (*string); c++)
        string++;

          c = 0;        /* tested at add_string: */
          goto add_string;

        case 'd':
        case 't':
        case 'T':
        case '@':
        case 'A':
          /* Make the current time/date into a string. */
          (void) time (&the_time);
#if defined (HAVE_TZSET)
          sv_tz ("TZ");        /* XXX -- just make sure */
#endif
          tm = localtime (&the_time);

          if (c == 'd')
        n = strftime (timebuf, sizeof (timebuf), "%a %b %d", tm);
          else if (c == 't')
        n = strftime (timebuf, sizeof (timebuf), "%H:%M:%S", tm);
          else if (c == 'T')
        n = strftime (timebuf, sizeof (timebuf), "%I:%M:%S", tm);
          else if (c == '@')
        n = strftime (timebuf, sizeof (timebuf), "%I:%M %p", tm);
          else if (c == 'A')
        n = strftime (timebuf, sizeof (timebuf), "%H:%M", tm);

          if (n == 0)
        timebuf[0] = '\0';
          else
        timebuf[sizeof(timebuf) - 1] = '\0';

          temp = savestring (timebuf);
          goto add_string;

        case 'D':        /* strftime format */
          if (string[1] != '{')        /* } */
        goto not_escape;

          (void) time (&the_time);
          tm = localtime (&the_time);
          string += 2;            /* skip { */
          timefmt = xmalloc (strlen (string) + 3);
          for (t = timefmt; *string && *string != '}'; )
        *t++ = *string++;
          *t = '\0';
          c = *string;    /* tested at add_string */
          if (timefmt[0] == '\0')
        {
          timefmt[0] = '%';
          timefmt[1] = 'X';    /* locale-specific current time */
          timefmt[2] = '\0';
        }
          n = strftime (timebuf, sizeof (timebuf), timefmt, tm);
          free (timefmt);

          if (n == 0)
        timebuf[0] = '\0';
          else
        timebuf[sizeof(timebuf) - 1] = '\0';

          if (promptvars || posixly_correct)
        /* Make sure that expand_prompt_string is called with a
           second argument of Q_DOUBLE_QUOTES if we use this
           function here. */
        temp = sh_backslash_quote_for_double_quotes (timebuf);
          else
        temp = savestring (timebuf);
          goto add_string;
          
        case 'n':
          temp = (char *)xmalloc (3);
          temp[0] = no_line_editing ? '\n' : '\r';
          temp[1] = no_line_editing ? '\0' : '\n';
          temp[2] = '\0';
          goto add_string;

        case 's':
          temp = base_pathname (shell_name);
          temp = savestring (temp);
          goto add_string;

        case 'v':
        case 'V':
          temp = (char *)xmalloc (16);
          if (c == 'v')
        strcpy (temp, dist_version);
          else
        sprintf (temp, "%s.%d", dist_version, patch_level);
          goto add_string;

        case 'w':
        case 'W':
          {
        /* Use the value of PWD because it is much more efficient. */
        char t_string[PATH_MAX];
        int tlen;

        temp = get_string_value ("PWD");

        if (temp == 0)
          {
            if (getcwd (t_string, sizeof(t_string)) == 0)
              {
            t_string[0] = '.';
            tlen = 1;
              }
            else
              tlen = strlen (t_string);
          }
        else
          {
            tlen = sizeof (t_string) - 1;
            strncpy (t_string, temp, tlen);
          }
        t_string[tlen] = '\0';

#if defined (MACOSX)
        /* Convert from "fs" format to "input" format */
        temp = fnx_fromfs (t_string, strlen (t_string));
        if (temp != t_string)
          strcpy (t_string, temp);
#endif

#define ROOT_PATH(x)    ((x)[0] == '/' && (x)[1] == 0)
#define DOUBLE_SLASH_ROOT(x)    ((x)[0] == '/' && (x)[1] == '/' && (x)[2] == 0)
        /* Abbreviate \W as ~ if $PWD == $HOME */
        if (c == 'W' && (((t = get_string_value ("HOME")) == 0) || STREQ (t, t_string) == 0))
          {
            if (ROOT_PATH (t_string) == 0 && DOUBLE_SLASH_ROOT (t_string) == 0)
              {
            t = strrchr (t_string, '/');
            if (t)
              memmove (t_string, t + 1, strlen (t));    /* strlen(t) to copy NULL */
              }
          }
#undef ROOT_PATH
#undef DOUBLE_SLASH_ROOT
        else
          /* polite_directory_format is guaranteed to return a string
             no longer than PATH_MAX - 1 characters. */
          strcpy (t_string, polite_directory_format (t_string));

        temp = trim_pathname (t_string, PATH_MAX - 1);
        /* If we're going to be expanding the prompt string later,
           quote the directory name. */
        if (promptvars || posixly_correct)
          /* Make sure that expand_prompt_string is called with a
             second argument of Q_DOUBLE_QUOTES if we use this
             function here. */
          temp = sh_backslash_quote_for_double_quotes (t_string);
        else
          temp = savestring (t_string);

        goto add_string;
          }

        case 'u':
          if (current_user.user_name == 0)
        get_current_user_info ();
          temp = savestring (current_user.user_name);
          goto add_string;

        case 'h':
        case 'H':
          temp = savestring (current_host_name);
          if (c == 'h' && (t = (char *)strchr (temp, '.')))
        *t = '\0';
          goto add_string;

        case '#':
          temp = itos (current_command_number);
          goto add_string;

        case '!':
#if !defined (HISTORY)
          temp = savestring ("1");
#else /* HISTORY */
          temp = itos (history_number ());
#endif /* HISTORY */
          goto add_string;

        case '$':
          t = temp = (char *)xmalloc (3);
          if ((promptvars || posixly_correct) && (current_user.euid != 0))
        *t++ = '\\';
          *t++ = current_user.euid == 0 ? '#' : '$';
          *t = '\0';
          goto add_string;

        case 'j':
          temp = itos (count_all_jobs ());
          goto add_string;

        case 'l':
#if defined (HAVE_TTYNAME)
          temp = (char *)ttyname (fileno (stdin));
          t = temp ? base_pathname (temp) : "tty";
          temp = savestring (t);
#else
          temp = savestring ("tty");
#endif /* !HAVE_TTYNAME */
          goto add_string;

#if defined (READLINE)
        case '[':
        case ']':
          if (no_line_editing)
        {
          string++;
          break;
        }
          temp = (char *)xmalloc (3);
          n = (c == '[') ? RL_PROMPT_START_IGNORE : RL_PROMPT_END_IGNORE;
          i = 0;
          if (n == CTLESC || n == CTLNUL)
        temp[i++] = CTLESC;
          temp[i++] = n;
          temp[i] = '\0';
          goto add_string;
#endif /* READLINE */

        case '\\':
        case 'a':
        case 'e':
        case 'r':
          temp = (char *)xmalloc (2);
          if (c == 'a')
        temp[0] = '\07';
          else if (c == 'e')
        temp[0] = '\033';
          else if (c == 'r')
        temp[0] = '\r';
          else            /* (c == '\\') */
            temp[0] = c;
          temp[1] = '\0';
          goto add_string;

        default:
not_escape:
          temp = (char *)xmalloc (3);
          temp[0] = '\\';
          temp[1] = c;
          temp[2] = '\0';

        add_string:
          if (c)
        string++;
          result =
        sub_append_string (temp, result, &result_index, &result_size);
          temp = (char *)NULL; /* Freed in sub_append_string (). */
          result[result_index] = '\0';
          break;
        }
    }
      else
    {
      RESIZE_MALLOCED_BUFFER (result, result_index, 3, result_size, PROMPT_GROWTH);
      result[result_index++] = c;
      result[result_index] = '\0';
    }
    }
#else /* !PROMPT_STRING_DECODE */
  result = savestring (string);
#endif /* !PROMPT_STRING_DECODE */

  /* Save the delimiter stack and point `dstack' to temp space so any
     command substitutions in the prompt string won't result in screwing
     up the parser's quoting state. */
  save_dstack = dstack;
  dstack = temp_dstack;
  dstack.delimiter_depth = 0;

  /* Perform variable and parameter expansion and command substitution on
     the prompt string. */
  if (promptvars || posixly_correct)
    {
      last_exit_value = last_command_exit_value;
      last_comsub_pid = last_command_subst_pid;
      list = expand_prompt_string (result, Q_DOUBLE_QUOTES, 0);
      free (result);
      result = string_list (list);
      dispose_words (list);
      last_command_exit_value = last_exit_value;
      last_command_subst_pid = last_comsub_pid;
    }
  else
    {
      t = dequote_string (result);
      free (result);
      result = t;
    }

  dstack = save_dstack;

  return (result);
}

 

 

bash-4.2.53.tar.gz源代码和可执行文件下载:

https://download.csdn.net/download/sitelist/15710786

 类似资料: