Skip to content
Snippets Groups Projects
macro.cc 129 KiB
Newer Older
Neil Booth's avatar
Neil Booth committed
/* Part of CPP library.  (Macro and #define handling.)
Jakub Jelinek's avatar
Jakub Jelinek committed
   Copyright (C) 1986-2025 Free Software Foundation, Inc.
Zack Weinberg's avatar
Zack Weinberg committed
   Written by Per Bothner, 1994.
   Based on CCCP program by Paul Rubin, June 1986
   Adapted to ANSI C, Richard Stallman, Jan 1987

This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation; either version 3, or (at your option) any
Zack Weinberg's avatar
Zack Weinberg committed
later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; see the file COPYING3.  If not see
<http://www.gnu.org/licenses/>.
Zack Weinberg's avatar
Zack Weinberg committed

 In other words, you are welcome to use, share and improve this program.
 You are forbidden to forbid anyone else to use, share and improve
 what you give them.   Help stamp out software-hoarding!  */

#include "config.h"
#include "system.h"
#include "cpplib.h"
#include "internal.h"
Zack Weinberg's avatar
Zack Weinberg committed

Neil Booth's avatar
Neil Booth committed
typedef struct macro_arg macro_arg;
/* This structure represents the tokens of a macro argument.  These
   tokens can be macro themselves, in which case they can be either
   expanded or unexpanded.  When they are expanded, this data
   structure keeps both the expanded and unexpanded forms.  */
Neil Booth's avatar
Neil Booth committed
struct macro_arg
{
  const cpp_token **first;	/* First token in unexpanded argument.  */
Kazu Hirata's avatar
Kazu Hirata committed
  const cpp_token **expanded;	/* Macro-expanded argument.  */
  const cpp_token *stringified;	/* Stringified argument.  */
Neil Booth's avatar
Neil Booth committed
  unsigned int count;		/* # of tokens in argument.  */
  unsigned int expanded_count;	/* # of tokens in expanded argument.  */
  location_t *virt_locs;	/* Where virtual locations for
				   unexpanded tokens are stored.  */
  location_t *expanded_virt_locs; /* Where virtual locations for
					  expanded tokens are
					  stored.  */
};

/* The kind of macro tokens which the instance of
   macro_arg_token_iter is supposed to iterate over.  */
enum macro_arg_token_kind {
  MACRO_ARG_TOKEN_NORMAL,
  /* This is a macro argument token that got transformed into a string
     literal, e.g. #foo.  */
  MACRO_ARG_TOKEN_STRINGIFIED,
  /* This is a token resulting from the expansion of a macro
     argument that was itself a macro.  */
  MACRO_ARG_TOKEN_EXPANDED
};

/* An iterator over tokens coming from a function-like macro
   argument.  */
typedef struct macro_arg_token_iter macro_arg_token_iter;
struct macro_arg_token_iter
{
  /* Whether or not -ftrack-macro-expansion is used.  */
  bool track_macro_exp_p;
  /* The kind of token over which we are supposed to iterate.  */
  enum macro_arg_token_kind kind;
  /* A pointer to the current token pointed to by the iterator.  */
  const cpp_token **token_ptr;
  /* A pointer to the "full" location of the current token.  If
Joseph Myers's avatar
Joseph Myers committed
     -ftrack-macro-expansion is used this location tracks loci across
     macro expansion.  */
  const location_t *location_ptr;
  /* The number of times the iterator went forward. This useful only
     when checking is enabled.  */
  size_t num_forwards;
#endif
Zack Weinberg's avatar
Zack Weinberg committed
};

/* Saved data about an identifier being used as a macro argument
   name.  */
struct macro_arg_saved_data {
  /* The canonical (UTF-8) spelling of this identifier.  */
  cpp_hashnode *canonical_node;
Nathan Sidwell's avatar
Nathan Sidwell committed
  /* The previous value & type of this identifier.  */
  union _cpp_hashnode_value value;
Nathan Sidwell's avatar
Nathan Sidwell committed
  node_type type;
Tom Tromey's avatar
Tom Tromey committed
static const char *vaopt_paste_error =
  N_("'##' cannot appear at either end of __VA_OPT__");

static void expand_arg (cpp_reader *, macro_arg *);

Tom Tromey's avatar
Tom Tromey committed
/* A class for tracking __VA_OPT__ state while iterating over a
   sequence of tokens.  This is used during both macro definition and
   expansion.  */
class vaopt_state {

 public:

Tom Tromey's avatar
Tom Tromey committed
  /* Initialize the state tracker.  ANY_ARGS is true if variable
     arguments were provided to the macro invocation.  */
  vaopt_state (cpp_reader *pfile, bool is_variadic, macro_arg *arg)
Tom Tromey's avatar
Tom Tromey committed
    : m_pfile (pfile),
Tom Tromey's avatar
Tom Tromey committed
    m_variadic (is_variadic),
    m_last_was_paste (false),
    m_stringify (false),
Tom Tromey's avatar
Tom Tromey committed
    m_paste_location (0),
Tom Tromey's avatar
Tom Tromey committed
  {
  }

  /* Given a token, update the state of this tracker and return a
     boolean indicating whether the token should be be included in the
     expansion.  */
  update_type update (const cpp_token *token)
  {
    /* If the macro isn't variadic, just don't bother.  */
    if (!m_variadic)
      return INCLUDE;

    if (token->type == CPP_NAME
	&& token->val.node.node == m_pfile->spec_nodes.n__VA_OPT__)
      {
	if (m_state > 0)
	  {
	    cpp_error_at (m_pfile, CPP_DL_ERROR, token->src_loc,
			  "%<__VA_OPT__%> may not appear in a %<__VA_OPT__%>");
Tom Tromey's avatar
Tom Tromey committed
	    return ERROR;
	  }
	++m_state;
	m_location = token->src_loc;
	m_stringify = (token->flags & STRINGIFY_ARG) != 0;
Tom Tromey's avatar
Tom Tromey committed
      }
    else if (m_state == 1)
      {
	if (token->type != CPP_OPEN_PAREN)
	  {
	    cpp_error_at (m_pfile, CPP_DL_ERROR, m_location,
			  "%<__VA_OPT__%> must be followed by an "
Tom Tromey's avatar
Tom Tromey committed
			  "open parenthesis");
	    return ERROR;
	  }
	++m_state;
	if (m_update == ERROR)
	  {
	    if (m_arg == NULL)
	      m_update = INCLUDE;
	    else
	      {
		m_update = DROP;
		if (!m_arg->expanded)
		  expand_arg (m_pfile, m_arg);
		for (unsigned idx = 0; idx < m_arg->expanded_count; ++idx)
		  if (m_arg->expanded[idx]->type != CPP_PADDING)
		    {
		      m_update = INCLUDE;
		      break;
		    }
	      }
	  }
Tom Tromey's avatar
Tom Tromey committed
	return DROP;
      }
    else if (m_state >= 2)
      {
	if (m_state == 2 && token->type == CPP_PASTE)
	  {
	    cpp_error_at (m_pfile, CPP_DL_ERROR, token->src_loc,
			  vaopt_paste_error);
	    return ERROR;
	  }
	/* Advance states before further considering this token, in
	   case we see a close paren immediately after the open
	   paren.  */
	if (m_state == 2)
	  ++m_state;

	bool was_paste = m_last_was_paste;
	m_last_was_paste = false;
	if (token->type == CPP_PASTE)
	  {
	    m_last_was_paste = true;
	    m_paste_location = token->src_loc;
	  }
	else if (token->type == CPP_OPEN_PAREN)
	  ++m_state;
	else if (token->type == CPP_CLOSE_PAREN)
	  {
	    --m_state;
	    if (m_state == 2)
	      {
		/* Saw the final paren.  */
		m_state = 0;

		if (was_paste)
		  {
		    cpp_error_at (m_pfile, CPP_DL_ERROR, token->src_loc,
				  vaopt_paste_error);
		    return ERROR;
		  }

Tom Tromey's avatar
Tom Tromey committed
	      }
	  }
Tom Tromey's avatar
Tom Tromey committed
      }

    /* Nothing to do with __VA_OPT__.  */
    return INCLUDE;
  }

  /* Ensure that any __VA_OPT__ was completed.  If ok, return true.
     Otherwise, issue an error and return false.  */
  bool completed ()
  {
    if (m_variadic && m_state != 0)
      cpp_error_at (m_pfile, CPP_DL_ERROR, m_location,
Tom Tromey's avatar
Tom Tromey committed
    return m_state == 0;
  }

  /* Return true for # __VA_OPT__.  */
  bool stringify () const
  {
    return m_stringify;
  }

Tom Tromey's avatar
Tom Tromey committed
 private:

  /* The cpp_reader.  */
  cpp_reader *m_pfile;

  /* The __VA_ARGS__ argument.  */
  macro_arg *m_arg;

Tom Tromey's avatar
Tom Tromey committed
  /* True if the macro is variadic.  */
  bool m_variadic;
  /* If true, the previous token was ##.  This is used to detect when
     a paste occurs at the end of the sequence.  */
  bool m_last_was_paste;
  /* True for #__VA_OPT__.  */
  bool m_stringify;
Tom Tromey's avatar
Tom Tromey committed

  /* The state variable:
     0 means not parsing
     1 means __VA_OPT__ seen, looking for "("
     2 means "(" seen (so the next token can't be "##")
     >= 3 means looking for ")", the number encodes the paren depth.  */
  int m_state;

  /* The location of the paste token.  */
  location_t m_paste_location;
Tom Tromey's avatar
Tom Tromey committed

  /* Location of the __VA_OPT__ token.  */
  location_t m_location;

  /* If __VA_ARGS__ substitutes to no preprocessing tokens,
     INCLUDE, otherwise DROP.  ERROR when unknown yet.  */
  update_type m_update;
Neil Booth's avatar
Neil Booth committed
/* Macro expansion.  */

static cpp_macro *get_deferred_or_lazy_macro (cpp_reader *, cpp_hashnode *,
					      location_t);
static int enter_macro_context (cpp_reader *, cpp_hashnode *,
				const cpp_token *, location_t);
static int builtin_macro (cpp_reader *, cpp_hashnode *,
			  location_t, location_t);
static void push_ptoken_context (cpp_reader *, cpp_hashnode *, _cpp_buff *,
				 const cpp_token **, unsigned int);
static void push_extended_tokens_context (cpp_reader *, cpp_hashnode *,
					  _cpp_buff *, location_t *,
					  const cpp_token **, unsigned int);
static _cpp_buff *collect_args (cpp_reader *, const cpp_hashnode *,
				_cpp_buff **, unsigned *);
static cpp_context *next_context (cpp_reader *);
static const cpp_token *padding_token (cpp_reader *, const cpp_token *);
static const cpp_token *new_string_token (cpp_reader *, uchar *, unsigned int);
static const cpp_token *stringify_arg (cpp_reader *, const cpp_token **,
static void paste_all_tokens (cpp_reader *, const cpp_token *);
static bool paste_tokens (cpp_reader *, location_t,
			  const cpp_token **, const cpp_token *);
static void alloc_expanded_arg_mem (cpp_reader *, macro_arg *, size_t);
static void ensure_expanded_arg_room (cpp_reader *, macro_arg *, size_t, size_t *);
static void delete_macro_args (_cpp_buff*, unsigned num_args);
static void set_arg_token (macro_arg *, const cpp_token *,
			   enum macro_arg_token_kind,
			   bool);
static const location_t *get_arg_token_location (const macro_arg *,
						      enum macro_arg_token_kind);
static const cpp_token **arg_token_ptr_at (const macro_arg *,
					   size_t,
					   enum macro_arg_token_kind,
					   location_t **virt_location);

static void macro_arg_token_iter_init (macro_arg_token_iter *, bool,
				       enum macro_arg_token_kind,
				       const macro_arg *,
				       const cpp_token **);
static const cpp_token *macro_arg_token_iter_get_token
(const macro_arg_token_iter *it);
static location_t macro_arg_token_iter_get_location
(const macro_arg_token_iter *);
static void macro_arg_token_iter_forward (macro_arg_token_iter *);
static _cpp_buff *tokens_buff_new (cpp_reader *, size_t,
static size_t tokens_buff_count (_cpp_buff *);
static const cpp_token **tokens_buff_last_token_ptr (_cpp_buff *);
static inline const cpp_token **tokens_buff_put_token_to (const cpp_token **,
                                                          const line_map_macro *,

static const cpp_token **tokens_buff_add_token (_cpp_buff *,
						const cpp_token *,
						const line_map_macro *,
						unsigned int);
static inline void tokens_buff_remove_last_token (_cpp_buff *);
static void replace_args (cpp_reader *, cpp_hashnode *, cpp_macro *,
			  macro_arg *, location_t);
static _cpp_buff *funlike_invocation_p (cpp_reader *, cpp_hashnode *,
					_cpp_buff **, unsigned *);
static cpp_macro *create_iso_definition (cpp_reader *);
Neil Booth's avatar
Neil Booth committed

/* #define directive parsing and handling.  */

static cpp_macro *lex_expansion_token (cpp_reader *, cpp_macro *);
static bool parse_params (cpp_reader *, unsigned *, bool *);
static void check_trad_stringification (cpp_reader *, const cpp_macro *,
					const cpp_string *);
static bool reached_end_of_context (cpp_context *);
static void consume_next_token_from_context (cpp_reader *pfile,
					     const cpp_token **,
					     location_t *);
static const cpp_token* cpp_get_token_1 (cpp_reader *, location_t *);
Zack Weinberg's avatar
Zack Weinberg committed

static cpp_hashnode* macro_of_context (cpp_context *context);

/* Statistical counter tracking the number of macros that got
   expanded.  */
line_map_uint_t num_expanded_macros_counter = 0;
/* Statistical counter tracking the total number tokens resulting
   from macro expansion.  */
line_map_uint_t num_macro_tokens_counter = 0;
/* Wrapper around cpp_get_token to skip CPP_PADDING tokens
   and not consume CPP_EOF.  */
const cpp_token *
_cpp_get_token_no_padding (cpp_reader *pfile)
{
  for (;;)
    {
      const cpp_token *ret = cpp_peek_token (pfile, 0);
      if (ret->type == CPP_EOF)
	return ret;
      ret = cpp_get_token (pfile);
      if (ret->type != CPP_PADDING)
	return ret;
    }
}

/* Helper function for builtin_has_include and builtin_has_embed.  */
static char *
builtin_has_include_1 (cpp_reader *pfile, const char *name, bool *paren,
		       bool *bracket, location_t *loc)
  if (!pfile->state.in_directive)
    cpp_error (pfile, CPP_DL_ERROR,
	       "%qs used outside of preprocessing directive", name);
  pfile->state.angled_headers = true;
  const auto sav_padding = pfile->state.directive_wants_padding;
  pfile->state.directive_wants_padding = true;
  const cpp_token *token = _cpp_get_token_no_padding (pfile);
  *paren = token->type == CPP_OPEN_PAREN;
  if (*paren)
    token = _cpp_get_token_no_padding (pfile);
  else
    cpp_error (pfile, CPP_DL_ERROR,
	       "missing %<(%> before %qs operand", name);
  pfile->state.angled_headers = false;
  pfile->state.directive_wants_padding = sav_padding;
  if (loc)
    *loc = token->src_loc;
  *bracket = token->type != CPP_STRING;
  char *fname = NULL;
  if (token->type == CPP_STRING || token->type == CPP_HEADER_NAME)
    {
      fname = XNEWVEC (char, token->val.str.len - 1);
      memcpy (fname, token->val.str.text + 1, token->val.str.len - 2);
      fname[token->val.str.len - 2] = '\0';
    }
  else if (token->type == CPP_LESS)
    fname = _cpp_bracket_include (pfile);
  else
    cpp_error (pfile, CPP_DL_ERROR,
	       "operator %qs requires a header-name", name);
  return fname;
}

/* Handle meeting "__has_include" builtin macro.  */

static int
builtin_has_include (cpp_reader *pfile, cpp_hashnode *op, bool has_next)
{
  int result = 0;
  bool paren, bracket;
  char *fname = builtin_has_include_1 (pfile, (const char *) NODE_NAME (op),
				       &paren, &bracket, NULL);

  if (fname)
    {
      /* Do not do the lookup if we're skipping, that's unnecessary
	 IO.  */
      if (!pfile->state.skip_eval
	  && _cpp_has_header (pfile, fname, bracket,
			      has_next ? IT_INCLUDE_NEXT : IT_INCLUDE))
	result = 1;

      XDELETEVEC (fname);
    }

      && _cpp_get_token_no_padding (pfile)->type != CPP_CLOSE_PAREN)
    cpp_error (pfile, CPP_DL_ERROR,
	       "missing %<)%> after %qs operand", NODE_NAME (op));
/* Handle the "__has_embed" expression.  */

static int
builtin_has_embed (cpp_reader *pfile)
{
  int result = 0;
  bool paren, bracket;
  struct cpp_embed_params params = {};
  char *fname = builtin_has_include_1 (pfile, "__has_embed", &paren,
				       &bracket, &params.loc);

  if (fname)
    {
      params.has_embed = true;
      auto save_in_directive = pfile->state.in_directive;
      auto save_angled_headers = pfile->state.angled_headers;
      auto save_directive_wants_padding = pfile->state.directive_wants_padding;
      auto save_op_stack = pfile->op_stack;
      auto save_op_limit = pfile->op_limit;
      auto save_skip_eval = pfile->state.skip_eval;
      auto save_mi_ind_cmacro = pfile->mi_ind_cmacro;
      /* Tell the lexer this is an embed directive.  */
      pfile->state.in_directive = 3;
      pfile->state.angled_headers = false;
      pfile->state.directive_wants_padding = false;
      pfile->op_stack = NULL;
      pfile->op_limit = NULL;
      bool ok = _cpp_parse_embed_params (pfile, &params);
      free (pfile->op_stack);
      pfile->state.in_directive = save_in_directive;
      pfile->state.angled_headers = save_angled_headers;
      pfile->state.directive_wants_padding = save_directive_wants_padding;
      pfile->op_stack = save_op_stack;
      pfile->op_limit = save_op_limit;
      pfile->state.skip_eval = save_skip_eval;
      pfile->mi_ind_cmacro = save_mi_ind_cmacro;

      if (!*fname)
	{
	  cpp_error_with_line (pfile, CPP_DL_ERROR, params.loc, 0,
			       "empty filename in %qs", "__has_embed");
	  ok = false;
	}

      /* Do not do the lookup if we're skipping, that's unnecessary
	 IO.  */
      if (ok && !pfile->state.skip_eval)
	result = _cpp_stack_embed (pfile, fname, bracket, &params);

      _cpp_free_embed_params_tokens (&params.base64);

      XDELETEVEC (fname);
    }
  else if (paren)
    _cpp_get_token_no_padding (pfile);

  return result;
}

/* Emits a warning if NODE is a macro defined in the main file that
   has not been used.  */
int
_cpp_warn_if_unused_macro (cpp_reader *pfile, cpp_hashnode *node,
			   void *v ATTRIBUTE_UNUSED)
  if (cpp_user_macro_p (node))
    {
      cpp_macro *macro = node->value.macro;

      if (!macro->used
	  && MAIN_FILE_P (linemap_check_ordinary
			    (linemap_lookup (pfile->line_table,
					     macro->line))))
	cpp_warning_with_line (pfile, CPP_W_UNUSED_MACROS, macro->line, 0,
			       "macro %qs is not used", NODE_NAME (node));
/* Allocates and returns a CPP_STRING token, containing TEXT of length
   LEN, after null-terminating it.  TEXT must be in permanent storage.  */
static const cpp_token *
new_string_token (cpp_reader *pfile, unsigned char *text, unsigned int len)
Neil Booth's avatar
Neil Booth committed
{
  cpp_token *token = _cpp_temp_token (pfile);
Neil Booth's avatar
Neil Booth committed

  text[len] = '\0';
Neil Booth's avatar
Neil Booth committed
  token->type = CPP_STRING;
  token->val.str.len = len;
  token->val.str.text = text;
Neil Booth's avatar
Neil Booth committed
  token->flags = 0;
  return token;
Neil Booth's avatar
Neil Booth committed
}

static const char * const monthnames[] =
{
  "Jan", "Feb", "Mar", "Apr", "May", "Jun",
  "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
};

/* Helper function for builtin_macro.  Returns the text generated by
   a builtin macro. */
_cpp_builtin_macro_text (cpp_reader *pfile, cpp_hashnode *node,
Neil Booth's avatar
Neil Booth committed
{
  const uchar *result = NULL;
  linenum_type number = 1;
Neil Booth's avatar
Neil Booth committed
  switch (node->value.builtin)
    {
    default:
      cpp_error (pfile, CPP_DL_ICE, "invalid built-in macro %qs",
    case BT_TIMESTAMP:
      {
	if (CPP_OPTION (pfile, warn_date_time))
	  cpp_warning (pfile, CPP_W_DATE_TIME, "macro %qs might prevent "
		       "reproducible builds", NODE_NAME (node));
	cpp_buffer *pbuffer = cpp_get_buffer (pfile);
	if (pbuffer->timestamp == NULL)
	  {
	    /* Initialize timestamp value of the assotiated file. */
            struct _cpp_file *file = cpp_get_file (pbuffer);
	    if (file)
	      {
    		/* Generate __TIMESTAMP__ string, that represents
		   the date and time of the last modification
		   of the current source file. The string constant
		   looks like "Sun Sep 16 01:03:52 1973".  */
		struct tm *tb = NULL;
		struct stat *st = _cpp_get_file_stat (file);
		if (st)
		  tb = localtime (&st->st_mtime);
		if (tb)
		  {
		    char *str = asctime (tb);
		    size_t len = strlen (str);
		    unsigned char *buf = _cpp_unaligned_alloc (pfile, len + 2);
		    buf[0] = '"';
		    strcpy ((char *) buf + 1, str);
		    buf[len] = '"';
		    pbuffer->timestamp = buf;
		  }
		else
		  {
		    cpp_errno (pfile, CPP_DL_WARNING,
			"could not determine file timestamp");
		    pbuffer->timestamp = UC"\"??? ??? ?? ??:??:?? ????\"";
		  }
	      }
	  }
	result = pbuffer->timestamp;
      }
      break;
Neil Booth's avatar
Neil Booth committed
    case BT_FILE:
Neil Booth's avatar
Neil Booth committed
    case BT_BASE_FILE:
Zack Weinberg's avatar
Zack Weinberg committed
      {
	unsigned int len;
	const char *name;

	if (node->value.builtin == BT_FILE
	    || node->value.builtin == BT_FILE_NAME)
	  {
	    name = linemap_get_expansion_filename (pfile->line_table,
						   pfile->line_table->highest_line);
	    if ((node->value.builtin == BT_FILE_NAME) && name)
	      name = lbasename (name);
	  }
	    name = _cpp_get_file_name (pfile->main_file);
	    if (!name)
	      abort ();
	if (pfile->cb.remap_filename && !pfile->state.in_directive)
	  name = pfile->cb.remap_filename (name);
	len = strlen (name);
	buf = _cpp_unaligned_alloc (pfile, len * 2 + 3);
	result = buf;
	*buf = '"';
	buf = cpp_quote_string (buf + 1, (const unsigned char *) name, len);
	*buf++ = '"';
	*buf = '\0';
Zack Weinberg's avatar
Zack Weinberg committed
      }
Neil Booth's avatar
Neil Booth committed
    case BT_INCLUDE_LEVEL:
      /* The line map depth counts the primary source as level 1, but
	 historically __INCLUDE_DEPTH__ has called the primary source
	 level 0.  */
      number = pfile->line_table->depth - 1;
Neil Booth's avatar
Neil Booth committed
      break;
Neil Booth's avatar
Neil Booth committed

    case BT_SPECLINE:
      /* If __LINE__ is embedded in a macro, it must expand to the
	 line of the macro's invocation, not its definition.
	 Otherwise things like assert() will not work properly.
	 See WG14 N1911, WG21 N4220 sec 6.5, and PR 61861.  */
      if (CPP_OPTION (pfile, traditional))
	loc = pfile->line_table->highest_line;
      else
	loc = linemap_resolve_location (pfile->line_table, loc,
					LRK_MACRO_EXPANSION_POINT, NULL);
      number = linemap_get_expansion_line (pfile->line_table, loc);
Neil Booth's avatar
Neil Booth committed
      break;
Neil Booth's avatar
Neil Booth committed

      /* __STDC__ has the value 1 under normal circumstances.
	 However, if (a) we are in a system header, (b) the option
	 stdc_0_in_system_headers is true (set by target config), and
	 (c) we are not in strictly conforming mode, then it has the
	 value 0.  (b) and (c) are already checked in cpp_init_builtins.  */
Neil Booth's avatar
Neil Booth committed
    case BT_STDC:
      if (_cpp_in_system_header (pfile))
Neil Booth's avatar
Neil Booth committed
      break;
Zack Weinberg's avatar
Zack Weinberg committed

Neil Booth's avatar
Neil Booth committed
    case BT_DATE:
    case BT_TIME:
      if (CPP_OPTION (pfile, warn_date_time))
	cpp_warning (pfile, CPP_W_DATE_TIME, "macro %qs might prevent "
		     "reproducible builds", NODE_NAME (node));
      if (pfile->date == NULL)
Neil Booth's avatar
Neil Booth committed
	{
	  /* Allocate __DATE__ and __TIME__ strings from permanent
	     storage.  We only do this once, and don't generate them
	     at init time, because time() and localtime() are very
	     slow on some systems.  */
	  auto kind = cpp_get_date (pfile, &tt);
	  if (kind == CPP_time_kind::UNKNOWN)
	      cpp_errno (pfile, CPP_DL_WARNING,
			 "could not determine date and time");
	      pfile->date = UC"\"??? ?? ????\"";
	      pfile->time = UC"\"??:??:??\"";
	      struct tm *tb = (kind == CPP_time_kind::FIXED
			       ? gmtime : localtime) (&tt);

	      pfile->date = _cpp_unaligned_alloc (pfile,
						  sizeof ("\"Oct 11 1347\""));
	      sprintf ((char *) pfile->date, "\"%s %2d %4d\"",
		       monthnames[tb->tm_mon], tb->tm_mday,
		       tb->tm_year + 1900);

	      pfile->time = _cpp_unaligned_alloc (pfile,
						  sizeof ("\"12:34:56\""));
	      sprintf ((char *) pfile->time, "\"%02d:%02d:%02d\"",
		       tb->tm_hour, tb->tm_min, tb->tm_sec);
	    }
Neil Booth's avatar
Neil Booth committed
      if (node->value.builtin == BT_DATE)
Neil Booth's avatar
Neil Booth committed
      else
Neil Booth's avatar
Neil Booth committed
      break;
Ollie Wild's avatar
Ollie Wild committed
      if (CPP_OPTION (pfile, directives_only) && pfile->state.in_directive)
	cpp_error (pfile, CPP_DL_ERROR,
		   "%<__COUNTER__%> expanded inside directive with "
		   "%<-fdirectives-only%>");
      number = pfile->counter++;
      break;
Joseph Myers's avatar
Joseph Myers committed
      number = pfile->cb.has_attribute (pfile, false);
      break;

    case BT_HAS_STD_ATTRIBUTE:
      number = pfile->cb.has_attribute (pfile, true);

    case BT_HAS_BUILTIN:
      number = pfile->cb.has_builtin (pfile);
      break;

    case BT_HAS_INCLUDE:
    case BT_HAS_INCLUDE_NEXT:
      number = builtin_has_include (pfile, node,
				    node->value.builtin == BT_HAS_INCLUDE_NEXT);
      break;
    case BT_HAS_EMBED:
      if (CPP_OPTION (pfile, traditional))
	{
	  cpp_error (pfile, CPP_DL_ERROR, /* FIXME should be DL_SORRY */
		     "%<__has_embed%> not supported in traditional C");
	  break;
	}
      number = builtin_has_embed (pfile);
      break;

    case BT_HAS_FEATURE:
    case BT_HAS_EXTENSION:
      number = pfile->cb.has_feature (pfile,
				      node->value.builtin == BT_HAS_FEATURE);
      break;
    }

  if (result == NULL)
    {
      /* 21 bytes holds all NUL-terminated unsigned 64-bit numbers.  */
      result = _cpp_unaligned_alloc (pfile, 21);
      sprintf ((char *) result, "%u", number);
    }

  return result;
/* Get an idempotent date.  Either the cached value, the value from
   source epoch, or failing that, the value from time(2).  Use this
   during compilation so that every time stamp is the same.  */
CPP_time_kind
cpp_get_date (cpp_reader *pfile, time_t *result)
{
  if (!pfile->time_stamp_kind)
    {
      int kind = 0;
      if (pfile->cb.get_source_date_epoch)
	{
	  /* Try reading the fixed epoch.  */
	  pfile->time_stamp = pfile->cb.get_source_date_epoch (pfile);
	  if (pfile->time_stamp != time_t (-1))
	    kind = int (CPP_time_kind::FIXED);
	}

      if (!kind)
	{
	  /* Pedantically time_t (-1) is a legitimate value for
	     "number of seconds since the Epoch".  It is a silly
	     time.   */
	  errno = 0;
	  pfile->time_stamp = time (nullptr);
	  /* Annoyingly a library could legally set errno and return a
	     valid time!  Bad library!  */
	  if (pfile->time_stamp == time_t (-1) && errno)
	    kind = errno;
	  else
	    kind = int (CPP_time_kind::DYNAMIC);
	}

      pfile->time_stamp_kind = kind;
    }

  *result = pfile->time_stamp;
  if (pfile->time_stamp_kind >= 0)
    {
      errno = pfile->time_stamp_kind;
      return CPP_time_kind::UNKNOWN;
    }

  return CPP_time_kind (pfile->time_stamp_kind);
}

/* Convert builtin macros like __FILE__ to a token and push it on the
   context stack.  Also handles _Pragma, for which a new token may not
   be created.  Returns 1 if it generates a new token context, 0 to
   return the token to the caller.  LOC is the location of the expansion
   point of the macro.  */
builtin_macro (cpp_reader *pfile, cpp_hashnode *node,
	       location_t loc, location_t expand_loc)
  if (node->value.builtin == BT_PRAGMA)
    {
Neil Booth's avatar
Neil Booth committed
      /* Don't interpret _Pragma within directives.  The standard is
         not clear on this, but to me this makes most sense.
         Similarly, don't interpret _Pragma inside expand_args, we might
         need to stringize it later on.  */
      if (pfile->state.in_directive || pfile->state.ignore__Pragma)
Neil Booth's avatar
Neil Booth committed
	return 0;

      return _cpp_do__Pragma (pfile, loc);
Neil Booth's avatar
Neil Booth committed
    }
  buf = _cpp_builtin_macro_text (pfile, node, expand_loc);
  len = ustrlen (buf);
  nbuf = (char *) alloca (len + 1);
  memcpy (nbuf, buf, len);
  nbuf[len]='\n';
  cpp_push_buffer (pfile, (uchar *) nbuf, len, /* from_stage3 */ true);
  _cpp_clean_line (pfile);

  /* Set pfile->cur_token as required by _cpp_lex_direct.  */
  pfile->cur_token = _cpp_temp_token (pfile);
  cpp_token *token = _cpp_lex_direct (pfile);
  /* We should point to the expansion point of the builtin macro.  */
  token->src_loc = loc;
  if (pfile->context->tokens_kind == TOKENS_KIND_EXTENDED)
    {
      /* We are tracking tokens resulting from macro expansion.
	 Create a macro line map and generate a virtual location for
	 the token resulting from the expansion of the built-in
	 macro.  */
      location_t *virt_locs = NULL;
      _cpp_buff *token_buf = tokens_buff_new (pfile, 1, &virt_locs);
      const line_map_macro * map =
	linemap_enter_macro (pfile->line_table, node, loc, 1);
      tokens_buff_add_token (token_buf, virt_locs, token,
			     pfile->line_table->builtin_location,
			     pfile->line_table->builtin_location,
			    map, /*macro_token_index=*/0);
      push_extended_tokens_context (pfile, node, token_buf, virt_locs,
				    (const cpp_token **)token_buf->base,
				    1);
    }
  else
    _cpp_push_token_context (pfile, NULL, token, 1);
  if (pfile->buffer->cur != pfile->buffer->rlimit)
    cpp_error (pfile, CPP_DL_ICE, "invalid built-in macro %qs",
	       NODE_NAME (node));
  _cpp_pop_buffer (pfile);

Neil Booth's avatar
Neil Booth committed
  return 1;
/* Copies SRC, of length LEN, to DEST, adding backslashes before all
   backslashes and double quotes. DEST must be of sufficient size.
   Returns a pointer to the end of the string.  */
cpp_quote_string (uchar *dest, const uchar *src, unsigned int len)
Neil Booth's avatar
Neil Booth committed
{
  while (len--)
    {
Zack Weinberg's avatar
Zack Weinberg committed

Neil Booth's avatar
Neil Booth committed
	{
	case '\n':
	  /* Naked LF can appear in raw string literals  */
	  c = 'n';
	  /* FALLTHROUGH */

	case '\\':
	case '"':
Neil Booth's avatar
Neil Booth committed
	  *dest++ = '\\';
	  /* FALLTHROUGH */

	default:
Neil Booth's avatar
Neil Booth committed
	  *dest++ = c;
	}
    }
Zack Weinberg's avatar
Zack Weinberg committed

Neil Booth's avatar
Neil Booth committed
  return dest;
}
Zack Weinberg's avatar
Zack Weinberg committed

/* Convert a token sequence FIRST to FIRST+COUNT-1 to a single string token
   according to the rules of the ISO C #-operator.  */
static const cpp_token *
stringify_arg (cpp_reader *pfile, const cpp_token **first, unsigned int count)
Neil Booth's avatar
Neil Booth committed
{
  unsigned char *dest;
  unsigned int i, escape_it, backslash_count = 0;
  const cpp_token *source = NULL;
Zack Weinberg's avatar
Zack Weinberg committed

  if (BUFF_ROOM (pfile->u_buff) < 3)
    _cpp_extend_buff (pfile, &pfile->u_buff, 3);
  dest = BUFF_FRONT (pfile->u_buff);
  *dest++ = '"';

Neil Booth's avatar
Neil Booth committed
  /* Loop, reading in the argument's tokens.  */
  for (i = 0; i < count; i++)
Neil Booth's avatar
Neil Booth committed
    {
      const cpp_token *token = first[i];

      if (token->type == CPP_PADDING)
	{
	  if (source == NULL
	      || (!(source->flags & PREV_WHITE)
		  && token->val.source == NULL))
	    source = token->val.source;
	  continue;
	}
Zack Weinberg's avatar
Zack Weinberg committed

      escape_it = (token->type == CPP_STRING || token->type == CPP_CHAR
		   || token->type == CPP_WSTRING || token->type == CPP_WCHAR
		   || token->type == CPP_STRING32 || token->type == CPP_CHAR32
		   || token->type == CPP_STRING16 || token->type == CPP_CHAR16
		   || token->type == CPP_UTF8STRING || token->type == CPP_UTF8CHAR
		   || cpp_userdef_string_p (token->type)
		   || cpp_userdef_char_p (token->type));
      /* Room for each char being written in octal, initial space and
	 final quote and NUL.  */
      len = cpp_token_len (token);
Neil Booth's avatar
Neil Booth committed
      if (escape_it)
	len *= 4;
      len += 3;
Zack Weinberg's avatar
Zack Weinberg committed

      if ((size_t) (BUFF_LIMIT (pfile->u_buff) - dest) < len)
Neil Booth's avatar
Neil Booth committed
	{
	  size_t len_so_far = dest - BUFF_FRONT (pfile->u_buff);
	  _cpp_extend_buff (pfile, &pfile->u_buff, len);
	  dest = BUFF_FRONT (pfile->u_buff) + len_so_far;
Neil Booth's avatar
Neil Booth committed
	}
Zack Weinberg's avatar
Zack Weinberg committed

      /* Leading white space?  */
      if (dest - 1 != BUFF_FRONT (pfile->u_buff))
	{
	  if (source == NULL)
	    source = token;
	  if (source->flags & PREV_WHITE)
	    *dest++ = ' ';
	}
      source = NULL;
Zack Weinberg's avatar
Zack Weinberg committed

Neil Booth's avatar
Neil Booth committed
      if (escape_it)
	{
	  _cpp_buff *buff = _cpp_get_buff (pfile, len);
	  unsigned char *buf = BUFF_FRONT (buff);
	  len = cpp_spell_token (pfile, token, buf, true) - buf;
	  dest = cpp_quote_string (dest, buf, len);
	  _cpp_release_buff (pfile, buff);
Neil Booth's avatar
Neil Booth committed
	}
      else
	dest = cpp_spell_token (pfile, token, dest, true);
Neil Booth's avatar
Neil Booth committed

      if (token->type == CPP_OTHER && token->val.str.text[0] == '\\')
Neil Booth's avatar
Neil Booth committed
	backslash_count++;
      else
	backslash_count = 0;