diff options
author | Ingo Schwarze <schwarze@openbsd.org> | 2018-12-18 22:00:02 +0000 |
---|---|---|
committer | Ingo Schwarze <schwarze@openbsd.org> | 2018-12-18 22:00:02 +0000 |
commit | 159280a707df1cb0a5c66c576c8a92527e2b6d03 (patch) | |
tree | 39eda1753a1e33467d67e27fbc936204dac792aa /roff.c | |
parent | 3f3ba36406a16c5a308bca995cea3bf9160e2e3e (diff) | |
download | mandoc-159280a707df1cb0a5c66c576c8a92527e2b6d03.tar.gz |
As a first step towards making roff_res() callable from mandoc_getarg(),
move the function mandoc_getarg() from mandoc.c to roff.c. It was
misplaced in mandoc.c in the first place; that file is intended for
utilities needed both by parsers and by formatters, while reading
macro arguments in copy mode is purely a task of the roff(7) parser.
Needed as a preliminary for an upcoming bugfix.
No code change.
Diffstat (limited to 'roff.c')
-rw-r--r-- | roff.c | 97 |
1 files changed, 97 insertions, 0 deletions
@@ -1547,6 +1547,103 @@ roff_res(struct roff *r, struct buf *buf, int ln, int pos) } /* + * Parse a quoted or unquoted roff-style request or macro argument. + * Return a pointer to the parsed argument, which is either the original + * pointer or advanced by one byte in case the argument is quoted. + * NUL-terminate the argument in place. + * Collapse pairs of quotes inside quoted arguments. + * Advance the argument pointer to the next argument, + * or to the NUL byte terminating the argument line. + */ +char * +mandoc_getarg(char **cpp, int ln, int *pos) +{ + char *start, *cp; + int quoted, pairs, white; + + /* Quoting can only start with a new word. */ + start = *cpp; + quoted = 0; + if ('"' == *start) { + quoted = 1; + start++; + } + + pairs = 0; + white = 0; + for (cp = start; '\0' != *cp; cp++) { + + /* + * Move the following text left + * after quoted quotes and after "\\" and "\t". + */ + if (pairs) + cp[-pairs] = cp[0]; + + if ('\\' == cp[0]) { + /* + * In copy mode, translate double to single + * backslashes and backslash-t to literal tabs. + */ + switch (cp[1]) { + case 'a': + case 't': + cp[0] = '\t'; + /* FALLTHROUGH */ + case '\\': + pairs++; + cp++; + break; + case ' ': + /* Skip escaped blanks. */ + if (0 == quoted) + cp++; + break; + default: + break; + } + } else if (0 == quoted) { + if (' ' == cp[0]) { + /* Unescaped blanks end unquoted args. */ + white = 1; + break; + } + } else if ('"' == cp[0]) { + if ('"' == cp[1]) { + /* Quoted quotes collapse. */ + pairs++; + cp++; + } else { + /* Unquoted quotes end quoted args. */ + quoted = 2; + break; + } + } + } + + /* Quoted argument without a closing quote. */ + if (1 == quoted) + mandoc_msg(MANDOCERR_ARG_QUOTE, ln, *pos, NULL); + + /* NUL-terminate this argument and move to the next one. */ + if (pairs) + cp[-pairs] = '\0'; + if ('\0' != *cp) { + *cp++ = '\0'; + while (' ' == *cp) + cp++; + } + *pos += (int)(cp - start) + (quoted ? 1 : 0); + *cpp = cp; + + if ('\0' == *cp && (white || ' ' == cp[-1])) + mandoc_msg(MANDOCERR_SPACE_EOL, ln, *pos, NULL); + + return start; +} + + +/* * Process text streams. */ static int |