Wolfgang Thallergcc16 merge934c3885 months agoHistory
1321 lines · 36.6 KB · C++
RawDownload
/* CPP Library - traditional lexical analysis and macro expansion.   Copyright (C) 2002-2026 Free Software Foundation, Inc.   Contributed by Neil Booth, May 2002 This program is free software; you can redistribute it and/or modify itunder the terms of the GNU General Public License as published by theFree Software Foundation; either version 3, or (at your option) anylater version. This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied warranty ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See theGNU General Public License for more details. You should have received a copy of the GNU General Public Licensealong with this program; see the file COPYING3.  If not see<http://www.gnu.org/licenses/>.  */ #include "config.h"#include "system.h"#include "cpplib.h"#include "internal.h" /* The replacement text of a function-like macro is stored as a   contiguous sequence of aligned blocks, each representing the text   between subsequent parameters.    Each block comprises the text between its surrounding parameters,   the length of that text, and the one-based index of the following   parameter.  The final block in the replacement text is easily   recognizable as it has an argument index of zero.  */ struct block{  unsigned int text_len;  unsigned short arg_index;  uchar text[1];}; #define BLOCK_HEADER_LEN offsetof (struct block, text)#define BLOCK_LEN(TEXT_LEN) CPP_ALIGN (BLOCK_HEADER_LEN + (TEXT_LEN)) /* Structure holding information about a function-like macro   invocation.  */struct fun_macro{  /* Memory buffer holding the trad_arg array.  */  _cpp_buff *buff;   /* An array of size the number of macro parameters + 1, containing     the offsets of the start of each macro argument in the output     buffer.  The argument continues until the character before the     start of the next one.  */  size_t *args;   /* The hashnode of the macro.  */  cpp_hashnode *node;   /* The offset of the macro name in the output buffer.  */  size_t offset;   /* The line the macro name appeared on.  */  location_t line;   /* Number of parameters.  */  unsigned int paramc;   /* Zero-based index of argument being currently lexed.  */  unsigned int argc;}; /* Lexing state.  It is mostly used to prevent macro expansion.  */enum ls {ls_none = 0,		/* Normal state.  */	 ls_fun_open,		/* When looking for '('.  */	 ls_fun_close,		/* When looking for ')'.  */	 ls_defined,		/* After defined.  */	 ls_defined_close,	/* Looking for ')' of defined().  */	 ls_hash,		/* After # in preprocessor conditional.  */	 ls_predicate,		/* After the predicate, maybe paren?  */	 ls_answer		/* In answer to predicate.  */}; /* Lexing TODO: Maybe handle space in escaped newlines.  Stop lex.cc   from recognizing comments and directives during its lexing pass.  */ static const uchar *skip_whitespace (cpp_reader *, const uchar *, int);static cpp_hashnode *lex_identifier (cpp_reader *, const uchar *);static const uchar *copy_comment (cpp_reader *, const uchar *, int);static void check_output_buffer (cpp_reader *, size_t);static void push_replacement_text (cpp_reader *, cpp_hashnode *);static bool scan_parameters (cpp_reader *, unsigned *);static bool recursive_macro (cpp_reader *, cpp_hashnode *);static void save_replacement_text (cpp_reader *, cpp_macro *, unsigned int);static void maybe_start_funlike (cpp_reader *, cpp_hashnode *, const uchar *,				 struct fun_macro *);static void save_argument (struct fun_macro *, size_t);static void replace_args_and_push (cpp_reader *, struct fun_macro *);static size_t canonicalize_text (uchar *, const uchar *, size_t, uchar *); /* Ensures we have N bytes' space in the output buffer, and   reallocates it if not.  */static voidcheck_output_buffer (cpp_reader *pfile, size_t n){  /* We might need two bytes to terminate an unterminated comment, and     one more to terminate the line with a NUL.  */  n += 2 + 1;   if (n > (size_t) (pfile->out.limit - pfile->out.cur))    {      size_t size = pfile->out.cur - pfile->out.base;      size_t new_size = (size + n) * 3 / 2;       pfile->out.base = XRESIZEVEC (unsigned char, pfile->out.base, new_size);      pfile->out.limit = pfile->out.base + new_size;      pfile->out.cur = pfile->out.base + size;    }} /* Skip a C-style block comment in a macro as a result of -CC.   PFILE->buffer->cur points to the initial asterisk of the comment,   change it to point to after the '*' and '/' characters that terminate it.   Return true if the macro has not been termined, in that case set   PFILE->buffer->cur to the end of the buffer.  */static boolskip_macro_block_comment (cpp_reader *pfile){  const uchar *cur = pfile->buffer->cur;   cur++;  if (*cur == '/')    cur++;   /* People like decorating comments with '*', so check for '/'     instead for efficiency.  */  while (! (*cur++ == '/' && cur[-2] == '*'))    if (cur[-1] == '\n')      {	pfile->buffer->cur = cur - 1;	return true;      }   pfile->buffer->cur = cur;  return false;} /* CUR points to the asterisk introducing a comment in the current   context.  IN_DEFINE is true if we are in the replacement text of a   macro.    The asterisk and following comment is copied to the buffer pointed   to by pfile->out.cur, which must be of sufficient size.   Unterminated comments are diagnosed, and correctly terminated in   the output.  pfile->out.cur is updated depending upon IN_DEFINE,   -C, -CC and pfile->state.in_directive.    Returns a pointer to the first character after the comment in the   input buffer.  */static const uchar *copy_comment (cpp_reader *pfile, const uchar *cur, int in_define){  bool unterminated, copy = false;  location_t src_loc = pfile->line_table->highest_line;  cpp_buffer *buffer = pfile->buffer;   buffer->cur = cur;  if (pfile->context->prev)    unterminated = skip_macro_block_comment (pfile);  else    unterminated = _cpp_skip_block_comment (pfile);   if (unterminated)    cpp_error_with_line (pfile, CPP_DL_ERROR, src_loc, 0,			 "unterminated comment");   /* Comments in directives become spaces so that tokens are properly     separated when the ISO preprocessor re-lexes the line.  The     exception is #define.  */  if (pfile->state.in_directive)    {      if (in_define)	{	  if (CPP_OPTION (pfile, discard_comments_in_macro_exp))	    pfile->out.cur--;	  else	    copy = true;	}      else	pfile->out.cur[-1] = ' ';    }  else if (CPP_OPTION (pfile, discard_comments))    pfile->out.cur--;  else    copy = true;   if (copy)    {      size_t len = (size_t) (buffer->cur - cur);      memcpy (pfile->out.cur, cur, len);      pfile->out.cur += len;      if (unterminated)	{	  *pfile->out.cur++ = '*';	  *pfile->out.cur++ = '/';	}    }   return buffer->cur;} /* CUR points to any character in the input buffer.  Skips over all   contiguous horizontal white space and NULs, including comments if   SKIP_COMMENTS, until reaching the first non-horizontal-whitespace   character or the end of the current context.  Escaped newlines are   removed.    The whitespace is copied verbatim to the output buffer, except that   comments are handled as described in copy_comment().   pfile->out.cur is updated.    Returns a pointer to the first character after the whitespace in   the input buffer.  */static const uchar *skip_whitespace (cpp_reader *pfile, const uchar *cur, int skip_comments){  uchar *out = pfile->out.cur;   for (;;)    {      unsigned int c = *cur++;      *out++ = c;       if (is_nvspace (c))	continue;       if (c == '/' && *cur == '*' && skip_comments)	{	  pfile->out.cur = out;	  cur = copy_comment (pfile, cur, false /* in_define */);	  out = pfile->out.cur;	  continue;	}       out--;      break;    }   pfile->out.cur = out;  return cur - 1;} /* Lexes and outputs an identifier starting at CUR, which is assumed   to point to a valid first character of an identifier.  Returns   the hashnode, and updates out.cur.  */static cpp_hashnode *lex_identifier (cpp_reader *pfile, const uchar *cur){  size_t len;  uchar *out = pfile->out.cur;  cpp_hashnode *result;   do    *out++ = *cur++;  while (is_numchar (*cur));   CUR (pfile->context) = cur;  len = out - pfile->out.cur;  result = CPP_HASHNODE (ht_lookup (pfile->hash_table, pfile->out.cur,				    len, HT_ALLOC));  pfile->out.cur = out;  return result;} /* Overlays the true file buffer temporarily with text of length LEN   starting at START.  The true buffer is restored upon calling   restore_buff().  */void_cpp_overlay_buffer (cpp_reader *pfile, const uchar *start, size_t len){  cpp_buffer *buffer = pfile->buffer;   pfile->overlaid_buffer = buffer;  pfile->saved_cur = buffer->cur;  pfile->saved_rlimit = buffer->rlimit;  pfile->saved_line_base = buffer->next_line;  buffer->need_line = false;   buffer->cur = start;  buffer->line_base = start;  buffer->rlimit = start + len;} /* Restores a buffer overlaid by _cpp_overlay_buffer().  */void_cpp_remove_overlay (cpp_reader *pfile){  cpp_buffer *buffer = pfile->overlaid_buffer;   buffer->cur = pfile->saved_cur;  buffer->rlimit = pfile->saved_rlimit;  buffer->line_base = pfile->saved_line_base;  buffer->need_line = true;   pfile->overlaid_buffer = NULL;} /* Reads a logical line into the output buffer.  Returns TRUE if there   is more text left in the buffer.  */bool_cpp_read_logical_line_trad (cpp_reader *pfile){  do    {      if (pfile->buffer->need_line && !_cpp_get_fresh_line (pfile))	{	  /* Now pop the buffer that _cpp_get_fresh_line did not.  */	  _cpp_pop_buffer (pfile);	  return false;	}    }  while (!_cpp_scan_out_logical_line (pfile, NULL, false)	 || pfile->state.skipping);   return pfile->buffer != NULL;} /* Return true if NODE is a fun_like macro.  */static inline boolfun_like_macro (cpp_hashnode *node){  if (cpp_builtin_macro_p (node))    return (node->value.builtin == BT_HAS_ATTRIBUTE	    || node->value.builtin == BT_HAS_STD_ATTRIBUTE	    || node->value.builtin == BT_HAS_BUILTIN	    || node->value.builtin == BT_HAS_INCLUDE	    || node->value.builtin == BT_HAS_INCLUDE_NEXT);  return node->value.macro->fun_like;} /* Set up state for finding the opening '(' of a function-like   macro.  */static voidmaybe_start_funlike (cpp_reader *pfile, cpp_hashnode *node, const uchar *start,		     struct fun_macro *macro){  unsigned int n;  if (cpp_builtin_macro_p (node))    n = 1;  else    n = node->value.macro->paramc;   if (macro->buff)    _cpp_release_buff (pfile, macro->buff);  macro->buff = _cpp_get_buff (pfile, (n + 1) * sizeof (size_t));  macro->args = (size_t *) BUFF_FRONT (macro->buff);  macro->node = node;  macro->offset = start - pfile->out.base;  macro->paramc = n;  macro->argc = 0;} /* Save the OFFSET of the start of the next argument to MACRO.  */static voidsave_argument (struct fun_macro *macro, size_t offset){  macro->argc++;  if (macro->argc <= macro->paramc)    macro->args[macro->argc] = offset;} /* Copies the next logical line in the current buffer (starting at   buffer->cur) to the output buffer.  The output is guaranteed to   terminate with a NUL character.  buffer->cur is updated.    If MACRO is non-NULL, then we are scanning the replacement list of   MACRO, and we call save_replacement_text() every time we meet an   argument.    If BUILTIN_MACRO_ARG is true, this is called to macro expand   arguments of builtin function-like macros.  */bool_cpp_scan_out_logical_line (cpp_reader *pfile, cpp_macro *macro,			    bool builtin_macro_arg){  bool result = true;  cpp_context *context;  const uchar *cur;  uchar *out;  struct fun_macro fmacro;  unsigned int c, paren_depth = 0, quote;  enum ls lex_state = ls_none;  bool header_ok;  const uchar *start_of_input_line;   fmacro.buff = NULL;  fmacro.args = NULL;  fmacro.node = NULL;  fmacro.offset = 0;  fmacro.line = 0;  fmacro.paramc = 0;  fmacro.argc = 0;   quote = 0;  header_ok = pfile->state.angled_headers;  CUR (pfile->context) = pfile->buffer->cur;  RLIMIT (pfile->context) = pfile->buffer->rlimit;  if (!builtin_macro_arg)    {      pfile->out.cur = pfile->out.base;      pfile->out.first_line = pfile->line_table->highest_line;    }  /* start_of_input_line is needed to make sure that directives really,     really start at the first character of the line.  */  start_of_input_line = pfile->buffer->cur; new_context:  context = pfile->context;  cur = CUR (context);  check_output_buffer (pfile, RLIMIT (context) - cur);  out = pfile->out.cur;   for (;;)    {      if (!context->prev	  && !builtin_macro_arg	  && cur >= pfile->buffer->notes[pfile->buffer->cur_note].pos)	{	  pfile->buffer->cur = cur;	  _cpp_process_line_notes (pfile, false);	}      c = *cur++;      *out++ = c;       /* Whitespace should "continue" out of the switch,	 non-whitespace should "break" out of it.  */      switch (c)	{	case ' ':	case '\t':	case '\f':	case '\v':	case '\0':	  continue; 	case '\n':	  /* If this is a macro's expansion, pop it.  */	  if (context->prev)	    {	      pfile->out.cur = out - 1;	      _cpp_pop_context (pfile);	      goto new_context;	    } 	  /* Omit the newline from the output buffer.  */	  pfile->out.cur = out - 1;	  pfile->buffer->cur = cur;	  if (builtin_macro_arg)	    goto done;	  pfile->buffer->need_line = true;	  CPP_INCREMENT_LINE (pfile, 0); 	  if ((lex_state == ls_fun_open || lex_state == ls_fun_close)	      && !pfile->state.in_directive	      && _cpp_get_fresh_line (pfile))	    {	      /* Newlines in arguments become a space, but we don't		 clear any in-progress quote.  */	      if (lex_state == ls_fun_close)		out[-1] = ' ';	      cur = pfile->buffer->cur;	      continue;	    }	  goto done; 	case '<':	  if (header_ok)	    quote = '>';	  break;	case '>':	  if (c == quote)	    quote = 0;	  break; 	case '"':	case '\'':	  if (c == quote)	    quote = 0;	  else if (!quote)	    quote = c;	  break; 	case '\\':	  /* Skip escaped quotes here, it's easier than above.  */	  if (*cur == '\\' || *cur == '"' || *cur == '\'')	    *out++ = *cur++;	  break; 	case '/':	  /* Traditional CPP does not recognize comments within	     literals.  */	  if (!quote && *cur == '*')

Lines 1–500 of 1321.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t