--- old/usr/src/cmd/mandoc/mdoc_argv.c	Wed Jul 30 20:55:09 2014
+++ new/usr/src/cmd/mandoc/mdoc_argv.c	Wed Jul 30 20:55:09 2014
@@ -1,6 +1,7 @@
-/*	$Id: mdoc_argv.c,v 1.82 2012/03/23 05:50:24 kristaps Exp $ */
+/*	$Id: mdoc_argv.c,v 1.89 2013/12/25 00:50:05 schwarze Exp $ */
 /*
  * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
+ * Copyright (c) 2012 Ingo Schwarze <schwarze@openbsd.org>
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -42,8 +43,7 @@
 enum	argvflag {
 	ARGV_NONE, /* no args to flag (e.g., -split) */
 	ARGV_SINGLE, /* one arg to flag (e.g., -file xxx)  */
-	ARGV_MULTI, /* multiple args (e.g., -column xxx yyy) */
-	ARGV_OPT_SINGLE /* optional arg (e.g., -offset [xxx]) */
+	ARGV_MULTI /* multiple args (e.g., -column xxx yyy) */
 };
 
 struct	mdocarg {
@@ -57,8 +57,6 @@
 static	int		 args_checkpunct(const char *, int);
 static	int		 argv_multi(struct mdoc *, int, 
 				struct mdoc_argv *, int *, char *);
-static	int		 argv_opt_single(struct mdoc *, int, 
-				struct mdoc_argv *, int *, char *);
 static	int		 argv_single(struct mdoc *, int, 
 				struct mdoc_argv *, int *, char *);
 
@@ -69,7 +67,7 @@
 	ARGV_NONE,	/* MDOC_Unfilled */
 	ARGV_NONE,	/* MDOC_Literal */
 	ARGV_SINGLE,	/* MDOC_File */
-	ARGV_OPT_SINGLE, /* MDOC_Offset */
+	ARGV_SINGLE,	/* MDOC_Offset */
 	ARGV_NONE,	/* MDOC_Bullet */
 	ARGV_NONE,	/* MDOC_Dash */
 	ARGV_NONE,	/* MDOC_Hyphen */
@@ -81,7 +79,7 @@
 	ARGV_NONE,	/* MDOC_Ohang */
 	ARGV_NONE,	/* MDOC_Inset */
 	ARGV_MULTI,	/* MDOC_Column */
-	ARGV_OPT_SINGLE, /* MDOC_Width */
+	ARGV_SINGLE,	/* MDOC_Width */
 	ARGV_NONE,	/* MDOC_Compact */
 	ARGV_NONE,	/* MDOC_Std */
 	ARGV_NONE,	/* MDOC_Filled */
@@ -146,7 +144,7 @@
 };
 
 static	const struct mdocarg mdocargs[MDOC_MAX] = {
-	{ ARGSFL_NONE, NULL }, /* Ap */
+	{ ARGSFL_DELIM, NULL }, /* Ap */
 	{ ARGSFL_NONE, NULL }, /* Dd */
 	{ ARGSFL_NONE, NULL }, /* Dt */
 	{ ARGSFL_NONE, NULL }, /* Os */
@@ -163,7 +161,7 @@
 	{ ARGSFL_DELIM, NULL }, /* Ad */ 
 	{ ARGSFL_DELIM, args_An }, /* An */
 	{ ARGSFL_DELIM, NULL }, /* Ar */
-	{ ARGSFL_NONE, NULL }, /* Cd */
+	{ ARGSFL_DELIM, NULL }, /* Cd */
 	{ ARGSFL_DELIM, NULL }, /* Cm */
 	{ ARGSFL_DELIM, NULL }, /* Dv */ 
 	{ ARGSFL_DELIM, NULL }, /* Er */ 
@@ -175,7 +173,7 @@
 	{ ARGSFL_DELIM, NULL }, /* Fn */ 
 	{ ARGSFL_DELIM, NULL }, /* Ft */ 
 	{ ARGSFL_DELIM, NULL }, /* Ic */ 
-	{ ARGSFL_NONE, NULL }, /* In */ 
+	{ ARGSFL_DELIM, NULL }, /* In */ 
 	{ ARGSFL_DELIM, NULL }, /* Li */
 	{ ARGSFL_NONE, NULL }, /* Nd */ 
 	{ ARGSFL_DELIM, NULL }, /* Nm */ 
@@ -243,7 +241,7 @@
 	{ ARGSFL_DELIM, NULL }, /* Xc */
 	{ ARGSFL_NONE, NULL }, /* Xo */
 	{ ARGSFL_NONE, NULL }, /* Fo */ 
-	{ ARGSFL_NONE, NULL }, /* Fc */ 
+	{ ARGSFL_DELIM, NULL }, /* Fc */ 
 	{ ARGSFL_NONE, NULL }, /* Oo */
 	{ ARGSFL_DELIM, NULL }, /* Oc */
 	{ ARGSFL_NONE, args_Bk }, /* Bk */
@@ -252,7 +250,7 @@
 	{ ARGSFL_NONE, NULL }, /* Hf */
 	{ ARGSFL_NONE, NULL }, /* Fr */
 	{ ARGSFL_NONE, NULL }, /* Ud */
-	{ ARGSFL_NONE, NULL }, /* Lb */
+	{ ARGSFL_DELIM, NULL }, /* Lb */
 	{ ARGSFL_NONE, NULL }, /* Lp */
 	{ ARGSFL_DELIM, NULL }, /* Lk */
 	{ ARGSFL_DELIM, NULL }, /* Mt */
@@ -262,7 +260,7 @@
 	{ ARGSFL_NONE, NULL }, /* %C */
 	{ ARGSFL_NONE, NULL }, /* Es */
 	{ ARGSFL_NONE, NULL }, /* En */
-	{ ARGSFL_NONE, NULL }, /* Dx */
+	{ ARGSFL_DELIM, NULL }, /* Dx */
 	{ ARGSFL_NONE, NULL }, /* %Q */
 	{ ARGSFL_NONE, NULL }, /* br */
 	{ ARGSFL_NONE, NULL }, /* sp */
@@ -277,7 +275,7 @@
  * one mandatory value, an optional single value, or no value.
  */
 enum margverr
-mdoc_argv(struct mdoc *m, int line, enum mdoct tok,
+mdoc_argv(struct mdoc *mdoc, int line, enum mdoct tok,
 		struct mdoc_arg **v, int *pos, char *buf)
 {
 	char		 *p, sv;
@@ -344,17 +342,13 @@
 
 	switch (argvflags[tmp.arg]) {
 	case (ARGV_SINGLE):
-		if ( ! argv_single(m, line, &tmp, pos, buf))
+		if ( ! argv_single(mdoc, line, &tmp, pos, buf))
 			return(ARGV_ERROR);
 		break;
 	case (ARGV_MULTI):
-		if ( ! argv_multi(m, line, &tmp, pos, buf))
+		if ( ! argv_multi(mdoc, line, &tmp, pos, buf))
 			return(ARGV_ERROR);
 		break;
-	case (ARGV_OPT_SINGLE):
-		if ( ! argv_opt_single(m, line, &tmp, pos, buf))
-			return(ARGV_ERROR);
-		break;
 	case (ARGV_NONE):
 		break;
 	}
@@ -413,14 +407,14 @@
 }
 
 enum margserr
-mdoc_zargs(struct mdoc *m, int line, int *pos, char *buf, char **v)
+mdoc_zargs(struct mdoc *mdoc, int line, int *pos, char *buf, char **v)
 {
 
-	return(args(m, line, pos, buf, ARGSFL_NONE, v));
+	return(args(mdoc, line, pos, buf, ARGSFL_NONE, v));
 }
 
 enum margserr
-mdoc_args(struct mdoc *m, int line, int *pos, 
+mdoc_args(struct mdoc *mdoc, int line, int *pos, 
 		char *buf, enum mdoct tok, char **v)
 {
 	enum argsflag	  fl;
@@ -429,7 +423,7 @@
 	fl = mdocargs[tok].flags;
 
 	if (MDOC_It != tok)
-		return(args(m, line, pos, buf, fl, v));
+		return(args(mdoc, line, pos, buf, fl, v));
 
 	/*
 	 * We know that we're in an `It', so it's reasonable to expect
@@ -438,7 +432,7 @@
 	 * safe fall-back into the default behaviour.
 	 */
 
-	for (n = m->last; n; n = n->parent)
+	for (n = mdoc->last; n; n = n->parent)
 		if (MDOC_Bl == n->tok)
 			if (LIST_column == n->norm->Bl.type) {
 				fl = ARGSFL_TABSEP;
@@ -445,18 +439,19 @@
 				break;
 			}
 
-	return(args(m, line, pos, buf, fl, v));
+	return(args(mdoc, line, pos, buf, fl, v));
 }
 
 static enum margserr
-args(struct mdoc *m, int line, int *pos, 
+args(struct mdoc *mdoc, int line, int *pos, 
 		char *buf, enum argsflag fl, char **v)
 {
 	char		*p, *pp;
+	int		 pairs;
 	enum margserr	 rc;
 
 	if ('\0' == buf[*pos]) {
-		if (MDOC_PPHRASE & m->flags)
+		if (MDOC_PPHRASE & mdoc->flags)
 			return(ARGS_EOLN);
 		/*
 		 * If we're not in a partial phrase and the flag for
@@ -463,10 +458,10 @@
 		 * being a phrase literal is still set, the punctuation
 		 * is unterminated.
 		 */
-		if (MDOC_PHRASELIT & m->flags)
-			mdoc_pmsg(m, line, *pos, MANDOCERR_BADQUOTE);
+		if (MDOC_PHRASELIT & mdoc->flags)
+			mdoc_pmsg(mdoc, line, *pos, MANDOCERR_BADQUOTE);
 
-		m->flags &= ~MDOC_PHRASELIT;
+		mdoc->flags &= ~MDOC_PHRASELIT;
 		return(ARGS_EOLN);
 	}
 
@@ -489,7 +484,7 @@
 		pp = NULL;
 
 		/* Scan ahead to unescaped `Ta'. */
-		if ( ! (MDOC_PHRASELIT & m->flags)) 
+		if ( ! (MDOC_PHRASELIT & mdoc->flags)) 
 			for (pp = *v; ; pp++) {
 				if (NULL == (pp = strstr(pp, "Ta")))
 					break;
@@ -523,7 +518,7 @@
 
 		/* Whitespace check for eoln case... */
 		if ('\0' == *p && ' ' == *(p - 1))
-			mdoc_pmsg(m, line, *pos, MANDOCERR_EOLNSPACE);
+			mdoc_pmsg(mdoc, line, *pos, MANDOCERR_EOLNSPACE);
 
 		*pos += (int)(p - *v);
 
@@ -541,37 +536,48 @@
 			/* Skip ahead. */ ;
 
 		return(rc);
-	} 
+	}
 
-	/* 
+	/*
 	 * Process a quoted literal.  A quote begins with a double-quote
 	 * and ends with a double-quote NOT preceded by a double-quote.
+	 * NUL-terminate the literal in place.
+	 * Collapse pairs of quotes inside quoted literals.
 	 * Whitespace is NOT involved in literal termination.
 	 */
 
-	if (MDOC_PHRASELIT & m->flags || '\"' == buf[*pos]) {
-		if ( ! (MDOC_PHRASELIT & m->flags))
+	if (MDOC_PHRASELIT & mdoc->flags || '\"' == buf[*pos]) {
+		if ( ! (MDOC_PHRASELIT & mdoc->flags))
 			*v = &buf[++(*pos)];
 
-		if (MDOC_PPHRASE & m->flags)
-			m->flags |= MDOC_PHRASELIT;
+		if (MDOC_PPHRASE & mdoc->flags)
+			mdoc->flags |= MDOC_PHRASELIT;
 
+		pairs = 0;
 		for ( ; buf[*pos]; (*pos)++) {
+			/* Move following text left after quoted quotes. */
+			if (pairs)
+				buf[*pos - pairs] = buf[*pos];
 			if ('\"' != buf[*pos])
 				continue;
+			/* Unquoted quotes end quoted args. */
 			if ('\"' != buf[*pos + 1])
 				break;
+			/* Quoted quotes collapse. */
+			pairs++;
 			(*pos)++;
 		}
+		if (pairs)
+			buf[*pos - pairs] = '\0';
 
 		if ('\0' == buf[*pos]) {
-			if (MDOC_PPHRASE & m->flags)
+			if (MDOC_PPHRASE & mdoc->flags)
 				return(ARGS_QWORD);
-			mdoc_pmsg(m, line, *pos, MANDOCERR_BADQUOTE);
+			mdoc_pmsg(mdoc, line, *pos, MANDOCERR_BADQUOTE);
 			return(ARGS_QWORD);
 		}
 
-		m->flags &= ~MDOC_PHRASELIT;
+		mdoc->flags &= ~MDOC_PHRASELIT;
 		buf[(*pos)++] = '\0';
 
 		if ('\0' == buf[*pos])
@@ -581,13 +587,13 @@
 			(*pos)++;
 
 		if ('\0' == buf[*pos])
-			mdoc_pmsg(m, line, *pos, MANDOCERR_EOLNSPACE);
+			mdoc_pmsg(mdoc, line, *pos, MANDOCERR_EOLNSPACE);
 
 		return(ARGS_QWORD);
 	}
 
 	p = &buf[*pos];
-	*v = mandoc_getarg(m->parse, &p, line, pos);
+	*v = mandoc_getarg(mdoc->parse, &p, line, pos);
 
 	return(ARGS_WORD);
 }
@@ -643,7 +649,7 @@
 }
 
 static int
-argv_multi(struct mdoc *m, int line, 
+argv_multi(struct mdoc *mdoc, int line, 
 		struct mdoc_argv *v, int *pos, char *buf)
 {
 	enum margserr	 ac;
@@ -652,7 +658,7 @@
 	for (v->sz = 0; ; v->sz++) {
 		if ('-' == buf[*pos])
 			break;
-		ac = args(m, line, pos, buf, ARGSFL_NONE, &p);
+		ac = args(mdoc, line, pos, buf, ARGSFL_NONE, &p);
 		if (ARGS_ERROR == ac)
 			return(0);
 		else if (ARGS_EOLN == ac)
@@ -669,16 +675,13 @@
 }
 
 static int
-argv_opt_single(struct mdoc *m, int line, 
+argv_single(struct mdoc *mdoc, int line, 
 		struct mdoc_argv *v, int *pos, char *buf)
 {
 	enum margserr	 ac;
 	char		*p;
 
-	if ('-' == buf[*pos])
-		return(1);
-
-	ac = args(m, line, pos, buf, ARGSFL_NONE, &p);
+	ac = args(mdoc, line, pos, buf, ARGSFL_NONE, &p);
 	if (ARGS_ERROR == ac)
 		return(0);
 	if (ARGS_EOLN == ac)
@@ -685,30 +688,6 @@
 		return(1);
 
 	v->sz = 1;
-	v->value = mandoc_malloc(sizeof(char *));
-	v->value[0] = mandoc_strdup(p);
-
-	return(1);
-}
-
-static int
-argv_single(struct mdoc *m, int line, 
-		struct mdoc_argv *v, int *pos, char *buf)
-{
-	int		 ppos;
-	enum margserr	 ac;
-	char		*p;
-
-	ppos = *pos;
-
-	ac = args(m, line, pos, buf, ARGSFL_NONE, &p);
-	if (ARGS_EOLN == ac) {
-		mdoc_pmsg(m, line, ppos, MANDOCERR_SYNTARGVCOUNT);
-		return(0);
-	} else if (ARGS_ERROR == ac)
-		return(0);
-
-	v->sz = 1;
 	v->value = mandoc_malloc(sizeof(char *));
 	v->value[0] = mandoc_strdup(p);
 
