Annotation of src/usr.bin/mandoc/roff.c, Revision 1.1
1.1 ! schwarze 1: /* $Id$ */
! 2: /*
! 3: * Copyright (c) 2010 Kristaps Dzonsons <kristaps@bsd.lv>
! 4: *
! 5: * Permission to use, copy, modify, and distribute this software for any
! 6: * purpose with or without fee is hereby granted, provided that the above
! 7: * copyright notice and this permission notice appear in all copies.
! 8: *
! 9: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
! 10: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
! 11: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
! 12: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
! 13: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
! 14: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
! 15: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
! 16: */
! 17: #ifdef HAVE_CONFIG_H
! 18: #include "config.h"
! 19: #endif
! 20:
! 21: #include <assert.h>
! 22: #include <stdlib.h>
! 23: #include <string.h>
! 24:
! 25: #include "mandoc.h"
! 26: #include "roff.h"
! 27:
! 28: enum rofft {
! 29: ROFF_de,
! 30: ROFF_dei,
! 31: ROFF_am,
! 32: ROFF_ami,
! 33: ROFF_ig,
! 34: ROFF_close,
! 35: ROFF_MAX
! 36: };
! 37:
! 38: struct roff {
! 39: struct roffnode *last; /* leaf of stack */
! 40: mandocmsg msg; /* err/warn/fatal messages */
! 41: void *data; /* privdata for messages */
! 42: };
! 43:
! 44: struct roffnode {
! 45: enum rofft tok; /* type of node */
! 46: struct roffnode *parent; /* up one in stack */
! 47: char *end; /* custom end-token */
! 48: int line; /* parse line */
! 49: int col; /* parse col */
! 50: };
! 51:
! 52: #define ROFF_ARGS struct roff *r, /* parse ctx */ \
! 53: enum rofft tok, /* tok of macro */ \
! 54: char **bufp, /* input buffer */ \
! 55: size_t *szp, /* size of input buffer */ \
! 56: int ln, /* parse line */ \
! 57: int ppos /* current pos in buffer */
! 58:
! 59: typedef enum rofferr (*roffproc)(ROFF_ARGS);
! 60:
! 61: struct roffmac {
! 62: const char *name; /* macro name */
! 63: roffproc sub; /* child of control black */
! 64: roffproc new; /* root of stack (type = ROFF_MAX) */
! 65: };
! 66:
! 67: static enum rofferr roff_new_close(ROFF_ARGS);
! 68: static enum rofferr roff_new_ig(ROFF_ARGS);
! 69: static enum rofferr roff_sub_ig(ROFF_ARGS);
! 70:
! 71: const struct roffmac roffs[ROFF_MAX] = {
! 72: { "de", roff_sub_ig, roff_new_ig },
! 73: { "dei", roff_sub_ig, roff_new_ig },
! 74: { "am", roff_sub_ig, roff_new_ig },
! 75: { "ami", roff_sub_ig, roff_new_ig },
! 76: { "ig", roff_sub_ig, roff_new_ig },
! 77: { ".", NULL, roff_new_close },
! 78: };
! 79:
! 80: static void roff_free1(struct roff *);
! 81: static enum rofft roff_hash_find(const char *);
! 82: static int roffnode_push(struct roff *,
! 83: enum rofft, int, int);
! 84: static void roffnode_pop(struct roff *);
! 85: static enum rofft roff_parse(const char *, int *);
! 86:
! 87:
! 88: /*
! 89: * Look up a roff token by its name. Returns ROFF_MAX if no macro by
! 90: * the nil-terminated string name could be found.
! 91: */
! 92: static enum rofft
! 93: roff_hash_find(const char *p)
! 94: {
! 95: int i;
! 96:
! 97: /* FIXME: make this be fast and efficient. */
! 98:
! 99: for (i = 0; i < (int)ROFF_MAX; i++)
! 100: if (0 == strcmp(roffs[i].name, p))
! 101: return((enum rofft)i);
! 102:
! 103: return(ROFF_MAX);
! 104: }
! 105:
! 106:
! 107: /*
! 108: * Pop the current node off of the stack of roff instructions currently
! 109: * pending.
! 110: */
! 111: static void
! 112: roffnode_pop(struct roff *r)
! 113: {
! 114: struct roffnode *p;
! 115:
! 116: if (NULL == (p = r->last))
! 117: return;
! 118: r->last = p->parent;
! 119: free(p);
! 120: }
! 121:
! 122:
! 123: /*
! 124: * Push a roff node onto the instruction stack. This must later be
! 125: * removed with roffnode_pop().
! 126: */
! 127: static int
! 128: roffnode_push(struct roff *r, enum rofft tok, int line, int col)
! 129: {
! 130: struct roffnode *p;
! 131:
! 132: if (NULL == (p = calloc(1, sizeof(struct roffnode)))) {
! 133: (*r->msg)(MANDOCERR_MEM, r->data, line, col, NULL);
! 134: return(0);
! 135: }
! 136:
! 137: p->tok = tok;
! 138: p->parent = r->last;
! 139: p->line = line;
! 140: p->col = col;
! 141:
! 142: r->last = p;
! 143: return(1);
! 144: }
! 145:
! 146:
! 147: static void
! 148: roff_free1(struct roff *r)
! 149: {
! 150:
! 151: while (r->last)
! 152: roffnode_pop(r);
! 153: }
! 154:
! 155:
! 156: void
! 157: roff_reset(struct roff *r)
! 158: {
! 159:
! 160: roff_free1(r);
! 161: }
! 162:
! 163:
! 164: void
! 165: roff_free(struct roff *r)
! 166: {
! 167:
! 168: roff_free1(r);
! 169: free(r);
! 170: }
! 171:
! 172:
! 173: struct roff *
! 174: roff_alloc(const mandocmsg msg, void *data)
! 175: {
! 176: struct roff *r;
! 177:
! 178: if (NULL == (r = calloc(1, sizeof(struct roff)))) {
! 179: (*msg)(MANDOCERR_MEM, data, 0, 0, NULL);
! 180: return(0);
! 181: }
! 182:
! 183: r->msg = msg;
! 184: r->data = data;
! 185: return(r);
! 186: }
! 187:
! 188:
! 189: enum rofferr
! 190: roff_parseln(struct roff *r, int ln, char **bufp, size_t *szp)
! 191: {
! 192: enum rofft t;
! 193: int ppos;
! 194:
! 195: if (NULL != r->last) {
! 196: /*
! 197: * If there's a node on the stack, then jump directly
! 198: * into its processing function.
! 199: */
! 200: t = r->last->tok;
! 201: assert(roffs[t].sub);
! 202: return((*roffs[t].sub)(r, t, bufp, szp, ln, 0));
! 203: } else if ('.' != (*bufp)[0] && NULL == r->last)
! 204: /* Return when in free text without a context. */
! 205: return(ROFF_CONT);
! 206:
! 207: /* There's nothing on the stack: make us anew. */
! 208:
! 209: if (ROFF_MAX == (t = roff_parse(*bufp, &ppos)))
! 210: return(ROFF_CONT);
! 211:
! 212: assert(roffs[t].new);
! 213: return((*roffs[t].new)(r, t, bufp, szp, ln, ppos));
! 214: }
! 215:
! 216:
! 217: /*
! 218: * Parse a roff node's type from the input buffer. This must be in the
! 219: * form of ".foo xxx" in the usual way.
! 220: */
! 221: static enum rofft
! 222: roff_parse(const char *buf, int *pos)
! 223: {
! 224: int j;
! 225: char mac[5];
! 226: enum rofft t;
! 227:
! 228: assert('.' == buf[0]);
! 229: *pos = 1;
! 230:
! 231: while (buf[*pos] && (' ' == buf[*pos] || '\t' == buf[*pos]))
! 232: (*pos)++;
! 233:
! 234: if ('\0' == buf[*pos])
! 235: return(ROFF_MAX);
! 236:
! 237: for (j = 0; j < 4; j++, (*pos)++)
! 238: if ('\0' == (mac[j] = buf[*pos]))
! 239: break;
! 240: else if (' ' == buf[*pos])
! 241: break;
! 242:
! 243: if (j == 4 || j < 1)
! 244: return(ROFF_MAX);
! 245:
! 246: mac[j] = '\0';
! 247:
! 248: if (ROFF_MAX == (t = roff_hash_find(mac)))
! 249: return(t);
! 250:
! 251: while (buf[*pos] && ' ' == buf[*pos])
! 252: (*pos)++;
! 253:
! 254: return(t);
! 255: }
! 256:
! 257:
! 258: /* ARGSUSED */
! 259: static enum rofferr
! 260: roff_sub_ig(ROFF_ARGS)
! 261: {
! 262: int i, j;
! 263:
! 264: /* Ignore free-text lines. */
! 265:
! 266: if ('.' != (*bufp)[ppos])
! 267: return(ROFF_IGN);
! 268:
! 269: if (r->last->end) {
! 270: i = ppos + 1;
! 271:
! 272: while ((*bufp)[i] && ' ' == (*bufp)[i])
! 273: i++;
! 274:
! 275: for (j = 0; r->last->end[j]; i++, j++)
! 276: if ((*bufp)[i] != r->last->end[j])
! 277: return(ROFF_IGN);
! 278:
! 279: if (r->last->end[j])
! 280: return(ROFF_IGN);
! 281: if ((*bufp)[i] && ' ' != (*bufp)[i])
! 282: return(ROFF_IGN);
! 283:
! 284: while (' ' == (*bufp)[i])
! 285: i++;
! 286:
! 287: } else if (ROFF_close != roff_parse(*bufp, &i))
! 288: return(ROFF_IGN);
! 289:
! 290: roffnode_pop(r);
! 291:
! 292: if ('\0' == (*bufp)[i])
! 293: return(ROFF_IGN);
! 294: if ( ! (*r->msg)(MANDOCERR_ARGSLOST, r->data, ln, i, NULL))
! 295: return(ROFF_ERR);
! 296:
! 297: return(ROFF_IGN);
! 298: }
! 299:
! 300:
! 301: /* ARGSUSED */
! 302: static enum rofferr
! 303: roff_new_close(ROFF_ARGS)
! 304: {
! 305:
! 306: if ( ! (*r->msg)(MANDOCERR_NOSCOPE, r->data, ln, ppos, NULL))
! 307: return(ROFF_ERR);
! 308:
! 309: return(ROFF_IGN);
! 310: }
! 311:
! 312:
! 313: /* ARGSUSED */
! 314: static enum rofferr
! 315: roff_new_ig(ROFF_ARGS)
! 316: {
! 317: int i;
! 318:
! 319: if ( ! roffnode_push(r, tok, ln, ppos))
! 320: return(ROFF_ERR);
! 321:
! 322: if (ROFF_ig != tok) {
! 323: while ((*bufp)[ppos] && ' ' != (*bufp)[ppos])
! 324: ppos++;
! 325: while (' ' == (*bufp)[ppos])
! 326: ppos++;
! 327: }
! 328:
! 329: i = (int)ppos;
! 330:
! 331: while ((*bufp)[i] && ' ' != (*bufp)[i])
! 332: i++;
! 333:
! 334: if (i == (int)ppos)
! 335: return(ROFF_IGN);
! 336:
! 337: if ((*bufp)[i])
! 338: if ( ! (*r->msg)(MANDOCERR_ARGSLOST, r->data, ln, i, NULL))
! 339: return(ROFF_ERR);
! 340:
! 341: /*
! 342: * If the macro has arguments, the first argument (up to the
! 343: * next whitespace) is interpreted as an argument marking the
! 344: * macro close. Thus, `.ig foo' will close at `.foo'.
! 345: *
! 346: * NOTE: the closing macro `.foo' in the above case is not
! 347: * allowed to have leading spaces with old groff! Thus `.foo'
! 348: * != `. foo'. Oh yeah, everything after the `.foo' is lost.
! 349: * Merry fucking Christmas.
! 350: */
! 351:
! 352: r->last->end = malloc((size_t)(i - ppos) + 1);
! 353: if (NULL == r->last->end) {
! 354: (*r->msg)(MANDOCERR_MEM, r->data, ln, ppos, NULL);
! 355: return(ROFF_ERR);
! 356: }
! 357:
! 358: memcpy(r->last->end, &(*bufp)[ppos], (size_t)(i - ppos));
! 359: r->last->end[i - ppos] = '\0';
! 360:
! 361: return(ROFF_IGN);
! 362: }
! 363:
! 364:
! 365: int
! 366: roff_endparse(struct roff *r)
! 367: {
! 368:
! 369: if (NULL == r->last)
! 370: return(1);
! 371: return((*r->msg)(MANDOCERR_SCOPEEXIT, r->data,
! 372: r->last->line, r->last->col, NULL));
! 373: }