void man_mdoc(void *arg, const struct mdoc *mdoc) { const struct mdoc_meta *meta; struct mdoc_node *n; meta = mdoc_meta(mdoc); n = mdoc_node(mdoc)->child; printf(".TH \"%s\" \"%s\" \"%s\" \"%s\" \"%s\"\n", meta->title, (meta->msec == NULL ? "" : meta->msec), meta->date, meta->os, meta->vol); /* Disable hyphenation and if nroff, disable justification. */ printf(".nh\n.if n .ad l"); outflags = MMAN_nl | MMAN_Sm; if (0 == fontqueue.size) { fontqueue.size = 8; fontqueue.head = fontqueue.tail = mandoc_malloc(8); *fontqueue.tail = 'R'; } while (n != NULL) { print_node(meta, n); n = n->next; } putchar('\n'); }
struct mchars * mchars_alloc(void) { struct mchars *tab; struct ln **htab; struct ln *pp; int i, hash; /* * Constructs a very basic chaining hashtable. The hash routine * is simply the integral value of the first character. * Subsequent entries are chained in the order they're processed. */ tab = mandoc_malloc(sizeof(struct mchars)); htab = mandoc_calloc(PRINT_HI - PRINT_LO + 1, sizeof(struct ln **)); for (i = 0; i < LINES_MAX; i++) { hash = (int)lines[i].code[0] - PRINT_LO; if (NULL == (pp = htab[hash])) { htab[hash] = &lines[i]; continue; } for ( ; pp->next; pp = pp->next) /* Scan ahead. */ ; pp->next = &lines[i]; } tab->htab = htab; return(tab); }
void man_mdoc(void *arg, const struct roff_man *mdoc) { struct roff_node *n; printf(".\\\" Automatically generated from an mdoc input file." " Do not edit.\n"); for (n = mdoc->first->child; n != NULL; n = n->next) { if (n->type != ROFFT_COMMENT) break; printf(".\\\"%s\n", n->string); } printf(".TH \"%s\" \"%s\" \"%s\" \"%s\" \"%s\"\n", mdoc->meta.title, (mdoc->meta.msec == NULL ? "" : mdoc->meta.msec), mdoc->meta.date, mdoc->meta.os, mdoc->meta.vol); /* Disable hyphenation and if nroff, disable justification. */ printf(".nh\n.if n .ad l"); outflags = MMAN_nl | MMAN_Sm; if (0 == fontqueue.size) { fontqueue.size = 8; fontqueue.head = fontqueue.tail = mandoc_malloc(8); *fontqueue.tail = 'R'; } for (; n != NULL; n = n->next) print_node(&mdoc->meta, n); putchar('\n'); }
static char * time2a(time_t t) { struct tm *tm; char *buf, *p; size_t ssz; int isz; tm = localtime(&t); /* * Reserve space: * up to 9 characters for the month (September) + blank * up to 2 characters for the day + comma + blank * 4 characters for the year and a terminating '\0' */ p = buf = mandoc_malloc(10 + 4 + 4 + 1); if (0 == (ssz = strftime(p, 10 + 1, "%B ", tm))) goto fail; p += (int)ssz; if (-1 == (isz = snprintf(p, 4 + 1, "%d, ", tm->tm_mday))) goto fail; p += isz; if (0 == strftime(p, 4 + 1, "%Y", tm)) goto fail; return(buf); fail: free(buf); return(NULL); }
void roff_setreg(struct roff *r, const char *name, int val, char sign) { struct roffreg *reg; /* Search for an existing register with the same name. */ reg = r->regtab; while (reg && strcmp(name, reg->key.p)) reg = reg->next; if (NULL == reg) { /* Create a new register. */ reg = mandoc_malloc(sizeof(struct roffreg)); reg->key.p = mandoc_strdup(name); reg->key.sz = strlen(name); reg->val = 0; reg->next = r->regtab; r->regtab = reg; } if ('+' == sign) reg->val += val; else if ('-' == sign) reg->val -= val; else reg->val = val; }
static int post_lb(POST_ARGS) { const char *p; char *buf; size_t sz; check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 1); assert(mdoc->last->child); assert(MDOC_TEXT == mdoc->last->child->type); p = mdoc_a2lib(mdoc->last->child->string); /* If lookup ok, replace with table value. */ if (p) { free(mdoc->last->child->string); mdoc->last->child->string = mandoc_strdup(p); return(1); } /* If not, use "library ``xxxx''. */ sz = strlen(mdoc->last->child->string) + 2 + strlen("\\(lqlibrary\\(rq"); buf = mandoc_malloc(sz); snprintf(buf, sz, "library \\(lq%s\\(rq", mdoc->last->child->string); free(mdoc->last->child->string); mdoc->last->child->string = buf; return(1); }
char * mandoc_strndup(const char *ptr, size_t sz) { char *p; p = mandoc_malloc(sz + 1); memcpy(p, ptr, sz); p[(int)sz] = '\0'; return p; }
/* ARGSUSED */ static enum rofferr roff_userdef(ROFF_ARGS) { const char *arg[9]; char *cp, *n1, *n2; int i; /* * Collect pointers to macro argument strings * and null-terminate them. */ cp = *bufp + pos; for (i = 0; i < 9; i++) arg[i] = '\0' == *cp ? "" : mandoc_getarg(r->parse, &cp, ln, &pos); /* * Expand macro arguments. */ *szp = 0; n1 = cp = mandoc_strdup(r->current_string); while (NULL != (cp = strstr(cp, "\\$"))) { i = cp[2] - '1'; if (0 > i || 8 < i) { /* Not an argument invocation. */ cp += 2; continue; } *szp = strlen(n1) - 3 + strlen(arg[i]) + 1; n2 = mandoc_malloc(*szp); strlcpy(n2, n1, (size_t)(cp - n1 + 1)); strlcat(n2, arg[i], *szp); strlcat(n2, cp + 3, *szp); cp = n2 + (cp - n1); free(n1); n1 = n2; } /* * Replace the macro invocation * by the expanded macro. */ free(*bufp); *bufp = n1; if (0 == *szp) *szp = strlen(*bufp) + 1; return(*szp > 1 && '\n' == (*bufp)[(int)*szp - 2] ? ROFF_REPARSE : ROFF_APPEND); }
struct tag * print_otag(struct html *h, enum htmltag tag, int sz, const struct htmlpair *p) { int i; struct tag *t; /* Push this tags onto the stack of open scopes. */ if ( ! (HTML_NOSTACK & htmltags[tag].flags)) { t = mandoc_malloc(sizeof(struct tag)); t->tag = tag; t->next = h->tags.head; h->tags.head = t; } else t = NULL; if ( ! (HTML_NOSPACE & h->flags)) if ( ! (HTML_CLRLINE & htmltags[tag].flags)) { /* Manage keeps! */ if ( ! (HTML_KEEP & h->flags)) { if (HTML_PREKEEP & h->flags) h->flags |= HTML_KEEP; putchar(' '); } else printf(" "); } if ( ! (h->flags & HTML_NONOSPACE)) h->flags &= ~HTML_NOSPACE; else h->flags |= HTML_NOSPACE; /* Print out the tag name and attributes. */ printf("<%s", htmltags[tag].name); for (i = 0; i < sz; i++) print_attr(h, htmlattrs[p[i].key], p[i].val); /* Accommodate for "well-formed" singleton escaping. */ if (HTML_AUTOCLOSE & htmltags[tag].flags) putchar('/'); putchar('>'); h->flags |= HTML_NOSPACE; if ((HTML_AUTOCLOSE | HTML_CLRLINE) & htmltags[tag].flags) putchar('\n'); return t; }
/* * Initialise the stored database key whose data buffer is shared * between uses (as the key must sometimes be constructed from an array * of */ static void dbt_init(DBT *key, size_t *ksz) { if (0 == *ksz) { assert(0 == key->size); assert(NULL == key->data); key->data = mandoc_malloc(MANDOC_BUFSZ); *ksz = MANDOC_BUFSZ; } key->size = 0; }
static void argv_single(struct mdoc *mdoc, int line, struct mdoc_argv *v, int *pos, char *buf) { enum margserr ac; char *p; ac = args(mdoc, line, pos, buf, ARGSFL_NONE, &p); if (ac == ARGS_EOLN) return; v->sz = 1; v->value = mandoc_malloc(sizeof(char *)); v->value[0] = mandoc_strdup(p); }
int mdoc_word_alloc(struct mdoc *m, int line, int pos, const char *p) { struct mdoc_node *n; size_t sv, len; len = strlen(p); n = node_alloc(m, line, pos, MDOC_MAX, MDOC_TEXT); n->string = mandoc_malloc(len + 1); sv = strlcpy(n->string, p, len + 1); /* Prohibit truncation. */ assert(sv < len + 1); if ( ! node_append(m, n)) return(0); m->next = MDOC_NEXT_SIBLING; return(1); }
static int argv_opt_single(struct mdoc *m, 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); if (ARGS_ERROR == ac) return(0); if (ARGS_EOLN == ac) 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); return(1); }
void man_mdoc(void *arg, const struct roff_man *mdoc) { struct roff_node *n; printf(".TH \"%s\" \"%s\" \"%s\" \"%s\" \"%s\"\n", mdoc->meta.title, (mdoc->meta.msec == NULL ? "" : mdoc->meta.msec), mdoc->meta.date, mdoc->meta.os, mdoc->meta.vol); /* Disable hyphenation and if nroff, disable justification. */ printf(".nh\n.if n .ad l"); outflags = MMAN_nl | MMAN_Sm; if (0 == fontqueue.size) { fontqueue.size = 8; fontqueue.head = fontqueue.tail = mandoc_malloc(8); *fontqueue.tail = 'R'; } for (n = mdoc->first->child; n != NULL; n = n->next) print_node(&mdoc->meta, n); putchar('\n'); }
static int post_at(POST_ARGS) { const char *p, *q; char *buf; size_t sz; /* * If we have a child, look it up in the standard keys. If a * key exist, use that instead of the child; if it doesn't, * prefix "AT&T UNIX " to the existing data. */ if (NULL == mdoc->last->child) return(1); assert(MDOC_TEXT == mdoc->last->child->type); p = mdoc_a2att(mdoc->last->child->string); if (p) { free(mdoc->last->child->string); mdoc->last->child->string = mandoc_strdup(p); } else { mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADATT); p = "AT&T UNIX "; q = mdoc->last->child->string; sz = strlen(p) + strlen(q) + 1; buf = mandoc_malloc(sz); strlcpy(buf, p, sz); strlcat(buf, q, sz); free(mdoc->last->child->string); mdoc->last->child->string = buf; } return(1); }
/* * In the current line, expand user-defined strings ("\*") * and references to number registers ("\n"). * Also check the syntax of other escape sequences. */ static enum rofferr roff_res(struct roff *r, char **bufp, size_t *szp, int ln, int pos) { char ubuf[12]; /* buffer to print the number */ const char *stesc; /* start of an escape sequence ('\\') */ const char *stnam; /* start of the name, after "[(*" */ const char *cp; /* end of the name, e.g. before ']' */ const char *res; /* the string to be substituted */ 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 naml; /* actual length of the escape name */ int expand_count; /* to avoid infinite loops */ expand_count = 0; again: cp = *bufp + pos; while (NULL != (cp = strchr(cp, '\\'))) { stesc = cp++; /* * The second character must be an asterisk or an n. * If it isn't, skip it anyway: It is escaped, * so it can't start another escape sequence. */ if ('\0' == *cp) return(ROFF_CONT); switch (*cp) { case ('*'): res = NULL; break; case ('n'): res = ubuf; break; default: if (ESCAPE_ERROR != mandoc_escape(&cp, NULL, NULL)) continue; mandoc_msg (MANDOCERR_BADESCAPE, r->parse, ln, (int)(stesc - *bufp), NULL); return(ROFF_CONT); } cp++; /* * The third character decides the length * of the name of the string or register. * Save a pointer to the name. */ switch (*cp) { case ('\0'): return(ROFF_CONT); case ('('): cp++; maxl = 2; break; case ('['): cp++; maxl = 0; break; default: maxl = 1; break; } stnam = cp; /* Advance to the end of the name. */ for (naml = 0; 0 == maxl || naml < maxl; naml++, cp++) { if ('\0' == *cp) { mandoc_msg (MANDOCERR_BADESCAPE, r->parse, ln, (int)(stesc - *bufp), NULL); return(ROFF_CONT); } if (0 == maxl && ']' == *cp) break; } /* * Retrieve the replacement string; if it is * undefined, resume searching for escapes. */ if (NULL == res) res = roff_getstrn(r, stnam, naml); else snprintf(ubuf, sizeof(ubuf), "%d", roff_getregn(r, stnam, naml)); if (NULL == res) { mandoc_msg (MANDOCERR_BADESCAPE, r->parse, ln, (int)(stesc - *bufp), NULL); res = ""; } /* Replace the escape sequence by the string. */ pos = stesc - *bufp; nsz = *szp + strlen(res) + 1; nbuf = mandoc_malloc(nsz); strlcpy(nbuf, *bufp, (size_t)(stesc - *bufp + 1)); strlcat(nbuf, res, nsz); strlcat(nbuf, cp + (maxl ? 0 : 1), nsz); free(*bufp); *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); }
struct tag * print_otag(struct html *h, enum htmltag tag, int sz, const struct htmlpair *p) { int i; struct tag *t; /* Push this tags onto the stack of open scopes. */ if ( ! (HTML_NOSTACK & htmltags[tag].flags)) { t = mandoc_malloc(sizeof(struct tag)); t->tag = tag; t->next = h->tags.head; h->tags.head = t; } else t = NULL; if ( ! (HTML_NOSPACE & h->flags)) if ( ! (HTML_CLRLINE & htmltags[tag].flags)) { /* Manage keeps! */ if ( ! (HTML_KEEP & h->flags)) { if (HTML_PREKEEP & h->flags) h->flags |= HTML_KEEP; putchar(' '); } else printf(" "); } if ( ! (h->flags & HTML_NONOSPACE)) h->flags &= ~HTML_NOSPACE; else h->flags |= HTML_NOSPACE; /* Print out the tag name and attributes. */ printf("<%s", htmltags[tag].name); for (i = 0; i < sz; i++) print_attr(h, htmlattrs[p[i].key], p[i].val); /* Add non-overridable attributes. */ if (TAG_HTML == tag && HTML_XHTML_1_0_STRICT == h->type) { print_attr(h, "xmlns", "http://www.w3.org/1999/xhtml"); print_attr(h, "xml:lang", "en"); print_attr(h, "lang", "en"); } /* Accommodate for XML "well-formed" singleton escaping. */ if (HTML_AUTOCLOSE & htmltags[tag].flags) switch (h->type) { case (HTML_XHTML_1_0_STRICT): putchar('/'); break; default: break; } putchar('>'); h->flags |= HTML_NOSPACE; if ((HTML_AUTOCLOSE | HTML_CLRLINE) & htmltags[tag].flags) putchar('\n'); return(t); }
/* * Pre-filter each and every line for reserved words (one beginning with * `\*', e.g., `\*(ab'). These must be handled before the actual line * is processed. * This also checks the syntax of regular escapes. */ static enum rofferr roff_res(struct roff *r, char **bufp, size_t *szp, int ln, int pos) { enum mandoc_esc esc; const char *stesc; /* start of an escape sequence ('\\') */ const char *stnam; /* start of the name, after "[(*" */ const char *cp; /* end of the name, e.g. before ']' */ const char *res; /* the string to be substituted */ int i, maxl, expand_count; size_t nsz; char *n; expand_count = 0; again: cp = *bufp + pos; while (NULL != (cp = strchr(cp, '\\'))) { stesc = cp++; /* * The second character must be an asterisk. * If it isn't, skip it anyway: It is escaped, * so it can't start another escape sequence. */ if ('\0' == *cp) return(ROFF_CONT); if ('*' != *cp) { res = cp; esc = mandoc_escape(&cp, NULL, NULL); if (ESCAPE_ERROR != esc) continue; cp = res; mandoc_msg (MANDOCERR_BADESCAPE, r->parse, ln, (int)(stesc - *bufp), NULL); return(ROFF_CONT); } cp++; /* * The third character decides the length * of the name of the string. * Save a pointer to the name. */ switch (*cp) { case ('\0'): return(ROFF_CONT); case ('('): cp++; maxl = 2; break; case ('['): cp++; maxl = 0; break; default: maxl = 1; break; } stnam = cp; /* Advance to the end of the name. */ for (i = 0; 0 == maxl || i < maxl; i++, cp++) { if ('\0' == *cp) { mandoc_msg (MANDOCERR_BADESCAPE, r->parse, ln, (int)(stesc - *bufp), NULL); return(ROFF_CONT); } if (0 == maxl && ']' == *cp) break; } /* * Retrieve the replacement string; if it is * undefined, resume searching for escapes. */ res = roff_getstrn(r, stnam, (size_t)i); if (NULL == res) { mandoc_msg (MANDOCERR_BADESCAPE, r->parse, ln, (int)(stesc - *bufp), NULL); res = ""; } /* Replace the escape sequence by the string. */ pos = stesc - *bufp; nsz = *szp + strlen(res) + 1; n = mandoc_malloc(nsz); strlcpy(n, *bufp, (size_t)(stesc - *bufp + 1)); strlcat(n, res, nsz); strlcat(n, cp + (maxl ? 0 : 1), nsz); free(*bufp); *bufp = n; *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); }
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); }
static void * hash_alloc(size_t sz, void *arg) { return(mandoc_malloc(sz)); }
/* ARGSUSED */ static enum rofferr roff_block(ROFF_ARGS) { int sv; size_t sz; char *name; name = NULL; if (ROFF_ig != tok) { if ('\0' == (*bufp)[pos]) { mandoc_msg(MANDOCERR_NOARGS, r->parse, ln, ppos, NULL); return(ROFF_IGN); } /* * Re-write `de1', since we don't really care about * groff's strange compatibility mode, into `de'. */ if (ROFF_de1 == tok) tok = ROFF_de; if (ROFF_de == tok) name = *bufp + pos; else mandoc_msg(MANDOCERR_REQUEST, r->parse, ln, ppos, roffs[tok].name); while ((*bufp)[pos] && ! isspace((unsigned char)(*bufp)[pos])) pos++; while (isspace((unsigned char)(*bufp)[pos])) (*bufp)[pos++] = '\0'; } roffnode_push(r, tok, name, ln, ppos); /* * At the beginning of a `de' macro, clear the existing string * with the same name, if there is one. New content will be * added from roff_block_text() in multiline mode. */ if (ROFF_de == tok) roff_setstr(r, name, "", 0); if ('\0' == (*bufp)[pos]) return(ROFF_IGN); /* If present, process the custom end-of-line marker. */ sv = pos; while ((*bufp)[pos] && ! isspace((unsigned char)(*bufp)[pos])) pos++; /* * Note: groff does NOT like escape characters in the input. * Instead of detecting this, we're just going to let it fly and * to hell with it. */ assert(pos > sv); sz = (size_t)(pos - sv); if (1 == sz && '.' == (*bufp)[sv]) return(ROFF_IGN); r->last->end = mandoc_malloc(sz + 1); memcpy(r->last->end, *bufp + sv, sz); r->last->end[(int)sz] = '\0'; if ((*bufp)[pos]) mandoc_msg(MANDOCERR_ARGSLOST, r->parse, ln, pos, NULL); return(ROFF_IGN); }
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); }
int main(int argc, char *argv[]) { struct mparse *mp; /* parse sequence */ struct manpaths dirs; struct mdb mdb; struct recs recs; enum op op; /* current operation */ const char *dir; int ch, i, flags; char dirbuf[MAXPATHLEN]; DB *hash; /* temporary keyword hashtable */ BTREEINFO info; /* btree configuration */ size_t sz1, sz2; struct buf buf, /* keyword buffer */ dbuf; /* description buffer */ struct of *of; /* list of files for processing */ extern int optind; extern char *optarg; progname = strrchr(argv[0], '/'); if (progname == NULL) progname = argv[0]; else ++progname; memset(&dirs, 0, sizeof(struct manpaths)); memset(&mdb, 0, sizeof(struct mdb)); memset(&recs, 0, sizeof(struct recs)); of = NULL; mp = NULL; hash = NULL; op = OP_DEFAULT; dir = NULL; while (-1 != (ch = getopt(argc, argv, "aC:d:tu:vW"))) switch (ch) { case ('a'): use_all = 1; break; case ('C'): if (op) { fprintf(stderr, "-C: conflicting options\n"); goto usage; } dir = optarg; op = OP_CONFFILE; break; case ('d'): if (op) { fprintf(stderr, "-d: conflicting options\n"); goto usage; } dir = optarg; op = OP_UPDATE; break; case ('t'): dup2(STDOUT_FILENO, STDERR_FILENO); if (op) { fprintf(stderr, "-t: conflicting options\n"); goto usage; } op = OP_TEST; use_all = 1; warnings = 1; break; case ('u'): if (op) { fprintf(stderr, "-u: conflicting options\n"); goto usage; } dir = optarg; op = OP_DELETE; break; case ('v'): verb++; break; case ('W'): warnings = 1; break; default: goto usage; } argc -= optind; argv += optind; if (OP_CONFFILE == op && argc > 0) { fprintf(stderr, "-C: too many arguments\n"); goto usage; } memset(&info, 0, sizeof(BTREEINFO)); info.lorder = 4321; info.flags = R_DUP; mp = mparse_alloc(MPARSE_AUTO, MANDOCLEVEL_FATAL, NULL, NULL); memset(&buf, 0, sizeof(struct buf)); memset(&dbuf, 0, sizeof(struct buf)); buf.size = dbuf.size = MANDOC_BUFSZ; buf.cp = mandoc_malloc(buf.size); dbuf.cp = mandoc_malloc(dbuf.size); if (OP_TEST == op) { ofile_argbuild(argc, argv, &of, "."); if (NULL == of) goto out; index_merge(of, mp, &dbuf, &buf, hash, &mdb, &recs, "."); goto out; } if (OP_UPDATE == op || OP_DELETE == op) { strlcat(mdb.dbn, dir, MAXPATHLEN); strlcat(mdb.dbn, "/", MAXPATHLEN); sz1 = strlcat(mdb.dbn, MANDOC_DB, MAXPATHLEN); strlcat(mdb.idxn, dir, MAXPATHLEN); strlcat(mdb.idxn, "/", MAXPATHLEN); sz2 = strlcat(mdb.idxn, MANDOC_IDX, MAXPATHLEN); if (sz1 >= MAXPATHLEN || sz2 >= MAXPATHLEN) { fprintf(stderr, "%s: path too long\n", dir); exit((int)MANDOCLEVEL_BADARG); } flags = O_CREAT | O_RDWR; mdb.db = dbopen(mdb.dbn, flags, 0644, DB_BTREE, &info); mdb.idx = dbopen(mdb.idxn, flags, 0644, DB_RECNO, NULL); if (NULL == mdb.db) { perror(mdb.dbn); exit((int)MANDOCLEVEL_SYSERR); } else if (NULL == mdb.idx) { perror(mdb.idxn); exit((int)MANDOCLEVEL_SYSERR); } ofile_argbuild(argc, argv, &of, dir); if (NULL == of) goto out; index_prune(of, &mdb, &recs, dir); /* * Go to the root of the respective manual tree. * This must work or no manuals may be found (they're * indexed relative to the root). */ if (OP_UPDATE == op) { if (-1 == chdir(dir)) { perror(dir); exit((int)MANDOCLEVEL_SYSERR); } index_merge(of, mp, &dbuf, &buf, hash, &mdb, &recs, dir); } goto out; } /* * Configure the directories we're going to scan. * If we have command-line arguments, use them. * If not, we use man(1)'s method (see mandocdb.8). */ if (argc > 0) { dirs.paths = mandoc_calloc(argc, sizeof(char *)); dirs.sz = argc; for (i = 0; i < argc; i++) dirs.paths[i] = mandoc_strdup(argv[i]); } else manpath_parse(&dirs, dir, NULL, NULL); for (i = 0; i < dirs.sz; i++) { /* * Go to the root of the respective manual tree. * This must work or no manuals may be found: * They are indexed relative to the root. */ if (-1 == chdir(dirs.paths[i])) { perror(dirs.paths[i]); exit((int)MANDOCLEVEL_SYSERR); } strlcpy(mdb.dbn, MANDOC_DB, MAXPATHLEN); strlcpy(mdb.idxn, MANDOC_IDX, MAXPATHLEN); flags = O_CREAT | O_TRUNC | O_RDWR; mdb.db = dbopen(mdb.dbn, flags, 0644, DB_BTREE, &info); mdb.idx = dbopen(mdb.idxn, flags, 0644, DB_RECNO, NULL); if (NULL == mdb.db) { perror(mdb.dbn); exit((int)MANDOCLEVEL_SYSERR); } else if (NULL == mdb.idx) { perror(mdb.idxn); exit((int)MANDOCLEVEL_SYSERR); } /* * Search for manuals and fill the new database. */ strlcpy(dirbuf, dirs.paths[i], MAXPATHLEN); ofile_dirbuild(".", "", "", 0, &of, dirbuf); if (NULL != of) { index_merge(of, mp, &dbuf, &buf, hash, &mdb, &recs, dirs.paths[i]); ofile_free(of); of = NULL; } (*mdb.db->close)(mdb.db); (*mdb.idx->close)(mdb.idx); mdb.db = NULL; mdb.idx = NULL; } out: if (mdb.db) (*mdb.db->close)(mdb.db); if (mdb.idx) (*mdb.idx->close)(mdb.idx); if (hash) (*hash->close)(hash); if (mp) mparse_free(mp); manpath_free(&dirs); ofile_free(of); free(buf.cp); free(dbuf.cp); free(recs.stack); return(MANDOCLEVEL_OK); usage: fprintf(stderr, "usage: %s [-av] [-C file] | dir ... | -t file ...\n" " -d dir [file ...] | " "-u dir [file ...]\n", progname); return((int)MANDOCLEVEL_BADARG); }
static void pg_search(const struct req *req) { struct mansearch search; struct manpaths paths; struct manpage *res; char **argv; char *query, *rp, *wp; size_t ressz; int argc; /* * Begin by chdir()ing into the root of the manpath. * This way we can pick up the database files, which are * relative to the manpath root. */ if (-1 == (chdir(req->q.manpath))) { fprintf(stderr, "chdir %s: %s\n", req->q.manpath, strerror(errno)); pg_error_internal(); return; } search.arch = req->q.arch; search.sec = req->q.sec; search.outkey = "Nd"; search.argmode = req->q.equal ? ARG_NAME : ARG_EXPR; search.firstmatch = 1; paths.sz = 1; paths.paths = mandoc_malloc(sizeof(char *)); paths.paths[0] = mandoc_strdup("."); /* * Break apart at spaces with backslash-escaping. */ argc = 0; argv = NULL; rp = query = mandoc_strdup(req->q.query); for (;;) { while (isspace((unsigned char)*rp)) rp++; if (*rp == '\0') break; argv = mandoc_reallocarray(argv, argc + 1, sizeof(char *)); argv[argc++] = wp = rp; for (;;) { if (isspace((unsigned char)*rp)) { *wp = '\0'; rp++; break; } if (rp[0] == '\\' && rp[1] != '\0') rp++; if (wp != rp) *wp = *rp; if (*rp == '\0') break; wp++; rp++; } } if (0 == mansearch(&search, &paths, argc, argv, &res, &ressz)) pg_noresult(req, "You entered an invalid query."); else if (0 == ressz) pg_noresult(req, "No results found."); else pg_searchres(req, res, ressz); free(query); mansearch_free(res, ressz); free(paths.paths[0]); free(paths.paths); }
static int post_bl_block_tag(POST_ARGS) { struct mdoc_node *n, *nn; size_t sz, ssz; int i; char buf[NUMSIZ]; /* * Calculate the -width for a `Bl -tag' list if it hasn't been * provided. Uses the first head macro. NOTE AGAIN: this is * ONLY if the -width argument has NOT been provided. See * post_bl_block_width() for converting the -width string. */ sz = 10; n = mdoc->last; for (nn = n->body->child; nn; nn = nn->next) { if (MDOC_It != nn->tok) continue; assert(MDOC_BLOCK == nn->type); nn = nn->head->child; if (nn == NULL) break; if (MDOC_TEXT == nn->type) { sz = strlen(nn->string) + 1; break; } if (0 != (ssz = mdoc_macro2len(nn->tok))) sz = ssz; break; } /* Defaults to ten ens. */ snprintf(buf, NUMSIZ, "%zun", sz); /* * We have to dynamically add this to the macro's argument list. * We're guaranteed that a MDOC_Width doesn't already exist. */ assert(n->args); i = (int)(n->args->argc)++; n->args->argv = mandoc_realloc(n->args->argv, n->args->argc * sizeof(struct mdoc_argv)); n->args->argv[i].arg = MDOC_Width; n->args->argv[i].line = n->line; n->args->argv[i].pos = n->pos; n->args->argv[i].sz = 1; n->args->argv[i].value = mandoc_malloc(sizeof(char *)); n->args->argv[i].value[0] = mandoc_strdup(buf); /* Set our width! */ n->norm->Bl.width = n->args->argv[i].value[0]; return(1); }
static int post_bl_head(POST_ARGS) { struct mdoc_node *np, *nn, *nnp; int i, j; if (LIST_column != mdoc->last->norm->Bl.type) /* FIXME: this should be ERROR class... */ return(hwarn_eq0(mdoc)); /* * Convert old-style lists, where the column width specifiers * trail as macro parameters, to the new-style ("normal-form") * lists where they're argument values following -column. */ /* First, disallow both types and allow normal-form. */ /* * TODO: technically, we can accept both and just merge the two * lists, but I'll leave that for another day. */ if (mdoc->last->norm->Bl.ncols && mdoc->last->nchild) { mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_COLUMNS); return(0); } else if (NULL == mdoc->last->child) return(1); np = mdoc->last->parent; assert(np->args); for (j = 0; j < (int)np->args->argc; j++) if (MDOC_Column == np->args->argv[j].arg) break; assert(j < (int)np->args->argc); assert(0 == np->args->argv[j].sz); /* * Accomodate for new-style groff column syntax. Shuffle the * child nodes, all of which must be TEXT, as arguments for the * column field. Then, delete the head children. */ np->args->argv[j].sz = (size_t)mdoc->last->nchild; np->args->argv[j].value = mandoc_malloc ((size_t)mdoc->last->nchild * sizeof(char *)); mdoc->last->norm->Bl.ncols = np->args->argv[j].sz; mdoc->last->norm->Bl.cols = (const char **)np->args->argv[j].value; for (i = 0, nn = mdoc->last->child; nn; i++) { np->args->argv[j].value[i] = nn->string; nn->string = NULL; nnp = nn; nn = nn->next; mdoc_node_delete(NULL, nnp); } mdoc->last->nchild = 0; mdoc->last->child = NULL; return(1); }
static int data(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, spans; cp = NULL; if (dp->last && dp->last->layout) cp = dp->last->layout->next; else if (NULL == dp->last) cp = dp->layout->first; /* * Skip over spanners, since * we want to match data with data layout cells in the header. */ while (cp && TBL_CELL_SPAN == cp->pos) cp = cp->next; /* * Stop processing when we reach the end of the available layout * cells. This means that we have extra input. */ if (NULL == cp) { mandoc_msg(MANDOCERR_TBLEXTRADAT, tbl->parse, ln, *pos, NULL); /* Skip to the end... */ while (p[*pos]) (*pos)++; return(1); } dat = mandoc_calloc(1, sizeof(struct tbl_dat)); dat->layout = cp; dat->pos = TBL_DATA_NONE; assert(TBL_CELL_SPAN != cp->pos); for (spans = 0, cp = cp->next; cp; cp = cp->next) if (TBL_CELL_SPAN == cp->pos) spans++; else break; dat->spans = spans; if (dp->last) { dp->last->next = dat; dp->last = dat; } else dp->last = dp->first = 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 && 'T' == p[sv] && '{' == p[sv + 1]) { tbl->part = TBL_PART_CDATA; return(0); } assert(*pos - sv >= 0); dat->string = mandoc_malloc((size_t)(*pos - sv + 1)); memcpy(dat->string, &p[sv], (size_t)(*pos - sv)); dat->string[*pos - sv] = '\0'; 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 (TBL_CELL_HORIZ == dat->layout->pos || TBL_CELL_DHORIZ == dat->layout->pos || TBL_CELL_DOWN == dat->layout->pos) if (TBL_DATA_DATA == dat->pos && '\0' != *dat->string) mandoc_msg(MANDOCERR_TBLIGNDATA, tbl->parse, ln, sv, NULL); return(1); }
static void pg_search(const struct req *req, char *path) { size_t tt, ressz; struct manpaths ps; int i, sz, rc; const char *ep, *start; struct res *res; char **cp; struct opts opt; struct expr *expr; if (req->q.manroot < 0 || 0 == req->psz) { resp_search(NULL, 0, (void *)req); return; } memset(&opt, 0, sizeof(struct opts)); ep = req->q.expr; opt.arch = req->q.arch; opt.cat = req->q.sec; rc = -1; sz = 0; cp = NULL; ressz = 0; res = NULL; /* * Begin by chdir()ing into the root of the manpath. * This way we can pick up the database files, which are * relative to the manpath root. */ assert(req->q.manroot < (int)req->psz); if (-1 == (chdir(req->p[req->q.manroot].path))) { perror(req->p[req->q.manroot].path); resp_search(NULL, 0, (void *)req); return; } memset(&ps, 0, sizeof(struct manpaths)); manpath_manconf(&ps, "etc/catman.conf"); /* * Poor man's tokenisation: just break apart by spaces. * Yes, this is half-ass. But it works for now. */ while (ep && isspace((unsigned char)*ep)) ep++; while (ep && '\0' != *ep) { cp = mandoc_realloc(cp, (sz + 1) * sizeof(char *)); start = ep; while ('\0' != *ep && ! isspace((unsigned char)*ep)) ep++; cp[sz] = mandoc_malloc((ep - start) + 1); memcpy(cp[sz], start, ep - start); cp[sz++][ep - start] = '\0'; while (isspace((unsigned char)*ep)) ep++; } /* * Pump down into apropos backend. * The resp_search() function is called with the results. */ expr = req->q.legacy ? termcomp(sz, cp, &tt) : exprcomp(sz, cp, &tt); if (NULL != expr) rc = apropos_search (ps.sz, ps.paths, &opt, expr, tt, (void *)req, &ressz, &res, resp_search); /* ...unless errors occured. */ if (0 == rc) resp_baddb(); else if (-1 == rc) resp_search(NULL, 0, NULL); for (i = 0; i < sz; i++) free(cp[i]); free(cp); resfree(res, ressz); exprfree(expr); manpath_free(&ps); }