+ /* If we have an identifier, check if it's a macro */
+ if (IsSym (Ident)) {
+
+ /* Check if it's a macro argument */
+ if ((ArgIdx = FindMacroArg (E->M, Ident)) >= 0) {
+
+ /* A macro argument. Get the corresponding actual argument. */
+ Arg = ME_GetActual (E, ArgIdx);
+
+ /* Copy any following whitespace */
+ HaveSpace = SkipWhitespace (0);
+
+ /* If a ## operator follows, we have to insert the actual
+ * argument as is, otherwise it must be macro replaced.
+ */
+ if (CurC == '#' && NextC == '#') {
+
+ /* ### Add placemarker if necessary */
+ SB_Append (&E->Replacement, Arg);
+
+ } else {
+
+ /* Replace the formal argument by a macro replaced copy
+ * of the actual.
+ */
+ SB_Reset (Arg);
+ MacroReplacement (Arg, &E->Replacement);
+
+ /* If we skipped whitespace before, re-add it now */
+ if (HaveSpace) {
+ SB_AppendChar (&E->Replacement, ' ');
+ }
+ }
+
+
+ } else {
+
+ /* An identifier, keep it */
+ SB_AppendStr (&E->Replacement, Ident);
+
+ }
+
+ } else if (CurC == '#' && NextC == '#') {
+
+ /* ## operator. */
+ NextChar ();
+ NextChar ();
+ SkipWhitespace (0);
+
+ /* Since we need to concatenate the token sequences, remove
+ * any whitespace that was added to target, since it must come
+ * from the input.
+ */
+ while (IsSpace (SB_LookAtLast (&E->Replacement))) {
+ SB_Drop (&E->Replacement, 1);
+ }
+
+ /* If the next token is an identifier which is a macro argument,
+ * replace it, otherwise do nothing.
+ */
+ if (IsSym (Ident)) {
+
+ /* Check if it's a macro argument */
+ if ((ArgIdx = FindMacroArg (E->M, Ident)) >= 0) {
+
+ /* Get the corresponding actual argument and add it. */
+ SB_Append (&E->Replacement, ME_GetActual (E, ArgIdx));
+
+ } else {
+
+ /* Just an ordinary identifier - add as is */
+ SB_AppendStr (&E->Replacement, Ident);
+
+ }
+ }
+
+ } else if (CurC == '#' && E->M->ArgCount >= 0) {
+
+ /* A # operator within a macro expansion of a function like
+ * macro. Read the following identifier and check if it's a
+ * macro parameter.
+ */
+ NextChar ();
+ SkipWhitespace (0);
+ if (!IsSym (Ident) || (ArgIdx = FindMacroArg (E->M, Ident)) < 0) {
+ PPError ("`#' is not followed by a macro parameter");
+ } else {
+ /* Make a valid string from Replacement */
+ Arg = ME_GetActual (E, ArgIdx);
+ SB_Reset (Arg);
+ Stringize (Arg, &E->Replacement);
+ }
+
+ } else if (IsQuote (CurC)) {
+ CopyQuotedString (&E->Replacement);