/* * Parse a token from the stream of text. * A token consists of one of the recognised eqn(7) strings. * Strings are separated by delimiting marks. * This returns EQN_TOK_EOF when there are no more tokens. * If the token is an unrecognised string literal, then it returns * EQN_TOK__MAX and sets the "p" pointer to an allocated, nil-terminated * string. * This must be later freed with free(3). */ static enum eqn_tok eqn_tok_parse(struct eqn_node *ep, char **p) { const char *start; size_t i, sz; int quoted; if (NULL != p) *p = NULL; quoted = ep->data[ep->cur] == '"'; if (NULL == (start = eqn_nexttok(ep, &sz))) return(EQN_TOK_EOF); if (quoted) { if (p != NULL) *p = mandoc_strndup(start, sz); return(EQN_TOK__MAX); } for (i = 0; i < EQN_TOK__MAX; i++) { if (NULL == eqn_toks[i]) continue; if (STRNEQ(start, sz, eqn_toks[i], strlen(eqn_toks[i]))) break; } if (i == EQN_TOK__MAX && NULL != p) *p = mandoc_strndup(start, sz); return(i); }
static void eqn_def(struct eqn_node *ep) { const char *start; size_t sz; struct eqn_def *def; int i; if ((start = eqn_nextrawtok(ep, &sz)) == NULL) { mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse, ep->eqn.ln, ep->eqn.pos, "define"); return; } /* * Search for a key that already exists. * Create a new key if none is found. */ if (NULL == (def = eqn_def_find(ep, start, sz))) { /* Find holes in string array. */ for (i = 0; i < (int)ep->defsz; i++) if (0 == ep->defs[i].keysz) break; if (i == (int)ep->defsz) { ep->defsz++; ep->defs = mandoc_reallocarray(ep->defs, ep->defsz, sizeof(struct eqn_def)); ep->defs[i].key = ep->defs[i].val = NULL; } def = ep->defs + i; free(def->key); def->key = mandoc_strndup(start, sz); def->keysz = sz; } start = eqn_next(ep, ep->data[(int)ep->cur], &sz, 0); if (start == NULL) { mandoc_vmsg(MANDOCERR_REQ_EMPTY, ep->parse, ep->eqn.ln, ep->eqn.pos, "define %s", def->key); free(def->key); free(def->val); def->key = def->val = NULL; def->keysz = def->valsz = 0; return; } free(def->val); def->val = mandoc_strndup(start, sz); def->valsz = sz; }
struct eqn_node * eqn_alloc(const char *name, int pos, int line, struct mparse *parse) { struct eqn_node *p; size_t sz; const char *end; p = mandoc_calloc(1, sizeof(struct eqn_node)); if (name && '\0' != *name) { sz = strlen(name); assert(sz); do { sz--; end = name + (int)sz; } while (' ' == *end || '\t' == *end); p->eqn.name = mandoc_strndup(name, sz + 1); } p->parse = parse; p->eqn.ln = line; p->eqn.pos = pos; p->gsize = EQN_DEFSIZE; return(p); }
static void pg_show(struct req *req, const char *fullpath) { char *manpath; const char *file; if ((file = strchr(fullpath, '/')) == NULL) { pg_error_badrequest( "You did not specify a page to show."); return; } manpath = mandoc_strndup(fullpath, file - fullpath); file++; if ( ! validate_manpath(req, manpath)) { pg_error_badrequest( "You specified an invalid manpath."); free(manpath); return; } /* * Begin by chdir()ing into the manpath. * This way we can pick up the database files, which are * relative to the manpath root. */ if (chdir(manpath) == -1) { fprintf(stderr, "chdir %s: %s\n", manpath, strerror(errno)); pg_error_internal(); free(manpath); return; } if (strcmp(manpath, "mandoc")) { free(req->q.manpath); req->q.manpath = manpath; } else free(manpath); if ( ! validate_filename(file)) { pg_error_badrequest( "You specified an invalid manual file."); return; } resp_begin_html(200, NULL); resp_searchform(req); resp_show(req, file); resp_end_html(); }
void man_deroff(char **dest, const struct man_node *n) { char *cp; size_t sz; if (MAN_TEXT != n->type) { for (n = n->child; n; n = n->next) man_deroff(dest, n); return; } /* Skip leading whitespace and escape sequences. */ cp = n->string; while ('\0' != *cp) { if ('\\' == *cp) { cp++; mandoc_escape((const char **)&cp, NULL, NULL); } else if (isspace((unsigned char)*cp)) cp++; else break; } /* Skip trailing whitespace. */ for (sz = strlen(cp); sz; sz--) if (0 == isspace((unsigned char)cp[sz-1])) break; /* Skip empty strings. */ if (0 == sz) return; if (NULL == *dest) { *dest = mandoc_strndup(cp, sz); return; } mandoc_asprintf(&cp, "%s %*s", *dest, (int)sz, cp); free(*dest); *dest = cp; }
/* * Scan for indexable paths. */ static void pathgen(struct req *req) { FILE *fp; char *dp; size_t dpsz; if (NULL == (fp = fopen("manpath.conf", "r"))) { fprintf(stderr, "%s/manpath.conf: %s\n", MAN_DIR, strerror(errno)); pg_error_internal(); exit(EXIT_FAILURE); } while (NULL != (dp = fgetln(fp, &dpsz))) { if ('\n' == dp[dpsz - 1]) dpsz--; req->p = mandoc_realloc(req->p, (req->psz + 1) * sizeof(char *)); dp = mandoc_strndup(dp, dpsz); if ( ! validate_urifrag(dp)) { fprintf(stderr, "%s/manpath.conf contains " "unsafe path \"%s\"\n", MAN_DIR, dp); pg_error_internal(); exit(EXIT_FAILURE); } if (NULL != strchr(dp, '/')) { fprintf(stderr, "%s/manpath.conf contains " "path with slash \"%s\"\n", MAN_DIR, dp); pg_error_internal(); exit(EXIT_FAILURE); } req->p[req->psz++] = dp; } if ( req->p == NULL ) { fprintf(stderr, "%s/manpath.conf is empty\n", MAN_DIR); pg_error_internal(); exit(EXIT_FAILURE); } }
void mdoc_deroff(char **dest, const struct mdoc_node *n) { char *cp; size_t sz; if (MDOC_TEXT != n->type) { for (n = n->child; n; n = n->next) mdoc_deroff(dest, n); return; } /* Skip leading whitespace. */ for (cp = n->string; '\0' != *cp; cp++) if (0 == isspace((unsigned char)*cp)) break; /* Skip trailing whitespace. */ for (sz = strlen(cp); sz; sz--) if (0 == isspace((unsigned char)cp[sz-1])) break; /* Skip empty strings. */ if (0 == sz) return; if (NULL == *dest) { *dest = mandoc_strndup(cp, sz); return; } mandoc_asprintf(&cp, "%s %*s", *dest, (int)sz, cp); free(*dest); *dest = cp; }
/* * Recursively parse an eqn(7) expression. */ static enum rofferr eqn_parse(struct eqn_node *ep, struct eqn_box *parent) { char sym[64]; struct eqn_box *cur; const char *start; char *p; size_t i, sz; enum eqn_tok tok, subtok; enum eqn_post pos; int size; assert(parent != NULL); /* * Empty equation. * Do not add it to the high-level syntax tree. */ if (ep->data == NULL) return(ROFF_IGN); next_tok: tok = eqn_tok_parse(ep, &p); this_tok: switch (tok) { case (EQN_TOK_UNDEF): eqn_undef(ep); break; case (EQN_TOK_NDEFINE): case (EQN_TOK_DEFINE): eqn_def(ep); break; case (EQN_TOK_TDEFINE): if (eqn_nextrawtok(ep, NULL) == NULL || eqn_next(ep, ep->data[(int)ep->cur], NULL, 0) == NULL) mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse, ep->eqn.ln, ep->eqn.pos, "tdefine"); break; case (EQN_TOK_DELIM): eqn_delim(ep); break; case (EQN_TOK_GFONT): if (eqn_nextrawtok(ep, NULL) == NULL) mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse, ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]); break; case (EQN_TOK_MARK): case (EQN_TOK_LINEUP): /* Ignore these. */ break; case (EQN_TOK_DYAD): case (EQN_TOK_VEC): case (EQN_TOK_UNDER): case (EQN_TOK_BAR): case (EQN_TOK_TILDE): case (EQN_TOK_HAT): case (EQN_TOK_DOT): case (EQN_TOK_DOTDOT): if (parent->last == NULL) { mandoc_msg(MANDOCERR_EQN_NOBOX, ep->parse, ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]); cur = eqn_box_alloc(ep, parent); cur->type = EQN_TEXT; cur->text = mandoc_strdup(""); } parent = eqn_box_makebinary(ep, EQNPOS_NONE, parent); parent->type = EQN_LISTONE; parent->expectargs = 1; switch (tok) { case (EQN_TOK_DOTDOT): strlcpy(sym, "\\[ad]", sizeof(sym)); break; case (EQN_TOK_VEC): strlcpy(sym, "\\[->]", sizeof(sym)); break; case (EQN_TOK_DYAD): strlcpy(sym, "\\[<>]", sizeof(sym)); break; case (EQN_TOK_TILDE): strlcpy(sym, "\\[a~]", sizeof(sym)); break; case (EQN_TOK_UNDER): strlcpy(sym, "\\[ul]", sizeof(sym)); break; case (EQN_TOK_BAR): strlcpy(sym, "\\[rl]", sizeof(sym)); break; case (EQN_TOK_DOT): strlcpy(sym, "\\[a.]", sizeof(sym)); break; case (EQN_TOK_HAT): strlcpy(sym, "\\[ha]", sizeof(sym)); break; default: abort(); } switch (tok) { case (EQN_TOK_DOTDOT): case (EQN_TOK_VEC): case (EQN_TOK_DYAD): case (EQN_TOK_TILDE): case (EQN_TOK_BAR): case (EQN_TOK_DOT): case (EQN_TOK_HAT): parent->top = mandoc_strdup(sym); break; case (EQN_TOK_UNDER): parent->bottom = mandoc_strdup(sym); break; default: abort(); } parent = parent->parent; break; case (EQN_TOK_FWD): case (EQN_TOK_BACK): case (EQN_TOK_DOWN): case (EQN_TOK_UP): subtok = eqn_tok_parse(ep, NULL); if (subtok != EQN_TOK__MAX) { mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse, ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]); tok = subtok; goto this_tok; } break; case (EQN_TOK_FAT): case (EQN_TOK_ROMAN): case (EQN_TOK_ITALIC): case (EQN_TOK_BOLD): while (parent->args == parent->expectargs) parent = parent->parent; /* * These values apply to the next word or sequence of * words; thus, we mark that we'll have a child with * exactly one of those. */ parent = eqn_box_alloc(ep, parent); parent->type = EQN_LISTONE; parent->expectargs = 1; switch (tok) { case (EQN_TOK_FAT): parent->font = EQNFONT_FAT; break; case (EQN_TOK_ROMAN): parent->font = EQNFONT_ROMAN; break; case (EQN_TOK_ITALIC): parent->font = EQNFONT_ITALIC; break; case (EQN_TOK_BOLD): parent->font = EQNFONT_BOLD; break; default: abort(); } break; case (EQN_TOK_SIZE): case (EQN_TOK_GSIZE): /* Accept two values: integral size and a single. */ if (NULL == (start = eqn_nexttok(ep, &sz))) { mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse, ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]); break; } size = mandoc_strntoi(start, sz, 10); if (-1 == size) { mandoc_msg(MANDOCERR_IT_NONUM, ep->parse, ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]); break; } if (EQN_TOK_GSIZE == tok) { ep->gsize = size; break; } parent = eqn_box_alloc(ep, parent); parent->type = EQN_LISTONE; parent->expectargs = 1; parent->size = size; break; case (EQN_TOK_FROM): case (EQN_TOK_TO): case (EQN_TOK_SUB): case (EQN_TOK_SUP): /* * We have a left-right-associative expression. * Repivot under a positional node, open a child scope * and keep on reading. */ if (parent->last == NULL) { mandoc_msg(MANDOCERR_EQN_NOBOX, ep->parse, ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]); cur = eqn_box_alloc(ep, parent); cur->type = EQN_TEXT; cur->text = mandoc_strdup(""); } /* Handle the "subsup" and "fromto" positions. */ if (EQN_TOK_SUP == tok && parent->pos == EQNPOS_SUB) { parent->expectargs = 3; parent->pos = EQNPOS_SUBSUP; break; } if (EQN_TOK_TO == tok && parent->pos == EQNPOS_FROM) { parent->expectargs = 3; parent->pos = EQNPOS_FROMTO; break; } switch (tok) { case (EQN_TOK_FROM): pos = EQNPOS_FROM; break; case (EQN_TOK_TO): pos = EQNPOS_TO; break; case (EQN_TOK_SUP): pos = EQNPOS_SUP; break; case (EQN_TOK_SUB): pos = EQNPOS_SUB; break; default: abort(); } parent = eqn_box_makebinary(ep, pos, parent); break; case (EQN_TOK_SQRT): while (parent->args == parent->expectargs) parent = parent->parent; /* * Accept a left-right-associative set of arguments just * like sub and sup and friends but without rebalancing * under a pivot. */ parent = eqn_box_alloc(ep, parent); parent->type = EQN_SUBEXPR; parent->pos = EQNPOS_SQRT; parent->expectargs = 1; break; case (EQN_TOK_OVER): /* * We have a right-left-associative fraction. * Close out anything that's currently open, then * rebalance and continue reading. */ if (parent->last == NULL) { mandoc_msg(MANDOCERR_EQN_NOBOX, ep->parse, ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]); cur = eqn_box_alloc(ep, parent); cur->type = EQN_TEXT; cur->text = mandoc_strdup(""); } while (EQN_SUBEXPR == parent->type) parent = parent->parent; parent = eqn_box_makebinary(ep, EQNPOS_OVER, parent); break; case (EQN_TOK_RIGHT): case (EQN_TOK_BRACE_CLOSE): /* * Close out the existing brace. * FIXME: this is a shitty sentinel: we should really * have a native EQN_BRACE type or whatnot. */ for (cur = parent; cur != NULL; cur = cur->parent) if (cur->type == EQN_LIST && (tok == EQN_TOK_BRACE_CLOSE || cur->left != NULL)) break; if (cur == NULL) { mandoc_msg(MANDOCERR_BLK_NOTOPEN, ep->parse, ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]); break; } parent = cur; if (EQN_TOK_RIGHT == tok) { if (NULL == (start = eqn_nexttok(ep, &sz))) { mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse, ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]); break; } /* Handling depends on right/left. */ if (STRNEQ(start, sz, "ceiling", 7)) { strlcpy(sym, "\\[rc]", sizeof(sym)); parent->right = mandoc_strdup(sym); } else if (STRNEQ(start, sz, "floor", 5)) { strlcpy(sym, "\\[rf]", sizeof(sym)); parent->right = mandoc_strdup(sym); } else parent->right = mandoc_strndup(start, sz); } parent = parent->parent; if (EQN_TOK_BRACE_CLOSE == tok && parent && (parent->type == EQN_PILE || parent->type == EQN_MATRIX)) parent = parent->parent; /* Close out any "singleton" lists. */ while (parent && parent->type == EQN_LISTONE && parent->args == parent->expectargs) parent = parent->parent; break; case (EQN_TOK_BRACE_OPEN): case (EQN_TOK_LEFT): /* * If we already have something in the stack and we're * in an expression, then rewind til we're not any more * (just like with the text node). */ while (parent->args == parent->expectargs) parent = parent->parent; if (EQN_TOK_LEFT == tok && (start = eqn_nexttok(ep, &sz)) == NULL) { mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse, ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]); break; } parent = eqn_box_alloc(ep, parent); parent->type = EQN_LIST; if (EQN_TOK_LEFT == tok) { if (STRNEQ(start, sz, "ceiling", 7)) { strlcpy(sym, "\\[lc]", sizeof(sym)); parent->left = mandoc_strdup(sym); } else if (STRNEQ(start, sz, "floor", 5)) { strlcpy(sym, "\\[lf]", sizeof(sym)); parent->left = mandoc_strdup(sym); } else parent->left = mandoc_strndup(start, sz); } break; case (EQN_TOK_PILE): case (EQN_TOK_LPILE): case (EQN_TOK_RPILE): case (EQN_TOK_CPILE): case (EQN_TOK_CCOL): case (EQN_TOK_LCOL): case (EQN_TOK_RCOL): while (parent->args == parent->expectargs) parent = parent->parent; parent = eqn_box_alloc(ep, parent); parent->type = EQN_PILE; parent->expectargs = 1; break; case (EQN_TOK_ABOVE): for (cur = parent; cur != NULL; cur = cur->parent) if (cur->type == EQN_PILE) break; if (cur == NULL) { mandoc_msg(MANDOCERR_IT_STRAY, ep->parse, ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]); break; } parent = eqn_box_alloc(ep, cur); parent->type = EQN_LIST; break; case (EQN_TOK_MATRIX): while (parent->args == parent->expectargs) parent = parent->parent; parent = eqn_box_alloc(ep, parent); parent->type = EQN_MATRIX; parent->expectargs = 1; break; case (EQN_TOK_EOF): /* * End of file! * TODO: make sure we're not in an open subexpression. */ return(ROFF_EQN); default: assert(tok == EQN_TOK__MAX); assert(NULL != p); /* * If we already have something in the stack and we're * in an expression, then rewind til we're not any more. */ while (parent->args == parent->expectargs) parent = parent->parent; cur = eqn_box_alloc(ep, parent); cur->type = EQN_TEXT; for (i = 0; i < EQNSYM__MAX; i++) if (0 == strcmp(eqnsyms[i].str, p)) { (void)snprintf(sym, sizeof(sym), "\\[%s]", eqnsyms[i].sym); cur->text = mandoc_strdup(sym); free(p); break; } if (i == EQNSYM__MAX) cur->text = p; /* * Post-process list status. */ while (parent->type == EQN_LISTONE && parent->args == parent->expectargs) parent = parent->parent; break; } goto next_tok; }
/* * Parse the QUERY_STRING for key-value pairs * and store the values into the query structure. */ static void http_parse(struct req *req, const char *qs) { char *key, *val; size_t keysz, valsz; req->q.manpath = NULL; req->q.arch = NULL; req->q.sec = NULL; req->q.query = NULL; req->q.equal = 1; key = val = NULL; while (*qs != '\0') { /* Parse one key. */ keysz = strcspn(qs, "=;&"); key = mandoc_strndup(qs, keysz); qs += keysz; if (*qs != '=') goto next; /* Parse one value. */ valsz = strcspn(++qs, ";&"); val = mandoc_strndup(qs, valsz); qs += valsz; /* Decode and catch encoding errors. */ if ( ! (http_decode(key) && http_decode(val))) goto next; /* Handle key-value pairs. */ if ( ! strcmp(key, "query")) set_query_attr(&req->q.query, &val); else if ( ! strcmp(key, "apropos")) req->q.equal = !strcmp(val, "0"); else if ( ! strcmp(key, "manpath")) { #ifdef COMPAT_OLDURI if ( ! strncmp(val, "OpenBSD ", 8)) { val[7] = '-'; if ('C' == val[8]) val[8] = 'c'; } #endif set_query_attr(&req->q.manpath, &val); } else if ( ! (strcmp(key, "sec") #ifdef COMPAT_OLDURI && strcmp(key, "sektion") #endif )) { if ( ! strcmp(val, "0")) *val = '\0'; set_query_attr(&req->q.sec, &val); } else if ( ! strcmp(key, "arch")) { if ( ! strcmp(val, "default")) *val = '\0'; set_query_attr(&req->q.arch, &val); } /* * The key must be freed in any case. * The val may have been handed over to the query * structure, in which case it is now NULL. */ next: free(key); key = NULL; free(val); val = NULL; if (*qs != '\0') qs++; } }
static void getdata(struct tbl_node *tbl, struct tbl_span *dp, int ln, const char *p, int *pos) { struct tbl_dat *dat; struct tbl_cell *cp; int sv; /* Advance to the next layout cell, skipping spanners. */ cp = dp->last == NULL ? dp->layout->first : dp->last->layout->next; while (cp != NULL && cp->pos == TBL_CELL_SPAN) cp = cp->next; /* * Stop processing when we reach the end of the available layout * cells. This means that we have extra input. */ if (cp == NULL) { mandoc_msg(MANDOCERR_TBLDATA_EXTRA, tbl->parse, ln, *pos, p + *pos); /* Skip to the end... */ while (p[*pos]) (*pos)++; return; } dat = mandoc_calloc(1, sizeof(*dat)); dat->layout = cp; dat->pos = TBL_DATA_NONE; dat->spans = 0; for (cp = cp->next; cp != NULL; cp = cp->next) if (cp->pos == TBL_CELL_SPAN) dat->spans++; else break; if (dp->last == NULL) dp->first = dat; else dp->last->next = dat; dp->last = dat; sv = *pos; while (p[*pos] && p[*pos] != tbl->opts.tab) (*pos)++; /* * Check for a continued-data scope opening. This consists of a * trailing `T{' at the end of the line. Subsequent lines, * until a standalone `T}', are included in our cell. */ if (*pos - sv == 2 && p[sv] == 'T' && p[sv + 1] == '{') { tbl->part = TBL_PART_CDATA; return; } dat->string = mandoc_strndup(p + sv, *pos - sv); if (p[*pos]) (*pos)++; if ( ! strcmp(dat->string, "_")) dat->pos = TBL_DATA_HORIZ; else if ( ! strcmp(dat->string, "=")) dat->pos = TBL_DATA_DHORIZ; else if ( ! strcmp(dat->string, "\\_")) dat->pos = TBL_DATA_NHORIZ; else if ( ! strcmp(dat->string, "\\=")) dat->pos = TBL_DATA_NDHORIZ; else dat->pos = TBL_DATA_DATA; if ((dat->layout->pos == TBL_CELL_HORIZ || dat->layout->pos == TBL_CELL_DHORIZ || dat->layout->pos == TBL_CELL_DOWN) && dat->pos == TBL_DATA_DATA && *dat->string != '\0') mandoc_msg(MANDOCERR_TBLDATA_SPAN, tbl->parse, ln, sv, dat->string); }
static void getdata(struct tbl_node *tbl, struct tbl_span *dp, int ln, const char *p, int *pos) { struct tbl_dat *dat, *pdat; struct tbl_cell *cp; struct tbl_span *pdp; int sv; /* * Determine the length of the string in the cell * and advance the parse point to the end of the cell. */ sv = *pos; while (p[*pos] != '\0' && p[*pos] != tbl->opts.tab) (*pos)++; /* Advance to the next layout cell, skipping spanners. */ cp = dp->last == NULL ? dp->layout->first : dp->last->layout->next; while (cp != NULL && cp->pos == TBL_CELL_SPAN) cp = cp->next; /* * If the current layout row is out of cells, allocate * a new cell if another row of the table has at least * this number of columns, or discard the input if we * are beyond the last column of the table as a whole. */ if (cp == NULL) { if (dp->layout->last->col + 1 < dp->opts->cols) { cp = mandoc_calloc(1, sizeof(*cp)); cp->pos = TBL_CELL_LEFT; dp->layout->last->next = cp; cp->col = dp->layout->last->col + 1; dp->layout->last = cp; } else { mandoc_msg(MANDOCERR_TBLDATA_EXTRA, ln, sv, "%s", p + sv); while (p[*pos] != '\0') (*pos)++; return; } } dat = mandoc_malloc(sizeof(*dat)); dat->layout = cp; dat->next = NULL; dat->string = NULL; dat->hspans = 0; dat->vspans = 0; dat->block = 0; dat->pos = TBL_DATA_NONE; /* * Increment the number of vertical spans in a data cell above, * if this cell vertically extends one or more cells above. * The iteration must be done over data rows, * not over layout rows, because one layout row * can be reused for more than one data row. */ if (cp->pos == TBL_CELL_DOWN || (*pos - sv == 2 && p[sv] == '\\' && p[sv + 1] == '^')) { pdp = dp; while ((pdp = pdp->prev) != NULL) { pdat = pdp->first; while (pdat != NULL && pdat->layout->col < dat->layout->col) pdat = pdat->next; if (pdat == NULL) break; if (pdat->layout->pos != TBL_CELL_DOWN && strcmp(pdat->string, "\\^") != 0) { pdat->vspans++; break; } } } /* * Count the number of horizontal spans to the right of this cell. * This is purely a matter of the layout, independent of the data. */ for (cp = cp->next; cp != NULL; cp = cp->next) if (cp->pos == TBL_CELL_SPAN) dat->hspans++; else break; /* Append the new data cell to the data row. */ if (dp->last == NULL) dp->first = dat; else dp->last->next = dat; dp->last = dat; /* * Check for a continued-data scope opening. This consists of a * trailing `T{' at the end of the line. Subsequent lines, * until a standalone `T}', are included in our cell. */ if (*pos - sv == 2 && p[sv] == 'T' && p[sv + 1] == '{') { tbl->part = TBL_PART_CDATA; return; } dat->string = mandoc_strndup(p + sv, *pos - sv); if (p[*pos] != '\0') (*pos)++; if ( ! strcmp(dat->string, "_")) dat->pos = TBL_DATA_HORIZ; else if ( ! strcmp(dat->string, "=")) dat->pos = TBL_DATA_DHORIZ; else if ( ! strcmp(dat->string, "\\_")) dat->pos = TBL_DATA_NHORIZ; else if ( ! strcmp(dat->string, "\\=")) dat->pos = TBL_DATA_NDHORIZ; else dat->pos = TBL_DATA_DATA; if ((dat->layout->pos == TBL_CELL_HORIZ || dat->layout->pos == TBL_CELL_DHORIZ || dat->layout->pos == TBL_CELL_DOWN) && dat->pos == TBL_DATA_DATA && *dat->string != '\0') mandoc_msg(MANDOCERR_TBLDATA_SPAN, ln, sv, "%s", dat->string); }
static void roff_setstrn(struct roffkv **r, const char *name, size_t namesz, const char *string, size_t stringsz, int multiline) { struct roffkv *n; char *c; int i; size_t oldch, newch; /* Search for an existing string with the same name. */ n = *r; while (n && strcmp(name, n->key.p)) n = n->next; if (NULL == n) { /* Create a new string table entry. */ n = mandoc_malloc(sizeof(struct roffkv)); n->key.p = mandoc_strndup(name, namesz); n->key.sz = namesz; n->val.p = NULL; n->val.sz = 0; n->next = *r; *r = n; } else if (0 == multiline) { /* In multiline mode, append; else replace. */ free(n->val.p); n->val.p = NULL; n->val.sz = 0; } if (NULL == string) return; /* * One additional byte for the '\n' in multiline mode, * and one for the terminating '\0'. */ newch = stringsz + (multiline ? 2u : 1u); if (NULL == n->val.p) { n->val.p = mandoc_malloc(newch); *n->val.p = '\0'; oldch = 0; } else { oldch = n->val.sz; n->val.p = mandoc_realloc(n->val.p, oldch + newch); } /* Skip existing content in the destination buffer. */ c = n->val.p + (int)oldch; /* Append new content to the destination buffer. */ i = 0; while (i < (int)stringsz) { /* * Rudimentary roff copy mode: * Handle escaped backslashes. */ if ('\\' == string[i] && '\\' == string[i + 1]) i++; *c++ = string[i++]; } /* Append terminating bytes. */ if (multiline) *c++ = '\n'; *c = '\0'; n->val.sz = (int)(c - n->val.p); }
/* ARGSUSED */ static enum rofferr roff_tr(ROFF_ARGS) { const char *p, *first, *second; size_t fsz, ssz; enum mandoc_esc esc; p = *bufp + pos; if ('\0' == *p) { mandoc_msg(MANDOCERR_ARGCOUNT, r->parse, ln, ppos, NULL); return(ROFF_IGN); } while ('\0' != *p) { fsz = ssz = 1; first = p++; if ('\\' == *first) { esc = mandoc_escape(&p, NULL, NULL); if (ESCAPE_ERROR == esc) { mandoc_msg (MANDOCERR_BADESCAPE, r->parse, ln, (int)(p - *bufp), NULL); return(ROFF_IGN); } fsz = (size_t)(p - first); } second = p++; if ('\\' == *second) { esc = mandoc_escape(&p, NULL, NULL); if (ESCAPE_ERROR == esc) { mandoc_msg (MANDOCERR_BADESCAPE, r->parse, ln, (int)(p - *bufp), NULL); return(ROFF_IGN); } ssz = (size_t)(p - second); } else if ('\0' == *second) { mandoc_msg(MANDOCERR_ARGCOUNT, r->parse, ln, (int)(p - *bufp), NULL); second = " "; p--; } if (fsz > 1) { roff_setstrn(&r->xmbtab, first, fsz, second, ssz, 0); continue; } if (NULL == r->xtab) r->xtab = mandoc_calloc (128, sizeof(struct roffstr)); free(r->xtab[(int)*first].p); r->xtab[(int)*first].p = mandoc_strndup(second, ssz); r->xtab[(int)*first].sz = ssz; } return(ROFF_IGN); }
static enum eqn_rest eqn_box(struct eqn_node *ep, struct eqn_box *last) { size_t sz; const char *start; char *left; char sym[64]; enum eqn_rest c; int i, size; struct eqn_box *bp; if (NULL == (start = eqn_nexttok(ep, &sz))) return(EQN_EOF); if (STRNEQ(start, sz, "}", 1)) return(EQN_DESCOPE); else if (STRNEQ(start, sz, "right", 5)) return(EQN_DESCOPE); else if (STRNEQ(start, sz, "above", 5)) return(EQN_DESCOPE); else if (STRNEQ(start, sz, "mark", 4)) return(EQN_OK); else if (STRNEQ(start, sz, "lineup", 6)) return(EQN_OK); for (i = 0; i < (int)EQN__MAX; i++) { if ( ! EQNSTREQ(&eqnparts[i].str, start, sz)) continue; return((*eqnparts[i].fp)(ep) ? EQN_OK : EQN_ERR); } if (STRNEQ(start, sz, "{", 1)) { if (EQN_DESCOPE != (c = eqn_eqn(ep, last))) { if (EQN_ERR != c) EQN_MSG(MANDOCERR_EQNSCOPE, ep); return(EQN_ERR); } eqn_rewind(ep); start = eqn_nexttok(ep, &sz); assert(start); if (STRNEQ(start, sz, "}", 1)) return(EQN_OK); EQN_MSG(MANDOCERR_EQNBADSCOPE, ep); return(EQN_ERR); } for (i = 0; i < (int)EQNPILE__MAX; i++) { if ( ! EQNSTREQ(&eqnpiles[i], start, sz)) continue; if (EQN_OK == (c = eqn_list(ep, last))) last->last->pile = (enum eqn_pilet)i; return(c); } if (STRNEQ(start, sz, "matrix", 6)) return(eqn_matrix(ep, last)); if (STRNEQ(start, sz, "left", 4)) { if (NULL == (start = eqn_nexttok(ep, &sz))) { EQN_MSG(MANDOCERR_EQNEOF, ep); return(EQN_ERR); } left = mandoc_strndup(start, sz); c = eqn_eqn(ep, last); if (last->last) last->last->left = left; else free(left); if (EQN_DESCOPE != c) return(c); assert(last->last); eqn_rewind(ep); start = eqn_nexttok(ep, &sz); assert(start); if ( ! STRNEQ(start, sz, "right", 5)) return(EQN_DESCOPE); if (NULL == (start = eqn_nexttok(ep, &sz))) { EQN_MSG(MANDOCERR_EQNEOF, ep); return(EQN_ERR); } last->last->right = mandoc_strndup(start, sz); return(EQN_OK); } for (i = 0; i < (int)EQNPOS__MAX; i++) { if ( ! EQNSTREQ(&eqnposs[i], start, sz)) continue; if (NULL == last->last) { EQN_MSG(MANDOCERR_EQNSYNT, ep); return(EQN_ERR); } last->last->pos = (enum eqn_post)i; if (EQN_EOF == (c = eqn_box(ep, last))) { EQN_MSG(MANDOCERR_EQNEOF, ep); return(EQN_ERR); } return(c); } for (i = 0; i < (int)EQNMARK__MAX; i++) { if ( ! EQNSTREQ(&eqnmarks[i], start, sz)) continue; if (NULL == last->last) { EQN_MSG(MANDOCERR_EQNSYNT, ep); return(EQN_ERR); } last->last->mark = (enum eqn_markt)i; if (EQN_EOF == (c = eqn_box(ep, last))) { EQN_MSG(MANDOCERR_EQNEOF, ep); return(EQN_ERR); } return(c); } for (i = 0; i < (int)EQNFONT__MAX; i++) { if ( ! EQNSTREQ(&eqnfonts[i], start, sz)) continue; if (EQN_EOF == (c = eqn_box(ep, last))) { EQN_MSG(MANDOCERR_EQNEOF, ep); return(EQN_ERR); } else if (EQN_OK == c) last->last->font = (enum eqn_fontt)i; return(c); } if (STRNEQ(start, sz, "size", 4)) { if (NULL == (start = eqn_nexttok(ep, &sz))) { EQN_MSG(MANDOCERR_EQNEOF, ep); return(EQN_ERR); } size = mandoc_strntoi(start, sz, 10); if (EQN_EOF == (c = eqn_box(ep, last))) { EQN_MSG(MANDOCERR_EQNEOF, ep); return(EQN_ERR); } else if (EQN_OK != c) return(c); last->last->size = size; } bp = eqn_box_alloc(ep, last); bp->type = EQN_TEXT; for (i = 0; i < (int)EQNSYM__MAX; i++) if (EQNSTREQ(&eqnsyms[i].str, start, sz)) { sym[63] = '\0'; snprintf(sym, 62, "\\[%s]", eqnsyms[i].sym); bp->text = mandoc_strdup(sym); return(EQN_OK); } bp->text = mandoc_strndup(start, sz); return(EQN_OK); }