version 1.203, 2014/04/05 20:34:57 |
version 1.205, 2014/04/07 21:00:08 |
Line 181 static enum rofferr roff_cond_text(ROFF_ARGS); |
|
Line 181 static enum rofferr roff_cond_text(ROFF_ARGS); |
|
static enum rofferr roff_cond_sub(ROFF_ARGS); |
static enum rofferr roff_cond_sub(ROFF_ARGS); |
static enum rofferr roff_ds(ROFF_ARGS); |
static enum rofferr roff_ds(ROFF_ARGS); |
static int roff_evalcond(const char *, int *); |
static int roff_evalcond(const char *, int *); |
|
static int roff_evalnum(const char *, int *, int *, int); |
|
static int roff_evalpar(const char *, int *, int *); |
static int roff_evalstrcond(const char *, int *); |
static int roff_evalstrcond(const char *, int *); |
static void roff_free1(struct roff *); |
static void roff_free1(struct roff *); |
static void roff_freereg(struct roffreg *); |
static void roff_freereg(struct roffreg *); |
Line 491 static enum rofferr |
|
Line 493 static enum rofferr |
|
roff_res(struct roff *r, char **bufp, size_t *szp, int ln, int pos) |
roff_res(struct roff *r, char **bufp, size_t *szp, int ln, int pos) |
{ |
{ |
char ubuf[12]; /* buffer to print the number */ |
char ubuf[12]; /* buffer to print the number */ |
|
const char *start; /* start of the string to process */ |
const char *stesc; /* start of an escape sequence ('\\') */ |
const char *stesc; /* start of an escape sequence ('\\') */ |
const char *stnam; /* start of the name, after "[(*" */ |
const char *stnam; /* start of the name, after "[(*" */ |
const char *cp; /* end of the name, e.g. before ']' */ |
const char *cp; /* end of the name, e.g. before ']' */ |
const char *res; /* the string to be substituted */ |
const char *res; /* the string to be substituted */ |
char *nbuf; /* new buffer to copy bufp to */ |
char *nbuf; /* new buffer to copy bufp to */ |
size_t nsz; /* size of the new buffer */ |
|
size_t maxl; /* expected length of the escape name */ |
size_t maxl; /* expected length of the escape name */ |
size_t naml; /* actual length of the escape name */ |
size_t naml; /* actual length of the escape name */ |
|
size_t ressz; /* size of the replacement string */ |
int expand_count; /* to avoid infinite loops */ |
int expand_count; /* to avoid infinite loops */ |
|
|
expand_count = 0; |
expand_count = 0; |
|
start = *bufp + pos; |
|
stesc = strchr(start, '\0') - 1; |
|
while (stesc-- > start) { |
|
|
again: |
/* Search backwards for the next backslash. */ |
cp = *bufp + pos; |
|
while (NULL != (cp = strchr(cp, '\\'))) { |
|
stesc = cp++; |
|
|
|
|
if ('\\' != *stesc) |
|
continue; |
|
|
|
/* If it is escaped, skip it. */ |
|
|
|
for (cp = stesc - 1; cp >= start; cp--) |
|
if ('\\' != *cp) |
|
break; |
|
|
|
if (0 == (stesc - cp) % 2) { |
|
stesc = cp; |
|
continue; |
|
} |
|
|
/* |
/* |
* The second character must be an asterisk or an n. |
* Everything except user-defined strings and number |
* If it isn't, skip it anyway: It is escaped, |
* registers is only checked, not expanded. |
* so it can't start another escape sequence. |
|
*/ |
*/ |
|
|
if ('\0' == *cp) |
cp = stesc + 1; |
return(ROFF_CONT); |
|
|
|
switch (*cp) { |
switch (*cp) { |
case ('*'): |
case ('*'): |
res = NULL; |
res = NULL; |
|
|
res = ubuf; |
res = ubuf; |
break; |
break; |
default: |
default: |
if (ESCAPE_ERROR != mandoc_escape(&cp, NULL, NULL)) |
if (ESCAPE_ERROR == mandoc_escape(&cp, NULL, NULL)) |
continue; |
mandoc_msg(MANDOCERR_BADESCAPE, r->parse, |
mandoc_msg |
ln, (int)(stesc - *bufp), NULL); |
(MANDOCERR_BADESCAPE, r->parse, |
continue; |
ln, (int)(stesc - *bufp), NULL); |
|
return(ROFF_CONT); |
|
} |
} |
|
|
cp++; |
if (EXPAND_LIMIT < ++expand_count) { |
|
mandoc_msg(MANDOCERR_ROFFLOOP, r->parse, |
|
ln, (int)(stesc - *bufp), NULL); |
|
return(ROFF_IGN); |
|
} |
|
|
/* |
/* |
* The third character decides the length |
* The third character decides the length |
|
|
* Save a pointer to the name. |
* Save a pointer to the name. |
*/ |
*/ |
|
|
switch (*cp) { |
switch (*++cp) { |
case ('\0'): |
case ('\0'): |
return(ROFF_CONT); |
continue; |
case ('('): |
case ('('): |
cp++; |
cp++; |
maxl = 2; |
maxl = 2; |
|
|
(MANDOCERR_BADESCAPE, |
(MANDOCERR_BADESCAPE, |
r->parse, ln, |
r->parse, ln, |
(int)(stesc - *bufp), NULL); |
(int)(stesc - *bufp), NULL); |
return(ROFF_CONT); |
continue; |
} |
} |
if (0 == maxl && ']' == *cp) |
if (0 == maxl && ']' == *cp) |
break; |
break; |
|
|
ln, (int)(stesc - *bufp), NULL); |
ln, (int)(stesc - *bufp), NULL); |
res = ""; |
res = ""; |
} |
} |
|
ressz = strlen(res); |
|
|
/* Replace the escape sequence by the string. */ |
/* Replace the escape sequence by the string. */ |
|
|
pos = stesc - *bufp; |
*szp += ressz + 1; |
|
nbuf = mandoc_malloc(*szp); |
|
|
nsz = *szp + strlen(res) + 1; |
|
nbuf = mandoc_malloc(nsz); |
|
|
|
strlcpy(nbuf, *bufp, (size_t)(stesc - *bufp + 1)); |
strlcpy(nbuf, *bufp, (size_t)(stesc - *bufp + 1)); |
strlcat(nbuf, res, nsz); |
strlcat(nbuf, res, *szp); |
strlcat(nbuf, cp + (maxl ? 0 : 1), nsz); |
strlcat(nbuf, cp + (maxl ? 0 : 1), *szp); |
|
|
free(*bufp); |
/* Prepare for the next replacement. */ |
|
|
|
start = nbuf + pos; |
|
stesc = nbuf + (stesc - *bufp) + ressz; |
|
free(*bufp); |
*bufp = nbuf; |
*bufp = nbuf; |
*szp = nsz; |
|
|
|
if (EXPAND_LIMIT >= ++expand_count) |
|
goto again; |
|
|
|
/* Just leave the string unexpanded. */ |
|
mandoc_msg(MANDOCERR_ROFFLOOP, r->parse, ln, pos, NULL); |
|
return(ROFF_IGN); |
|
} |
} |
return(ROFF_CONT); |
return(ROFF_CONT); |
} |
} |
Line 1107 roff_cond_text(ROFF_ARGS) |
|
Line 1117 roff_cond_text(ROFF_ARGS) |
|
return(rr ? ROFF_CONT : ROFF_IGN); |
return(rr ? ROFF_CONT : ROFF_IGN); |
} |
} |
|
|
|
/* |
|
* Parse a single signed integer number. Stop at the first non-digit. |
|
* If there is at least one digit, return success and advance the |
|
* parse point, else return failure and let the parse point unchanged. |
|
* Ignore overflows, treat them just like the C language. |
|
*/ |
static int |
static int |
roff_getnum(const char *v, int *pos, int *res) |
roff_getnum(const char *v, int *pos, int *res) |
{ |
{ |
Line 1118 roff_getnum(const char *v, int *pos, int *res) |
|
Line 1134 roff_getnum(const char *v, int *pos, int *res) |
|
p++; |
p++; |
|
|
for (*res = 0; isdigit((unsigned char)v[p]); p++) |
for (*res = 0; isdigit((unsigned char)v[p]); p++) |
*res += 10 * *res + v[p] - '0'; |
*res = 10 * *res + v[p] - '0'; |
if (p == *pos + n) |
if (p == *pos + n) |
return 0; |
return 0; |
|
|
Line 1129 roff_getnum(const char *v, int *pos, int *res) |
|
Line 1145 roff_getnum(const char *v, int *pos, int *res) |
|
return 1; |
return 1; |
} |
} |
|
|
static int |
|
roff_getop(const char *v, int *pos, char *res) |
|
{ |
|
int e; |
|
|
|
*res = v[*pos]; |
|
e = v[*pos + 1] == '='; |
|
|
|
switch (*res) { |
|
case '=': |
|
break; |
|
case '>': |
|
if (e) |
|
*res = 'g'; |
|
break; |
|
case '<': |
|
if (e) |
|
*res = 'l'; |
|
break; |
|
default: |
|
return(0); |
|
} |
|
|
|
*pos += 1 + e; |
|
|
|
return(*res); |
|
} |
|
|
|
/* |
/* |
* Evaluate a string comparison condition. |
* Evaluate a string comparison condition. |
* The first character is the delimiter. |
* The first character is the delimiter. |
|
|
return(match); |
return(match); |
} |
} |
|
|
|
/* |
|
* Evaluate an optionally negated single character, numerical, |
|
* or string condition. |
|
*/ |
static int |
static int |
roff_evalcond(const char *v, int *pos) |
roff_evalcond(const char *v, int *pos) |
{ |
{ |
int wanttrue, lh, rh; |
int wanttrue, number; |
char op; |
|
|
|
if ('!' == v[*pos]) { |
if ('!' == v[*pos]) { |
wanttrue = 0; |
wanttrue = 0; |
Line 1233 roff_evalcond(const char *v, int *pos) |
|
Line 1224 roff_evalcond(const char *v, int *pos) |
|
break; |
break; |
} |
} |
|
|
if (!roff_getnum(v, pos, &lh)) |
if (roff_evalnum(v, pos, &number, 0)) |
|
return((number > 0) == wanttrue); |
|
else |
return(roff_evalstrcond(v, pos) == wanttrue); |
return(roff_evalstrcond(v, pos) == wanttrue); |
if (!roff_getop(v, pos, &op)) |
|
return((lh > 0) == wanttrue); |
|
if (!roff_getnum(v, pos, &rh)) |
|
return(0); |
|
|
|
switch (op) { |
|
case 'g': |
|
return((lh >= rh) == wanttrue); |
|
case 'l': |
|
return((lh <= rh) == wanttrue); |
|
case '=': |
|
return((lh == rh) == wanttrue); |
|
case '>': |
|
return((lh > rh) == wanttrue); |
|
case '<': |
|
return((lh < rh) == wanttrue); |
|
default: |
|
return(0); |
|
} |
|
} |
} |
|
|
/* ARGSUSED */ |
/* ARGSUSED */ |
Line 1371 roff_ds(ROFF_ARGS) |
|
Line 1345 roff_ds(ROFF_ARGS) |
|
return(ROFF_IGN); |
return(ROFF_IGN); |
} |
} |
|
|
|
/* |
|
* Parse a single operator, one or two characters long. |
|
* If the operator is recognized, return success and advance the |
|
* parse point, else return failure and let the parse point unchanged. |
|
*/ |
|
static int |
|
roff_getop(const char *v, int *pos, char *res) |
|
{ |
|
|
|
*res = v[*pos]; |
|
|
|
switch (*res) { |
|
case ('+'): |
|
/* FALLTHROUGH */ |
|
case ('-'): |
|
/* FALLTHROUGH */ |
|
case ('*'): |
|
/* FALLTHROUGH */ |
|
case ('/'): |
|
/* FALLTHROUGH */ |
|
case ('%'): |
|
/* FALLTHROUGH */ |
|
case ('&'): |
|
/* FALLTHROUGH */ |
|
case (':'): |
|
break; |
|
case '<': |
|
switch (v[*pos + 1]) { |
|
case ('='): |
|
*res = 'l'; |
|
(*pos)++; |
|
break; |
|
case ('>'): |
|
*res = '!'; |
|
(*pos)++; |
|
break; |
|
case ('?'): |
|
*res = 'i'; |
|
(*pos)++; |
|
break; |
|
default: |
|
break; |
|
} |
|
break; |
|
case '>': |
|
switch (v[*pos + 1]) { |
|
case ('='): |
|
*res = 'g'; |
|
(*pos)++; |
|
break; |
|
case ('?'): |
|
*res = 'a'; |
|
(*pos)++; |
|
break; |
|
default: |
|
break; |
|
} |
|
break; |
|
case '=': |
|
if ('=' == v[*pos + 1]) |
|
(*pos)++; |
|
break; |
|
default: |
|
return(0); |
|
} |
|
(*pos)++; |
|
|
|
return(*res); |
|
} |
|
|
|
/* |
|
* Evaluate either a parenthesized numeric expression |
|
* or a single signed integer number. |
|
*/ |
|
static int |
|
roff_evalpar(const char *v, int *pos, int *res) |
|
{ |
|
|
|
if ('(' != v[*pos]) |
|
return(roff_getnum(v, pos, res)); |
|
|
|
(*pos)++; |
|
if ( ! roff_evalnum(v, pos, res, 1)) |
|
return(0); |
|
|
|
/* If the trailing parenthesis is missing, ignore the error. */ |
|
if (')' == v[*pos]) |
|
(*pos)++; |
|
|
|
return(1); |
|
} |
|
|
|
/* |
|
* Evaluate a complete numeric expression. |
|
* Proceed left to right, there is no concept of precedence. |
|
*/ |
|
static int |
|
roff_evalnum(const char *v, int *pos, int *res, int skipwhite) |
|
{ |
|
int mypos, operand2; |
|
char operator; |
|
|
|
if (NULL == pos) { |
|
mypos = 0; |
|
pos = &mypos; |
|
} |
|
|
|
if (skipwhite) |
|
while (isspace((unsigned char)v[*pos])) |
|
(*pos)++; |
|
|
|
if ( ! roff_evalpar(v, pos, res)) |
|
return(0); |
|
|
|
while (1) { |
|
if (skipwhite) |
|
while (isspace((unsigned char)v[*pos])) |
|
(*pos)++; |
|
|
|
if ( ! roff_getop(v, pos, &operator)) |
|
break; |
|
|
|
if (skipwhite) |
|
while (isspace((unsigned char)v[*pos])) |
|
(*pos)++; |
|
|
|
if ( ! roff_evalpar(v, pos, &operand2)) |
|
return(0); |
|
|
|
if (skipwhite) |
|
while (isspace((unsigned char)v[*pos])) |
|
(*pos)++; |
|
|
|
switch (operator) { |
|
case ('+'): |
|
*res += operand2; |
|
break; |
|
case ('-'): |
|
*res -= operand2; |
|
break; |
|
case ('*'): |
|
*res *= operand2; |
|
break; |
|
case ('/'): |
|
*res /= operand2; |
|
break; |
|
case ('%'): |
|
*res %= operand2; |
|
break; |
|
case ('<'): |
|
*res = *res < operand2; |
|
break; |
|
case ('>'): |
|
*res = *res > operand2; |
|
break; |
|
case ('l'): |
|
*res = *res <= operand2; |
|
break; |
|
case ('g'): |
|
*res = *res >= operand2; |
|
break; |
|
case ('='): |
|
*res = *res == operand2; |
|
break; |
|
case ('!'): |
|
*res = *res != operand2; |
|
break; |
|
case ('&'): |
|
*res = *res && operand2; |
|
break; |
|
case (':'): |
|
*res = *res || operand2; |
|
break; |
|
case ('i'): |
|
if (operand2 < *res) |
|
*res = operand2; |
|
break; |
|
case ('a'): |
|
if (operand2 > *res) |
|
*res = operand2; |
|
break; |
|
default: |
|
abort(); |
|
} |
|
} |
|
return(1); |
|
} |
|
|
void |
void |
roff_setreg(struct roff *r, const char *name, int val, char sign) |
roff_setreg(struct roff *r, const char *name, int val, char sign) |
{ |
{ |
Line 1480 roff_freereg(struct roffreg *reg) |
|
Line 1642 roff_freereg(struct roffreg *reg) |
|
} |
} |
} |
} |
|
|
/* ARGSUSED */ |
|
static enum rofferr |
static enum rofferr |
roff_nr(ROFF_ARGS) |
roff_nr(ROFF_ARGS) |
{ |
{ |
const char *key; |
const char *key; |
char *val; |
char *val; |
size_t sz; |
|
int iv; |
int iv; |
char sign; |
char sign; |
|
|
Line 1497 roff_nr(ROFF_ARGS) |
|
Line 1657 roff_nr(ROFF_ARGS) |
|
if ('+' == sign || '-' == sign) |
if ('+' == sign || '-' == sign) |
val++; |
val++; |
|
|
sz = strspn(val, "0123456789"); |
if (roff_evalnum(val, NULL, &iv, 0)) |
iv = sz ? mandoc_strntoi(val, sz, 10) : 0; |
roff_setreg(r, key, iv, sign); |
|
|
roff_setreg(r, key, iv, sign); |
|
|
|
return(ROFF_IGN); |
return(ROFF_IGN); |
} |
} |