diff directive.c @ 49:8a204d153398

Intercept multiline comments earlier. Leave same-line comments alone. Fixes all of the pathological comment behavior we've noticed so far.
author David A. Holland
date Sun, 31 Mar 2013 01:04:43 -0400
parents 337110e7240a
children 90c6052410ce
line wrap: on
line diff
--- a/directive.c	Sat Mar 30 23:29:08 2013 -0400
+++ b/directive.c	Sun Mar 31 01:04:43 2013 -0400
@@ -39,6 +39,7 @@
 #include "directive.h"
 #include "macro.h"
 #include "eval.h"
+#include "output.h"
 
 struct ifstate {
 	struct ifstate *prev;
@@ -49,6 +50,7 @@
 };
 
 static struct ifstate *ifstate;
+static bool in_multiline_comment;
 
 ////////////////////////////////////////////////////////////
 // common parsing bits
@@ -408,23 +410,138 @@
 	complain_fail();
 }
 
+/*
+ * If desired, warn about a nested comment. The comment begins at
+ * offset POS from the place P.
+ */
+static
+void
+warn_nestcomment(const struct place *p, size_t pos)
+{
+	struct place p2;
+
+	if (warns.nestcomment) {
+		p2 = *p;
+		p2.column += pos;
+		complain(p, "Warning: %c%c within comment",
+			 '/', '*');
+		if (mode.werror) {
+			complain_failed();
+		}
+	}
+}
+
+/*
+ * Check for comment delimiters in LINE. If a multi-line comment is
+ * continuing or ending, set ACOMM to its length. If a multi-line
+ * comment is starting, set BCOMM to its length. Set TEXT to the
+ * length of text that is not commented out, or that contains comments
+ * that both begin and end on this line. ACOMM + TEXT + BCOMM == LEN.
+ *
+ * Updates in_multiline_comment to the appropriate state for after
+ * this line is handled.
+ */
+static
+size_t
+directive_scancomments(const struct place *p, char *line, size_t len,
+		       size_t *acomm, size_t *text, size_t *bcomm)
+{
+	size_t pos;
+	size_t first_commentend;
+	size_t last_commentstart;
+	bool incomment;
+
+	first_commentend = len;
+	last_commentstart = len;
+	incomment = in_multiline_comment;
+	for (pos = 0; pos+1 < len; pos++) {
+		if (line[pos] == '/' && line[pos+1] == '*') {
+			if (incomment) {
+				warn_nestcomment(p, pos);
+			} else {
+				incomment = true;
+				last_commentstart = pos;
+			}
+		} else if (line[pos] == '*' && line[pos+1] == '/') {
+			if (incomment) {
+				incomment = false;
+				if (first_commentend == len) {
+					first_commentend = pos;
+				}
+				last_commentstart = len;
+			} else {
+				/* stray end-comment; should we care? */
+			}
+		}
+	}
+
+	if (in_multiline_comment && first_commentend < last_commentstart) {
+		/* multiline comment ends */
+		/* first_commentend points to the star, adjust */
+		*acomm = first_commentend + 2;
+		*text = len - *acomm;
+	} else if (in_multiline_comment) {
+		/* comment did not end, so another one cannot have started */
+		assert(last_commentstart == len);
+		*acomm = len;
+		*text = 0;
+	} else {
+		*acomm = 0;
+		*text = len;
+	}
+
+	*bcomm = len - last_commentstart;
+	*text -= *bcomm;
+
+	in_multiline_comment = incomment;
+	return len;
+}
+
 void
 directive_gotline(struct place *p, char *line, size_t len)
 {
+	size_t acomm;	/* length of comment ending on this line */
+	size_t text;	/* length of non-multi-line-comment text */
+	size_t bcomm;	/* length of comment beginning on this line */
 	size_t skip;
 
+	directive_scancomments(p, line, len, &acomm, &text, &bcomm);
+
+	if (acomm > 0) {
+		if (mode.output_retain_comments && ifstate->curtrue) {
+			/*
+			 * Do not expand the comment; send it straight
+			 * to the output. This will cause it to appear
+			 * first if we're partway through collecting a
+			 * macro argument. Too bad. This isn't a
+			 * standard mode anyway.
+			 */
+			output(p, line, acomm);
+		}
+		p->column += acomm;
+	}
+
 	/* check if we have a directive line */
-	skip = strspn(line, ws);
-	if (line[skip] == '#') {
+	skip = strspn(line + acomm, ws);
+	if (acomm == 0 && line[skip] == '#') {
 		skip = skip + 1 + strspn(line + skip + 1, ws);
+		assert(skip <= text);
 		p->column += skip;
-		directive_gotdirective(p, line+skip, len-skip);
+		directive_gotdirective(p, line+skip, text-skip);
+		p->column += text-skip;
 	} else if (ifstate->curtrue) {
-		macro_sendline(p, line, len);
+		macro_sendline(p, line + acomm, text);
+		p->column += text;
+	}
+
+	if (bcomm > 0) {
+		if (mode.output_retain_comments && ifstate->curtrue) {
+			output(p, line + acomm + text, bcomm);
+		}
+		p->column += bcomm;
 	}
 }
 
-
 void
 directive_goteof(struct place *p)
 {