*** 1.1	1996/08/02 19:48:38
--- cccp.c	1996/08/07 18:52:04
***************
*** 3,8 ****
--- 3,11 ----
     Written by Paul Rubin, June 1986
     Adapted to ANSI C, Richard Stallman, Jan 1987
  
+    See http://www.pmg.lcs.mit.edu/~andru/mlm.html for discussion of the
+    multi-line macro extension added by Andrew Myers, August 1996.
+ 
  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 2, or (at your option) any
***************
*** 1677,1683 ****
  	break;
  
        case 'v':
! 	fprintf (stderr, "GNU CPP version %s", version_string);
  #ifdef TARGET_VERSION
  	TARGET_VERSION;
  #endif
--- 1680,1686 ----
  	break;
  
        case 'v':
! 	fprintf (stderr, "GNU CPP version %s (with MLM)", version_string);
  #ifdef TARGET_VERSION
  	TARGET_VERSION;
  #endif
***************
*** 3223,3228 ****
--- 3226,3234 ----
  	       If the arg gets stringified, newline space makes nothing.  */
  	    *obp++ = *ibp++;
  	  }
+ 	} else if (*ibp == '#') {
+ 	    /* hit an embedded line directive, emit # */
+ 	    obp[-1] = *ibp++;
  	} else abort ();	/* Newline followed by something random?  */
  	break;
        }
***************
*** 3641,3646 ****
--- 3647,3732 ----
  }
  
  /*
+  * Skip whitespace, backslash-newlines, and comments, and return the
+  * next following character, or "limit" if none.
+  */
+ static U_CHAR *find_directive_name(FILE_BUF *ip, U_CHAR *bp, U_CHAR *limit) {
+     while (bp < limit) {
+ 	if (is_hor_space[*bp]) {
+ 	if (*bp != ' ' && *bp != '\t' && pedantic)
+ 	    pedwarn ("%s in preprocessing directive", char_name[*bp]);
+ 	bp++;
+ 	} else if (*bp == '/' && (bp[1] == '*'
+ 				|| (cplusplus_comments && bp[1] == '/'))) {
+ 	ip->bufp = bp + 2;
+ 	skip_to_end_of_comment (ip, &ip->lineno, 0);
+ 	bp = ip->bufp;
+ 	} else if (*bp == '\\' && bp[1] == '\n') {
+ 	bp += 2; ip->lineno++;
+ 	} else break;
+     }
+     return bp;
+ }
+ 
+ /*
+  * Find end of directive name starting at "cp".
+  * If we encounter a backslash-newline, exchange it with any following
+  * symbol-constituents so that we end up with a contiguous name.
+  */
+ static U_CHAR *find_directive_name_end(FILE_BUF *ip, U_CHAR *cp) {
+   while (1) {
+     if (is_idchar[*cp])
+       cp++;
+     else {
+       if (*cp == '\\' && cp[1] == '\n')
+ 	name_newline_fix (cp);
+       if (is_idchar[*cp])
+ 	cp++;
+       else break;
+     }
+   }
+   return cp;
+ }
+ 
+ /* Check for the "#end" at the end of a multi-line directive. Return -1
+  * if the directive is badly formatted, 0 if the directive does not end
+  * at this point, and 1 if it does. The buffer pointer is left pointing
+  * to the newline.
+  */
+ static int newline_check(FILE_BUF *ip, U_CHAR *nl, U_CHAR *limit) {
+     U_CHAR *bp = nl + 1;
+     U_CHAR *ep;
+     if (limit - bp < 5) return -1; /* can't be an #end out there */
+     if (*bp != '#') return 0;
+     bp = find_directive_name(ip, bp + 1, limit);
+     ep = find_directive_name_end(ip, bp);
+     if (0 == bcmp(bp, "end", ep - bp)) {
+ 	ep = find_directive_name(ip, ep, limit);
+ 	ip->bufp = ep; /* leave it pointing to nl */
+ 	return 1;
+     }
+     if (0 == bcmp(bp, "begin", ep - bp)) {
+ 	warning("nested `#begin'");
+     }
+     ip->bufp = nl;
+     return 0;
+ }
+ 
+ /* A conservative estimate of the number of raw newlines (from a multi-line
+    directive) found between buf and limit */
+ static int raw_newline_count(U_CHAR *buf, U_CHAR *limit) {
+     int c = 0;
+     while (buf < limit) {
+ 	switch (*buf) {
+ 	    case '\\': buf++; break;
+ 	    case '\n': c++; break;
+ 	}
+ 	buf++;
+     }
+     return c;
+ }
+ 
+ /*
   * Process a # directive.  Expects IP->bufp to point after the '#', as in
   * `#define foo bar'.  Passes to the directive handler
   * (do_define, do_include, etc.): the addresses of the 1st and
***************
*** 3654,3659 ****
--- 3740,3746 ----
   * Otherwise, returns zero, without advancing the input pointer.
   */
  
+ 
  static int
  handle_directive (ip, op)
       FILE_BUF *ip, *op;
***************
*** 3667,3715 ****
       to get rid of comments or backslash-newlines.  */
    int copy_directive = 0;
  
    U_CHAR *ident, *after_ident;
  
    bp = ip->bufp;
  
    /* Record where the directive started.  do_xifdef needs this.  */
    directive_start = bp - 1;
  
!   /* Skip whitespace and \-newline.  */
!   while (1) {
!     if (is_hor_space[*bp]) {
!       if (*bp != ' ' && *bp != '\t' && pedantic)
! 	pedwarn ("%s in preprocessing directive", char_name[*bp]);
!       bp++;
!     } else if (*bp == '/' && (bp[1] == '*'
! 			      || (cplusplus_comments && bp[1] == '/'))) {
!       ip->bufp = bp + 2;
!       skip_to_end_of_comment (ip, &ip->lineno, 0);
!       bp = ip->bufp;
!     } else if (*bp == '\\' && bp[1] == '\n') {
!       bp += 2; ip->lineno++;
!     } else break;
!   }
! 
!   /* Now find end of directive name.
!      If we encounter a backslash-newline, exchange it with any following
!      symbol-constituents so that we end up with a contiguous name.  */
  
!   cp = bp;
!   while (1) {
!     if (is_idchar[*cp])
!       cp++;
!     else {
!       if (*cp == '\\' && cp[1] == '\n')
! 	name_newline_fix (cp);
!       if (is_idchar[*cp])
! 	cp++;
!       else break;
!     }
!   }
    ident_length = cp - bp;
    ident = bp;
    after_ident = cp;
  
    /* A line of just `#' becomes blank.  */
  
    if (ident_length == 0 && *after_ident == '\n') {
--- 3754,3792 ----
       to get rid of comments or backslash-newlines.  */
    int copy_directive = 0;
  
+   /* Nonzero means we are reading a multi-line directive */
+   int multi_line = 0;
+   int current_line;
+ 
    U_CHAR *ident, *after_ident;
  
    bp = ip->bufp;
  
+ while (1) {
    /* Record where the directive started.  do_xifdef needs this.  */
    directive_start = bp - 1;
  
!   bp = find_directive_name(ip, bp, ip->buf + ip->length);
  
!   cp = find_directive_name_end(ip, bp);
    ident_length = cp - bp;
    ident = bp;
    after_ident = cp;
  
+   if (0 == bcmp("begin", bp, ident_length)) {
+     if (multi_line) {
+ 	warning("repeated `begin'");
+ 	return 0;
+     }
+     multi_line = 1;
+     copy_directive = 1;
+     bp = cp;
+   } else {
+     break;
+   }
+ }
+   current_line = ip->lineno;
+ 
    /* A line of just `#' becomes blank.  */
  
    if (ident_length == 0 && *after_ident == '\n') {
***************
*** 3841,3847 ****
  	    bp = ip->bufp;
  	    /* No need to copy the directive because of a comment at the end;
  	       just don't include the comment in the directive.  */
! 	    if (bp == limit || *bp == '\n') {
  	      bp = obp;
  	      goto endloop1;
  	    }
--- 3918,3924 ----
  	    bp = ip->bufp;
  	    /* No need to copy the directive because of a comment at the end;
  	       just don't include the comment in the directive.  */
! 	    if (bp == limit || (*bp == '\n' && !multi_line)) {
  	      bp = obp;
  	      goto endloop1;
  	    }
***************
*** 3860,3867 ****
--- 3937,3952 ----
  
  	case '\n':
  	  --bp;		/* Point to the newline */
+ 	 if (multi_line) {
+ 	    switch(newline_check(ip, bp, limit)) {
+ 		case -1: warning("No terminating #end for #begin"); return 0;
+ 		case 0: bp++; ip->lineno++; break; /* ok */
+ 		case 1: ip->lineno++; goto endloop1;
+ 	    }
+ 	 } else {
  	  ip->bufp = bp;
  	  goto endloop1;
+ 	 }
  	}
        }
        ip->bufp = bp;
***************
*** 3905,3912 ****
  	register U_CHAR *xp = buf;
  	/* Need to copy entire directive into temp buffer before dispatching */
  
! 	cp = (U_CHAR *) alloca (bp - buf + 5); /* room for directive plus
! 						  some slop */
  	buf = cp;
  
  	/* Copy to the new buffer, deleting comments
--- 3990,4000 ----
  	register U_CHAR *xp = buf;
  	/* Need to copy entire directive into temp buffer before dispatching */
  
! 	int cpsize = bp - buf + 5; /* room for directive plus some slop */
! 	if (multi_line) cpsize += raw_newline_count(buf, bp) *
! 				    (strlen(ip->nominal_fname) * 4 + 10);
! 	    /* room for line directives */
! 	cp = (U_CHAR *) alloca (cpsize);
  	buf = cp;
  
  	/* Copy to the new buffer, deleting comments
***************
*** 3918,3925 ****
  
  	  switch (c) {
  	  case '\n':
! 	    abort ();  /* A bare newline should never part of the line.  */
! 	    break;
  
  	    /* <...> is special for #include.  */
  	  case '<':
--- 4006,4023 ----
  
  	  switch (c) {
  	  case '\n':
! 	  {
! 	    char *line_directive_buf;
! 	    *cp++ = '\n'; /* make a newline newline */
!             if (!no_line_directives) {
! 		line_directive_buf =
! 		    (char *) alloca (4 * strlen (ip->nominal_fname) + 100);
! 		sprintf(line_directive_buf, "\n# %d \"%s\"\n\n", ++current_line,
! 		    ip->nominal_fname); /* first newline escapes # */
! 		strcpy(cp, line_directive_buf);
! 		cp += strlen(line_directive_buf);
! 	    }
! 	  } break;
  
  	    /* <...> is special for #include.  */
  	  case '<':
***************
*** 5988,5993 ****
--- 6086,6095 ----
  
      if (!traditional) {
        switch (c) {
+       case '\n':
+         *exp_p++ = *p++; /* suck up next char too; newline escapes it. */
+ 	break;
+ 
        case '\'':
        case '\"':
          if (expected_delimiter != '\0') {
