check point work on RT_CAT builtin macro
authorThomas Walker Lynch <eknp9n@reasoningtechnology.com>
Fri, 16 May 2025 16:26:39 +0000 (16:26 +0000)
committerThomas Walker Lynch <eknp9n@reasoningtechnology.com>
Fri, 16 May 2025 16:26:39 +0000 (16:26 +0000)
21 files changed:
developer/experimentπŸ–‰/assign_directive.c [deleted file]
developer/script_Deb-12.10_gcc-12.4.1πŸ–‰/#project_extract.sh# [new file with mode: 0755]
developer/script_Deb-12.10_gcc-12.4.1πŸ–‰/RT_extensions_diff.sh [deleted file]
developer/script_Deb-12.10_gcc-12.4.1πŸ–‰/RT_extensions_install.sh [deleted file]
developer/script_Deb-12.10_gcc-12.4.1πŸ–‰/RT_extensions_libcpp_capture.sh [deleted file]
developer/script_Deb-12.10_gcc-12.4.1πŸ–‰/RT_extensions_libcpp_save.sh [deleted file]
developer/script_Deb-12.10_gcc-12.4.1πŸ–‰/build_all.sh
developer/script_Deb-12.10_gcc-12.4.1πŸ–‰/clean_dist.sh
developer/script_Deb-12.10_gcc-12.4.1πŸ–‰/clean_toolchain.sh [new file with mode: 0755]
developer/script_Deb-12.10_gcc-12.4.1πŸ–‰/ext_capture.sh [new file with mode: 0755]
developer/script_Deb-12.10_gcc-12.4.1πŸ–‰/ext_diff.sh [new file with mode: 0755]
developer/script_Deb-12.10_gcc-12.4.1πŸ–‰/ext_install.sh [new file with mode: 0755]
developer/script_Deb-12.10_gcc-12.4.1πŸ–‰/ext_save.sh [new file with mode: 0755]
developer/script_Deb-12.10_gcc-12.4.1πŸ–‰/library/directives.cc
developer/script_Deb-12.10_gcc-12.4.1πŸ–‰/library/include/cpplib.h
developer/script_Deb-12.10_gcc-12.4.1πŸ–‰/library/init.cc
developer/script_Deb-12.10_gcc-12.4.1πŸ–‰/library/macro.cc
developer/script_Deb-12.10_gcc-12.4.1πŸ–‰/project_extract.sh
developer/script_Deb-12.10_gcc-12.4.1πŸ–‰/project_setup.sh
env_tester
tmp/macro.cc [new file with mode: 0644]

diff --git a/developer/experimentπŸ–‰/assign_directive.c b/developer/experimentπŸ–‰/assign_directive.c
deleted file mode 100644 (file)
index 76e1eb4..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-#include <stdio.h>
-
-#assign ()(Number)(0x2d9)
-
-#assign ()(NAME)(ONE)
-#assign ()[ NAME() ](1)
-
-#undef NAME
-#define NAME TwentySeven
-#assign ()[NAME](
-  Number / 27
-)
-
-int main(void){
-#if 1
-  printf("forty-two: %x\n" ,Number);
-  printf("ONE: %x\n" ,ONE);
-  printf("TwentySeven: %x\n" ,TwentySeven);
-#endif
-  printf("And thus begins the dance.\n");
-  return 0;
-}
diff --git a/developer/script_Deb-12.10_gcc-12.4.1πŸ–‰/#project_extract.sh# b/developer/script_Deb-12.10_gcc-12.4.1πŸ–‰/#project_extract.sh#
new file mode 100755 (executable)
index 0000000..272470d
--- /dev/null
@@ -0,0 +1,55 @@
+#!/bin/bash
+# extracts (unpacks) the source tarballs held in upstream/ into the source/ directory.
+# Will not extract if target already exists
+# Delete any malformed extractions before running again
+#
+# gcc is not installed as a tar file, rather it is git cloned directly into source/ as part of the downloading from upstream sources. Hence, there is nothing to extract.
+
+set -euo pipefail
+
+source "$(dirname "$0")/environment.sh"
+
+had_error=0
+i=0
+
+while [ $i -lt ${#UPSTREAM_TARBALL_LIST[@]} ]; do
+  tarball="${UPSTREAM_TARBALL_LIST[$i]}"
+  i=$((i + 3))
+
+  src_path="$UPSTREAM/$tarball"
+
+  # Strip compression suffix to guess subdirectory name
+  base_name="${tarball%%.tar.*}"  # safer across .tar.gz, .tar.zst, etc.
+  target_dir="$SRC/$base_name"
+
+  if [[ -d "$target_dir" ]]; then
+    echo "⚑ Already exists, skipping: $target_dir"
+    continue
+  fi
+
+  if [[ ! -f "$src_path" ]]; then
+    echo "❌ Missing tarball: $src_path"
+    had_error=1
+    continue
+  fi
+
+  echo "tar -xf $tarball"
+  if ! (cd "$SRC" && tar -xf "$src_path"); then
+    echo "❌ Extraction failed: $tarball"
+    had_error=1
+    continue
+  fi
+
+  if [[ -d "$target_dir" ]]; then
+    echo "Extracted to: $target_dir"
+  else
+    echo "❌ Target not found after extraction: $target_dir"
+    had_error=1
+  fi
+done
+
+if [[ $had_error -eq 0 ]]; then
+  echo "βœ… All tarballs extracted successfully"
+else
+  echo "❌ Some extractions failed or were incomplete"
+fi
diff --git a/developer/script_Deb-12.10_gcc-12.4.1πŸ–‰/RT_extensions_diff.sh b/developer/script_Deb-12.10_gcc-12.4.1πŸ–‰/RT_extensions_diff.sh
deleted file mode 100755 (executable)
index 4c928e6..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-#!/bin/bash
-set -euo pipefail
-
-# Provides RT_CPP_FILES
-source "$(dirname "$0")/environment.sh"
-
-if [[ -z "${ROOT:-}" ]]; then
-  echo "❌ ROOT environment variable is not set. Aborting."
-  exit 1
-fi
-if [[ -z "${SCRIPT_DIR:-}" ]]; then
-  echo "❌ SCRIPT_DIR environment variable is not set. Aborting."
-  exit 1
-fi
-
-SRCDIR="library/"
-DESTDIR="$GCC_SRC/libcpp/"
-
-if [[ ! -d "$SRCDIR" ]]; then
-  echo "❌ Source directory '$SRCDIR' does not exist."
-  exit 1
-fi
-
-if [[ ! -d "$DESTDIR" ]]; then
-  echo "❌ Destination directory '$DESTDIR' does not exist."
-  exit 1
-fi
-
-echo "πŸ” Diffing library β†” libcpp..."
-
-for file in "${RT_CPP_FILES[@]}"; do
-  SRC="$SRCDIR/$file"
-  DEST="$DESTDIR/$file"
-
-  echo "πŸ”Έ $file"
-
-  if [[ ! -f "$SRC" ]]; then
-    echo "  βš οΈ  Missing in library/: $SRC"
-    continue
-  fi
-
-  if [[ ! -f "$DEST" ]]; then
-    echo "  βš οΈ  Missing in libcpp/: $DEST"
-    continue
-  fi
-
-  if cmp -s "$SRC" "$DEST"; then
-    echo "  βœ… No differences."
-  else
-    echo "  β— Differences found:"
-    diff -u "$DEST" "$SRC" || true
-  fi
-done
-
-echo "βœ… Diff check complete."
diff --git a/developer/script_Deb-12.10_gcc-12.4.1πŸ–‰/RT_extensions_install.sh b/developer/script_Deb-12.10_gcc-12.4.1πŸ–‰/RT_extensions_install.sh
deleted file mode 100755 (executable)
index df789e7..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-#!/bin/bash
-set -euo pipefail
-
-# provides RT_CPP_FILES
-source "$(dirname "$0")/environment.sh"
-
-# Check ROOT is set
-if [[ -z "${ROOT:-}" ]]; then
-  echo "❌ ROOT environment variable is not set. Aborting."
-  exit 1
-fi
-
-SRCDIR="library/"
-DESTDIR="$GCC_SRC/libcpp/"
-
-if [[ ! -d "$SRCDIR" ]]; then
-  echo "❌ Source directory '$SRCDIR' does not exist."
-  exit 1
-fi
-
-if [[ ! -d "$DESTDIR" ]]; then
-  echo "❌ Destination directory '$DESTDIR' does not exist."
-  exit 1
-fi
-
-echo "πŸ“‹ Installing files from $SRCDIR to $DESTDIR..."
-
-for file in "${RT_CPP_FILES[@]}"; do
-  SRC="$SRCDIR/$file"
-  DEST="$DESTDIR/$file"
-
-  if [[ ! -f "$SRC" ]]; then
-    echo "⚠️  Source file '$SRC' not found. Skipping."
-    continue
-  fi
-
-  if [[ ! -f "$DEST" || "$SRC" -nt "$DEST" ]]; then
-    echo "πŸ“₯ Installing (newer or missing): $file"
-    cp -p "$SRC" "$DEST"
-  elif [[ "$DEST" -nt "$SRC" ]]; then
-    echo "⚠️  Destination file '$file' is newer than the source."
-    echo "πŸ” Showing diff:"
-    diff -u "$DEST" "$SRC" || true
-    echo -n "❓ Overwrite destination '$file' with older source? [y/N]: "
-    read -r response
-    if [[ "$response" == "y" || "$response" == "Y" ]]; then
-      echo "πŸ“₯ Overwriting: $file"
-      cp -p "$SRC" "$DEST"
-    else
-      echo "⏭️  Skipping: $file"
-    fi
-  else
-    echo "βœ… Up-to-date: $file"
-  fi
-done
-
-echo "βœ… Installation complete."
diff --git a/developer/script_Deb-12.10_gcc-12.4.1πŸ–‰/RT_extensions_libcpp_capture.sh b/developer/script_Deb-12.10_gcc-12.4.1πŸ–‰/RT_extensions_libcpp_capture.sh
deleted file mode 100755 (executable)
index ab0813f..0000000
+++ /dev/null
@@ -1,100 +0,0 @@
-#!/bin/bash
-set -euo pipefail
-
-# provides RT_CPP_FILES
-source "$(dirname "$0")/environment.sh"
-
-
-echo "⚠️  You probably don't want to run this script. The files in \$ROOT/script_Deb-12.10_gcc-12.4.1πŸ–‰/library are intended to be the authoritative copies."
-echo "So you did the bad thing and edited the files directly in the GCC source tree? Then this script is for you. ;-)"
-echo
-
-echo -n "Continue❓ [y/N]: "
-read -r response
-if [[ "$response" == "y" || "$response" == "Y" ]]; then
-  :
-else
-  exit 1
-fi
-
-
-if [[ -z "${ROOT:-}" ]]; then
-  echo "❌ ROOT environment variable is not set. Aborting."
-  exit 1
-fi
-if [[ -z "${SCRIPT_DIR:-}" ]]; then
-  echo "❌ SCRIPT_DIR environment variable is not set. Aborting."
-  exit 1
-fi
-SRCDIR="library/"
-DESTDIR="$GCC_SRC/libcpp/"
-
-
-SRCDIR="$ROOT/source/gcc-12.2.0/libcpp"
-DESTDIR="$ROOT/script_Deb-12.10_gcc-12.4.1πŸ–‰/library"
-
-if [[ ! -d "$SRCDIR" ]]; then
-  echo "❌ Source directory '$SRCDIR' does not exist."
-  exit 1
-fi
-
-if [[ ! -d "$DESTDIR" ]]; then
-  echo "❌ Destination directory '$DESTDIR' does not exist."
-  exit 1
-fi
-
-echo "πŸ“‹ Checking files in $SRCDIR to copy to $DESTDIR..."
-
-for file in "${RT_CPP_FILES[@]}"; do
-  SRC="$SRCDIR/$file"
-  DEST="$DESTDIR/$file"
-
-  mkdir -p "$(dirname "$DEST")"
-
-  if [[ ! -f "$SRC" ]]; then
-    echo "⚠️  Source file '$SRC' not found. Skipping."
-    continue
-  fi
-
-  if [[ ! -f "$DEST" ]]; then
-    echo "πŸ“€ No destination file. Copying: $file"
-    cp -p "$SRC" "$DEST"
-    continue
-  fi
-
-  if cmp -s "$SRC" "$DEST"; then
-    echo "βœ… No changes: $file"
-    continue
-  fi
-
-  if [[ "$SRC" -nt "$DEST" ]]; then
-    echo "πŸ“€ Source is newer and differs. Copying: $file"
-    cp -p "$SRC" "$DEST"
-  elif [[ "$DEST" -nt "$SRC" ]]; then
-    echo "⚠️  Destination file '$file' is newer than source and differs."
-    echo "πŸ” Showing diff:"
-    diff -u "$DEST" "$SRC" || true
-    echo -n "❓ Overwrite the authoritative '$file' with the older source version? [y/N]: "
-    read -r response
-    if [[ "$response" == "y" || "$response" == "Y" ]]; then
-      echo "πŸ“€ Overwriting with older source: $file"
-      cp -p "$SRC" "$DEST"
-    else
-      echo "❌ Skipping: $file"
-    fi
-  else
-    echo "⚠️  Files differ but timestamps are equal: $file"
-    echo "πŸ” Showing diff:"
-    diff -u "$DEST" "$SRC" || true
-    echo -n "❓ Overwrite anyway? [y/N]: "
-    read -r response
-    if [[ "$response" == "y" || "$response" == "Y" ]]; then
-      cp -p "$SRC" "$DEST"
-      echo "πŸ“€ Overwritten."
-    else
-      echo "❌ Skipped."
-    fi
-  fi
-done
-
-echo "βœ… Capture complete."
diff --git a/developer/script_Deb-12.10_gcc-12.4.1πŸ–‰/RT_extensions_libcpp_save.sh b/developer/script_Deb-12.10_gcc-12.4.1πŸ–‰/RT_extensions_libcpp_save.sh
deleted file mode 100755 (executable)
index fe2a212..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-#!/bin/bash
-set -euo pipefail
-
-# provides RT_CPP_FILES
-source "$(dirname "$0")/environment.sh"
-
-# Save original versions of libcpp files to prevent accidental loss
-# Appends _orig after the .cc extension (e.g., macro.cc β†’ macro.cc_orig)
-# Files remain in place but can be manually diffed or restored if needed
-
-if [[ -z "${ROOT:-}" ]]; then
-  echo "❌ ROOT environment variable is not set. Aborting."
-  exit 1
-fi
-if [[ -z "${SCRIPT_DIR:-}" ]]; then
-  echo "❌ SCRIPT_DIR environment variable is not set. Aborting."
-  exit 1
-fi
-
-TARGETDIR="$GCC_SRC/libcpp/"
-
-if [[ ! -d "$TARGETDIR" ]]; then
-  echo "❌ Target directory '$TARGETDIR' does not exist."
-  exit 1
-fi
-
-echo "πŸ“¦ Saving original copies of target files..."
-
-for file in "${RT_CPP_FILES[@]}"; do
-  SRC="$TARGETDIR/$file"
-  BACKUP="$SRC"_orig
-
-  if [[ ! -f "$SRC" ]]; then
-    echo "⚠️  Source file '$SRC' not found. Skipping."
-    continue
-  fi
-
-  if [[ -f "$BACKUP" ]]; then
-    echo "βœ… Already saved: $file β†’ $(basename "$BACKUP")"
-  else
-    cp -p "$SRC" "$BACKUP"
-    echo "πŸ“ Saved: $file β†’ $(basename "$BACKUP")"
-  fi
-done
-
-echo "βœ… All originals saved."
index d4f81ea..767e6af 100755 (executable)
@@ -15,3 +15,12 @@ source "$SCRIPT_DIR/environment.sh"
 
 echo "Toolchain build complete"
 "$TOOLCHAIN/bin/gcc" --version
+
+# test
+
+./RT_extentions_libcpp_save.sh
+./RT_extentions_install.sh
+./rebuild_gcc.sh
+
+echo "Toolchain built with RT_extensions installed"
+"$TOOLCHAIN/bin/gcc" --version
index 009e01e..3b319ec 100755 (executable)
@@ -19,6 +19,10 @@ source "$(dirname "$0")/environment.sh"
   ! ! rmdir "$SRC" >& /dev/null && echo "rmdir $SRC"
   ! ! rmdir "$UPSTREAM" >& /dev/null && echo "rmdir $UPSTREAM"
 
+# Remove binaries from toolchain (if they were copied to release, those copies remain).
+#
+  "./clean_toolchain.sh"
+
 # Remove project directories
 #
   for dir in "${PROJECT_SUBDIR_LIST[@]}" "${PROJECT_DIR_LIST[@]}"; do
diff --git a/developer/script_Deb-12.10_gcc-12.4.1πŸ–‰/clean_toolchain.sh b/developer/script_Deb-12.10_gcc-12.4.1πŸ–‰/clean_toolchain.sh
new file mode 100755 (executable)
index 0000000..435c1ab
--- /dev/null
@@ -0,0 +1,17 @@
+#!/bin/bash
+# clean_toolchain.sh β€“ Remove installed GCC toolchain artifacts
+
+set -euo pipefail
+
+source "$(dirname "$0")/environment.sh"
+
+echo "🧹 Cleaning installed toolchain at: $TOOLCHAIN"
+
+if [[ -d "$TOOLCHAIN" ]]; then
+  echo "rm -rf $TOOLCHAIN"
+  rm -rf "$TOOLCHAIN"
+else
+  echo "⚠️ Toolchain directory not found: $TOOLCHAIN (nothing to remove)"
+fi
+
+echo "βœ… Installed toolchain cleaned."
diff --git a/developer/script_Deb-12.10_gcc-12.4.1πŸ–‰/ext_capture.sh b/developer/script_Deb-12.10_gcc-12.4.1πŸ–‰/ext_capture.sh
new file mode 100755 (executable)
index 0000000..ab0813f
--- /dev/null
@@ -0,0 +1,100 @@
+#!/bin/bash
+set -euo pipefail
+
+# provides RT_CPP_FILES
+source "$(dirname "$0")/environment.sh"
+
+
+echo "⚠️  You probably don't want to run this script. The files in \$ROOT/script_Deb-12.10_gcc-12.4.1πŸ–‰/library are intended to be the authoritative copies."
+echo "So you did the bad thing and edited the files directly in the GCC source tree? Then this script is for you. ;-)"
+echo
+
+echo -n "Continue❓ [y/N]: "
+read -r response
+if [[ "$response" == "y" || "$response" == "Y" ]]; then
+  :
+else
+  exit 1
+fi
+
+
+if [[ -z "${ROOT:-}" ]]; then
+  echo "❌ ROOT environment variable is not set. Aborting."
+  exit 1
+fi
+if [[ -z "${SCRIPT_DIR:-}" ]]; then
+  echo "❌ SCRIPT_DIR environment variable is not set. Aborting."
+  exit 1
+fi
+SRCDIR="library/"
+DESTDIR="$GCC_SRC/libcpp/"
+
+
+SRCDIR="$ROOT/source/gcc-12.2.0/libcpp"
+DESTDIR="$ROOT/script_Deb-12.10_gcc-12.4.1πŸ–‰/library"
+
+if [[ ! -d "$SRCDIR" ]]; then
+  echo "❌ Source directory '$SRCDIR' does not exist."
+  exit 1
+fi
+
+if [[ ! -d "$DESTDIR" ]]; then
+  echo "❌ Destination directory '$DESTDIR' does not exist."
+  exit 1
+fi
+
+echo "πŸ“‹ Checking files in $SRCDIR to copy to $DESTDIR..."
+
+for file in "${RT_CPP_FILES[@]}"; do
+  SRC="$SRCDIR/$file"
+  DEST="$DESTDIR/$file"
+
+  mkdir -p "$(dirname "$DEST")"
+
+  if [[ ! -f "$SRC" ]]; then
+    echo "⚠️  Source file '$SRC' not found. Skipping."
+    continue
+  fi
+
+  if [[ ! -f "$DEST" ]]; then
+    echo "πŸ“€ No destination file. Copying: $file"
+    cp -p "$SRC" "$DEST"
+    continue
+  fi
+
+  if cmp -s "$SRC" "$DEST"; then
+    echo "βœ… No changes: $file"
+    continue
+  fi
+
+  if [[ "$SRC" -nt "$DEST" ]]; then
+    echo "πŸ“€ Source is newer and differs. Copying: $file"
+    cp -p "$SRC" "$DEST"
+  elif [[ "$DEST" -nt "$SRC" ]]; then
+    echo "⚠️  Destination file '$file' is newer than source and differs."
+    echo "πŸ” Showing diff:"
+    diff -u "$DEST" "$SRC" || true
+    echo -n "❓ Overwrite the authoritative '$file' with the older source version? [y/N]: "
+    read -r response
+    if [[ "$response" == "y" || "$response" == "Y" ]]; then
+      echo "πŸ“€ Overwriting with older source: $file"
+      cp -p "$SRC" "$DEST"
+    else
+      echo "❌ Skipping: $file"
+    fi
+  else
+    echo "⚠️  Files differ but timestamps are equal: $file"
+    echo "πŸ” Showing diff:"
+    diff -u "$DEST" "$SRC" || true
+    echo -n "❓ Overwrite anyway? [y/N]: "
+    read -r response
+    if [[ "$response" == "y" || "$response" == "Y" ]]; then
+      cp -p "$SRC" "$DEST"
+      echo "πŸ“€ Overwritten."
+    else
+      echo "❌ Skipped."
+    fi
+  fi
+done
+
+echo "βœ… Capture complete."
diff --git a/developer/script_Deb-12.10_gcc-12.4.1πŸ–‰/ext_diff.sh b/developer/script_Deb-12.10_gcc-12.4.1πŸ–‰/ext_diff.sh
new file mode 100755 (executable)
index 0000000..4c928e6
--- /dev/null
@@ -0,0 +1,55 @@
+#!/bin/bash
+set -euo pipefail
+
+# Provides RT_CPP_FILES
+source "$(dirname "$0")/environment.sh"
+
+if [[ -z "${ROOT:-}" ]]; then
+  echo "❌ ROOT environment variable is not set. Aborting."
+  exit 1
+fi
+if [[ -z "${SCRIPT_DIR:-}" ]]; then
+  echo "❌ SCRIPT_DIR environment variable is not set. Aborting."
+  exit 1
+fi
+
+SRCDIR="library/"
+DESTDIR="$GCC_SRC/libcpp/"
+
+if [[ ! -d "$SRCDIR" ]]; then
+  echo "❌ Source directory '$SRCDIR' does not exist."
+  exit 1
+fi
+
+if [[ ! -d "$DESTDIR" ]]; then
+  echo "❌ Destination directory '$DESTDIR' does not exist."
+  exit 1
+fi
+
+echo "πŸ” Diffing library β†” libcpp..."
+
+for file in "${RT_CPP_FILES[@]}"; do
+  SRC="$SRCDIR/$file"
+  DEST="$DESTDIR/$file"
+
+  echo "πŸ”Έ $file"
+
+  if [[ ! -f "$SRC" ]]; then
+    echo "  βš οΈ  Missing in library/: $SRC"
+    continue
+  fi
+
+  if [[ ! -f "$DEST" ]]; then
+    echo "  βš οΈ  Missing in libcpp/: $DEST"
+    continue
+  fi
+
+  if cmp -s "$SRC" "$DEST"; then
+    echo "  βœ… No differences."
+  else
+    echo "  β— Differences found:"
+    diff -u "$DEST" "$SRC" || true
+  fi
+done
+
+echo "βœ… Diff check complete."
diff --git a/developer/script_Deb-12.10_gcc-12.4.1πŸ–‰/ext_install.sh b/developer/script_Deb-12.10_gcc-12.4.1πŸ–‰/ext_install.sh
new file mode 100755 (executable)
index 0000000..33033a5
--- /dev/null
@@ -0,0 +1,58 @@
+#!/bin/bash
+# transfer.sh β€“ Install RT library files into GCC libcpp source tree.
+# Usage:
+#   ./transfer.sh             β†’ transfers all files in RT_CPP_FILES
+#   ./transfer.sh init.cc     β†’ transfers only init.cc
+#   ./transfer.sh init.cc macro.cc  β†’ transfers just those
+
+set -euo pipefail
+
+# provides: $ROOT, $RT_CPP_FILES, $GCC_SRC
+source "$(dirname "$0")/environment.sh"
+
+SRCDIR="library"
+DESTDIR="$GCC_SRC/libcpp"
+
+# Validate environment
+[[ -z "${ROOT:-}" ]] && { echo "❌ ROOT is not set. Aborting."; exit 1; }
+[[ ! -d "$SRCDIR" ]] && { echo "❌ Source directory '$SRCDIR' missing."; exit 1; }
+[[ ! -d "$DESTDIR" ]] && { echo "❌ Destination directory '$DESTDIR' missing."; exit 1; }
+
+# Determine list of files to transfer
+if [[ $# -eq 0 ]]; then
+  file_list=("${RT_CPP_FILES[@]}")
+else
+  file_list=("$@")
+fi
+
+echo "πŸ“‹ Transferring files to $DESTDIR..."
+
+for file in "${file_list[@]}"; do
+  src="$SRCDIR/$file"
+  dest="$DESTDIR/$file"
+
+  if [[ ! -f "$src" ]]; then
+    echo "⚠️  Missing source file: $src"
+    continue
+  fi
+
+  if [[ ! -f "$dest" || "$src" -nt "$dest" ]]; then
+    echo "πŸ“₯ Copying (newer or missing): $file"
+    cp -p "$src" "$dest"
+  elif [[ "$dest" -nt "$src" ]]; then
+    echo "⚠️  Destination '$file' is newer than source."
+    diff -u "$dest" "$src" || true
+    echo -n "❓ Overwrite destination '$file'? [y/N]: "
+    read -r response
+    if [[ "$response" =~ ^[Yy]$ ]]; then
+      echo "πŸ“₯ Overwriting: $file"
+      cp -p "$src" "$dest"
+    else
+      echo "⏭️  Skipped: $file"
+    fi
+  else
+    echo "βœ… Up-to-date: $file"
+  fi
+done
+
+echo "βœ… Transfer complete."
diff --git a/developer/script_Deb-12.10_gcc-12.4.1πŸ–‰/ext_save.sh b/developer/script_Deb-12.10_gcc-12.4.1πŸ–‰/ext_save.sh
new file mode 100755 (executable)
index 0000000..fe2a212
--- /dev/null
@@ -0,0 +1,46 @@
+#!/bin/bash
+set -euo pipefail
+
+# provides RT_CPP_FILES
+source "$(dirname "$0")/environment.sh"
+
+# Save original versions of libcpp files to prevent accidental loss
+# Appends _orig after the .cc extension (e.g., macro.cc β†’ macro.cc_orig)
+# Files remain in place but can be manually diffed or restored if needed
+
+if [[ -z "${ROOT:-}" ]]; then
+  echo "❌ ROOT environment variable is not set. Aborting."
+  exit 1
+fi
+if [[ -z "${SCRIPT_DIR:-}" ]]; then
+  echo "❌ SCRIPT_DIR environment variable is not set. Aborting."
+  exit 1
+fi
+
+TARGETDIR="$GCC_SRC/libcpp/"
+
+if [[ ! -d "$TARGETDIR" ]]; then
+  echo "❌ Target directory '$TARGETDIR' does not exist."
+  exit 1
+fi
+
+echo "πŸ“¦ Saving original copies of target files..."
+
+for file in "${RT_CPP_FILES[@]}"; do
+  SRC="$TARGETDIR/$file"
+  BACKUP="$SRC"_orig
+
+  if [[ ! -f "$SRC" ]]; then
+    echo "⚠️  Source file '$SRC' not found. Skipping."
+    continue
+  fi
+
+  if [[ -f "$BACKUP" ]]; then
+    echo "βœ… Already saved: $file β†’ $(basename "$BACKUP")"
+  else
+    cp -p "$SRC" "$BACKUP"
+    echo "πŸ“ Saved: $file β†’ $(basename "$BACKUP")"
+  fi
+done
+
+echo "βœ… All originals saved."
index 3633f3a..bd83444 100644 (file)
@@ -167,7 +167,6 @@ static void cpp_pop_definition (cpp_reader *, struct def_pragma_macro *);
   D(assert        ,T_ASSERT        ,EXTENSION   ,DEPRECATED)    /* SVR4 */ \
   D(unassert      ,T_UNASSERT      ,EXTENSION   ,DEPRECATED)    /* SVR4 */ \
   D(sccs          ,T_SCCS          ,EXTENSION   ,IN_I)         /* SVR4? */ \
-  // RT exxtenssions:
   D(rt_macro      ,T_MACRO         ,EXTENSION   ,IN_I)                     \
   D(assign        ,T_ASSIGN        ,EXTENSION   ,IN_I)
 
index cb51af7..6bf316a 100644 (file)
@@ -928,7 +928,7 @@ enum cpp_builtin_type
   BT_HAS_INCLUDE_NEXT,         /* `__has_include_next(x)' */
 
   // RT Extension
-  BT_CAT                        /* `__CAT(SEP ,...)' */
+  BT_RT_CAT                     /* `RT_CAT(SEP ,...)' */
 };
 
 #define CPP_HASHNODE(HNODE)    ((cpp_hashnode *) (HNODE))
index b5637cb..2d5b0a7 100644 (file)
@@ -402,6 +402,8 @@ struct builtin_macro
 #define B(n, t, f)    { DSC(n), t, f }
 static const struct builtin_macro builtin_array[] =
 {
+  B("RT_CAT",           BT_RT_CAT,        true),  /* RT Extension */
+
   B("__TIMESTAMP__",    BT_TIMESTAMP,     false),
   B("__TIME__",                 BT_TIME,          false),
   B("__DATE__",                 BT_DATE,          false),
@@ -420,14 +422,14 @@ static const struct builtin_macro builtin_array[] =
   B("__has_builtin",    BT_HAS_BUILTIN,   true),
   B("__has_include",    BT_HAS_INCLUDE,   true),
   B("__has_include_next",BT_HAS_INCLUDE_NEXT,   true),
-  /* Keep builtins not used for -traditional-cpp at the end, and
-     update init_builtins() if any more are added.  */
-  B("_Pragma",          BT_PRAGMA,        true),
-  B("__STDC__",                 BT_STDC,          true),
-
-  // RT Extension
-  B("__CAT",            BT_CAT,          true),
-
+  /* The following macros are excluded when -traditional-cpp is used.
+     Therefore, they must appear at the end of this array so that they can be
+     easily removed by slicing in cpp_init_special_builtins().
+     (If you add new built-ins that should be excluded in traditional mode,
+     place them *before* __STDC__ and update cpp_init_special_builtins() accordingly.)
+  */
+  B("_Pragma",          BT_PRAGMA,        true), 
+  B("__STDC__",                 BT_STDC,          true)
 };
 #undef B
 
index 6f2be44..6053bbb 100644 (file)
@@ -681,7 +681,7 @@ _cpp_builtin_macro_text (cpp_reader *pfile, cpp_hashnode *node,
                                    node->value.builtin == BT_HAS_INCLUDE_NEXT);
       break;
 
-    case BT_CAT:
+    case BT_RT_CAT:
 
       const char *str = "calico";
       size_t len = strlen(str) + 1;
index e114d34..0051e9b 100755 (executable)
@@ -1,6 +1,9 @@
 #!/bin/bash
+# extracts (unpacks) the source tarballs held in upstream/ into the source/ directory.
 # Will not extract if target already exists
 # Delete any malformed extractions before running again
+#
+# gcc is not installed as a tar ball, rather it is git cloned as part of the downloading from upstream sources.
 
 set -euo pipefail
 
index 4754a49..953a99c 100755 (executable)
@@ -43,5 +43,5 @@ fi
 
 echo
 echo "Created project structure:"
-tree -L 2 "$REPO_HOME" 2>/dev/null || find "$REPO_HOME" -maxdepth 2
+tree -L 2 "$REPO_HOME" 2>/dev/null || find "$REPO_HOME" -maxdepth 2
 
index 44194f8..1a3f89e 100644 (file)
@@ -5,6 +5,9 @@ if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then
   exit 1
 fi
 
-export ROOT=$(dirname "$script_afp")
-export PATH="$ROOT/release/amd64_Deb-12.10_gcc-12.4.1/bin:$PATH"
+export REPO_HOME=$(dirname "$script_afp")
+export DEVELOPER="$REPO_HOME/developer"
+export SCRIPT_DIR="$DEVELOPER"/script_Deb-12.10_gcc-12.4.1πŸ–‰
+
+export PATH="$REPO_HOME/release/amd64_Deb-12.10_gcc-12.4.1/bin:$PATH"
 
diff --git a/tmp/macro.cc b/tmp/macro.cc
new file mode 100644 (file)
index 0000000..aa7d03a
--- /dev/null
@@ -0,0 +1,5082 @@
+/* Part of CPP library.  (Macro and #define handling.)
+   Copyright (C) 1986-2022 Free Software Foundation, Inc.
+   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
+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/>.
+
+ 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!  */
+
+#pragma GCC diagnostic ignored "-Wparentheses"
+
+
+#include "config.h"
+#include "system.h"
+#include "cpplib.h"
+#include "internal.h"
+
+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.  */
+struct macro_arg
+{
+  const cpp_token **first;     /* First token in unexpanded argument.  */
+  const cpp_token **expanded;  /* Macro-expanded argument.  */
+  const cpp_token *stringified;        /* Stringified argument.  */
+  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
+     -ftrack-macro-expansion is used this location tracks loci across
+     macro expansion.  */
+  const location_t *location_ptr;
+#if CHECKING_P
+  /* The number of times the iterator went forward. This useful only
+     when checking is enabled.  */
+  size_t num_forwards;
+#endif
+};
+
+/* 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;
+  /* The previous value & type of this identifier.  */
+  union _cpp_hashnode_value value;
+  node_type type;
+};
+
+static const char *vaopt_paste_error =
+  N_("'##' cannot appear at either end of __VA_OPT__");
+
+static void expand_arg (cpp_reader *, macro_arg *);
+
+/* 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:
+
+  enum update_type
+  {
+    ERROR,
+    DROP,
+    INCLUDE,
+    BEGIN,
+    END
+  };
+
+  /* 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)
+    : m_pfile (pfile),
+    m_arg (arg),
+    m_variadic (is_variadic),
+    m_last_was_paste (false),
+    m_stringify (false),
+    m_state (0),
+    m_paste_location (0),
+    m_location (0),
+    m_update (ERROR)
+  {
+  }
+
+  /* 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__");
+           return ERROR;
+         }
+       ++m_state;
+       m_location = token->src_loc;
+       m_stringify = (token->flags & STRINGIFY_ARG) != 0;
+       return BEGIN;
+      }
+    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 "
+                         "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;
+                   }
+             }
+         }
+       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;
+                 }
+
+               return END;
+             }
+         }
+       return m_update;
+      }
+
+    /* 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,
+                   "unterminated __VA_OPT__");
+    return m_state == 0;
+  }
+
+  /* Return true for # __VA_OPT__.  */
+  bool stringify () const
+  {
+    return m_stringify;
+  }
+
+ private:
+
+  /* The cpp_reader.  */
+  cpp_reader *m_pfile;
+
+  /* The __VA_ARGS__ argument.  */
+  macro_arg *m_arg;
+
+  /* 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;
+
+  /* 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;
+
+  /* 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;
+};
+
+/* 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 **,
+                                      unsigned int);
+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 *,
+                          location_t, size_t,
+                          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,
+                                  location_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 **,
+                                                          location_t *,
+                                                          const cpp_token *,
+                                                          location_t,
+                                                          location_t,
+                                                          const line_map_macro *,
+                                                          unsigned int);
+
+static const cpp_token **tokens_buff_add_token (_cpp_buff *,
+                                               location_t *,
+                                               const cpp_token *,
+                                               location_t,
+                                               location_t,
+                                               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 *);
+
+/* #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 *);
+
+static cpp_hashnode* macro_of_context (cpp_context *context);
+
+/* Statistical counter tracking the number of macros that got
+   expanded.  */
+unsigned num_expanded_macros_counter = 0;
+/* Statistical counter tracking the total number tokens resulting
+   from macro expansion.  */
+unsigned num_macro_tokens_counter = 0;
+
+/* Wrapper around cpp_get_token to skip CPP_PADDING tokens
+   and not consume CPP_EOF.  */
+static 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;
+    }
+}
+
+/* Handle meeting "__has_include" builtin macro.  */
+
+static int
+builtin_has_include (cpp_reader *pfile, cpp_hashnode *op, bool has_next)
+{
+  int result = 0;
+
+  if (!pfile->state.in_directive)
+    cpp_error (pfile, CPP_DL_ERROR,
+              "\"%s\" used outside of preprocessing directive",
+              NODE_NAME (op));
+
+  pfile->state.angled_headers = true;
+  const cpp_token *token = cpp_get_token_no_padding (pfile);
+  bool paren = token->type == CPP_OPEN_PAREN;
+  if (paren)
+    token = cpp_get_token_no_padding (pfile);
+  else
+    cpp_error (pfile, CPP_DL_ERROR,
+              "missing '(' before \"%s\" operand", NODE_NAME (op));
+  pfile->state.angled_headers = false;
+
+  bool 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 \"%s\" requires a header-name", NODE_NAME (op));
+
+  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);
+    }
+
+  if (paren
+      && cpp_get_token_no_padding (pfile)->type != CPP_CLOSE_PAREN)
+    cpp_error (pfile, CPP_DL_ERROR,
+              "missing ')' after \"%s\" operand", NODE_NAME (op));
+
+  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 \"%s\" is not used", NODE_NAME (node));
+    }
+
+  return 1;
+}
+
+/* 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)
+{
+  cpp_token *token = _cpp_temp_token (pfile);
+
+  text[len] = '\0';
+  token->type = CPP_STRING;
+  token->val.str.len = len;
+  token->val.str.text = text;
+  token->flags = 0;
+  return token;
+}
+
+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. */
+const uchar *
+_cpp_builtin_macro_text (cpp_reader *pfile, cpp_hashnode *node,
+                        location_t loc)
+{
+  const uchar *result = NULL;
+  linenum_type number = 1;
+
+  switch (node->value.builtin)
+    {
+    default:
+      cpp_error (pfile, CPP_DL_ICE, "invalid built-in macro \"%s\"",
+                NODE_NAME (node));
+      break;
+
+    case BT_TIMESTAMP:
+      {
+       if (CPP_OPTION (pfile, warn_date_time))
+         cpp_warning (pfile, CPP_W_DATE_TIME, "macro \"%s\" 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;
+    case BT_FILE:
+    case BT_FILE_NAME:
+    case BT_BASE_FILE:
+      {
+       unsigned int len;
+       const char *name;
+       uchar *buf;
+
+       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);
+         }
+       else
+         {
+           name = _cpp_get_file_name (pfile->main_file);
+           if (!name)
+             abort ();
+         }
+       if (pfile->cb.remap_filename)
+         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';
+      }
+      break;
+
+    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;
+      break;
+
+    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);
+      break;
+
+      /* __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.  */
+    case BT_STDC:
+      if (_cpp_in_system_header (pfile))
+       number = 0;
+      else
+       number = 1;
+      break;
+
+    case BT_DATE:
+    case BT_TIME:
+      if (CPP_OPTION (pfile, warn_date_time))
+       cpp_warning (pfile, CPP_W_DATE_TIME, "macro \"%s\" might prevent "
+                    "reproducible builds", NODE_NAME (node));
+      if (pfile->date == NULL)
+       {
+         /* 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.  */
+         time_t tt;
+         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"\"??:??:??\"";
+           }
+         else
+           {
+             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);
+           }
+       }
+
+      if (node->value.builtin == BT_DATE)
+       result = pfile->date;
+      else
+       result = pfile->time;
+      break;
+
+    case BT_COUNTER:
+      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;
+
+    case BT_HAS_ATTRIBUTE:
+      number = pfile->cb.has_attribute (pfile, false);
+      break;
+
+    case BT_HAS_STD_ATTRIBUTE:
+      number = pfile->cb.has_attribute (pfile, true);
+      break;
+
+    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;
+    }
+
+  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.  */
+static int
+builtin_macro (cpp_reader *pfile, cpp_hashnode *node,
+              location_t loc, location_t expand_loc)
+{
+  const uchar *buf;
+  size_t len;
+  char *nbuf;
+
+  if (node->value.builtin == BT_PRAGMA)
+    {
+      /* 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)
+       return 0;
+
+      return _cpp_do__Pragma (pfile, loc);
+    }
+
+  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 \"%s\"",
+              NODE_NAME (node));
+  _cpp_pop_buffer (pfile);
+
+  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.  */
+uchar *
+cpp_quote_string (uchar *dest, const uchar *src, unsigned int len)
+{
+  while (len--)
+    {
+      uchar c = *src++;
+
+      switch (c)
+       {
+       case '\n':
+         /* Naked LF can appear in raw string literals  */
+         c = 'n';
+         /* FALLTHROUGH */
+
+       case '\\':
+       case '"':
+         *dest++ = '\\';
+         /* FALLTHROUGH */
+
+       default:
+         *dest++ = c;
+       }
+    }
+
+  return dest;
+}
+
+/* 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)
+{
+  unsigned char *dest;
+  unsigned int i, escape_it, backslash_count = 0;
+  const cpp_token *source = NULL;
+  size_t len;
+
+  if (BUFF_ROOM (pfile->u_buff) < 3)
+    _cpp_extend_buff (pfile, &pfile->u_buff, 3);
+  dest = BUFF_FRONT (pfile->u_buff);
+  *dest++ = '"';
+
+  /* Loop, reading in the argument's tokens.  */
+  for (i = 0; i < count; i++)
+    {
+      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;
+       }
+
+      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);
+      if (escape_it)
+       len *= 4;
+      len += 3;
+
+      if ((size_t) (BUFF_LIMIT (pfile->u_buff) - dest) < len)
+       {
+         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;
+       }
+
+      /* Leading white space?  */
+      if (dest - 1 != BUFF_FRONT (pfile->u_buff))
+       {
+         if (source == NULL)
+           source = token;
+         if (source->flags & PREV_WHITE)
+           *dest++ = ' ';
+       }
+      source = NULL;
+
+      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);
+       }
+      else
+       dest = cpp_spell_token (pfile, token, dest, true);
+
+      if (token->type == CPP_OTHER && token->val.str.text[0] == '\\')
+       backslash_count++;
+      else
+       backslash_count = 0;
+    }
+
+  /* Ignore the final \ of invalid string literals.  */
+  if (backslash_count & 1)
+    {
+      cpp_error (pfile, CPP_DL_WARNING,
+                "invalid string literal, ignoring final '\\'");
+      dest--;
+    }
+
+  /* Commit the memory, including NUL, and return the token.  */
+  *dest++ = '"';
+  len = dest - BUFF_FRONT (pfile->u_buff);
+  BUFF_FRONT (pfile->u_buff) = dest + 1;
+  return new_string_token (pfile, dest - len, len);
+}
+
+/* Try to paste two tokens.  On success, return nonzero.  In any
+   case, PLHS is updated to point to the pasted token, which is
+   guaranteed to not have the PASTE_LEFT flag set.  LOCATION is
+   the virtual location used for error reporting.  */
+static bool
+paste_tokens (cpp_reader *pfile, location_t location,
+             const cpp_token **plhs, const cpp_token *rhs)
+{
+  unsigned char *buf, *end, *lhsend;
+  cpp_token *lhs;
+  unsigned int len;
+
+  len = cpp_token_len (*plhs) + cpp_token_len (rhs) + 2;
+  buf = (unsigned char *) alloca (len);
+  end = lhsend = cpp_spell_token (pfile, *plhs, buf, true);
+
+  /* Avoid comment headers, since they are still processed in stage 3.
+     It is simpler to insert a space here, rather than modifying the
+     lexer to ignore comments in some circumstances.  Simply returning
+     false doesn't work, since we want to clear the PASTE_LEFT flag.  */
+  if ((*plhs)->type == CPP_DIV && rhs->type != CPP_EQ)
+    *end++ = ' ';
+  /* In one obscure case we might see padding here.  */
+  if (rhs->type != CPP_PADDING)
+    end = cpp_spell_token (pfile, rhs, end, true);
+  *end = '\n';
+
+  cpp_push_buffer (pfile, buf, end - buf, /* from_stage3 */ true);
+  _cpp_clean_line (pfile);
+
+  /* Set pfile->cur_token as required by _cpp_lex_direct.  */
+  pfile->cur_token = _cpp_temp_token (pfile);
+  lhs = _cpp_lex_direct (pfile);
+  if (pfile->buffer->cur != pfile->buffer->rlimit)
+    {
+      location_t saved_loc = lhs->src_loc;
+
+      _cpp_pop_buffer (pfile);
+
+      unsigned char *rhsstart = lhsend;
+      if ((*plhs)->type == CPP_DIV && rhs->type != CPP_EQ)
+       rhsstart++;
+
+      /* We have to remove the PASTE_LEFT flag from the old lhs, but
+        we want to keep the new location.  */
+      *lhs = **plhs;
+      *plhs = lhs;
+      lhs->src_loc = saved_loc;
+      lhs->flags &= ~PASTE_LEFT;
+
+      /* Mandatory error for all apart from assembler.  */
+      if (CPP_OPTION (pfile, lang) != CLK_ASM)
+       cpp_error_with_line (pfile, CPP_DL_ERROR, location, 0,
+                            "pasting \"%.*s\" and \"%.*s\" does not give "
+                            "a valid preprocessing token",
+                            (int) (lhsend - buf), buf,
+                            (int) (end - rhsstart), rhsstart);
+      return false;
+    }
+
+  lhs->flags |= (*plhs)->flags & (PREV_WHITE | PREV_FALLTHROUGH);
+  *plhs = lhs;
+  _cpp_pop_buffer (pfile);
+  return true;
+}
+
+/* Handles an arbitrarily long sequence of ## operators, with initial
+   operand LHS.  This implementation is left-associative,
+   non-recursive, and finishes a paste before handling succeeding
+   ones.  If a paste fails, we back up to the RHS of the failing ##
+   operator before pushing the context containing the result of prior
+   successful pastes, with the effect that the RHS appears in the
+   output stream after the pasted LHS normally.  */
+static void
+paste_all_tokens (cpp_reader *pfile, const cpp_token *lhs)
+{
+  const cpp_token *rhs = NULL;
+  cpp_context *context = pfile->context;
+  location_t virt_loc = 0;
+
+  /* We are expanding a macro and we must have been called on a token
+     that appears at the left hand side of a ## operator.  */
+  if (macro_of_context (pfile->context) == NULL
+      || (!(lhs->flags & PASTE_LEFT)))
+    abort ();
+
+  if (context->tokens_kind == TOKENS_KIND_EXTENDED)
+    /* The caller must have called consume_next_token_from_context
+       right before calling us.  That has incremented the pointer to
+       the current virtual location.  So it now points to the location
+       of the token that comes right after *LHS.  We want the
+       resulting pasted token to have the location of the current
+       *LHS, though.  */
+    virt_loc = context->c.mc->cur_virt_loc[-1];
+  else
+    /* We are not tracking macro expansion.  So the best virtual
+       location we can get here is the expansion point of the macro we
+       are currently expanding.  */
+    virt_loc = pfile->invocation_location;
+
+  do
+    {
+      /* Take the token directly from the current context.  We can do
+        this, because we are in the replacement list of either an
+        object-like macro, or a function-like macro with arguments
+        inserted.  In either case, the constraints to #define
+        guarantee we have at least one more token.  */
+      if (context->tokens_kind == TOKENS_KIND_DIRECT)
+       rhs = FIRST (context).token++;
+      else if (context->tokens_kind == TOKENS_KIND_INDIRECT)
+       rhs = *FIRST (context).ptoken++;
+      else if (context->tokens_kind == TOKENS_KIND_EXTENDED)
+       {
+         /* So we are in presence of an extended token context, which
+            means that each token in this context has a virtual
+            location attached to it.  So let's not forget to update
+            the pointer to the current virtual location of the
+            current token when we update the pointer to the current
+            token */
+
+         rhs = *FIRST (context).ptoken++;
+         /* context->c.mc must be non-null, as if we were not in a
+            macro context, context->tokens_kind could not be equal to
+            TOKENS_KIND_EXTENDED.  */
+         context->c.mc->cur_virt_loc++;
+       }
+
+      if (rhs->type == CPP_PADDING)
+       {
+         if (rhs->flags & PASTE_LEFT)
+           abort ();
+       }
+      if (!paste_tokens (pfile, virt_loc, &lhs, rhs))
+       {
+         _cpp_backup_tokens (pfile, 1);
+         break;
+       }
+    }
+  while (rhs->flags & PASTE_LEFT);
+
+  /* Put the resulting token in its own context.  */
+  if (context->tokens_kind == TOKENS_KIND_EXTENDED)
+    {
+      location_t *virt_locs = NULL;
+      _cpp_buff *token_buf = tokens_buff_new (pfile, 1, &virt_locs);
+      tokens_buff_add_token (token_buf, virt_locs, lhs,
+                            virt_loc, 0, NULL, 0);
+      push_extended_tokens_context (pfile, context->c.mc->macro_node,
+                                   token_buf, virt_locs,
+                                   (const cpp_token **)token_buf->base, 1);
+    }
+  else
+    _cpp_push_token_context (pfile, NULL, lhs, 1);
+}
+
+/* Returns TRUE if the number of arguments ARGC supplied in an
+   invocation of the MACRO referenced by NODE is valid.  An empty
+   invocation to a macro with no parameters should pass ARGC as zero.
+
+   Note that MACRO cannot necessarily be deduced from NODE, in case
+   NODE was redefined whilst collecting arguments.  */
+bool
+_cpp_arguments_ok (cpp_reader *pfile, cpp_macro *macro, const cpp_hashnode *node, unsigned int argc)
+{
+  if (argc == macro->paramc)
+    return true;
+
+  if (argc < macro->paramc)
+    {
+      /* In C++20 (here the va_opt flag is used), and also as a GNU
+        extension, variadic arguments are allowed to not appear in
+        the invocation at all.
+        e.g. #define debug(format, args...) something
+        debug("string");
+
+        This is exactly the same as if an empty variadic list had been
+        supplied - debug("string", ).  */
+
+      if (argc + 1 == macro->paramc && macro->variadic)
+       {
+         if (CPP_PEDANTIC (pfile) && ! macro->syshdr
+             && ! CPP_OPTION (pfile, va_opt))
+           {
+             if (CPP_OPTION (pfile, cplusplus))
+               cpp_error (pfile, CPP_DL_PEDWARN,
+                          "ISO C++11 requires at least one argument "
+                          "for the \"...\" in a variadic macro");
+             else
+               cpp_error (pfile, CPP_DL_PEDWARN,
+                          "ISO C99 requires at least one argument "
+                          "for the \"...\" in a variadic macro");
+           }
+         return true;
+       }
+
+      cpp_error (pfile, CPP_DL_ERROR,
+                "macro \"%s\" requires %u arguments, but only %u given",
+                NODE_NAME (node), macro->paramc, argc);
+    }
+  else
+    cpp_error (pfile, CPP_DL_ERROR,
+              "macro \"%s\" passed %u arguments, but takes just %u",
+              NODE_NAME (node), argc, macro->paramc);
+
+  if (macro->line > RESERVED_LOCATION_COUNT)
+    cpp_error_at (pfile, CPP_DL_NOTE, macro->line, "macro \"%s\" defined here",
+                 NODE_NAME (node));
+
+  return false;
+}
+
+/* Reads and returns the arguments to a function-like macro
+   invocation.  Assumes the opening parenthesis has been processed.
+   If there is an error, emits an appropriate diagnostic and returns
+   NULL.  Each argument is terminated by a CPP_EOF token, for the
+   future benefit of expand_arg().  If there are any deferred
+   #pragma directives among macro arguments, store pointers to the
+   CPP_PRAGMA ... CPP_PRAGMA_EOL tokens into *PRAGMA_BUFF buffer.
+
+   What is returned is the buffer that contains the memory allocated
+   to hold the macro arguments.  NODE is the name of the macro this
+   function is dealing with.  If NUM_ARGS is non-NULL, *NUM_ARGS is
+   set to the actual number of macro arguments allocated in the
+   returned buffer.  */
+static _cpp_buff *
+collect_args (cpp_reader *pfile, const cpp_hashnode *node,
+             _cpp_buff **pragma_buff, unsigned *num_args)
+{
+  _cpp_buff *buff, *base_buff;
+  cpp_macro *macro;
+  macro_arg *args, *arg;
+  const cpp_token *token;
+  unsigned int argc;
+  location_t virt_loc;
+  bool track_macro_expansion_p = CPP_OPTION (pfile, track_macro_expansion);
+  unsigned num_args_alloced = 0;
+
+  macro = node->value.macro;
+  if (macro->paramc)
+    argc = macro->paramc;
+  else
+    argc = 1;
+
+#define DEFAULT_NUM_TOKENS_PER_MACRO_ARG 50
+#define ARG_TOKENS_EXTENT 1000
+
+  buff = _cpp_get_buff (pfile, argc * (DEFAULT_NUM_TOKENS_PER_MACRO_ARG
+                                      * sizeof (cpp_token *)
+                                      + sizeof (macro_arg)));
+  base_buff = buff;
+  args = (macro_arg *) buff->base;
+  memset (args, 0, argc * sizeof (macro_arg));
+  buff->cur = (unsigned char *) &args[argc];
+  arg = args, argc = 0;
+
+  /* Collect the tokens making up each argument.  We don't yet know
+     how many arguments have been supplied, whether too many or too
+     few.  Hence the slightly bizarre usage of "argc" and "arg".  */
+  do
+    {
+      unsigned int paren_depth = 0;
+      unsigned int ntokens = 0;
+      unsigned virt_locs_capacity = DEFAULT_NUM_TOKENS_PER_MACRO_ARG;
+      num_args_alloced++;
+
+      argc++;
+      arg->first = (const cpp_token **) buff->cur;
+      if (track_macro_expansion_p)
+       {
+         virt_locs_capacity = DEFAULT_NUM_TOKENS_PER_MACRO_ARG;
+         arg->virt_locs = XNEWVEC (location_t,
+                                   virt_locs_capacity);
+       }
+
+      for (;;)
+       {
+         /* Require space for 2 new tokens (including a CPP_EOF).  */
+         if ((unsigned char *) &arg->first[ntokens + 2] > buff->limit)
+           {
+             buff = _cpp_append_extend_buff (pfile, buff,
+                                             ARG_TOKENS_EXTENT
+                                             * sizeof (cpp_token *));
+             arg->first = (const cpp_token **) buff->cur;
+           }
+         if (track_macro_expansion_p
+             && (ntokens + 2 > virt_locs_capacity))
+           {
+             virt_locs_capacity += ARG_TOKENS_EXTENT;
+             arg->virt_locs = XRESIZEVEC (location_t,
+                                          arg->virt_locs,
+                                          virt_locs_capacity);
+           }
+
+         token = cpp_get_token_1 (pfile, &virt_loc);
+
+         if (token->type == CPP_PADDING)
+           {
+             /* Drop leading padding.  */
+             if (ntokens == 0)
+               continue;
+           }
+         else if (token->type == CPP_OPEN_PAREN)
+           paren_depth++;
+         else if (token->type == CPP_CLOSE_PAREN)
+           {
+             if (paren_depth-- == 0)
+               break;
+           }
+         else if (token->type == CPP_COMMA)
+           {
+             /* A comma does not terminate an argument within
+                parentheses or as part of a variable argument.  */
+             if (paren_depth == 0
+                 && ! (macro->variadic && argc == macro->paramc))
+               break;
+           }
+         else if (token->type == CPP_EOF
+                  || (token->type == CPP_HASH && token->flags & BOL))
+           break;
+         else if (token->type == CPP_PRAGMA && !(token->flags & PRAGMA_OP))
+           {
+             cpp_token *newtok = _cpp_temp_token (pfile);
+
+             /* CPP_PRAGMA token lives in directive_result, which will
+                be overwritten on the next directive.  */
+             *newtok = *token;
+             token = newtok;
+             do
+               {
+                 if (*pragma_buff == NULL
+                     || BUFF_ROOM (*pragma_buff) < sizeof (cpp_token *))
+                   {
+                     _cpp_buff *next;
+                     if (*pragma_buff == NULL)
+                       *pragma_buff
+                         = _cpp_get_buff (pfile, 32 * sizeof (cpp_token *));
+                     else
+                       {
+                         next = *pragma_buff;
+                         *pragma_buff
+                           = _cpp_get_buff (pfile,
+                                            (BUFF_FRONT (*pragma_buff)
+                                             - (*pragma_buff)->base) * 2);
+                         (*pragma_buff)->next = next;
+                       }
+                   }
+                 *(const cpp_token **) BUFF_FRONT (*pragma_buff) = token;
+                 BUFF_FRONT (*pragma_buff) += sizeof (cpp_token *);
+                 if (token->type == CPP_PRAGMA_EOL)
+                   break;
+                 token = cpp_get_token_1 (pfile, &virt_loc);
+               }
+             while (token->type != CPP_EOF);
+
+             /* In deferred pragmas parsing_args and prevent_expansion
+                had been changed, reset it.  */
+             pfile->state.parsing_args = 2;
+             pfile->state.prevent_expansion = 1;
+
+             if (token->type == CPP_EOF)
+               break;
+             else
+               continue;
+           }
+         set_arg_token (arg, token, virt_loc,
+                        ntokens, MACRO_ARG_TOKEN_NORMAL,
+                        CPP_OPTION (pfile, track_macro_expansion));
+         ntokens++;
+       }
+
+      /* Drop trailing padding.  */
+      while (ntokens > 0 && arg->first[ntokens - 1]->type == CPP_PADDING)
+       ntokens--;
+
+      arg->count = ntokens;
+      /* Append an EOF to mark end-of-argument.  */
+      set_arg_token (arg, &pfile->endarg, token->src_loc,
+                    ntokens, MACRO_ARG_TOKEN_NORMAL,
+                    CPP_OPTION (pfile, track_macro_expansion));
+
+      /* Terminate the argument.  Excess arguments loop back and
+        overwrite the final legitimate argument, before failing.  */
+      if (argc <= macro->paramc)
+       {
+         buff->cur = (unsigned char *) &arg->first[ntokens + 1];
+         if (argc != macro->paramc)
+           arg++;
+       }
+    }
+  while (token->type != CPP_CLOSE_PAREN && token->type != CPP_EOF);
+
+  if (token->type == CPP_EOF)
+    {
+      /* Unless the EOF is marking the end of an argument, it's a fake
+        one from the end of a file that _cpp_clean_line will not have
+        advanced past.  */
+      if (token == &pfile->endarg)
+       _cpp_backup_tokens (pfile, 1);
+      cpp_error (pfile, CPP_DL_ERROR,
+                "unterminated argument list invoking macro \"%s\"",
+                NODE_NAME (node));
+    }
+  else
+    {
+      /* A single empty argument is counted as no argument.  */
+      if (argc == 1 && macro->paramc == 0 && args[0].count == 0)
+       argc = 0;
+      if (_cpp_arguments_ok (pfile, macro, node, argc))
+       {
+         /* GCC has special semantics for , ## b where b is a varargs
+            parameter: we remove the comma if b was omitted entirely.
+            If b was merely an empty argument, the comma is retained.
+            If the macro takes just one (varargs) parameter, then we
+            retain the comma only if we are standards conforming.
+
+            If FIRST is NULL replace_args () swallows the comma.  */
+         if (macro->variadic && (argc < macro->paramc
+                                 || (argc == 1 && args[0].count == 0
+                                     && !CPP_OPTION (pfile, std))))
+           args[macro->paramc - 1].first = NULL;
+         if (num_args)
+           *num_args = num_args_alloced;
+         return base_buff;
+       }
+    }
+
+  /* An error occurred.  */
+  _cpp_release_buff (pfile, base_buff);
+  return NULL;
+}
+
+/* Search for an opening parenthesis to the macro of NODE, in such a
+   way that, if none is found, we don't lose the information in any
+   intervening padding tokens.  If we find the parenthesis, collect
+   the arguments and return the buffer containing them.  PRAGMA_BUFF
+   argument is the same as in collect_args.  If NUM_ARGS is non-NULL,
+   *NUM_ARGS is set to the number of arguments contained in the
+   returned buffer.  */
+static _cpp_buff *
+funlike_invocation_p (cpp_reader *pfile, cpp_hashnode *node,
+                     _cpp_buff **pragma_buff, unsigned *num_args)
+{
+  const cpp_token *token, *padding = NULL;
+
+  for (;;)
+    {
+      token = cpp_get_token (pfile);
+      if (token->type != CPP_PADDING)
+       break;
+      gcc_assert ((token->flags & PREV_WHITE) == 0);
+      if (padding == NULL
+         || padding->val.source == NULL
+         || (!(padding->val.source->flags & PREV_WHITE)
+             && token->val.source == NULL))
+       padding = token;
+    }
+
+  if (token->type == CPP_OPEN_PAREN)
+    {
+      pfile->state.parsing_args = 2;
+      return collect_args (pfile, node, pragma_buff, num_args);
+    }
+  
+  /* Back up.  A CPP_EOF is either an EOF from an argument we're
+     expanding, or a fake one from lex_direct.  We want to backup the
+     former, but not the latter.  We may have skipped padding, in
+     which case backing up more than one token when expanding macros
+     is in general too difficult.  We re-insert it in its own
+     context.  */
+  if (token->type != CPP_EOF || token == &pfile->endarg)
+    {
+      _cpp_backup_tokens (pfile, 1);
+      if (padding)
+       _cpp_push_token_context (pfile, NULL, padding, 1);
+    }
+
+  return NULL;
+}
+
+/* Return the real number of tokens in the expansion of MACRO.  */
+static inline unsigned int
+macro_real_token_count (const cpp_macro *macro)
+{
+  if (__builtin_expect (!macro->extra_tokens, true))
+    return macro->count;
+
+  for (unsigned i = macro->count; i--;)
+    if (macro->exp.tokens[i].type != CPP_PASTE)
+      return i + 1;
+
+  return 0;
+}
+
+/* Push the context of a macro with hash entry NODE onto the context
+   stack.  If we can successfully expand the macro, we push a context
+   containing its yet-to-be-rescanned replacement list and return one.
+   If there were additionally any unexpanded deferred #pragma
+   directives among macro arguments, push another context containing
+   the pragma tokens before the yet-to-be-rescanned replacement list
+   and return two.  Otherwise, we don't push a context and return
+   zero. LOCATION is the location of the expansion point of the
+   macro.  */
+static int
+enter_macro_context (cpp_reader *pfile, cpp_hashnode *node,
+                    const cpp_token *result, location_t location)
+{
+  /* The presence of a macro invalidates a file's controlling macro.  */
+  pfile->mi_valid = false;
+
+  pfile->state.angled_headers = false;
+
+  /* From here to when we push the context for the macro later down
+     this function, we need to flag the fact that we are about to
+     expand a macro.  This is useful when -ftrack-macro-expansion is
+     turned off.  In that case, we need to record the location of the
+     expansion point of the top-most macro we are about to to expand,
+     into pfile->invocation_location.  But we must not record any such
+     location once the process of expanding the macro starts; that is,
+     we must not do that recording between now and later down this
+     function where set this flag to FALSE.  */
+  pfile->about_to_expand_macro_p = true;
+
+  if (cpp_user_macro_p (node))
+    {
+      cpp_macro *macro = node->value.macro;
+      _cpp_buff *pragma_buff = NULL;
+
+      if (macro->fun_like)
+       {
+         _cpp_buff *buff;
+         unsigned num_args = 0;
+
+         pfile->state.prevent_expansion++;
+         pfile->keep_tokens++;
+         pfile->state.parsing_args = 1;
+         buff = funlike_invocation_p (pfile, node, &pragma_buff,
+                                      &num_args);
+         pfile->state.parsing_args = 0;
+         pfile->keep_tokens--;
+         pfile->state.prevent_expansion--;
+
+         if (buff == NULL)
+           {
+             if (CPP_WTRADITIONAL (pfile) && ! node->value.macro->syshdr)
+               cpp_warning (pfile, CPP_W_TRADITIONAL,
+ "function-like macro \"%s\" must be used with arguments in traditional C",
+                            NODE_NAME (node));
+
+             if (pragma_buff)
+               _cpp_release_buff (pfile, pragma_buff);
+
+             pfile->about_to_expand_macro_p = false;
+             return 0;
+           }
+
+         if (macro->paramc > 0)
+           replace_args (pfile, node, macro,
+                         (macro_arg *) buff->base,
+                         location);
+         /* Free the memory used by the arguments of this
+            function-like macro.  This memory has been allocated by
+            funlike_invocation_p and by replace_args.  */
+         delete_macro_args (buff, num_args);
+       }
+
+      /* Disable the macro within its expansion.  */
+      node->flags |= NODE_DISABLED;
+
+      /* Laziness can only affect the expansion tokens of the macro,
+        not its fun-likeness or parameters.  */
+      _cpp_maybe_notify_macro_use (pfile, node, location);
+      if (pfile->cb.used)
+       pfile->cb.used (pfile, location, node);
+
+      macro->used = 1;
+
+      if (macro->paramc == 0)
+       {
+         unsigned tokens_count = macro_real_token_count (macro);
+         if (CPP_OPTION (pfile, track_macro_expansion))
+           {
+             unsigned int i;
+             const cpp_token *src = macro->exp.tokens;
+             const line_map_macro *map;
+             location_t *virt_locs = NULL;
+             _cpp_buff *macro_tokens
+               = tokens_buff_new (pfile, tokens_count, &virt_locs);
+
+             /* Create a macro map to record the locations of the
+                tokens that are involved in the expansion. LOCATION
+                is the location of the macro expansion point.  */
+             map = linemap_enter_macro (pfile->line_table,
+                                        node, location, tokens_count);
+             for (i = 0; i < tokens_count; ++i)
+               {
+                 tokens_buff_add_token (macro_tokens, virt_locs,
+                                        src, src->src_loc,
+                                        src->src_loc, map, i);
+                 ++src;
+               }
+             push_extended_tokens_context (pfile, node,
+                                           macro_tokens,
+                                           virt_locs,
+                                           (const cpp_token **)
+                                           macro_tokens->base,
+                                           tokens_count);
+           }
+         else
+           _cpp_push_token_context (pfile, node, macro->exp.tokens,
+                                    tokens_count);
+         num_macro_tokens_counter += tokens_count;
+       }
+
+      if (pragma_buff)
+       {
+         if (!pfile->state.in_directive)
+           _cpp_push_token_context (pfile, NULL,
+                                    padding_token (pfile, result), 1);
+         do
+           {
+             unsigned tokens_count;
+             _cpp_buff *tail = pragma_buff->next;
+             pragma_buff->next = NULL;
+             tokens_count = ((const cpp_token **) BUFF_FRONT (pragma_buff)
+                             - (const cpp_token **) pragma_buff->base);
+             push_ptoken_context (pfile, NULL, pragma_buff,
+                                  (const cpp_token **) pragma_buff->base,
+                                  tokens_count);
+             pragma_buff = tail;
+             if (!CPP_OPTION (pfile, track_macro_expansion))
+               num_macro_tokens_counter += tokens_count;
+
+           }
+         while (pragma_buff != NULL);
+         pfile->about_to_expand_macro_p = false;
+         return 2;
+       }
+
+      pfile->about_to_expand_macro_p = false;
+      return 1;
+    }
+
+  pfile->about_to_expand_macro_p = false;
+  /* Handle built-in macros and the _Pragma operator.  */
+  {
+    location_t expand_loc;
+
+    if (/* The top-level macro invocation that triggered the expansion
+          we are looking at is with a function-like user macro ...  */
+       cpp_fun_like_macro_p (pfile->top_most_macro_node)
+       /* ... and we are tracking the macro expansion.  */
+       && CPP_OPTION (pfile, track_macro_expansion))
+      /* Then the location of the end of the macro invocation is the
+        location of the expansion point of this macro.  */
+      expand_loc = location;
+    else
+      /* Otherwise, the location of the end of the macro invocation is
+        the location of the expansion point of that top-level macro
+        invocation.  */
+      expand_loc = pfile->invocation_location;
+
+    return builtin_macro (pfile, node, location, expand_loc);
+  }
+}
+
+/* De-allocate the memory used by BUFF which is an array of instances
+   of macro_arg.  NUM_ARGS is the number of instances of macro_arg
+   present in BUFF.  */
+static void
+delete_macro_args (_cpp_buff *buff, unsigned num_args)
+{
+  macro_arg *macro_args;
+  unsigned i;
+
+  if (buff == NULL)
+    return;
+
+  macro_args = (macro_arg *) buff->base;
+
+  /* Walk instances of macro_arg to free their expanded tokens as well
+     as their macro_arg::virt_locs members.  */
+  for (i = 0; i < num_args; ++i)
+    {
+      if (macro_args[i].expanded)
+       {
+         free (macro_args[i].expanded);
+         macro_args[i].expanded = NULL;
+       }
+      if (macro_args[i].virt_locs)
+       {
+         free (macro_args[i].virt_locs);
+         macro_args[i].virt_locs = NULL;
+       }
+      if (macro_args[i].expanded_virt_locs)
+       {
+         free (macro_args[i].expanded_virt_locs);
+         macro_args[i].expanded_virt_locs = NULL;
+       }
+    }
+  _cpp_free_buff (buff);
+}
+
+/* Set the INDEXth token of the macro argument ARG. TOKEN is the token
+   to set, LOCATION is its virtual location.  "Virtual" location means
+   the location that encodes loci across macro expansion. Otherwise
+   it has to be TOKEN->SRC_LOC.  KIND is the kind of tokens the
+   argument ARG is supposed to contain.  Note that ARG must be
+   tailored so that it has enough room to contain INDEX + 1 numbers of
+   tokens, at least.  */
+static void
+set_arg_token (macro_arg *arg, const cpp_token *token,
+              location_t location, size_t index,
+              enum macro_arg_token_kind kind,
+              bool track_macro_exp_p)
+{
+  const cpp_token **token_ptr;
+  location_t *loc = NULL;
+
+  token_ptr =
+    arg_token_ptr_at (arg, index, kind,
+                     track_macro_exp_p ? &loc : NULL);
+  *token_ptr = token;
+
+  if (loc != NULL)
+    {
+      /* We can't set the location of a stringified argument
+        token and we can't set any location if we aren't tracking
+        macro expansion locations.   */
+      gcc_checking_assert (kind != MACRO_ARG_TOKEN_STRINGIFIED
+                          && track_macro_exp_p);
+      *loc = location;
+    }
+}
+
+/* Get the pointer to the location of the argument token of the
+   function-like macro argument ARG.  This function must be called
+   only when we -ftrack-macro-expansion is on.  */
+static const location_t *
+get_arg_token_location (const macro_arg *arg,
+                       enum macro_arg_token_kind kind)
+{
+  const location_t *loc = NULL;
+  const cpp_token **token_ptr =
+    arg_token_ptr_at (arg, 0, kind, (location_t **) &loc);
+
+  if (token_ptr == NULL)
+    return NULL;
+
+  return loc;
+}
+
+/* Return the pointer to the INDEXth token of the macro argument ARG.
+   KIND specifies the kind of token the macro argument ARG contains.
+   If VIRT_LOCATION is non NULL, *VIRT_LOCATION is set to the address
+   of the virtual location of the returned token if the
+   -ftrack-macro-expansion flag is on; otherwise, it's set to the
+   spelling location of the returned token.  */
+static const cpp_token **
+arg_token_ptr_at (const macro_arg *arg, size_t index,
+                 enum macro_arg_token_kind kind,
+                 location_t **virt_location)
+{
+  const cpp_token **tokens_ptr = NULL;
+
+  switch (kind)
+    {
+    case MACRO_ARG_TOKEN_NORMAL:
+      tokens_ptr = arg->first;
+      break;
+    case MACRO_ARG_TOKEN_STRINGIFIED:      
+      tokens_ptr = (const cpp_token **) &arg->stringified;
+      break;
+    case MACRO_ARG_TOKEN_EXPANDED:
+       tokens_ptr = arg->expanded;
+      break;
+    }
+
+  if (tokens_ptr == NULL)
+    /* This can happen for e.g, an empty token argument to a
+       funtion-like macro.  */
+    return tokens_ptr;
+
+  if (virt_location)
+    {
+      if (kind == MACRO_ARG_TOKEN_NORMAL)
+       *virt_location = &arg->virt_locs[index];
+      else if (kind == MACRO_ARG_TOKEN_EXPANDED)
+       *virt_location = &arg->expanded_virt_locs[index];
+      else if (kind == MACRO_ARG_TOKEN_STRINGIFIED)
+       *virt_location =
+         (location_t *) &tokens_ptr[index]->src_loc;
+    }
+  return &tokens_ptr[index];
+}
+
+/* Initialize an iterator so that it iterates over the tokens of a
+   function-like macro argument.  KIND is the kind of tokens we want
+   ITER to iterate over. TOKEN_PTR points the first token ITER will
+   iterate over.  */
+static void
+macro_arg_token_iter_init (macro_arg_token_iter *iter,
+                          bool track_macro_exp_p,
+                          enum macro_arg_token_kind kind,
+                          const macro_arg *arg,
+                          const cpp_token **token_ptr)
+{
+  iter->track_macro_exp_p = track_macro_exp_p;
+  iter->kind = kind;
+  iter->token_ptr = token_ptr;
+  /* Unconditionally initialize this so that the compiler doesn't warn
+     about iter->location_ptr being possibly uninitialized later after
+     this code has been inlined somewhere.  */
+  iter->location_ptr = NULL;
+  if (track_macro_exp_p)
+    iter->location_ptr = get_arg_token_location (arg, kind);
+#if CHECKING_P
+  iter->num_forwards = 0;
+  if (track_macro_exp_p
+      && token_ptr != NULL
+      && iter->location_ptr == NULL)
+    abort ();
+#endif
+}
+
+/* Move the iterator one token forward. Note that if IT was
+   initialized on an argument that has a stringified token, moving it
+   forward doesn't make sense as a stringified token is essentially one
+   string.  */
+static void
+macro_arg_token_iter_forward (macro_arg_token_iter *it)
+{
+  switch (it->kind)
+    {
+    case MACRO_ARG_TOKEN_NORMAL:
+    case MACRO_ARG_TOKEN_EXPANDED:
+      it->token_ptr++;
+      if (it->track_macro_exp_p)
+       it->location_ptr++;
+      break;
+    case MACRO_ARG_TOKEN_STRINGIFIED:
+#if CHECKING_P
+      if (it->num_forwards > 0)
+       abort ();
+#endif
+      break;
+    }
+
+#if CHECKING_P
+  it->num_forwards++;
+#endif
+}
+
+/* Return the token pointed to by the iterator.  */
+static const cpp_token *
+macro_arg_token_iter_get_token (const macro_arg_token_iter *it)
+{
+#if CHECKING_P
+  if (it->kind == MACRO_ARG_TOKEN_STRINGIFIED
+      && it->num_forwards > 0)
+    abort ();
+#endif
+  if (it->token_ptr == NULL)
+    return NULL;
+  return *it->token_ptr;
+}
+
+/* Return the location of the token pointed to by the iterator.*/
+static location_t
+macro_arg_token_iter_get_location (const macro_arg_token_iter *it)
+{
+#if CHECKING_P
+  if (it->kind == MACRO_ARG_TOKEN_STRINGIFIED
+      && it->num_forwards > 0)
+    abort ();
+#endif
+  if (it->track_macro_exp_p)
+    return *it->location_ptr;
+  else
+    return (*it->token_ptr)->src_loc;
+}
+
+/* Return the index of a token [resulting from macro expansion] inside
+   the total list of tokens resulting from a given macro
+   expansion. The index can be different depending on whether if we
+   want each tokens resulting from function-like macro arguments
+   expansion to have a different location or not.
+
+   E.g, consider this function-like macro: 
+
+        #define M(x) x - 3
+
+   Then consider us "calling" it (and thus expanding it) like:
+   
+       M(1+4)
+
+   It will be expanded into:
+
+       1+4-3
+
+   Let's consider the case of the token '4'.
+
+   Its index can be 2 (it's the third token of the set of tokens
+   resulting from the expansion) or it can be 0 if we consider that
+   all tokens resulting from the expansion of the argument "1+2" have
+   the same index, which is 0. In this later case, the index of token
+   '-' would then be 1 and the index of token '3' would be 2.
+
+   The later case is useful to use less memory e.g, for the case of
+   the user using the option -ftrack-macro-expansion=1.
+
+   ABSOLUTE_TOKEN_INDEX is the index of the macro argument token we
+   are interested in.  CUR_REPLACEMENT_TOKEN is the token of the macro
+   parameter (inside the macro replacement list) that corresponds to
+   the macro argument for which ABSOLUTE_TOKEN_INDEX is a token index
+   of.
+
+   If we refer to the example above, for the '4' argument token,
+   ABSOLUTE_TOKEN_INDEX would be set to 2, and CUR_REPLACEMENT_TOKEN
+   would be set to the token 'x', in the replacement list "x - 3" of
+   macro M.
+
+   This is a subroutine of replace_args.  */
+inline static unsigned
+expanded_token_index (cpp_reader *pfile, cpp_macro *macro,
+                     const cpp_token *cur_replacement_token,
+                     unsigned absolute_token_index)
+{
+  if (CPP_OPTION (pfile, track_macro_expansion) > 1)
+    return absolute_token_index;
+  return cur_replacement_token - macro->exp.tokens;
+}
+
+/* Copy whether PASTE_LEFT is set from SRC to *PASTE_FLAG.  */
+
+static void
+copy_paste_flag (cpp_reader *pfile, const cpp_token **paste_flag,
+                const cpp_token *src)
+{
+  cpp_token *token = _cpp_temp_token (pfile);
+  token->type = (*paste_flag)->type;
+  token->val = (*paste_flag)->val;
+  if (src->flags & PASTE_LEFT)
+    token->flags = (*paste_flag)->flags | PASTE_LEFT;
+  else
+    token->flags = (*paste_flag)->flags & ~PASTE_LEFT;
+  *paste_flag = token;
+}
+
+/* True IFF the last token emitted into BUFF (if any) is PTR.  */
+
+static bool
+last_token_is (_cpp_buff *buff, const cpp_token **ptr)
+{
+  return (ptr && tokens_buff_last_token_ptr (buff) == ptr);
+}
+
+/* Replace the parameters in a function-like macro of NODE with the
+   actual ARGS, and place the result in a newly pushed token context.
+   Expand each argument before replacing, unless it is operated upon
+   by the # or ## operators. EXPANSION_POINT_LOC is the location of
+   the expansion point of the macro. E.g, the location of the
+   function-like macro invocation.  */
+static void
+replace_args (cpp_reader *pfile, cpp_hashnode *node, cpp_macro *macro,
+             macro_arg *args, location_t expansion_point_loc)
+{
+  unsigned int i, total;
+  const cpp_token *src, *limit;
+  const cpp_token **first = NULL;
+  macro_arg *arg;
+  _cpp_buff *buff = NULL;
+  location_t *virt_locs = NULL;
+  unsigned int exp_count;
+  const line_map_macro *map = NULL;
+  int track_macro_exp;
+
+  /* First, fully macro-expand arguments, calculating the number of
+     tokens in the final expansion as we go.  The ordering of the if
+     statements below is subtle; we must handle stringification before
+     pasting.  */
+
+  /* EXP_COUNT is the number of tokens in the macro replacement
+     list.  TOTAL is the number of tokens /after/ macro parameters
+     have been replaced by their arguments.   */
+  exp_count = macro_real_token_count (macro);
+  total = exp_count;
+  limit = macro->exp.tokens + exp_count;
+
+  for (src = macro->exp.tokens; src < limit; src++)
+    if (src->type == CPP_MACRO_ARG)
+      {
+       /* Leading and trailing padding tokens.  */
+       total += 2;
+       /* Account for leading and padding tokens in exp_count too.
+          This is going to be important later down this function,
+          when we want to handle the case of (track_macro_exp <
+          2).  */
+       exp_count += 2;
+
+       /* We have an argument.  If it is not being stringified or
+          pasted it is macro-replaced before insertion.  */
+       arg = &args[src->val.macro_arg.arg_no - 1];
+
+       if (src->flags & STRINGIFY_ARG)
+         {
+           if (!arg->stringified)
+             arg->stringified = stringify_arg (pfile, arg->first, arg->count);
+         }
+       else if ((src->flags & PASTE_LEFT)
+                || (src != macro->exp.tokens && (src[-1].flags & PASTE_LEFT)))
+         total += arg->count - 1;
+       else
+         {
+           if (!arg->expanded)
+             expand_arg (pfile, arg);
+           total += arg->expanded_count - 1;
+         }
+      }
+
+  /* When the compiler is called with the -ftrack-macro-expansion
+     flag, we need to keep track of the location of each token that
+     results from macro expansion.
+
+     A token resulting from macro expansion is not a new token. It is
+     simply the same token as the token coming from the macro
+     definition.  The new things that are allocated are the buffer
+     that holds the tokens resulting from macro expansion and a new
+     location that records many things like the locus of the expansion
+     point as well as the original locus inside the definition of the
+     macro.  This location is called a virtual location.
+     
+     So the buffer BUFF holds a set of cpp_token*, and the buffer
+     VIRT_LOCS holds the virtual locations of the tokens held by BUFF.
+
+     Both of these two buffers are going to be hung off of the macro
+     context, when the latter is pushed.  The memory allocated to
+     store the tokens and their locations is going to be freed once
+     the context of macro expansion is popped.
+     
+     As far as tokens are concerned, the memory overhead of
+     -ftrack-macro-expansion is proportional to the number of
+     macros that get expanded multiplied by sizeof (location_t).
+     The good news is that extra memory gets freed when the macro
+     context is freed, i.e shortly after the macro got expanded.  */
+
+  /* Is the -ftrack-macro-expansion flag in effect?  */
+  track_macro_exp = CPP_OPTION (pfile, track_macro_expansion);
+
+  /* Now allocate memory space for tokens and locations resulting from
+     the macro expansion, copy the tokens and replace the arguments.
+     This memory must be freed when the context of the macro MACRO is
+     popped.  */
+  buff = tokens_buff_new (pfile, total, track_macro_exp ? &virt_locs : NULL);
+
+  first = (const cpp_token **) buff->base;
+
+  /* Create a macro map to record the locations of the tokens that are
+     involved in the expansion.  Note that the expansion point is set
+     to the location of the closing parenthesis.  Otherwise, the
+     subsequent map created for the first token that comes after the
+     macro map might have a wrong line number.  That would lead to
+     tokens with wrong line numbers after the macro expansion.  This
+     adds up to the memory overhead of the -ftrack-macro-expansion
+     flag; for every macro that is expanded, a "macro map" is
+     created.  */
+  if (track_macro_exp)
+    {
+      int num_macro_tokens = total;
+      if (track_macro_exp < 2)
+       /* Then the number of macro tokens won't take in account the
+          fact that function-like macro arguments can expand to
+          multiple tokens. This is to save memory at the expense of
+          accuracy.
+
+          Suppose we have #define SQUARE(A) A * A
+
+          And then we do SQUARE(2+3)
+
+          Then the tokens 2, +, 3, will have the same location,
+          saying they come from the expansion of the argument A.  */
+       num_macro_tokens = exp_count;
+      map = linemap_enter_macro (pfile->line_table, node,
+                                expansion_point_loc,
+                                num_macro_tokens);
+    }
+  i = 0;
+  vaopt_state vaopt_tracker (pfile, macro->variadic, &args[macro->paramc - 1]);
+  const cpp_token **vaopt_start = NULL;
+  for (src = macro->exp.tokens; src < limit; src++)
+    {
+      unsigned int arg_tokens_count;
+      macro_arg_token_iter from;
+      const cpp_token **paste_flag = NULL;
+      const cpp_token **tmp_token_ptr;
+
+      /* __VA_OPT__ handling.  */
+      vaopt_state::update_type vostate = vaopt_tracker.update (src);
+      if (__builtin_expect (vostate != vaopt_state::INCLUDE, false))
+       {
+         if (vostate == vaopt_state::BEGIN)
+           {
+             /* Padding on the left of __VA_OPT__ (unless RHS of ##).  */
+             if (src != macro->exp.tokens && !(src[-1].flags & PASTE_LEFT))
+               {
+                 const cpp_token *t = padding_token (pfile, src);
+                 unsigned index = expanded_token_index (pfile, macro, src, i);
+                 /* Allocate a virtual location for the padding token and
+                    append the token and its location to BUFF and
+                    VIRT_LOCS.   */
+                 tokens_buff_add_token (buff, virt_locs, t,
+                                        t->src_loc, t->src_loc,
+                                        map, index);
+               }
+             vaopt_start = tokens_buff_last_token_ptr (buff);
+           }
+         else if (vostate == vaopt_state::END)
+           {
+             const cpp_token **start = vaopt_start;
+             vaopt_start = NULL;
+
+             paste_flag = tokens_buff_last_token_ptr (buff);
+
+             if (vaopt_tracker.stringify ())
+               {
+                 unsigned int count
+                   = start ? paste_flag - start : tokens_buff_count (buff);
+                 const cpp_token **first
+                   = start ? start + 1
+                           : (const cpp_token **) (buff->base);
+                 unsigned int i, j;
+
+                 /* Paste any tokens that need to be pasted before calling
+                    stringify_arg, because stringify_arg uses pfile->u_buff
+                    which paste_tokens can use as well.  */
+                 for (i = 0, j = 0; i < count; i++, j++)
+                   {
+                     const cpp_token *token = first[i];
+
+                     if (token->flags & PASTE_LEFT)
+                       {
+                         location_t virt_loc = pfile->invocation_location;
+                         const cpp_token *rhs;
+                         do
+                           {
+                             if (i == count)
+                               abort ();
+                             rhs = first[++i];
+                             if (!paste_tokens (pfile, virt_loc, &token, rhs))
+                               {
+                                 --i;
+                                 break;
+                               }
+                           }
+                         while (rhs->flags & PASTE_LEFT);
+                       }
+
+                     first[j] = token;
+                   }
+                 if (j != i)
+                   {
+                     while (i-- != j)
+                       tokens_buff_remove_last_token (buff);
+                     count = j;
+                   }
+
+                 const cpp_token *t = stringify_arg (pfile, first, count);
+                 while (count--)
+                   tokens_buff_remove_last_token (buff);
+                 if (src->flags & PASTE_LEFT)
+                   copy_paste_flag (pfile, &t, src);
+                 tokens_buff_add_token (buff, virt_locs,
+                                        t, t->src_loc, t->src_loc,
+                                        NULL, 0);
+                 continue;
+               }
+             if (start && paste_flag == start && (*start)->flags & PASTE_LEFT)
+               /* If __VA_OPT__ expands to nothing (either because __VA_ARGS__
+                  is empty or because it is __VA_OPT__() ), drop PASTE_LEFT
+                  flag from previous token.  */
+               copy_paste_flag (pfile, start, &pfile->avoid_paste);
+             if (src->flags & PASTE_LEFT)
+               {
+                 /* Don't avoid paste after all.  */
+                 while (paste_flag && paste_flag != start
+                        && *paste_flag == &pfile->avoid_paste)
+                   {
+                     tokens_buff_remove_last_token (buff);
+                     paste_flag = tokens_buff_last_token_ptr (buff);
+                   }
+
+                 /* With a non-empty __VA_OPT__ on the LHS of ##, the last
+                    token should be flagged PASTE_LEFT.  */
+                 if (paste_flag && (*paste_flag)->type != CPP_PADDING)
+                   copy_paste_flag (pfile, paste_flag, src);
+               }
+             else
+               {
+                 /* Otherwise, avoid paste on RHS, __VA_OPT__(c)d or
+                    __VA_OPT__(c)__VA_OPT__(d).  */
+                 const cpp_token *t = &pfile->avoid_paste;
+                 tokens_buff_add_token (buff, virt_locs,
+                                        t, t->src_loc, t->src_loc,
+                                        NULL, 0);
+               }
+           }
+         continue;
+       }
+
+      if (src->type != CPP_MACRO_ARG)
+       {
+         /* Allocate a virtual location for token SRC, and add that
+            token and its virtual location into the buffers BUFF and
+            VIRT_LOCS.  */
+         unsigned index = expanded_token_index (pfile, macro, src, i);
+         tokens_buff_add_token (buff, virt_locs, src,
+                                src->src_loc, src->src_loc,
+                                map, index);
+         i += 1;
+         continue;
+       }
+
+      paste_flag = 0;
+      arg = &args[src->val.macro_arg.arg_no - 1];
+      /* SRC is a macro parameter that we need to replace with its
+        corresponding argument.  So at some point we'll need to
+        iterate over the tokens of the macro argument and copy them
+        into the "place" now holding the correspondig macro
+        parameter.  We are going to use the iterator type
+        macro_argo_token_iter to handle that iterating.  The 'if'
+        below is to initialize the iterator depending on the type of
+        tokens the macro argument has.  It also does some adjustment
+        related to padding tokens and some pasting corner cases.  */
+      if (src->flags & STRINGIFY_ARG)
+       {
+         arg_tokens_count = 1;
+         macro_arg_token_iter_init (&from,
+                                    CPP_OPTION (pfile,
+                                                track_macro_expansion),
+                                    MACRO_ARG_TOKEN_STRINGIFIED,
+                                    arg, &arg->stringified);
+       }
+      else if (src->flags & PASTE_LEFT)
+       {
+         arg_tokens_count = arg->count;
+         macro_arg_token_iter_init (&from,
+                                    CPP_OPTION (pfile,
+                                                track_macro_expansion),
+                                    MACRO_ARG_TOKEN_NORMAL,
+                                    arg, arg->first);
+       }
+      else if (src != macro->exp.tokens && (src[-1].flags & PASTE_LEFT))
+       {
+         int num_toks;
+         arg_tokens_count = arg->count;
+         macro_arg_token_iter_init (&from,
+                                    CPP_OPTION (pfile,
+                                                track_macro_expansion),
+                                    MACRO_ARG_TOKEN_NORMAL,
+                                    arg, arg->first);
+
+         num_toks = tokens_buff_count (buff);
+
+         if (num_toks != 0)
+           {
+             /* So the current parameter token is pasted to the previous
+                token in the replacement list.  Let's look at what
+                we have as previous and current arguments.  */
+
+             /* This is the previous argument's token ...  */
+             tmp_token_ptr = tokens_buff_last_token_ptr (buff);
+
+             if ((*tmp_token_ptr)->type == CPP_COMMA
+                 && macro->variadic
+                 && src->val.macro_arg.arg_no == macro->paramc)
+               {
+                 /* ... which is a comma; and the current parameter
+                    is the last parameter of a variadic function-like
+                    macro.  If the argument to the current last
+                    parameter is NULL, then swallow the comma,
+                    otherwise drop the paste flag.  */
+                 if (macro_arg_token_iter_get_token (&from) == NULL)
+                   tokens_buff_remove_last_token (buff);
+                 else
+                   paste_flag = tmp_token_ptr;
+               }
+             /* Remove the paste flag if the RHS is a placemarker.  */
+             else if (arg_tokens_count == 0)
+               paste_flag = tmp_token_ptr;
+           }
+       }
+      else
+       {
+         arg_tokens_count = arg->expanded_count;
+         macro_arg_token_iter_init (&from,
+                                    CPP_OPTION (pfile,
+                                                track_macro_expansion),
+                                    MACRO_ARG_TOKEN_EXPANDED,
+                                    arg, arg->expanded);
+
+         if (last_token_is (buff, vaopt_start))
+           {
+             /* We're expanding an arg at the beginning of __VA_OPT__.
+                Skip padding. */
+             while (arg_tokens_count)
+               {
+                 const cpp_token *t = macro_arg_token_iter_get_token (&from);
+                 if (t->type != CPP_PADDING)
+                   break;
+                 macro_arg_token_iter_forward (&from);
+                 --arg_tokens_count;
+               }
+           }
+       }
+
+      /* Padding on the left of an argument (unless RHS of ##).  */
+      if ((!pfile->state.in_directive || pfile->state.directive_wants_padding)
+         && src != macro->exp.tokens
+         && !(src[-1].flags & PASTE_LEFT)
+         && !last_token_is (buff, vaopt_start))
+       {
+         const cpp_token *t = padding_token (pfile, src);
+         unsigned index = expanded_token_index (pfile, macro, src, i);
+         /* Allocate a virtual location for the padding token and
+            append the token and its location to BUFF and
+            VIRT_LOCS.   */
+         tokens_buff_add_token (buff, virt_locs, t,
+                                t->src_loc, t->src_loc,
+                                map, index);
+       }
+
+      if (arg_tokens_count)
+       {
+         /* So now we've got the number of tokens that make up the
+            argument that is going to replace the current parameter
+            in the macro's replacement list.  */
+         unsigned int j;
+         for (j = 0; j < arg_tokens_count; ++j)
+           {
+             /* So if track_macro_exp is < 2, the user wants to
+                save extra memory while tracking macro expansion
+                locations.  So in that case here is what we do:
+
+                Suppose we have #define SQUARE(A) A * A
+
+                And then we do SQUARE(2+3)
+
+                Then the tokens 2, +, 3, will have the same location,
+                saying they come from the expansion of the argument
+                A.
+
+             So that means we are going to ignore the COUNT tokens
+             resulting from the expansion of the current macro
+             argument. In other words all the ARG_TOKENS_COUNT tokens
+             resulting from the expansion of the macro argument will
+             have the index I.  Normally, each of those tokens should
+             have index I+J.  */
+             unsigned token_index = i;
+             unsigned index;
+             if (track_macro_exp > 1)
+               token_index += j;
+
+             index = expanded_token_index (pfile, macro, src, token_index);
+             const cpp_token *tok = macro_arg_token_iter_get_token (&from);
+             tokens_buff_add_token (buff, virt_locs, tok,
+                                    macro_arg_token_iter_get_location (&from),
+                                    src->src_loc, map, index);
+             macro_arg_token_iter_forward (&from);
+           }
+
+         /* With a non-empty argument on the LHS of ##, the last
+            token should be flagged PASTE_LEFT.  */
+         if (src->flags & PASTE_LEFT)
+           paste_flag
+             = (const cpp_token **) tokens_buff_last_token_ptr (buff);
+       }
+      else if (CPP_PEDANTIC (pfile) && ! CPP_OPTION (pfile, c99)
+              && ! macro->syshdr && ! _cpp_in_system_header (pfile))
+       {
+         if (CPP_OPTION (pfile, cplusplus))
+           cpp_pedwarning (pfile, CPP_W_PEDANTIC,
+                           "invoking macro %s argument %d: "
+                           "empty macro arguments are undefined"
+                           " in ISO C++98",
+                           NODE_NAME (node), src->val.macro_arg.arg_no);
+         else if (CPP_OPTION (pfile, cpp_warn_c90_c99_compat))
+           cpp_pedwarning (pfile, 
+                           CPP_OPTION (pfile, cpp_warn_c90_c99_compat) > 0
+                           ? CPP_W_C90_C99_COMPAT : CPP_W_PEDANTIC,
+                           "invoking macro %s argument %d: "
+                           "empty macro arguments are undefined"
+                           " in ISO C90",
+                           NODE_NAME (node), src->val.macro_arg.arg_no);
+       }
+      else if (CPP_OPTION (pfile, cpp_warn_c90_c99_compat) > 0
+              && ! CPP_OPTION (pfile, cplusplus)
+              && ! macro->syshdr && ! _cpp_in_system_header (pfile))
+       cpp_warning (pfile, CPP_W_C90_C99_COMPAT,
+                    "invoking macro %s argument %d: "
+                    "empty macro arguments are undefined"
+                    " in ISO C90",
+                    NODE_NAME (node), src->val.macro_arg.arg_no);
+
+      /* Avoid paste on RHS (even case count == 0).  */
+      if (!pfile->state.in_directive && !(src->flags & PASTE_LEFT))
+       {
+         const cpp_token *t = &pfile->avoid_paste;
+         tokens_buff_add_token (buff, virt_locs,
+                                t, t->src_loc, t->src_loc,
+                                NULL, 0);
+       }
+
+      /* Add a new paste flag, or remove an unwanted one.  */
+      if (paste_flag)
+       copy_paste_flag (pfile, paste_flag, src);
+
+      i += arg_tokens_count;
+    }
+
+  if (track_macro_exp)
+    push_extended_tokens_context (pfile, node, buff, virt_locs, first,
+                                 tokens_buff_count (buff));
+  else
+    push_ptoken_context (pfile, node, buff, first,
+                        tokens_buff_count (buff));
+
+  num_macro_tokens_counter += tokens_buff_count (buff);
+}
+
+/* Return a special padding token, with padding inherited from SOURCE.  */
+static const cpp_token *
+padding_token (cpp_reader *pfile, const cpp_token *source)
+{
+  cpp_token *result = _cpp_temp_token (pfile);
+
+  result->type = CPP_PADDING;
+
+  /* Data in GCed data structures cannot be made const so far, so we
+     need a cast here.  */
+  result->val.source = (cpp_token *) source;
+  result->flags = 0;
+  return result;
+}
+
+/* Get a new uninitialized context.  Create a new one if we cannot
+   re-use an old one.  */
+static cpp_context *
+next_context (cpp_reader *pfile)
+{
+  cpp_context *result = pfile->context->next;
+
+  if (result == 0)
+    {
+      result = XNEW (cpp_context);
+      memset (result, 0, sizeof (cpp_context));
+      result->prev = pfile->context;
+      result->next = 0;
+      pfile->context->next = result;
+    }
+
+  pfile->context = result;
+  return result;
+}
+
+/* Push a list of pointers to tokens.  */
+static void
+push_ptoken_context (cpp_reader *pfile, cpp_hashnode *macro, _cpp_buff *buff,
+                    const cpp_token **first, unsigned int count)
+{
+  cpp_context *context = next_context (pfile);
+
+  context->tokens_kind = TOKENS_KIND_INDIRECT;
+  context->c.macro = macro;
+  context->buff = buff;
+  FIRST (context).ptoken = first;
+  LAST (context).ptoken = first + count;
+}
+
+/* Push a list of tokens.
+
+   A NULL macro means that we should continue the current macro
+   expansion, in essence.  That means that if we are currently in a
+   macro expansion context, we'll make the new pfile->context refer to
+   the current macro.  */
+void
+_cpp_push_token_context (cpp_reader *pfile, cpp_hashnode *macro,
+                        const cpp_token *first, unsigned int count)
+{
+  cpp_context *context;
+
+   if (macro == NULL)
+     macro = macro_of_context (pfile->context);
+
+   context = next_context (pfile);
+   context->tokens_kind = TOKENS_KIND_DIRECT;
+   context->c.macro = macro;
+   context->buff = NULL;
+   FIRST (context).token = first;
+   LAST (context).token = first + count;
+}
+
+/* Build a context containing a list of tokens as well as their
+   virtual locations and push it.  TOKENS_BUFF is the buffer that
+   contains the tokens pointed to by FIRST.  If TOKENS_BUFF is
+   non-NULL, it means that the context owns it, meaning that
+   _cpp_pop_context will free it as well as VIRT_LOCS_BUFF that
+   contains the virtual locations.
+
+   A NULL macro means that we should continue the current macro
+   expansion, in essence.  That means that if we are currently in a
+   macro expansion context, we'll make the new pfile->context refer to
+   the current macro.  */
+static void
+push_extended_tokens_context (cpp_reader *pfile,
+                             cpp_hashnode *macro,
+                             _cpp_buff *token_buff,
+                             location_t *virt_locs,
+                             const cpp_token **first,
+                             unsigned int count)
+{
+  cpp_context *context;
+  macro_context *m;
+
+  if (macro == NULL)
+    macro = macro_of_context (pfile->context);
+
+  context = next_context (pfile);
+  context->tokens_kind = TOKENS_KIND_EXTENDED;
+  context->buff = token_buff;
+
+  m = XNEW (macro_context);
+  m->macro_node = macro;
+  m->virt_locs = virt_locs;
+  m->cur_virt_loc = virt_locs;
+  context->c.mc = m;
+  FIRST (context).ptoken = first;
+  LAST (context).ptoken = first + count;
+}
+
+/* Push a traditional macro's replacement text.  */
+void
+_cpp_push_text_context (cpp_reader *pfile, cpp_hashnode *macro,
+                       const uchar *start, size_t len)
+{
+  cpp_context *context = next_context (pfile);
+
+  context->tokens_kind = TOKENS_KIND_DIRECT;
+  context->c.macro = macro;
+  context->buff = NULL;
+  CUR (context) = start;
+  RLIMIT (context) = start + len;
+  macro->flags |= NODE_DISABLED;
+}
+
+/* Creates a buffer that holds tokens a.k.a "token buffer", usually
+   for the purpose of storing them on a cpp_context. If VIRT_LOCS is
+   non-null (which means that -ftrack-macro-expansion is on),
+   *VIRT_LOCS is set to a newly allocated buffer that is supposed to
+   hold the virtual locations of the tokens resulting from macro
+   expansion.  */
+static _cpp_buff*
+tokens_buff_new (cpp_reader *pfile, size_t len,
+                location_t **virt_locs)
+{
+  size_t tokens_size = len * sizeof (cpp_token *);
+  size_t locs_size = len * sizeof (location_t);
+
+  if (virt_locs != NULL)
+    *virt_locs = XNEWVEC (location_t, locs_size);
+  return _cpp_get_buff (pfile, tokens_size);
+}
+
+/* Returns the number of tokens contained in a token buffer.  The
+   buffer holds a set of cpp_token*.  */
+static size_t
+tokens_buff_count (_cpp_buff *buff)
+{
+  return (BUFF_FRONT (buff) - buff->base) / sizeof (cpp_token *);
+}
+
+/* Return a pointer to the last token contained in the token buffer
+   BUFF.  */
+static const cpp_token **
+tokens_buff_last_token_ptr (_cpp_buff *buff)
+{
+  if (BUFF_FRONT (buff) == buff->base)
+    return NULL;
+  return &((const cpp_token **) BUFF_FRONT (buff))[-1];
+}
+
+/* Remove the last token contained in the token buffer TOKENS_BUFF.
+   If VIRT_LOCS_BUFF is non-NULL,  it should point at the buffer
+   containing the virtual locations of the tokens in TOKENS_BUFF; in
+   which case the function updates that buffer as well.   */
+static inline void
+tokens_buff_remove_last_token (_cpp_buff *tokens_buff)
+
+{
+  if (BUFF_FRONT (tokens_buff) > tokens_buff->base)
+    BUFF_FRONT (tokens_buff) =
+      (unsigned char *) &((cpp_token **) BUFF_FRONT (tokens_buff))[-1];
+}
+
+/* Insert a token into the token buffer at the position pointed to by
+   DEST.  Note that the buffer is not enlarged so the previous token
+   that was at *DEST is overwritten.  VIRT_LOC_DEST, if non-null,
+   means -ftrack-macro-expansion is effect; it then points to where to
+   insert the virtual location of TOKEN.  TOKEN is the token to
+   insert.  VIRT_LOC is the virtual location of the token, i.e, the
+   location possibly encoding its locus across macro expansion.  If
+   TOKEN is an argument of a function-like macro (inside a macro
+   replacement list), PARM_DEF_LOC is the spelling location of the
+   macro parameter that TOKEN is replacing, in the replacement list of
+   the macro.  If TOKEN is not an argument of a function-like macro or
+   if it doesn't come from a macro expansion, then VIRT_LOC can just
+   be set to the same value as PARM_DEF_LOC.  If MAP is non null, it
+   means TOKEN comes from a macro expansion and MAP is the macro map
+   associated to the macro.  MACRO_TOKEN_INDEX points to the index of
+   the token in the macro map; it is not considered if MAP is NULL.
+
+   Upon successful completion this function returns the a pointer to
+   the position of the token coming right after the insertion
+   point.  */
+static inline const cpp_token **
+tokens_buff_put_token_to (const cpp_token **dest,
+                         location_t *virt_loc_dest,
+                         const cpp_token *token,
+                         location_t virt_loc,
+                         location_t parm_def_loc,
+                         const line_map_macro *map,
+                         unsigned int macro_token_index)
+{
+  location_t macro_loc = virt_loc;
+  const cpp_token **result;
+
+  if (virt_loc_dest)
+    {
+      /* -ftrack-macro-expansion is on.  */
+      if (map)
+       macro_loc = linemap_add_macro_token (map, macro_token_index,
+                                            virt_loc, parm_def_loc);
+      *virt_loc_dest = macro_loc;
+    }
+  *dest = token;
+  result = &dest[1];
+
+  return result;
+}
+
+/* Adds a token at the end of the tokens contained in BUFFER.  Note
+   that this function doesn't enlarge BUFFER when the number of tokens
+   reaches BUFFER's size; it aborts in that situation.
+
+   TOKEN is the token to append. VIRT_LOC is the virtual location of
+   the token, i.e, the location possibly encoding its locus across
+   macro expansion. If TOKEN is an argument of a function-like macro
+   (inside a macro replacement list), PARM_DEF_LOC is the location of
+   the macro parameter that TOKEN is replacing.  If TOKEN doesn't come
+   from a macro expansion, then VIRT_LOC can just be set to the same
+   value as PARM_DEF_LOC.  If MAP is non null, it means TOKEN comes
+   from a macro expansion and MAP is the macro map associated to the
+   macro.  MACRO_TOKEN_INDEX points to the index of the token in the
+   macro map; It is not considered if MAP is NULL.  If VIRT_LOCS is
+   non-null, it means -ftrack-macro-expansion is on; in which case
+   this function adds the virtual location DEF_LOC to the VIRT_LOCS
+   array, at the same index as the one of TOKEN in BUFFER.  Upon
+   successful completion this function returns the a pointer to the
+   position of the token coming right after the insertion point.  */
+static const cpp_token **
+tokens_buff_add_token (_cpp_buff *buffer,
+                      location_t *virt_locs,
+                      const cpp_token *token,
+                      location_t virt_loc,
+                      location_t parm_def_loc,
+                      const line_map_macro *map,
+                      unsigned int macro_token_index)
+{
+  const cpp_token **result;
+  location_t *virt_loc_dest = NULL;
+  unsigned token_index = 
+    (BUFF_FRONT (buffer) - buffer->base) / sizeof (cpp_token *);
+
+  /* Abort if we pass the end the buffer.  */
+  if (BUFF_FRONT (buffer) > BUFF_LIMIT (buffer))
+    abort ();
+
+  if (virt_locs != NULL)
+    virt_loc_dest = &virt_locs[token_index];
+
+  result =
+    tokens_buff_put_token_to ((const cpp_token **) BUFF_FRONT (buffer),
+                             virt_loc_dest, token, virt_loc, parm_def_loc,
+                             map, macro_token_index);
+
+  BUFF_FRONT (buffer) = (unsigned char *) result;
+  return result;
+}
+
+/* Allocate space for the function-like macro argument ARG to store
+   the tokens resulting from the macro-expansion of the tokens that
+   make up ARG itself. That space is allocated in ARG->expanded and
+   needs to be freed using free.  */
+static void
+alloc_expanded_arg_mem (cpp_reader *pfile, macro_arg *arg, size_t capacity)
+{
+  gcc_checking_assert (arg->expanded == NULL
+                      && arg->expanded_virt_locs == NULL);
+
+  arg->expanded = XNEWVEC (const cpp_token *, capacity);
+  if (CPP_OPTION (pfile, track_macro_expansion))
+    arg->expanded_virt_locs = XNEWVEC (location_t, capacity);
+
+}
+
+/* If necessary, enlarge ARG->expanded to so that it can contain SIZE
+   tokens.  */
+static void
+ensure_expanded_arg_room (cpp_reader *pfile, macro_arg *arg,
+                         size_t size, size_t *expanded_capacity)
+{
+  if (size <= *expanded_capacity)
+    return;
+
+  size *= 2;
+
+  arg->expanded =
+    XRESIZEVEC (const cpp_token *, arg->expanded, size);
+  *expanded_capacity = size;
+
+  if (CPP_OPTION (pfile, track_macro_expansion))
+    {
+      if (arg->expanded_virt_locs == NULL)
+       arg->expanded_virt_locs = XNEWVEC (location_t, size);
+      else
+       arg->expanded_virt_locs = XRESIZEVEC (location_t,
+                                             arg->expanded_virt_locs,
+                                             size);
+    }
+}
+
+/* 
+  Expand an argument ARG before replacing parameters in a
+  function-like macro.  This works by pushing a context with the
+  argument's tokens, and then expanding that into a temporary buffer
+  as if it were a normal part of the token stream.  collect_args()
+  has terminated the argument's tokens with a CPP_EOF so that we know
+  when we have fully expanded the argument. 
+ */
+static void
+expand_arg (cpp_reader *pfile, macro_arg *arg)
+{
+  size_t capacity;
+  bool saved_warn_trad;
+  bool track_macro_exp_p = CPP_OPTION (pfile, track_macro_expansion);
+  bool saved_ignore__Pragma;
+
+  if (arg->count == 0
+      || arg->expanded != NULL)
+    return;
+
+  /* Don't warn about funlike macros when pre-expanding.  */
+  saved_warn_trad = CPP_WTRADITIONAL (pfile);
+  CPP_WTRADITIONAL (pfile) = 0;
+
+  /* Loop, reading in the tokens of the argument.  */
+  capacity = 256;
+  alloc_expanded_arg_mem (pfile, arg, capacity);
+
+  if (track_macro_exp_p)
+    push_extended_tokens_context (pfile, NULL, NULL,
+                                 arg->virt_locs,
+                                 arg->first,
+                                 arg->count + 1);
+  else
+    push_ptoken_context (pfile, NULL, NULL,
+                        arg->first, arg->count + 1);
+
+  saved_ignore__Pragma = pfile->state.ignore__Pragma;
+  pfile->state.ignore__Pragma = 1;
+
+  for (;;)
+    {
+      const cpp_token *token;
+      location_t location;
+
+      ensure_expanded_arg_room (pfile, arg, arg->expanded_count + 1,
+                               &capacity);
+
+      token = cpp_get_token_1 (pfile, &location);
+
+      if (token->type == CPP_EOF)
+       break;
+
+      set_arg_token (arg, token, location,
+                    arg->expanded_count, MACRO_ARG_TOKEN_EXPANDED,
+                    CPP_OPTION (pfile, track_macro_expansion));
+      arg->expanded_count++;
+    }
+
+  _cpp_pop_context (pfile);
+
+  CPP_WTRADITIONAL (pfile) = saved_warn_trad;
+  pfile->state.ignore__Pragma = saved_ignore__Pragma;
+}
+
+/* Returns the macro associated to the current context if we are in
+   the context a macro expansion, NULL otherwise.  */
+static cpp_hashnode*
+macro_of_context (cpp_context *context)
+{
+  if (context == NULL)
+    return NULL;
+
+  return (context->tokens_kind == TOKENS_KIND_EXTENDED)
+    ? context->c.mc->macro_node
+    : context->c.macro;
+}
+
+/* Return TRUE iff we are expanding a macro or are about to start
+   expanding one.  If we are effectively expanding a macro, the
+   function macro_of_context returns a pointer to the macro being
+   expanded.  */
+static bool
+in_macro_expansion_p (cpp_reader *pfile)
+{
+  if (pfile == NULL)
+    return false;
+
+  return (pfile->about_to_expand_macro_p 
+         || macro_of_context (pfile->context));
+}
+
+/* Pop the current context off the stack, re-enabling the macro if the
+   context represented a macro's replacement list.  Initially the
+   context structure was not freed so that we can re-use it later, but
+   now we do free it to reduce peak memory consumption.  */
+void
+_cpp_pop_context (cpp_reader *pfile)
+{
+  cpp_context *context = pfile->context;
+
+  /* We should not be popping the base context.  */
+  gcc_assert (context != &pfile->base_context);
+
+  if (context->c.macro)
+    {
+      cpp_hashnode *macro;
+      if (context->tokens_kind == TOKENS_KIND_EXTENDED)
+       {
+         macro_context *mc = context->c.mc;
+         macro = mc->macro_node;
+         /* If context->buff is set, it means the life time of tokens
+            is bound to the life time of this context; so we must
+            free the tokens; that means we must free the virtual
+            locations of these tokens too.  */
+         if (context->buff && mc->virt_locs)
+           {
+             free (mc->virt_locs);
+             mc->virt_locs = NULL;
+           }
+         free (mc);
+         context->c.mc = NULL;
+       }
+      else
+       macro = context->c.macro;
+
+      /* Beware that MACRO can be NULL in cases like when we are
+        called from expand_arg.  In those cases, a dummy context with
+        tokens is pushed just for the purpose of walking them using
+        cpp_get_token_1.  In that case, no 'macro' field is set into
+        the dummy context.  */
+      if (macro != NULL
+         /* Several contiguous macro expansion contexts can be
+            associated to the same macro; that means it's the same
+            macro expansion that spans across all these (sub)
+            contexts.  So we should re-enable an expansion-disabled
+            macro only when we are sure we are really out of that
+            macro expansion.  */
+         && macro_of_context (context->prev) != macro)
+       macro->flags &= ~NODE_DISABLED;
+
+      if (macro == pfile->top_most_macro_node && context->prev == NULL)
+       /* We are popping the context of the top-most macro node.  */
+       pfile->top_most_macro_node = NULL;
+    }
+
+  if (context->buff)
+    {
+      /* Decrease memory peak consumption by freeing the memory used
+        by the context.  */
+      _cpp_free_buff (context->buff);
+    }
+
+  pfile->context = context->prev;
+  /* decrease peak memory consumption by feeing the context.  */
+  pfile->context->next = NULL;
+  free (context);
+}
+
+/* Return TRUE if we reached the end of the set of tokens stored in
+   CONTEXT, FALSE otherwise.  */
+static inline bool
+reached_end_of_context (cpp_context *context)
+{
+  if (context->tokens_kind == TOKENS_KIND_DIRECT)
+      return FIRST (context).token == LAST (context).token;
+  else if (context->tokens_kind == TOKENS_KIND_INDIRECT
+          || context->tokens_kind == TOKENS_KIND_EXTENDED)
+    return FIRST (context).ptoken == LAST (context).ptoken;
+  else
+    abort ();
+}
+
+/* Consume the next token contained in the current context of PFILE,
+   and return it in *TOKEN. It's "full location" is returned in
+   *LOCATION. If -ftrack-macro-location is in effeect, fFull location"
+   means the location encoding the locus of the token across macro
+   expansion; otherwise it's just is the "normal" location of the
+   token which (*TOKEN)->src_loc.  */
+static inline void
+consume_next_token_from_context (cpp_reader *pfile,
+                                const cpp_token ** token,
+                                location_t *location)
+{
+  cpp_context *c = pfile->context;
+
+  if ((c)->tokens_kind == TOKENS_KIND_DIRECT)
+    {
+      *token = FIRST (c).token;
+      *location = (*token)->src_loc;
+      FIRST (c).token++;
+    }
+  else if ((c)->tokens_kind == TOKENS_KIND_INDIRECT)           
+    {
+      *token = *FIRST (c).ptoken;
+      *location = (*token)->src_loc;
+      FIRST (c).ptoken++;
+    }
+  else if ((c)->tokens_kind == TOKENS_KIND_EXTENDED)
+    {
+      macro_context *m = c->c.mc;
+      *token = *FIRST (c).ptoken;
+      if (m->virt_locs)
+       {
+         *location = *m->cur_virt_loc;
+         m->cur_virt_loc++;
+       }
+      else
+       *location = (*token)->src_loc;
+      FIRST (c).ptoken++;
+    }
+  else
+    abort ();
+}
+
+/* In the traditional mode of the preprocessor, if we are currently in
+   a directive, the location of a token must be the location of the
+   start of the directive line.  This function returns the proper
+   location if we are in the traditional mode, and just returns
+   LOCATION otherwise.  */
+
+static inline location_t
+maybe_adjust_loc_for_trad_cpp (cpp_reader *pfile, location_t location)
+{
+  if (CPP_OPTION (pfile, traditional))
+    {
+      if (pfile->state.in_directive)
+       return pfile->directive_line;
+    }
+  return location;
+}
+
+/* Routine to get a token as well as its location.
+
+   Macro expansions and directives are transparently handled,
+   including entering included files.  Thus tokens are post-macro
+   expansion, and after any intervening directives.  External callers
+   see CPP_EOF only at EOF.  Internal callers also see it when meeting
+   a directive inside a macro call, when at the end of a directive and
+   state.in_directive is still 1, and at the end of argument
+   pre-expansion.
+
+   LOC is an out parameter; *LOC is set to the location "as expected
+   by the user".  Please read the comment of
+   cpp_get_token_with_location to learn more about the meaning of this
+   location.  */
+static const cpp_token*
+cpp_get_token_1 (cpp_reader *pfile, location_t *location)
+{
+  const cpp_token *result;
+  /* This token is a virtual token that either encodes a location
+     related to macro expansion or a spelling location.  */
+  location_t virt_loc = 0;
+  /* pfile->about_to_expand_macro_p can be overriden by indirect calls
+     to functions that push macro contexts.  So let's save it so that
+     we can restore it when we are about to leave this routine.  */
+  bool saved_about_to_expand_macro = pfile->about_to_expand_macro_p;
+
+  for (;;)
+    {
+      cpp_hashnode *node;
+      cpp_context *context = pfile->context;
+
+      /* Context->prev == 0 <=> base context.  */
+      if (!context->prev)
+       {
+         result = _cpp_lex_token (pfile);
+         virt_loc = result->src_loc;
+       }
+      else if (!reached_end_of_context (context))
+       {
+         consume_next_token_from_context (pfile, &result,
+                                          &virt_loc);
+         if (result->flags & PASTE_LEFT)
+           {
+             paste_all_tokens (pfile, result);
+             if (pfile->state.in_directive)
+               continue;
+             result = padding_token (pfile, result);
+             goto out;
+           }
+       }
+      else
+       {
+         if (pfile->context->c.macro)
+           ++num_expanded_macros_counter;
+         _cpp_pop_context (pfile);
+         if (pfile->state.in_directive)
+           continue;
+         result = &pfile->avoid_paste;
+         goto out;
+       }
+
+      if (pfile->state.in_directive && result->type == CPP_COMMENT)
+       continue;
+
+      if (result->type != CPP_NAME)
+       break;
+
+      node = result->val.node.node;
+
+      if (node->type == NT_VOID || (result->flags & NO_EXPAND))
+       break;
+
+      if (!(node->flags & NODE_USED)
+         && node->type == NT_USER_MACRO
+         && !node->value.macro
+         && !cpp_get_deferred_macro (pfile, node, result->src_loc))
+       break;
+
+      if (!(node->flags & NODE_DISABLED))
+       {
+         int ret = 0;
+         /* If not in a macro context, and we're going to start an
+            expansion, record the location and the top level macro
+            about to be expanded.  */
+         if (!in_macro_expansion_p (pfile))
+           {
+             pfile->invocation_location = result->src_loc;
+             pfile->top_most_macro_node = node;
+           }
+         if (pfile->state.prevent_expansion)
+           break;
+
+         /* Conditional macros require that a predicate be evaluated
+            first.  */
+         if ((node->flags & NODE_CONDITIONAL) != 0)
+           {
+             if (pfile->cb.macro_to_expand)
+               {
+                 bool whitespace_after;
+                 const cpp_token *peek_tok = cpp_peek_token (pfile, 0);
+
+                 whitespace_after = (peek_tok->type == CPP_PADDING
+                                     || (peek_tok->flags & PREV_WHITE));
+                 node = pfile->cb.macro_to_expand (pfile, result);
+                 if (node)
+                   ret = enter_macro_context (pfile, node, result, virt_loc);
+                 else if (whitespace_after)
+                   {
+                     /* If macro_to_expand hook returned NULL and it
+                        ate some tokens, see if we don't need to add
+                        a padding token in between this and the
+                        next token.  */
+                     peek_tok = cpp_peek_token (pfile, 0);
+                     if (peek_tok->type != CPP_PADDING
+                         && (peek_tok->flags & PREV_WHITE) == 0)
+                       _cpp_push_token_context (pfile, NULL,
+                                                padding_token (pfile,
+                                                               peek_tok), 1);
+                   }
+               }
+           }
+         else
+           ret = enter_macro_context (pfile, node, result, virt_loc);
+         if (ret)
+           {
+             if (pfile->state.in_directive || ret == 2)
+               continue;
+             result = padding_token (pfile, result);
+             goto out;
+           }
+       }
+      else
+       {
+         /* Flag this token as always unexpandable.  FIXME: move this
+            to collect_args()?.  */
+         cpp_token *t = _cpp_temp_token (pfile);
+         t->type = result->type;
+         t->flags = result->flags | NO_EXPAND;
+         t->val = result->val;
+         result = t;
+       }
+
+      break;
+    }
+
+ out:
+  if (location != NULL)
+    {
+      if (virt_loc == 0)
+       virt_loc = result->src_loc;
+      *location = virt_loc;
+
+      if (!CPP_OPTION (pfile, track_macro_expansion)
+         && macro_of_context (pfile->context) != NULL)
+       /* We are in a macro expansion context, are not tracking
+          virtual location, but were asked to report the location
+          of the expansion point of the macro being expanded.  */
+       *location = pfile->invocation_location;
+
+      *location = maybe_adjust_loc_for_trad_cpp (pfile, *location);
+    }
+
+  pfile->about_to_expand_macro_p = saved_about_to_expand_macro;
+
+  if (pfile->state.directive_file_token
+      && !pfile->state.parsing_args
+      && !(result->type == CPP_PADDING || result->type == CPP_COMMENT)
+      && !(15 & --pfile->state.directive_file_token))
+    {
+      /* Do header-name frobbery.  Concatenate < ... > as approprate.
+        Do header search if needed, and finally drop the outer <> or
+        "".  */
+      pfile->state.angled_headers = false;
+
+      /* Do angle-header reconstitution.  Then do include searching.
+        We'll always end up with a ""-quoted header-name in that
+        case.  If searching finds nothing, we emit a diagnostic and
+        an empty string.  */
+      size_t len = 0;
+      char *fname = NULL;
+
+      cpp_token *tmp = _cpp_temp_token (pfile);
+      *tmp = *result;
+
+      tmp->type = CPP_HEADER_NAME;
+      bool need_search = !pfile->state.directive_file_token;
+      pfile->state.directive_file_token = 0;
+
+      bool angle = result->type != CPP_STRING;
+      if (result->type == CPP_HEADER_NAME
+         || (result->type == CPP_STRING && result->val.str.text[0] != 'R'))
+       {
+         len = result->val.str.len - 2;
+         fname = XNEWVEC (char, len + 1);
+         memcpy (fname, result->val.str.text + 1, len);
+         fname[len] = 0;
+       }
+      else if (result->type == CPP_LESS)
+       fname = _cpp_bracket_include (pfile);
+
+      if (fname)
+       {
+         /* We have a header-name.  Look it up.  This will emit an
+            unfound diagnostic.  Canonicalize the found name.  */
+         const char *found = fname;
+
+         if (need_search)
+           {
+             found = _cpp_find_header_unit (pfile, fname, angle, tmp->src_loc);
+             if (!found)
+               found = "";
+             len = strlen (found);
+           }
+         /* Force a leading './' if it's not absolute.  */
+         bool dotme = (found[0] == '.' ? !IS_DIR_SEPARATOR (found[1])
+                       : found[0] && !IS_ABSOLUTE_PATH (found));
+
+         if (BUFF_ROOM (pfile->u_buff) < len + 1 + dotme * 2)
+           _cpp_extend_buff (pfile, &pfile->u_buff, len + 1 + dotme * 2);
+         unsigned char *buf = BUFF_FRONT (pfile->u_buff);
+         size_t pos = 0;
+             
+         if (dotme)
+           {
+             buf[pos++] = '.';
+             /* Apparently '/' is unconditional.  */
+             buf[pos++] = '/';
+           }
+         memcpy (&buf[pos], found, len);
+         pos += len;
+         buf[pos] = 0;
+
+         tmp->val.str.len = pos;
+         tmp->val.str.text = buf;
+
+         tmp->type = CPP_HEADER_NAME;
+         XDELETEVEC (fname);
+         
+         result = tmp;
+       }
+    }
+
+  return result;
+}
+
+/* External routine to get a token.  Also used nearly everywhere
+   internally, except for places where we know we can safely call
+   _cpp_lex_token directly, such as lexing a directive name.
+
+   Macro expansions and directives are transparently handled,
+   including entering included files.  Thus tokens are post-macro
+   expansion, and after any intervening directives.  External callers
+   see CPP_EOF only at EOF.  Internal callers also see it when meeting
+   a directive inside a macro call, when at the end of a directive and
+   state.in_directive is still 1, and at the end of argument
+   pre-expansion.  */
+const cpp_token *
+cpp_get_token (cpp_reader *pfile)
+{
+  return cpp_get_token_1 (pfile, NULL);
+}
+
+/* Like cpp_get_token, but also returns a virtual token location
+   separate from the spelling location carried by the returned token.
+
+   LOC is an out parameter; *LOC is set to the location "as expected
+   by the user".  This matters when a token results from macro
+   expansion; in that case the token's spelling location indicates the
+   locus of the token in the definition of the macro but *LOC
+   virtually encodes all the other meaningful locuses associated to
+   the token.
+
+   What? virtual location? Yes, virtual location.
+
+   If the token results from macro expansion and if macro expansion
+   location tracking is enabled its virtual location encodes (at the
+   same time):
+
+   - the spelling location of the token
+
+   - the locus of the macro expansion point
+
+   - the locus of the point where the token got instantiated as part
+     of the macro expansion process.
+
+   You have to use the linemap API to get the locus you are interested
+   in from a given virtual location.
+
+   Note however that virtual locations are not necessarily ordered for
+   relations '<' and '>'.  One must use the function
+   linemap_location_before_p instead of using the relational operator
+   '<'.
+
+   If macro expansion tracking is off and if the token results from
+   macro expansion the virtual location is the expansion point of the
+   macro that got expanded.
+
+   When the token doesn't result from macro expansion, the virtual
+   location is just the same thing as its spelling location.  */
+
+const cpp_token *
+cpp_get_token_with_location (cpp_reader *pfile, location_t *loc)
+{
+  return cpp_get_token_1 (pfile, loc);
+}
+
+/* Returns true if we're expanding an object-like macro that was
+   defined in a system header.  Just checks the macro at the top of
+   the stack.  Used for diagnostic suppression.
+   Also return true for builtin macros.  */
+int
+cpp_sys_macro_p (cpp_reader *pfile)
+{
+  cpp_hashnode *node = NULL;
+
+  if (pfile->context->tokens_kind == TOKENS_KIND_EXTENDED)
+    node = pfile->context->c.mc->macro_node;
+  else
+    node = pfile->context->c.macro;
+
+  if (!node)
+    return false;
+  if (cpp_builtin_macro_p (node))
+    return true;
+  return node->value.macro && node->value.macro->syshdr;
+}
+
+/* Read each token in, until end of the current file.  Directives are
+   transparently processed.  */
+void
+cpp_scan_nooutput (cpp_reader *pfile)
+{
+  /* Request a CPP_EOF token at the end of this file, rather than
+     transparently continuing with the including file.  */
+  pfile->buffer->return_at_eof = true;
+
+  pfile->state.discarding_output++;
+  pfile->state.prevent_expansion++;
+
+  if (CPP_OPTION (pfile, traditional))
+    while (_cpp_read_logical_line_trad (pfile))
+      ;
+  else
+    while (cpp_get_token (pfile)->type != CPP_EOF)
+      ;
+
+  pfile->state.discarding_output--;
+  pfile->state.prevent_expansion--;
+}
+
+/* Step back one or more tokens obtained from the lexer.  */
+void
+_cpp_backup_tokens_direct (cpp_reader *pfile, unsigned int count)
+{
+  pfile->lookaheads += count;
+  while (count--)
+    {
+      pfile->cur_token--;
+      if (pfile->cur_token == pfile->cur_run->base
+          /* Possible with -fpreprocessed and no leading #line.  */
+          && pfile->cur_run->prev != NULL)
+        {
+          pfile->cur_run = pfile->cur_run->prev;
+          pfile->cur_token = pfile->cur_run->limit;
+        }
+    }
+}
+
+/* Step back one (or more) tokens.  Can only step back more than 1 if
+   they are from the lexer, and not from macro expansion.  */
+void
+_cpp_backup_tokens (cpp_reader *pfile, unsigned int count)
+{
+  if (pfile->context->prev == NULL)
+    _cpp_backup_tokens_direct (pfile, count);
+  else
+    {
+      if (count != 1)
+       abort ();
+      if (pfile->context->tokens_kind == TOKENS_KIND_DIRECT)
+       FIRST (pfile->context).token--;
+      else if (pfile->context->tokens_kind == TOKENS_KIND_INDIRECT)
+       FIRST (pfile->context).ptoken--;
+      else if (pfile->context->tokens_kind == TOKENS_KIND_EXTENDED)
+       {
+         FIRST (pfile->context).ptoken--;
+         if (pfile->context->c.macro)
+           {
+             macro_context *m = pfile->context->c.mc;
+             m->cur_virt_loc--;
+             gcc_checking_assert (m->cur_virt_loc >= m->virt_locs);
+           }
+         else
+           abort ();
+       }
+      else
+       abort ();
+    }
+}
+
+/* #define directive parsing and handling.  */
+
+/* Returns true if a macro redefinition warning is required.  */
+static bool
+warn_of_redefinition (cpp_reader *pfile, cpp_hashnode *node,
+                     const cpp_macro *macro2)
+{
+  /* Some redefinitions need to be warned about regardless.  */
+  if (node->flags & NODE_WARN)
+    return true;
+
+  /* Suppress warnings for builtins that lack the NODE_WARN flag,
+     unless Wbuiltin-macro-redefined.  */
+  if (cpp_builtin_macro_p (node))
+    return CPP_OPTION (pfile, warn_builtin_macro_redefined);
+
+  /* Redefinitions of conditional (context-sensitive) macros, on
+     the other hand, must be allowed silently.  */
+  if (node->flags & NODE_CONDITIONAL)
+    return false;
+
+  if (cpp_macro *macro1 = get_deferred_or_lazy_macro (pfile, node, macro2->line))
+    return cpp_compare_macros (macro1, macro2);
+  return false;
+}
+
+/* Return TRUE if MACRO1 and MACRO2 differ.  */
+
+bool
+cpp_compare_macros (const cpp_macro *macro1, const cpp_macro *macro2)
+{
+  /* Redefinition of a macro is allowed if and only if the old and new
+     definitions are the same.  (6.10.3 paragraph 2).  */
+
+  /* Don't check count here as it can be different in valid
+     traditional redefinitions with just whitespace differences.  */
+  if (macro1->paramc != macro2->paramc
+      || macro1->fun_like != macro2->fun_like
+      || macro1->variadic != macro2->variadic)
+    return true;
+
+  /* Check parameter spellings.  */
+  for (unsigned i = macro1->paramc; i--; )
+    if (macro1->parm.params[i] != macro2->parm.params[i])
+      return true;
+
+  /* Check the replacement text or tokens.  */
+  if (macro1->kind == cmk_traditional)
+    return _cpp_expansions_different_trad (macro1, macro2);
+
+  if (macro1->count != macro2->count)
+    return true;
+
+  for (unsigned i= macro1->count; i--; )
+    if (!_cpp_equiv_tokens (&macro1->exp.tokens[i], &macro2->exp.tokens[i]))
+      return true;
+
+  return false;
+}
+
+/* Free the definition of hashnode H.  */
+void
+_cpp_free_definition (cpp_hashnode *h)
+{
+  /* Macros and assertions no longer have anything to free.  */
+  h->type = NT_VOID;
+  h->value.answers = NULL;
+  h->flags &= ~(NODE_DISABLED | NODE_USED);
+}
+
+/* Save parameter NODE (spelling SPELLING) to the parameter list of
+   macro MACRO.  Returns true on success, false on failure.   */
+bool
+_cpp_save_parameter (cpp_reader *pfile, unsigned n, cpp_hashnode *node,
+                    cpp_hashnode *spelling)
+{
+  /* Constraint 6.10.3.6 - duplicate parameter names.  */
+  if (node->type == NT_MACRO_ARG)
+    {
+      cpp_error (pfile, CPP_DL_ERROR, "duplicate macro parameter \"%s\"",
+                NODE_NAME (node));
+      return false;
+    }
+
+  unsigned len = (n + 1) * sizeof (struct macro_arg_saved_data);
+  if (len > pfile->macro_buffer_len)
+    {
+      pfile->macro_buffer
+       = XRESIZEVEC (unsigned char, pfile->macro_buffer, len);
+      pfile->macro_buffer_len = len;
+    }
+  
+  macro_arg_saved_data *saved = (macro_arg_saved_data *)pfile->macro_buffer;
+  saved[n].canonical_node = node;
+  saved[n].value = node->value;
+  saved[n].type = node->type;
+
+  void *base = _cpp_reserve_room (pfile, n * sizeof (cpp_hashnode *),
+                                 sizeof (cpp_hashnode *));
+  ((cpp_hashnode **)base)[n] = spelling;
+
+  /* Morph into a macro arg.  */
+  node->type = NT_MACRO_ARG;
+  /* Index is 1 based.  */
+  node->value.arg_index = n + 1;
+
+  return true;
+}
+
+/* Restore the parameters to their previous state.  */
+void
+_cpp_unsave_parameters (cpp_reader *pfile, unsigned n)
+{
+  /* Clear the fast argument lookup indices.  */
+  while (n--)
+    {
+      struct macro_arg_saved_data *save =
+       &((struct macro_arg_saved_data *) pfile->macro_buffer)[n];
+
+      struct cpp_hashnode *node = save->canonical_node;
+      node->type = save->type;
+      node->value = save->value;
+    }
+}
+
+/* Check the syntax of the parameters in a MACRO definition.  Return
+   false on failure.  Set *N_PTR and *VARADIC_PTR as appropriate.
+   '(' ')'
+   '(' parm-list ',' last-parm ')'
+   '(' last-parm ')'
+   parm-list: name
+            | parm-list, name
+   last-parm: name
+           | name '...'
+            | '...'
+*/
+
+static bool
+parse_params (cpp_reader *pfile, unsigned *n_ptr, bool *varadic_ptr)
+{
+  unsigned nparms = 0;
+  bool ok = false;
+
+  for (bool prev_ident = false;;)
+    {
+      const cpp_token *token = _cpp_lex_token (pfile);
+
+      switch (token->type)
+       {
+       case CPP_COMMENT:
+         /* Allow/ignore comments in parameter lists if we are
+            preserving comments in macro expansions.  */
+         if (!CPP_OPTION (pfile, discard_comments_in_macro_exp))
+           break;
+
+         /* FALLTHRU  */
+       default:
+       bad:
+         {
+           const char *const msgs[5] =
+             {
+              N_("expected parameter name, found \"%s\""),
+              N_("expected ',' or ')', found \"%s\""),
+              N_("expected parameter name before end of line"),
+              N_("expected ')' before end of line"),
+              N_("expected ')' after \"...\"")
+             };
+           unsigned ix = prev_ident;
+           const unsigned char *as_text = NULL;
+           if (*varadic_ptr)
+             ix = 4;
+           else if (token->type == CPP_EOF)
+             ix += 2;
+           else
+             as_text = cpp_token_as_text (pfile, token);
+           cpp_error (pfile, CPP_DL_ERROR, msgs[ix], as_text);
+         }
+         goto out;
+
+       case CPP_NAME:
+         if (prev_ident || *varadic_ptr)
+           goto bad;
+         prev_ident = true;
+
+         if (!_cpp_save_parameter (pfile, nparms, token->val.node.node,
+                                   token->val.node.spelling))
+           goto out;
+         nparms++;
+         break;
+
+       case CPP_CLOSE_PAREN:
+         if (prev_ident || !nparms || *varadic_ptr)
+           {
+             ok = true;
+             goto out;
+           }
+
+         /* FALLTHRU */
+       case CPP_COMMA:
+         if (!prev_ident || *varadic_ptr)
+           goto bad;
+         prev_ident = false;
+         break;
+
+       case CPP_ELLIPSIS:
+         if (*varadic_ptr)
+           goto bad;
+         *varadic_ptr = true;
+         if (!prev_ident)
+           {
+             /* An ISO bare ellipsis.  */
+             _cpp_save_parameter (pfile, nparms,
+                                  pfile->spec_nodes.n__VA_ARGS__,
+                                  pfile->spec_nodes.n__VA_ARGS__);
+             nparms++;
+             pfile->state.va_args_ok = 1;
+             if (! CPP_OPTION (pfile, c99)
+                 && CPP_OPTION (pfile, cpp_pedantic)
+                 && CPP_OPTION (pfile, warn_variadic_macros))
+               cpp_pedwarning
+                 (pfile, CPP_W_VARIADIC_MACROS,
+                  CPP_OPTION (pfile, cplusplus)
+                  ? N_("anonymous variadic macros were introduced in C++11")
+                  : N_("anonymous variadic macros were introduced in C99"));
+             else if (CPP_OPTION (pfile, cpp_warn_c90_c99_compat) > 0
+                      && ! CPP_OPTION (pfile, cplusplus))
+               cpp_error (pfile, CPP_DL_WARNING,
+                          "anonymous variadic macros were introduced in C99");
+           }
+         else if (CPP_OPTION (pfile, cpp_pedantic)
+                  && CPP_OPTION (pfile, warn_variadic_macros))
+           cpp_pedwarning (pfile, CPP_W_VARIADIC_MACROS,
+                           CPP_OPTION (pfile, cplusplus)
+                           ? N_("ISO C++ does not permit named variadic macros")
+                           : N_("ISO C does not permit named variadic macros"));
+         break;
+       }
+    }
+
+ out:
+  *n_ptr = nparms;
+
+  return ok;
+}
+
+/* Lex a token from the expansion of MACRO, but mark parameters as we
+   find them and warn of traditional stringification.  */
+static cpp_macro *
+lex_expansion_token (cpp_reader *pfile, cpp_macro *macro)
+{
+  macro = (cpp_macro *)_cpp_reserve_room (pfile,
+                                         sizeof (cpp_macro) - sizeof (cpp_token)
+                                         + macro->count * sizeof (cpp_token),
+                                         sizeof (cpp_token));
+  cpp_token *saved_cur_token = pfile->cur_token;
+  pfile->cur_token = &macro->exp.tokens[macro->count];
+  cpp_token *token = _cpp_lex_direct (pfile);
+  pfile->cur_token = saved_cur_token;
+
+  /* Is this a parameter?  */
+  if (token->type == CPP_NAME && token->val.node.node->type == NT_MACRO_ARG)
+    {
+      /* Morph into a parameter reference.  */
+      cpp_hashnode *spelling = token->val.node.spelling;
+      token->type = CPP_MACRO_ARG;
+      token->val.macro_arg.arg_no = token->val.node.node->value.arg_index;
+      token->val.macro_arg.spelling = spelling;
+    }
+  else if (CPP_WTRADITIONAL (pfile) && macro->paramc > 0
+          && (token->type == CPP_STRING || token->type == CPP_CHAR))
+    check_trad_stringification (pfile, macro, &token->val.str);
+
+  return macro;
+}
+
+static cpp_macro *
+create_iso_definition (cpp_reader *pfile)
+{
+  bool following_paste_op = false;
+  const char *paste_op_error_msg =
+    N_("'##' cannot appear at either end of a macro expansion");
+  unsigned int num_extra_tokens = 0;
+  unsigned nparms = 0;
+  cpp_hashnode **params = NULL;
+  bool varadic = false;
+  bool ok = false;
+  cpp_macro *macro = NULL;
+
+  /* Look at the first token, to see if this is a function-like
+     macro.   */
+  cpp_token first;
+  cpp_token *saved_cur_token = pfile->cur_token;
+  pfile->cur_token = &first;
+  cpp_token *token = _cpp_lex_direct (pfile);
+  pfile->cur_token = saved_cur_token;
+
+  if (token->flags & PREV_WHITE)
+    /* Preceeded by space, must be part of expansion.  */;
+  else if (token->type == CPP_OPEN_PAREN)
+    {
+      /* An open-paren, get a parameter list.  */
+      if (!parse_params (pfile, &nparms, &varadic))
+       goto out;
+
+      params = (cpp_hashnode **)_cpp_commit_buff
+       (pfile, sizeof (cpp_hashnode *) * nparms);
+      token = NULL;
+    }
+  else if (token->type != CPP_EOF
+          && !(token->type == CPP_COMMENT
+               && ! CPP_OPTION (pfile, discard_comments_in_macro_exp)))
+    {
+      /* While ISO C99 requires whitespace before replacement text
+        in a macro definition, ISO C90 with TC1 allows characters
+        from the basic source character set there.  */
+      if (CPP_OPTION (pfile, c99))
+       cpp_error (pfile, CPP_DL_PEDWARN,
+                  CPP_OPTION (pfile, cplusplus)
+                  ? N_("ISO C++11 requires whitespace after the macro name")
+                  : N_("ISO C99 requires whitespace after the macro name"));
+      else
+       {
+         enum cpp_diagnostic_level warntype = CPP_DL_WARNING;
+         switch (token->type)
+           {
+           case CPP_ATSIGN:
+           case CPP_AT_NAME:
+           case CPP_OBJC_STRING:
+             /* '@' is not in basic character set.  */
+             warntype = CPP_DL_PEDWARN;
+             break;
+           case CPP_OTHER:
+             /* Basic character set sans letters, digits and _.  */
+             if (strchr ("!\"#%&'()*+,-./:;<=>?[\\]^{|}~",
+                         token->val.str.text[0]) == NULL)
+               warntype = CPP_DL_PEDWARN;
+             break;
+           default:
+             /* All other tokens start with a character from basic
+                character set.  */
+             break;
+           }
+         cpp_error (pfile, warntype,
+                    "missing whitespace after the macro name");
+       }
+    }
+
+  macro = _cpp_new_macro (pfile, cmk_macro,
+                         _cpp_reserve_room (pfile, 0, sizeof (cpp_macro)));
+
+  if (!token)
+    {
+      macro->variadic = varadic;
+      macro->paramc = nparms;
+      macro->parm.params = params;
+      macro->fun_like = true;
+    }
+  else
+    {
+      /* Preserve the token we peeked, there is already a single slot for it.  */
+      macro->exp.tokens[0] = *token;
+      token = &macro->exp.tokens[0];
+      macro->count = 1;
+    }
+
+  for (vaopt_state vaopt_tracker (pfile, macro->variadic, NULL);; token = NULL)
+    {
+      if (!token)
+       {
+         macro = lex_expansion_token (pfile, macro);
+         token = &macro->exp.tokens[macro->count++];
+       }
+
+      /* Check the stringifying # constraint 6.10.3.2.1 of
+        function-like macros when lexing the subsequent token.  */
+      if (macro->count > 1 && token[-1].type == CPP_HASH && macro->fun_like)
+       {
+         if (token->type == CPP_MACRO_ARG
+             || (macro->variadic
+                 && token->type == CPP_NAME
+                 && token->val.node.node == pfile->spec_nodes.n__VA_OPT__))
+           {
+             if (token->flags & PREV_WHITE)
+               token->flags |= SP_PREV_WHITE;
+             if (token[-1].flags & DIGRAPH)
+               token->flags |= SP_DIGRAPH;
+             token->flags &= ~PREV_WHITE;
+             token->flags |= STRINGIFY_ARG;
+             token->flags |= token[-1].flags & PREV_WHITE;
+             token[-1] = token[0];
+             macro->count--;
+           }
+         /* Let assembler get away with murder.  */
+         else if (CPP_OPTION (pfile, lang) != CLK_ASM)
+           {
+             cpp_error (pfile, CPP_DL_ERROR,
+                        "'#' is not followed by a macro parameter");
+             goto out;
+           }
+       }
+
+      if (token->type == CPP_EOF)
+       {
+         /* Paste operator constraint 6.10.3.3.1:
+            Token-paste ##, can appear in both object-like and
+            function-like macros, but not at the end.  */
+         if (following_paste_op)
+           {
+             cpp_error (pfile, CPP_DL_ERROR, paste_op_error_msg);
+             goto out;
+           }
+         if (!vaopt_tracker.completed ())
+           goto out;
+         break;
+       }
+
+      /* Paste operator constraint 6.10.3.3.1.  */
+      if (token->type == CPP_PASTE)
+       {
+         /* Token-paste ##, can appear in both object-like and
+            function-like macros, but not at the beginning.  */
+         if (macro->count == 1)
+           {
+             cpp_error (pfile, CPP_DL_ERROR, paste_op_error_msg);
+             goto out;
+           }
+
+         if (following_paste_op)
+           {
+             /* Consecutive paste operators.  This one will be moved
+                to the end.  */
+             num_extra_tokens++;
+             token->val.token_no = macro->count - 1;
+           }
+         else
+           {
+             /* Drop the paste operator.  */
+             --macro->count;
+             token[-1].flags |= PASTE_LEFT;
+             if (token->flags & DIGRAPH)
+               token[-1].flags |= SP_DIGRAPH;
+             if (token->flags & PREV_WHITE)
+               token[-1].flags |= SP_PREV_WHITE;
+           }
+         following_paste_op = true;
+       }
+      else
+       following_paste_op = false;
+
+      if (vaopt_tracker.update (token) == vaopt_state::ERROR)
+       goto out;
+    }
+
+  /* We're committed to winning now.  */
+  ok = true;
+
+  /* Don't count the CPP_EOF.  */
+  macro->count--;
+
+  macro = (cpp_macro *)_cpp_commit_buff
+    (pfile, sizeof (cpp_macro) - sizeof (cpp_token)
+     + sizeof (cpp_token) * macro->count);
+
+  /* Clear whitespace on first token.  */
+  if (macro->count)
+    macro->exp.tokens[0].flags &= ~PREV_WHITE;
+
+  if (num_extra_tokens)
+    {
+      /* Place second and subsequent ## or %:%: tokens in sequences of
+        consecutive such tokens at the end of the list to preserve
+        information about where they appear, how they are spelt and
+        whether they are preceded by whitespace without otherwise
+        interfering with macro expansion.   Remember, this is
+        extremely rare, so efficiency is not a priority.  */
+      cpp_token *temp = (cpp_token *)_cpp_reserve_room
+       (pfile, 0, num_extra_tokens * sizeof (cpp_token));
+      unsigned extra_ix = 0, norm_ix = 0;
+      cpp_token *exp = macro->exp.tokens;
+      for (unsigned ix = 0; ix != macro->count; ix++)
+       if (exp[ix].type == CPP_PASTE)
+         temp[extra_ix++] = exp[ix];
+       else
+         exp[norm_ix++] = exp[ix];
+      memcpy (&exp[norm_ix], temp, num_extra_tokens * sizeof (cpp_token));
+
+      /* Record there are extra tokens.  */
+      macro->extra_tokens = 1;
+    }
+
+ out:
+  pfile->state.va_args_ok = 0;
+  _cpp_unsave_parameters (pfile, nparms);
+
+  return ok ? macro : NULL;
+}
+
+cpp_macro *
+_cpp_new_macro (cpp_reader *pfile, cpp_macro_kind kind, void *placement)
+{
+  cpp_macro *macro = (cpp_macro *) placement;
+
+  /* Zero init all the fields.  This'll tell the compiler know all the
+     following inits are writing a virgin object.  */
+  memset (macro, 0, offsetof (cpp_macro, exp));
+
+  macro->line = pfile->directive_line;
+  macro->parm.params = 0;
+  macro->lazy = 0;
+  macro->paramc = 0;
+  macro->variadic = 0;
+  macro->used = !CPP_OPTION (pfile, warn_unused_macros);
+  macro->count = 0;
+  macro->fun_like = 0;
+  macro->imported_p = false;
+  macro->extra_tokens = 0;
+  /* To suppress some diagnostics.  */
+  macro->syshdr = pfile->buffer && pfile->buffer->sysp != 0;
+
+  macro->kind = kind;
+
+  return macro;
+}
+
+/* Parse a macro and save its expansion.  Returns nonzero on success.  */
+bool
+_cpp_create_definition (cpp_reader *pfile, cpp_hashnode *node)
+{
+  cpp_macro *macro;
+
+  if (CPP_OPTION (pfile, traditional))
+    macro = _cpp_create_trad_definition (pfile);
+  else
+    macro = create_iso_definition (pfile);
+
+  if (!macro)
+    return false;
+
+  if (cpp_macro_p (node))
+    {
+      if (CPP_OPTION (pfile, warn_unused_macros))
+       _cpp_warn_if_unused_macro (pfile, node, NULL);
+
+      if (warn_of_redefinition (pfile, node, macro))
+       {
+          const enum cpp_warning_reason reason
+           = (cpp_builtin_macro_p (node) && !(node->flags & NODE_WARN))
+           ? CPP_W_BUILTIN_MACRO_REDEFINED : CPP_W_NONE;
+
+         bool warned = 
+           cpp_pedwarning_with_line (pfile, reason,
+                                     pfile->directive_line, 0,
+                                     "\"%s\" redefined", NODE_NAME (node));
+
+         if (warned && cpp_user_macro_p (node))
+           cpp_error_with_line (pfile, CPP_DL_NOTE,
+                                node->value.macro->line, 0,
+                        "this is the location of the previous definition");
+       }
+      _cpp_free_definition (node);
+    }
+
+  /* Enter definition in hash table.  */
+  node->type = NT_USER_MACRO;
+  node->value.macro = macro;
+  if (! ustrncmp (NODE_NAME (node), DSC ("__STDC_"))
+      && ustrcmp (NODE_NAME (node), (const uchar *) "__STDC_FORMAT_MACROS")
+      /* __STDC_LIMIT_MACROS and __STDC_CONSTANT_MACROS are mentioned
+        in the C standard, as something that one must use in C++.
+        However DR#593 and C++11 indicate that they play no role in C++.
+        We special-case them anyway.  */
+      && ustrcmp (NODE_NAME (node), (const uchar *) "__STDC_LIMIT_MACROS")
+      && ustrcmp (NODE_NAME (node), (const uchar *) "__STDC_CONSTANT_MACROS"))
+    node->flags |= NODE_WARN;
+
+  /* If user defines one of the conditional macros, remove the
+     conditional flag */
+  node->flags &= ~NODE_CONDITIONAL;
+
+  return true;
+}
+
+extern void
+cpp_define_lazily (cpp_reader *pfile, cpp_hashnode *node, unsigned num)
+{
+  cpp_macro *macro = node->value.macro;
+
+  gcc_checking_assert (pfile->cb.user_lazy_macro && macro && num < UCHAR_MAX);
+
+  macro->lazy = num + 1;
+}
+
+/* NODE is a deferred macro, resolve it, returning the definition
+   (which may be NULL).  */
+cpp_macro *
+cpp_get_deferred_macro (cpp_reader *pfile, cpp_hashnode *node,
+                       location_t loc)
+{
+  gcc_checking_assert (node->type == NT_USER_MACRO);
+
+  node->value.macro = pfile->cb.user_deferred_macro (pfile, loc, node);
+
+  if (!node->value.macro)
+    node->type = NT_VOID;
+
+  return node->value.macro;
+}
+
+static cpp_macro *
+get_deferred_or_lazy_macro (cpp_reader *pfile, cpp_hashnode *node,
+                           location_t loc)
+{
+  cpp_macro *macro = node->value.macro;
+  if (!macro)
+    {
+      macro = cpp_get_deferred_macro (pfile, node, loc);
+      gcc_checking_assert (!macro || !macro->lazy);
+    }
+  else if (macro->lazy)
+    {
+      pfile->cb.user_lazy_macro (pfile, macro, macro->lazy - 1);
+      macro->lazy = 0;
+    }
+
+  return macro;
+}
+
+/* Notify the use of NODE in a macro-aware context (i.e. expanding it,
+   or testing its existance).  Also applies any lazy definition.
+   Return FALSE if the macro isn't really there.  */
+
+extern bool
+_cpp_notify_macro_use (cpp_reader *pfile, cpp_hashnode *node,
+                      location_t loc)
+{
+  node->flags |= NODE_USED;
+  switch (node->type)
+    {
+    case NT_USER_MACRO:
+      if (!get_deferred_or_lazy_macro (pfile, node, loc))
+       return false;
+      /* FALLTHROUGH.  */
+
+    case NT_BUILTIN_MACRO:
+      if (pfile->cb.used_define)
+       pfile->cb.used_define (pfile, loc, node);
+      break;
+
+    case NT_VOID:
+      if (pfile->cb.used_undef)
+       pfile->cb.used_undef (pfile, loc, node);
+      break;
+
+    default:
+      abort ();
+    }
+
+  return true;
+}
+
+/* Warn if a token in STRING matches one of a function-like MACRO's
+   parameters.  */
+static void
+check_trad_stringification (cpp_reader *pfile, const cpp_macro *macro,
+                           const cpp_string *string)
+{
+  unsigned int i, len;
+  const uchar *p, *q, *limit;
+
+  /* Loop over the string.  */
+  limit = string->text + string->len - 1;
+  for (p = string->text + 1; p < limit; p = q)
+    {
+      /* Find the start of an identifier.  */
+      while (p < limit && !is_idstart (*p))
+       p++;
+
+      /* Find the end of the identifier.  */
+      q = p;
+      while (q < limit && is_idchar (*q))
+       q++;
+
+      len = q - p;
+
+      /* Loop over the function macro arguments to see if the
+        identifier inside the string matches one of them.  */
+      for (i = 0; i < macro->paramc; i++)
+       {
+         const cpp_hashnode *node = macro->parm.params[i];
+
+         if (NODE_LEN (node) == len
+             && !memcmp (p, NODE_NAME (node), len))
+           {
+             cpp_warning (pfile, CPP_W_TRADITIONAL,
+          "macro argument \"%s\" would be stringified in traditional C",
+                        NODE_NAME (node));
+             break;
+           }
+       }
+    }
+}
+
+/* Returns the name, arguments and expansion of a macro, in a format
+   suitable to be read back in again, and therefore also for DWARF 2
+   debugging info.  e.g. "PASTE(X, Y) X ## Y", or "MACNAME EXPANSION".
+   Caller is expected to generate the "#define" bit if needed.  The
+   returned text is temporary, and automatically freed later.  */
+const unsigned char *
+cpp_macro_definition (cpp_reader *pfile, cpp_hashnode *node)
+{
+  gcc_checking_assert (cpp_user_macro_p (node));
+
+  if (const cpp_macro *macro = get_deferred_or_lazy_macro (pfile, node, 0))
+    return cpp_macro_definition (pfile, node, macro);
+  return NULL;
+}
+
+const unsigned char *
+cpp_macro_definition (cpp_reader *pfile, cpp_hashnode *node,
+                     const cpp_macro *macro)
+{
+  unsigned int i, len;
+  unsigned char *buffer;
+
+  /* Calculate length.  */
+  len = NODE_LEN (node) * 10 + 2;              /* ' ' and NUL.  */
+  if (macro->fun_like)
+    {
+      len += 4;                /* "()" plus possible final ".." of named
+                          varargs (we have + 1 below).  */
+      for (i = 0; i < macro->paramc; i++)
+       len += NODE_LEN (macro->parm.params[i]) + 1; /* "," */
+    }
+
+  /* This should match below where we fill in the buffer.  */
+  if (CPP_OPTION (pfile, traditional))
+    len += _cpp_replacement_text_len (macro);
+  else
+    {
+      unsigned int count = macro_real_token_count (macro);
+      for (i = 0; i < count; i++)
+       {
+         const cpp_token *token = &macro->exp.tokens[i];
+
+         if (token->type == CPP_MACRO_ARG)
+           len += NODE_LEN (token->val.macro_arg.spelling);
+         else
+           len += cpp_token_len (token);
+
+         if (token->flags & STRINGIFY_ARG)
+           len++;                      /* "#" */
+         if (token->flags & PASTE_LEFT)
+           len += 3;           /* " ##" */
+         if (token->flags & PREV_WHITE)
+           len++;              /* " " */
+       }
+    }
+
+  if (len > pfile->macro_buffer_len)
+    {
+      pfile->macro_buffer = XRESIZEVEC (unsigned char,
+                                        pfile->macro_buffer, len);
+      pfile->macro_buffer_len = len;
+    }
+
+  /* Fill in the buffer.  Start with the macro name.  */
+  buffer = pfile->macro_buffer;
+  buffer = _cpp_spell_ident_ucns (buffer, node);
+
+  /* Parameter names.  */
+  if (macro->fun_like)
+    {
+      *buffer++ = '(';
+      for (i = 0; i < macro->paramc; i++)
+       {
+         cpp_hashnode *param = macro->parm.params[i];
+
+         if (param != pfile->spec_nodes.n__VA_ARGS__)
+           {
+             memcpy (buffer, NODE_NAME (param), NODE_LEN (param));
+             buffer += NODE_LEN (param);
+           }
+
+         if (i + 1 < macro->paramc)
+           /* Don't emit a space after the comma here; we're trying
+              to emit a Dwarf-friendly definition, and the Dwarf spec
+              forbids spaces in the argument list.  */
+           *buffer++ = ',';
+         else if (macro->variadic)
+           *buffer++ = '.', *buffer++ = '.', *buffer++ = '.';
+       }
+      *buffer++ = ')';
+    }
+
+  /* The Dwarf spec requires a space after the macro name, even if the
+     definition is the empty string.  */
+  *buffer++ = ' ';
+
+  if (CPP_OPTION (pfile, traditional))
+    buffer = _cpp_copy_replacement_text (macro, buffer);
+  else if (macro->count)
+  /* Expansion tokens.  */
+    {
+      unsigned int count = macro_real_token_count (macro);
+      for (i = 0; i < count; i++)
+       {
+         const cpp_token *token = &macro->exp.tokens[i];
+
+         if (token->flags & PREV_WHITE)
+           *buffer++ = ' ';
+         if (token->flags & STRINGIFY_ARG)
+           *buffer++ = '#';
+
+         if (token->type == CPP_MACRO_ARG)
+           {
+             memcpy (buffer,
+                     NODE_NAME (token->val.macro_arg.spelling),
+                     NODE_LEN (token->val.macro_arg.spelling));
+             buffer += NODE_LEN (token->val.macro_arg.spelling);
+           }
+         else
+           buffer = cpp_spell_token (pfile, token, buffer, true);
+
+         if (token->flags & PASTE_LEFT)
+           {
+             *buffer++ = ' ';
+             *buffer++ = '#';
+             *buffer++ = '#';
+             /* Next has PREV_WHITE; see _cpp_create_definition.  */
+           }
+       }
+    }
+
+  *buffer = '\0';
+  return pfile->macro_buffer;
+}
+
+
+//--------------------------------------------------------------------------------
+// RT extensions 
+//--------------------------------------------------------------------------------
+
+#define DebugParseClause 0
+#define DebugAssign 0
+#define DebugRTMacro 0
+
+// see directives.cc
+extern const char *cpp_token_as_text(const cpp_token *token);
+extern void print_token_list (const cpp_token *tokens, size_t count);
+
+// a helper function for probing where the parser thinks it is in the source
+void
+debug_peek_token (cpp_reader *pfile)
+{
+  cpp_token *tok = _cpp_lex_direct(pfile);
+
+  cpp_error_with_line(
+    pfile,
+    CPP_DL_ERROR,
+    tok->src_loc,
+    0,
+    "DEBUG: next token is: `%s`",
+    (const char *) cpp_token_as_text(tok)
+  );
+
+  _cpp_backup_tokens(pfile, 1);
+}
+
+
+/*--------------------------------------------------------------------------------
+  Parse a clause
+
+    clause     ::= "(" literal? ")" 
+                 | "[" expr? "]"
+                 | tokens_to_eol ;
+
+    literal    ::= ; sequence parsed into tokens, no expansion
+    expr       ::= ; sequence parsed into tokens with recursive expansion of each token
+    tokens_to_eol ::= ; all tokens until logical end-of-line (including multi-line with `\`)
+
+  Notes:
+    - The first two forms are explicitly delimited with parentheses or brackets,
+      and may be empty (e.g., `()` or `[]`). Newlines are taken as white space.
+    - The third form is implicit: it consumes all remaining tokens on the directive line.
+      This is typical for simple macro bodies (e.g., in `#define NAME body`).
+
+*/
+
+// collects the body of a #define or related directive 
+typedef enum parse_clause_status {
+  PCS_OK = 0,                        // Normal successful collection
+
+  PCS_ERR_EXPECTED_OPEN_DELIM,      // Failed to find expected opening '('
+  PCS_ERR_UNEXPECTED_EOF,           // Hit real EOF before matching ')'
+  PCS_ERR_PASTE_AT_END,             // Trailing '##' paste operator
+  PCS_ERR_HASH_NOT_FOLLOWED_BY_ARG, // '#' not followed by macro parameter
+  PCS_ERR_VAOPT_STATE_INVALID,      // __VA_OPT__ or variadic tracking error
+  PCS_ERR_EOF_FETCH_FAILED,         // Failed to fetch next line after EOF
+  PCS_ERR_UNKNOWN                   // Fallback error (should not occur)
+} parse_clause_status;
+
+void print_parse_clause_status(enum parse_clause_status status)
+{
+#if 1
+  const char *message = NULL;
+  switch (status)
+    {
+    case PCS_OK:
+      message = "parse_clause status is OK";
+      break;
+    case PCS_ERR_EXPECTED_OPEN_DELIM:
+      message = "expected opening delimiter such as '(' but did not find it.";
+      break;
+    case PCS_ERR_UNEXPECTED_EOF:
+      message = "unexpected EOF before closing ')'.";
+      break;
+    case PCS_ERR_PASTE_AT_END:
+      message = "paste operator '##' appeared at the beginning or end of macro body.";
+      break;
+    case PCS_ERR_HASH_NOT_FOLLOWED_BY_ARG:
+      message = "'#' was not followed by a valid macro parameter.";
+      break;
+    case PCS_ERR_VAOPT_STATE_INVALID:
+      message = "invalid __VA_OPT__ tracking state.";
+      break;
+    case PCS_ERR_EOF_FETCH_FAILED:
+      message = "_cpp_get_fresh_line() failed to fetch next line.";
+      break;
+    case PCS_ERR_UNKNOWN:
+    default:
+      message = "unknown or unhandled error.";
+      break;
+    }
+  fprintf(stderr, "%s\n", message);
+#endif
+}
+
+// if not paren_matching, then ends with CPP_EOF
+static enum parse_clause_status parse_clause_body_expand(
+  cpp_reader *pfile
+  ,cpp_macro *macro
+  ,bool paren_matching
+  ,enum cpp_ttype opening
+  ,enum cpp_ttype closing
+){
+
+  #if DebugParseClause
+    fprintf(stderr, "entering parse_clause_body_expand\n");
+    if(paren_matching) 
+      fprintf(stderr, "  paren_matching\n");
+    else
+      fprintf(stderr, "  no paren_matching\n");
+  #endif
+
+  int paren_depth = 1; 
+  const cpp_token *token;
+  location_t src_loc;
+
+  for(;;){
+    
+    /* get a token
+    */
+      token = cpp_get_token_1 (pfile, &src_loc);
+      // this is necessary for the name expr, but does it impact potential other uses of parse_clause?  Another flag for this perhaps?
+      if(token->type == CPP_PADDING) continue;
+      macro = (cpp_macro *)_cpp_reserve_room(
+        pfile,
+        sizeof(cpp_macro) + macro->count * sizeof(cpp_token),
+        sizeof(cpp_token)
+      );
+      macro->exp.tokens[macro->count] = *token;
+
+      #if DebugParseClause
+        fprintf( stderr, "token: %s\n", cpp_token_as_text(token) );
+      #endif
+
+      // lexer supports line macros by inserting CPP_EOF at line ends
+      if(paren_matching && token->type == CPP_EOF){
+        #if DebugParseClause
+          fprintf( stderr, "CPP_EOF during parse with parentheses matching \n");
+        #endif
+        if(!_cpp_get_fresh_line(pfile)){
+          return PCS_ERR_EOF_FETCH_FAILED;
+        }
+        continue;
+      }
+
+    /* parentheses matching overhead
+    */
+      if(paren_matching){
+
+        if (token->type == opening) {
+          paren_depth++;
+        }
+        else if (token->type == closing) {
+          paren_depth--;
+          if (paren_depth < 0) {
+            cpp_error(pfile, CPP_DL_ERROR, "unmatched closing delimiter");
+            return PCS_ERR_UNEXPECTED_EOF;
+          }
+        }
+
+        #if DebugParseClause
+          if( token->type == opening || token->type == closing){
+            fprintf( stderr, "new paren_depth: %d\n", paren_depth);
+          }
+        #endif
+      }
+
+
+    /* Determine if routine has lexed the final macro body token and should exit.
+    */
+      if(
+        paren_matching && paren_depth == 0 && token->type == closing
+        || !paren_matching && token->type == CPP_EOF
+      ){
+        return PCS_OK;
+      }
+
+      // commit the new token
+      macro->count++;
+
+  }// end for next token loop
+
+}
+
+bool cgls_flag = false;
+
+// if not paren_matching, then ends with CPP_EOF
+static enum parse_clause_status parse_clause_body_literal(
+  cpp_reader *pfile
+  ,cpp_macro *macro
+  ,bool paren_matching
+  ,enum cpp_ttype opening
+  ,enum cpp_ttype closing
+  ,unsigned int *num_extra_tokens_out
+){
+
+  #if DebugParseClause
+    fprintf(stderr, "entering parse_clause_body_literal\n");
+    if(paren_matching) 
+      fprintf(stderr, "  paren_matching\n");
+    else
+      fprintf(stderr, "  no paren_matching\n");
+  #endif
+
+  bool following_paste_op = false;
+  unsigned int num_extra_tokens = 0;
+  int paren_depth = 1; 
+  cpp_token *lex_token;
+  const char *paste_op_error_msg =
+      N_("'##' cannot appear at either end of a macro expansion");
+
+
+  for(vaopt_state vaopt_tracker (pfile, macro->variadic, NULL);;){
+    
+    /* get a token
+    */
+      // first parses lex_token onto `macro->exp.tokens[macro->count]`
+      // then pulls the token off of `macro->exp.tokens[macro->count]`
+      // reassigns macro due to possible macro->exp.tokens buffer expansion
+      macro = lex_expansion_token(pfile, macro);
+      lex_token = &macro->exp.tokens[macro->count];
+
+      #if DebugParseClause
+        fprintf( stderr, "lex_token: %s\n", cpp_token_as_text(lex_token) );
+      #endif
+
+      // lexer will insert CPP_EOF at the end of each line, because cpp originally only did line macros.
+      if(paren_matching && lex_token->type == CPP_EOF){
+        #if DebugParseClause
+          fprintf( stderr, "CPP_EOF during parse with parentheses matching \n");
+        #endif
+        if(!_cpp_get_fresh_line(pfile)){
+          return PCS_ERR_EOF_FETCH_FAILED;
+        }
+        continue;
+      }
+
+    /* tag macro args
+    */
+      if (macro->count > 1 && lex_token[-1].type == CPP_HASH && macro->fun_like)
+        {
+          if (lex_token->type == CPP_MACRO_ARG
+              || (macro->variadic
+                  && lex_token->type == CPP_NAME
+                  && lex_token->val.node.node == pfile->spec_nodes.n__VA_OPT__))
+            {
+              if (lex_token->flags & PREV_WHITE)
+                lex_token->flags |= SP_PREV_WHITE;
+              if (lex_token[-1].flags & DIGRAPH)
+                lex_token->flags |= SP_DIGRAPH;
+              lex_token->flags &= ~PREV_WHITE;
+              lex_token->flags |= STRINGIFY_ARG;
+              lex_token->flags |= lex_token[-1].flags & PREV_WHITE;
+              lex_token[-1] = lex_token[0];
+              macro->count--;
+            }
+          else if (CPP_OPTION (pfile, lang) != CLK_ASM)
+            {
+              cpp_error(pfile, CPP_DL_ERROR,
+                        "'#' is not followed by a macro parameter");
+              return PCS_ERR_HASH_NOT_FOLLOWED_BY_ARG;
+            }
+        }
+
+    /* paste end cases
+    */
+      if (lex_token->type == CPP_PASTE)
+        {
+          if (macro->count == 0)
+            {
+              cpp_error(pfile, CPP_DL_ERROR, paste_op_error_msg);
+              return PCS_ERR_PASTE_AT_END; // the font end of the buffer
+            }
+
+          if (following_paste_op)
+            {
+              num_extra_tokens++;
+              lex_token->val.token_no = macro->count - 1;
+            }
+          else
+            {
+              --macro->count;
+              lex_token[-1].flags |= PASTE_LEFT;
+              if (lex_token->flags & DIGRAPH)
+                lex_token[-1].flags |= SP_DIGRAPH;
+              if (lex_token->flags & PREV_WHITE)
+                lex_token[-1].flags |= SP_PREV_WHITE;
+            }
+          following_paste_op = true;
+        }
+      else{
+        following_paste_op = false;
+      }
+
+    /* parentheses matching overhead
+    */
+      if(paren_matching){
+
+        if (lex_token->type == opening) {
+          paren_depth++;
+        }
+        else if (lex_token->type == closing) {
+          paren_depth--;
+          if (paren_depth < 0) {
+            cpp_error(pfile, CPP_DL_ERROR, "unmatched closing delimiter");
+            return PCS_ERR_UNEXPECTED_EOF;
+          }
+        }
+
+        #if DebugParseClause
+          if( lex_token->type == opening || lex_token->type == closing){
+            fprintf( stderr, "new paren_depth: %d\n", paren_depth);
+          }
+        #endif
+      }
+
+
+    /* Determine if routine has lexed the final macro body token and should exit.
+    */
+      if(
+        paren_matching && paren_depth == 0 && lex_token->type == closing
+        || !paren_matching && lex_token->type == CPP_EOF
+      ){
+
+        if(following_paste_op){
+          cpp_error(pfile, CPP_DL_ERROR, paste_op_error_msg);
+          return PCS_ERR_PASTE_AT_END;
+        }
+        if (vaopt_tracker.update(lex_token) == vaopt_state::ERROR){
+          return PCS_ERR_VAOPT_STATE_INVALID;
+        }
+        if( !vaopt_tracker.completed() ){
+          return PCS_ERR_VAOPT_STATE_INVALID;
+        }
+
+        *num_extra_tokens_out = num_extra_tokens;
+
+        return PCS_OK;
+      }
+
+      // commit the new token
+      macro->count++;
+
+  }// end for next token loop
+
+}
+
+/*
+  Given a cpp_macro and cpp_reader reference.
+  Returns the body tokens in `macro->exp.tokens`.
+
+  The macro need not have been committed.
+
+  Perhaps should be returning the status instead of bool, as it
+  is a bit confusing to see a status enum with it being returned
+  here. The status enum's current purpose is to feed debug messages.
+
+*/
+
+static parse_clause_status
+parse_clause(
+  cpp_reader *pfile
+  ,cpp_macro *macro
+  ,bool paren_matching
+  ,unsigned int *num_extra_tokens_out
+){
+
+  #if DebugParseClause
+    fprintf(stderr, "entering parse_clause\n");
+  #endif
+
+  int saved_keep_tokens = pfile->keep_tokens;
+  int saved_in_directive = pfile->state.in_directive;
+  bool expand_tokens;
+  cpp_token *token;
+  enum cpp_ttype opening ,closing;
+  parse_clause_status status;
+
+  expand_tokens = false; // default for #define EOL terminated body
+  if (paren_matching)
+    {
+      // the next token must be the opening paren
+      token = _cpp_lex_direct(pfile);
+      if(token->type == CPP_OPEN_PAREN){
+        expand_tokens = false;
+        opening = CPP_OPEN_PAREN;
+        closing = CPP_CLOSE_PAREN;
+      }else if(token->type == CPP_OPEN_SQUARE){
+        expand_tokens = true;
+        opening = CPP_OPEN_SQUARE;
+        closing = CPP_CLOSE_SQUARE;
+      }else{
+        cpp_error_with_line(
+          pfile
+          ,CPP_DL_ERROR
+          ,token->src_loc
+          ,0
+          ,"expected body delimiter '(', but found: %s"
+          ,cpp_token_as_text(token)
+        );
+        return PCS_ERR_EXPECTED_OPEN_DELIM;
+      }
+
+      // allow a multiple line body
+      pfile->keep_tokens = 1;
+      pfile->state.in_directive = 0;
+    }
+
+  if(expand_tokens){
+    status = parse_clause_body_expand(
+        pfile
+        ,macro
+        ,paren_matching
+        ,opening
+        ,closing
+      );
+  }else{
+    status = parse_clause_body_literal(
+        pfile
+        ,macro
+        ,paren_matching
+        ,opening
+        ,closing
+        ,num_extra_tokens_out
+      );
+  }
+
+  #if DebugParseClause
+    fprintf(stderr, "parse_clause returning: ");
+    print_parse_clause_status(status);  
+  #endif
+                          
+  if(paren_matching){
+    pfile->keep_tokens = saved_keep_tokens;
+    pfile->state.in_directive = saved_in_directive;
+  }
+
+  return status;
+}
+
+/*
+  Check if a collected macro body reduces to a single identifier token.
+
+  Preconditions:
+    - macro is non-null
+    - macro->exp.tokens has been populated (e.g., via parse_clause)
+    - macro->count is valid
+
+  Returns:
+    - cpp_hashnode* if valid (i.e., single CPP_NAME token)
+    - NULL if invalid, and emits error message
+
+
+Note in do_define in directives.cc there is some logic related to callbacks and warning if trying to redefine a built-in macro. That should be integrated here.
+
+
+*/
+static cpp_hashnode *
+name_clause_is_name(cpp_reader *pfile, const cpp_macro *macro)
+{
+  if (!macro || macro->count != 1)
+    {
+      cpp_error(pfile, CPP_DL_ERROR,
+                "expected exactly one token in assign name expression, got %u",
+                macro ? macro->count : 0);
+      return NULL;
+    }
+
+  const cpp_token *tok = &macro->exp.tokens[0];
+
+  if (tok->type != CPP_NAME)
+    {
+      cpp_error(pfile, CPP_DL_ERROR,
+                "expected identifier in assign name expression, got: %s",
+                cpp_token_as_text(tok));
+      return NULL;
+    }
+
+  return tok->val.node.node;
+}
+
+
+
+
+/*--------------------------------------------------------------------------------
+ `#assign` directive
+
+    called from directives.cc::do_assign()
+
+*/
+
+bool _cpp_create_assign(cpp_reader *pfile){
+
+
+  /* Parse name clause into a temporary macro. 
+
+     This macro will not be committed, so it will be overwritten on the next _cpp_new_macro call.
+  */
+    cpp_macro *name_macro = _cpp_new_macro(
+      pfile
+      ,cmk_macro
+      ,_cpp_reserve_room( pfile, 0, sizeof(cpp_macro) ) 
+    );
+    name_macro->variadic = false;
+    name_macro->paramc = 0;
+    name_macro->parm.params = NULL;
+    name_macro->fun_like = false;
+
+    unsigned int num_extra_tokens = 0;
+
+    // This routine requires a macro argument, hence the creation of a temporary macro.
+    parse_clause(
+      pfile 
+      ,name_macro 
+      ,true // use paren matching
+      ,&num_extra_tokens 
+    );
+    #if DebugAssign
+      fprintf(stderr,"name_macro->count: %d\n" ,name_macro->count);
+      fprintf(stderr,"assign directive name tokens:\n");
+      print_token_list(name_macro->exp.tokens ,name_macro->count);
+    #endif
+
+  /* The name clause must be either a literally valid name, or it must expand into
+     a valid name, depending if the programmer used () or [].
+     If valid, keep the name node.
+  */
+    cpp_hashnode *name_node = name_clause_is_name(pfile ,name_macro);
+    if(name_node){
+      #if DebugAssign
+        fprintf(
+          stderr
+          ,"assign macro name: '%.*s'\n"
+          ,(int) NODE_LEN(name_node) 
+          ,NODE_NAME(name_node)
+        );
+      #endif
+    }else{
+      #if DebugAssign
+        fprintf(stderr, "node is not a name\n");
+      #endif
+      return false;
+    }
+
+  /* Unpaint name_node
+
+     There are three scenarios where name_node will already exist in the symbol table
+     before the name clause of `#assign` is evaluated:
+
+       1. A macro definition already exists for name_node, and the name clause
+          is not expanded (i.e., it was delineated with '()').
+
+       2. A macro definition exists, and the name clause *is* expanded (i.e., it
+          was delineated with '[]'), but name_node was painted and thus skipped
+          during expansion.
+
+       3. A macro definition exists and was not painted initially, but the name
+          clause expands recursively to itself (e.g., `list -> list`), resulting
+          in name_node being painted *during* the name clause evaluation.
+
+     After the name clause is parsed, the body clause might be expanded. If so,
+     name_node must not be painted β€” this ensures that it will expand at least once. This enables patterns like:
+
+         #assign ()(list)(list second)
+
+     ...to work even if 'list' was painted prior to entering #assign.
+
+     If the macro recurs during evaluation of the body clause, it will be automatically painted by the expansion engine, as usual. 
+
+     Note also: upon exit from this routine, the newly created macro will *not* be painted. Its disabled flag will remain clear. 
+
+    Consequently, for a recursive macro, assign can be called repeatedly to get 'one more level' of evaluation upon each call.
+  */
+    if (cpp_macro_p(name_node)) {
+      name_node->flags &= ~NODE_DISABLED;
+    }
+
+  /* create a new macro and put the #assign body clause in it
+  */
+    cpp_macro *body_macro = _cpp_new_macro(
+      pfile
+      ,cmk_macro
+      ,_cpp_reserve_room( pfile, 0, sizeof(cpp_macro) ) 
+    );
+    body_macro->variadic = false;
+    body_macro->paramc = 0;
+    body_macro->parm.params = NULL;
+    body_macro->fun_like = false;
+
+    parse_clause(
+      pfile 
+      ,body_macro 
+      ,true // parenthesis delineated
+      ,&num_extra_tokens 
+    );
+    #if DebugAssign
+      fprintf(stderr,"assign directive body tokens:\n");
+      print_token_list(body_macro->exp.tokens ,body_macro->count);
+    #endif
+
+
+    cpp_macro *assign_macro = (cpp_macro *)_cpp_commit_buff(
+      pfile
+      ,sizeof(cpp_macro) - sizeof(cpp_token) + sizeof(cpp_token) * body_macro->count
+    );
+    assign_macro->count = body_macro->count;
+    memcpy(
+      assign_macro->exp.tokens
+      ,body_macro->exp.tokens
+      ,sizeof(cpp_token) * body_macro->count
+    );
+    body_macro->variadic = false;
+    body_macro->paramc = 0;
+    body_macro->parm.params = NULL;
+    body_macro->fun_like = false;
+
+  /* Install the assign macro under name_node.
+
+     If name_node previously had a macro definition, discard it.
+     Then install the new macro, and clear any disabled flag.
+
+     This ensures the assigned macro can be expanded immediately,
+     even if it appeared in its own body clause and was painted.
+  */
+    name_node->flags &= ~NODE_USED;
+
+    if (cpp_macro_p(name_node)) {
+      // There is no mechanism in libcpp to free the memory taken by a committed macro, but wec an cast it adrift.
+      name_node->value.macro = NULL;
+    }
+    name_node->type = NT_USER_MACRO;
+    name_node->value.macro = assign_macro;
+    name_node->flags &= ~NODE_DISABLED;
+
+
+  /* all done
+  */
+  #if DebugAssign
+    fprintf(
+      stderr
+      ,"macro '%.*s' assigned successfully.\n\n"
+      ,(int) NODE_LEN(name_node)
+      ,NODE_NAME(name_node)
+    );
+  #endif
+
+  return true;
+
+}
+
+
+/*--------------------------------------------------------------------------------
+  Given a pfile, returns a macro definition.
+
+  #macro name (parameter [,parameter] ...) (body_expr)
+  #macro name () (body_expr)
+
+  Upon entry, the name was already been parsed in directives.cc::do_macro, so the next token will be the opening paren of the parameter list.
+
+  Thi code is similar to `_cpp_create_definition` though uses paren blancing around the body, instead of requiring the macro body be on a single line.
+
+  The cpp_macro struct is defined in cpplib.h:  `struct GTY(()) cpp_macro {` it has a flexible array field in a union as a last member: cpp_token tokens[1];
+
+  This code was derived from create_iso_definition(). The break out portions shared
+  with create_macro_definition code should be shared with the main code, so that there
+  is only one place for edits.
+
+*/
+static enum parse_clause_status
+parse_paren_clause(
+  cpp_reader *pfile,
+  cpp_macro *macro,
+  unsigned int *num_extra_tokens_out
+){
+  cpp_token *token = _cpp_lex_direct(pfile);
+  if (token->type != CPP_OPEN_PAREN) {
+    cpp_error_with_line(
+      pfile,
+      CPP_DL_ERROR,
+      token->src_loc,
+      0,
+      "expected '(' to open macro body, but found: %s",
+      cpp_token_as_text(token)
+    );
+    return PCS_ERR_EXPECTED_OPEN_DELIM;
+  }
+
+  // allow a multiple line body
+  int saved_keep_tokens    = pfile->keep_tokens;
+  int saved_in_directive   = pfile->state.in_directive;
+
+  // turn on multi-line parsing
+  pfile->keep_tokens       = 1;
+  pfile->state.in_directive = 0;
+
+  parse_clause_status status = parse_clause_body_literal(
+    pfile,
+    macro,
+    true, // paren_matching
+    CPP_OPEN_PAREN,
+    CPP_CLOSE_PAREN,
+    num_extra_tokens_out
+  );
+
+  pfile->keep_tokens      = saved_keep_tokens;
+  pfile->state.in_directive = saved_in_directive;
+
+  return status;
+}
+
+static cpp_macro *create_rt_macro (cpp_reader *pfile){
+
+  #if DebugRTMacro
+    fprintf(stderr,"entering create_rt_macro\n");
+  #endif
+
+  unsigned int num_extra_tokens = 0;
+  unsigned paramc = 0;
+  cpp_hashnode **params = NULL;
+  bool varadic = false;
+  bool ok = false;
+  cpp_macro *macro = NULL;
+  parse_clause_status status;
+
+  /*
+    After these six lines of code, the next token, hopefully being '(', will be in the variable 'token'.
+
+    _cpp_lex_direct() is going to clobber  pfile->cur_token with the token pointer, so
+    it is saved then restored.
+  */
+    cpp_token first;
+    cpp_token *saved_cur_token = pfile->cur_token;
+    pfile->cur_token = &first;
+    cpp_token *token = _cpp_lex_direct (pfile);
+    pfile->cur_token = saved_cur_token;
+
+  /* parse parameter list
+
+      after parse_parms runs, the next token returned by pfile will be subsequent to the parameter list, e.g.:
+         7 |   #macro Q(f ,...) printf(f ,__VA_ARGS__)
+           |                    ^~~~~~
+  */
+    if(token->type != CPP_OPEN_PAREN){
+      cpp_error_with_line(
+        pfile
+        ,CPP_DL_ERROR
+        ,token->src_loc
+        ,0
+        ,"expected '(' to open arguments list, but found: %s"
+        ,cpp_token_as_text(token)
+      );
+      goto out;
+    }
+
+    if( !parse_params(pfile, &paramc, &varadic) ) goto out;
+
+    // finalizes the reserved room, otherwise it will be reused on the next reserve room call.
+    params = (cpp_hashnode **)_cpp_commit_buff( pfile, sizeof (cpp_hashnode *) * paramc );
+    token = NULL;
+
+  /* parse body macro
+
+     A macro struct instance is variable size, due to tokens added to the macro.exp.tokens
+     during parse, and possible reallocations.
+
+     Function like macros will later need space to hold parameter values.
+  */
+    macro = _cpp_new_macro(
+      pfile
+      ,cmk_macro
+      ,_cpp_reserve_room( pfile, 0, sizeof(cpp_macro) ) 
+    );
+    // used by parse_clause_literal
+    macro->variadic = varadic;
+    macro->paramc = paramc;
+    macro->parm.params = params;
+    macro->fun_like = true;
+
+
+    status = parse_paren_clause(pfile ,macro ,&num_extra_tokens);
+    if( status != PCS_OK ){
+      fprintf(stderr, "parse_paren_clause returned: ");
+      print_parse_clause_status(status);  
+      goto out;
+    }
+
+    #if DebugRTMacro
+      fprintf(stderr,"rt_macro directive body tokens:\n");
+      print_token_list(macro->exp.tokens ,macro->count);
+    #endif
+
+    // commit the macro, attach the parameter list
+    ok = true;
+    macro = (cpp_macro *)_cpp_commit_buff(
+      pfile
+      ,
+        sizeof (cpp_macro) 
+        - sizeof (cpp_token) 
+        + sizeof (cpp_token) * macro->count 
+        + sizeof(cpp_hashnode *) * paramc
+    );
+    macro->variadic = varadic;
+    macro->paramc = paramc;
+    macro->parm.params = params;
+    macro->fun_like = true;
+
+  /* some end cases we must clean up
+  */
+    /*
+      It might be that the first token of the macro body was preceded by white space, so
+      the white space flag is set. However, upon expansion, there might not be a white
+      space before said token, so the following code clears the flag.
+    */
+    if (macro->count)
+      macro->exp.tokens[0].flags &= ~PREV_WHITE;
+
+    /*
+      Identifies consecutive ## tokens (a.k.a. CPP_PASTE) that were invalid or ambiguous,
+
+      Removes them from the main macro body,
+
+      Stashes them at the end of the tokens[] array in the same memory,
+
+      Sets macro->extra_tokens = 1 to signal their presence.
+    */
+    if (num_extra_tokens)
+      {
+        /* Place second and subsequent ## or %:%: tokens in sequences of
+           consecutive such tokens at the end of the list to preserve
+           information about where they appear, how they are spelt and
+           whether they are preceded by whitespace without otherwise
+           interfering with macro expansion.   Remember, this is
+           extremely rare, so efficiency is not a priority.  */
+        cpp_token *temp = (cpp_token *)_cpp_reserve_room
+          (pfile, 0, num_extra_tokens * sizeof (cpp_token));
+        unsigned extra_ix = 0, norm_ix = 0;
+        cpp_token *exp = macro->exp.tokens;
+        for (unsigned ix = 0; ix != macro->count; ix++)
+          if (exp[ix].type == CPP_PASTE)
+            temp[extra_ix++] = exp[ix];
+          else
+            exp[norm_ix++] = exp[ix];
+        memcpy (&exp[norm_ix], temp, num_extra_tokens * sizeof (cpp_token));
+
+        /* Record there are extra tokens.  */
+        macro->extra_tokens = 1;
+      }
+
+ out:
+
+  /*
+    - This resets a flag in the parser’s state machine, pfile.
+    - The field `va_args_ok` tracks whether the current macro body is allowed to reference `__VA_ARGS__` (or more precisely, `__VA_OPT__`).
+    - It's set **while parsing a macro body** that might use variadic logic β€” particularly in `vaopt_state` tracking.
+
+    Resetting it here ensures that future macros aren't accidentally parsed under the assumption that variadic substitution is valid.
+  */
+  pfile->state.va_args_ok = 0;
+
+  /*
+    Earlier we did:
+      if (!parse_params(pfile, &paramc, &variadic)) goto out;
+    This cleans up temporary memory used by parse_params.
+  */
+  _cpp_unsave_parameters (pfile, paramc);
+
+  return ok ? macro : NULL;
+}
+
+/*
+  called from directives.cc:: do_macro
+*/
+bool
+_cpp_create_rt_macro(cpp_reader *pfile, cpp_hashnode *node){
+
+  #if DebugRTMacro
+    fprintf(stderr,"entering _cpp_create_macro\n");
+  #endif
+
+  cpp_macro *macro;
+  macro = create_rt_macro (pfile);
+
+  if (!macro)
+    return false;
+
+  if (cpp_macro_p (node))
+    {
+      if (CPP_OPTION (pfile, warn_unused_macros))
+       _cpp_warn_if_unused_macro (pfile, node, NULL);
+
+      if (warn_of_redefinition (pfile, node, macro))
+       {
+          const enum cpp_warning_reason reason
+           = (cpp_builtin_macro_p (node) && !(node->flags & NODE_WARN))
+           ? CPP_W_BUILTIN_MACRO_REDEFINED : CPP_W_NONE;
+
+         bool warned = 
+           cpp_pedwarning_with_line (pfile, reason,
+                                     pfile->directive_line, 0,
+                                     "\"%s\" redefined", NODE_NAME (node));
+
+         if (warned && cpp_user_macro_p (node))
+           cpp_error_with_line (pfile, CPP_DL_NOTE,
+                                node->value.macro->line, 0,
+                        "this is the location of the previous definition");
+       }
+      _cpp_free_definition (node);
+    }
+
+  /* Enter definition in hash table.  */
+  node->type = NT_USER_MACRO;
+  node->value.macro = macro;
+  if (! ustrncmp (NODE_NAME (node), DSC ("__STDC_"))
+      && ustrcmp (NODE_NAME (node), (const uchar *) "__STDC_FORMAT_MACROS")
+      /* __STDC_LIMIT_MACROS and __STDC_CONSTANT_MACROS are mentioned
+        in the C standard, as something that one must use in C++.
+        However DR#593 and C++11 indicate that they play no role in C++.
+        We special-case them anyway.  */
+      && ustrcmp (NODE_NAME (node), (const uchar *) "__STDC_LIMIT_MACROS")
+      && ustrcmp (NODE_NAME (node), (const uchar *) "__STDC_CONSTANT_MACROS"))
+    node->flags |= NODE_WARN;
+
+  /* If user defines one of the conditional macros, remove the
+     conditional flag */
+  node->flags &= ~NODE_CONDITIONAL;
+
+  return true;
+}
+
+
+
+
+
+
+
+
+
+
+
+