static void pmandoc(struct mparse *mp, int fd, const char *fn, int list) { struct roff_man *man; int line, col; mparse_readfd(mp, fd, fn); close(fd); mparse_result(mp, &man, NULL); line = 1; col = 0; if (man == NULL) return; if (man->macroset == MACROSET_MDOC) { mdoc_validate(man); pmdoc(man->first->child, &line, &col, list); } else { man_validate(man); pman(man->first->child, &line, &col, list); } if ( ! list) putchar('\n'); }
static void pmandoc(struct mparse *mp, int fd, const char *fn, int list) { struct mdoc *mdoc; struct man *man; int line, col; if (mparse_readfd(mp, fd, fn) >= MANDOCLEVEL_FATAL) { fprintf(stderr, "%s: Parse failure\n", fn); return; } mparse_result(mp, &mdoc, &man, NULL); line = 1; col = 0; if (mdoc) pmdoc(mdoc_node(mdoc), &line, &col, list); else if (man) pman(man_node(man), &line, &col, list); else return; if ( ! list) putchar('\n'); }
static void format(const struct req *req, const char *file) { struct manoutput conf; struct mparse *mp; struct roff_man *man; void *vp; int fd; int usepath; if (-1 == (fd = open(file, O_RDONLY, 0))) { puts("<P>You specified an invalid manual file.</P>"); return; } mchars_alloc(); mp = mparse_alloc(MPARSE_SO, MANDOCLEVEL_BADARG, NULL, req->q.manpath); mparse_readfd(mp, fd, file); close(fd); memset(&conf, 0, sizeof(conf)); conf.fragment = 1; usepath = strcmp(req->q.manpath, req->p[0]); mandoc_asprintf(&conf.man, "%s?query=%%N&sec=%%S%s%s%s%s", scriptname, req->q.arch ? "&arch=" : "", req->q.arch ? req->q.arch : "", usepath ? "&manpath=" : "", usepath ? req->q.manpath : ""); mparse_result(mp, &man, NULL); if (man == NULL) { fprintf(stderr, "fatal mandoc error: %s/%s\n", req->q.manpath, file); pg_error_internal(); mparse_free(mp); mchars_free(); return; } vp = html_alloc(&conf); if (man->macroset == MACROSET_MDOC) { mdoc_validate(man); html_mdoc(vp, man); } else { man_validate(man); html_man(vp, man); } html_free(vp); mparse_free(mp); mchars_free(); free(conf.man); }
static void format(const struct req *req, const char *file) { struct mparse *mp; int fd; struct mdoc *mdoc; struct man *man; void *vp; enum mandoclevel rc; char opts[PATH_MAX + 128]; if (-1 == (fd = open(file, O_RDONLY, 0))) { resp_baddb(); return; } mp = mparse_alloc(MPARSE_AUTO, MANDOCLEVEL_FATAL, NULL, NULL, NULL); rc = mparse_readfd(mp, fd, file); close(fd); if (rc >= MANDOCLEVEL_FATAL) { resp_baddb(); return; } snprintf(opts, sizeof(opts), "fragment," "man=%s/search.html?sec=%%S&expr=Nm~^%%N$," /*"includes=/cgi-bin/man.cgi/usr/include/%%I"*/, progname); mparse_result(mp, &mdoc, &man); if (NULL == man && NULL == mdoc) { resp_baddb(); mparse_free(mp); return; } resp_begin_html(200, NULL); resp_searchform(req); vp = html_alloc(opts); if (NULL != mdoc) html_mdoc(vp, mdoc); else html_man(vp, man); puts("</BODY>\n" "</HTML>"); html_free(vp); mparse_free(mp); }
static void pmandoc(struct mparse *mp, int fd, const char *fn, int list) { struct mdoc *mdoc; struct man *man; int line, col; mparse_readfd(mp, fd, fn); mparse_result(mp, &mdoc, &man, NULL); line = 1; col = 0; if (mdoc) pmdoc(mdoc_node(mdoc), &line, &col, list); else if (man) pman(man_node(man), &line, &col, list); else return; if ( ! list) putchar('\n'); }
void index_merge(const struct of *of, struct mparse *mp, struct buf *dbuf, struct buf *buf, DB *hash, struct mdb *mdb, struct recs *recs, const char *basedir) { recno_t rec; int ch, skip; DBT key, val; DB *files; /* temporary file name table */ char emptystring[1] = {'\0'}; struct mdoc *mdoc; struct man *man; char *p; const char *fn, *msec, *march, *mtitle; uint64_t mask; size_t sv; unsigned seq; uint64_t vbuf[2]; char type; if (warnings) { files = NULL; hash_reset(&files); } rec = 0; for (of = of->first; of; of = of->next) { fn = of->fname; /* * Try interpreting the file as mdoc(7) or man(7) * source code, unless it is already known to be * formatted. Fall back to formatted mode. */ mparse_reset(mp); mdoc = NULL; man = NULL; if ((MANDOC_SRC & of->src_form || ! (MANDOC_FORM & of->src_form)) && MANDOCLEVEL_FATAL > mparse_readfd(mp, -1, fn)) mparse_result(mp, &mdoc, &man); if (NULL != mdoc) { msec = mdoc_meta(mdoc)->msec; march = mdoc_meta(mdoc)->arch; if (NULL == march) march = ""; mtitle = mdoc_meta(mdoc)->title; } else if (NULL != man) { msec = man_meta(man)->msec; march = ""; mtitle = man_meta(man)->title; } else { msec = of->sec; march = of->arch; mtitle = of->title; } /* * Check whether the manual section given in a file * agrees with the directory where the file is located. * Some manuals have suffixes like (3p) on their * section number either inside the file or in the * directory name, some are linked into more than one * section, like encrypt(1) = makekey(8). Do not skip * manuals for such reasons. */ skip = 0; assert(of->sec); assert(msec); if (strcasecmp(msec, of->sec)) WARNING(fn, basedir, "Section \"%s\" manual " "in \"%s\" directory", msec, of->sec); /* * Manual page directories exist for each kernel * architecture as returned by machine(1). * However, many manuals only depend on the * application architecture as returned by arch(1). * For example, some (2/ARM) manuals are shared * across the "armish" and "zaurus" kernel * architectures. * A few manuals are even shared across completely * different architectures, for example fdformat(1) * on amd64, i386, sparc, and sparc64. * Thus, warn about architecture mismatches, * but don't skip manuals for this reason. */ assert(of->arch); assert(march); if (strcasecmp(march, of->arch)) WARNING(fn, basedir, "Architecture \"%s\" " "manual in \"%s\" directory", march, of->arch); /* * By default, skip a file if the title given * in the file disagrees with the file name. * Do not warn, this happens for all MLINKs. */ assert(of->title); assert(mtitle); if (strcasecmp(mtitle, of->title)) skip = 1; /* * Build a title string for the file. If it matches * the location of the file, remember the title as * found; else, remember it as missing. */ if (warnings) { buf->len = 0; buf_appendb(buf, mtitle, strlen(mtitle)); buf_appendb(buf, "(", 1); buf_appendb(buf, msec, strlen(msec)); if ('\0' != *march) { buf_appendb(buf, "/", 1); buf_appendb(buf, march, strlen(march)); } buf_appendb(buf, ")", 2); for (p = buf->cp; '\0' != *p; p++) *p = tolower(*p); key.data = buf->cp; key.size = buf->len; val.data = NULL; val.size = 0; if (0 == skip) val.data = emptystring; else { ch = (*files->get)(files, &key, &val, 0); if (ch < 0) { perror("hash"); exit((int)MANDOCLEVEL_SYSERR); } else if (ch > 0) { val.data = (void *)fn; val.size = strlen(fn) + 1; } else val.data = NULL; } if (NULL != val.data && (*files->put)(files, &key, &val, 0) < 0) { perror("hash"); exit((int)MANDOCLEVEL_SYSERR); } } if (skip && !use_all) continue; /* * The index record value consists of a nil-terminated * filename, a nil-terminated manual section, and a * nil-terminated description. Use the actual * location of the file, such that the user can find * it with man(1). Since the description may not be * set, we set a sentinel to see if we're going to * write a nil byte in its place. */ dbuf->len = 0; type = mdoc ? 'd' : (man ? 'a' : 'c'); buf_appendb(dbuf, &type, 1); buf_appendb(dbuf, fn, strlen(fn) + 1); buf_appendb(dbuf, of->sec, strlen(of->sec) + 1); buf_appendb(dbuf, of->title, strlen(of->title) + 1); buf_appendb(dbuf, of->arch, strlen(of->arch) + 1); sv = dbuf->len; /* * Collect keyword/mask pairs. * Each pair will become a new btree node. */ hash_reset(&hash); if (mdoc) pmdoc_node(hash, buf, dbuf, mdoc_node(mdoc), mdoc_meta(mdoc)); else if (man) pman_node(hash, buf, dbuf, man_node(man)); else pformatted(hash, buf, dbuf, of, basedir); /* Test mode, do not access any database. */ if (NULL == mdb->db || NULL == mdb->idx) continue; /* * Make sure the file name is always registered * as an .Nm search key. */ buf->len = 0; buf_append(buf, of->title); hash_put(hash, buf, TYPE_Nm); /* * Reclaim an empty index record, if available. * Use its record number for all new btree nodes. */ if (recs->cur > 0) { recs->cur--; rec = recs->stack[(int)recs->cur]; } else if (recs->last > 0) { rec = recs->last; recs->last = 0; } else rec++; vbuf[1] = htobe64(rec); /* * Copy from the in-memory hashtable of pending * keyword/mask pairs into the database. */ seq = R_FIRST; while (0 == (ch = (*hash->seq)(hash, &key, &val, seq))) { seq = R_NEXT; assert(sizeof(uint64_t) == val.size); memcpy(&mask, val.data, val.size); vbuf[0] = htobe64(mask); val.size = sizeof(vbuf); val.data = &vbuf; dbt_put(mdb->db, mdb->dbn, &key, &val); } if (ch < 0) { perror("hash"); exit((int)MANDOCLEVEL_SYSERR); } /* * Apply to the index. If we haven't had a description * set, put an empty one in now. */ if (dbuf->len == sv) buf_appendb(dbuf, "", 1); key.data = &rec; key.size = sizeof(recno_t); val.data = dbuf->cp; val.size = dbuf->len; if (verb) printf("%s: Adding to index: %s\n", basedir, fn); dbt_put(mdb->idx, mdb->idxn, &key, &val); } /* * Iterate the remembered file titles and check that * all files can be found by their main title. */ if (warnings) { seq = R_FIRST; while (0 == (*files->seq)(files, &key, &val, seq)) { seq = R_NEXT; if (val.size) WARNING((char *)val.data, basedir, "Probably unreachable, title " "is %s", (char *)key.data); } (*files->close)(files); } }
/* * Main parse routine for a buffer. * It assumes encoding and line numbering are already set up. * It can recurse directly (for invocations of user-defined * macros, inline equations, and input line traps) * and indirectly (for .so file inclusion). */ static void mparse_buf_r(struct mparse *curp, struct buf blk, size_t i, int start) { const struct tbl_span *span; struct buf ln; const char *save_file; char *cp; size_t pos; /* byte number in the ln buffer */ enum rofferr rr; int of; int lnn; /* line number in the real file */ int fd; unsigned char c; memset(&ln, 0, sizeof(ln)); lnn = curp->line; pos = 0; while (i < blk.sz) { if (0 == pos && '\0' == blk.buf[i]) break; if (start) { curp->line = lnn; curp->reparse_count = 0; if (lnn < 3 && curp->filenc & MPARSE_UTF8 && curp->filenc & MPARSE_LATIN1) curp->filenc = preconv_cue(&blk, i); } while (i < blk.sz && (start || blk.buf[i] != '\0')) { /* * When finding an unescaped newline character, * leave the character loop to process the line. * Skip a preceding carriage return, if any. */ if ('\r' == blk.buf[i] && i + 1 < blk.sz && '\n' == blk.buf[i + 1]) ++i; if ('\n' == blk.buf[i]) { ++i; ++lnn; break; } /* * Make sure we have space for the worst * case of 11 bytes: "\\[u10ffff]\0" */ if (pos + 11 > ln.sz) resize_buf(&ln, 256); /* * Encode 8-bit input. */ c = blk.buf[i]; if (c & 0x80) { if ( ! (curp->filenc && preconv_encode( &blk, &i, &ln, &pos, &curp->filenc))) { mandoc_vmsg(MANDOCERR_CHAR_BAD, curp, curp->line, pos, "0x%x", c); ln.buf[pos++] = '?'; i++; } continue; } /* * Exclude control characters. */ if (c == 0x7f || (c < 0x20 && c != 0x09)) { mandoc_vmsg(c == 0x00 || c == 0x04 || c > 0x0a ? MANDOCERR_CHAR_BAD : MANDOCERR_CHAR_UNSUPP, curp, curp->line, pos, "0x%x", c); i++; if (c != '\r') ln.buf[pos++] = '?'; continue; } /* Trailing backslash = a plain char. */ if (blk.buf[i] != '\\' || i + 1 == blk.sz) { ln.buf[pos++] = blk.buf[i++]; continue; } /* * Found escape and at least one other character. * When it's a newline character, skip it. * When there is a carriage return in between, * skip that one as well. */ if ('\r' == blk.buf[i + 1] && i + 2 < blk.sz && '\n' == blk.buf[i + 2]) ++i; if ('\n' == blk.buf[i + 1]) { i += 2; ++lnn; continue; } if ('"' == blk.buf[i + 1] || '#' == blk.buf[i + 1]) { i += 2; /* Comment, skip to end of line */ for (; i < blk.sz; ++i) { if ('\n' == blk.buf[i]) { ++i; ++lnn; break; } } /* Backout trailing whitespaces */ for (; pos > 0; --pos) { if (ln.buf[pos - 1] != ' ') break; if (pos > 2 && ln.buf[pos - 2] == '\\') break; } break; } /* Catch escaped bogus characters. */ c = (unsigned char) blk.buf[i+1]; if ( ! (isascii(c) && (isgraph(c) || isblank(c)))) { mandoc_vmsg(MANDOCERR_CHAR_BAD, curp, curp->line, pos, "0x%x", c); i += 2; ln.buf[pos++] = '?'; continue; } /* Some other escape sequence, copy & cont. */ ln.buf[pos++] = blk.buf[i++]; ln.buf[pos++] = blk.buf[i++]; } if (pos >= ln.sz) resize_buf(&ln, 256); ln.buf[pos] = '\0'; /* * A significant amount of complexity is contained by * the roff preprocessor. It's line-oriented but can be * expressed on one line, so we need at times to * readjust our starting point and re-run it. The roff * preprocessor can also readjust the buffers with new * data, so we pass them in wholesale. */ of = 0; /* * Maintain a lookaside buffer of all parsed lines. We * only do this if mparse_keep() has been invoked (the * buffer may be accessed with mparse_getkeep()). */ if (curp->secondary) { curp->secondary->buf = mandoc_realloc( curp->secondary->buf, curp->secondary->sz + pos + 2); memcpy(curp->secondary->buf + curp->secondary->sz, ln.buf, pos); curp->secondary->sz += pos; curp->secondary->buf [curp->secondary->sz] = '\n'; curp->secondary->sz++; curp->secondary->buf [curp->secondary->sz] = '\0'; } rerun: rr = roff_parseln(curp->roff, curp->line, &ln, &of); switch (rr) { case ROFF_REPARSE: if (REPARSE_LIMIT >= ++curp->reparse_count) mparse_buf_r(curp, ln, of, 0); else mandoc_msg(MANDOCERR_ROFFLOOP, curp, curp->line, pos, NULL); pos = 0; continue; case ROFF_APPEND: pos = strlen(ln.buf); continue; case ROFF_RERUN: goto rerun; case ROFF_IGN: pos = 0; continue; case ROFF_SO: if ( ! (curp->options & MPARSE_SO) && (i >= blk.sz || blk.buf[i] == '\0')) { curp->sodest = mandoc_strdup(ln.buf + of); free(ln.buf); return; } /* * We remove `so' clauses from our lookaside * buffer because we're going to descend into * the file recursively. */ if (curp->secondary) curp->secondary->sz -= pos + 1; save_file = curp->file; if ((fd = mparse_open(curp, ln.buf + of)) != -1) { mparse_readfd(curp, fd, ln.buf + of); close(fd); curp->file = save_file; } else { curp->file = save_file; mandoc_vmsg(MANDOCERR_SO_FAIL, curp, curp->line, pos, ".so %s", ln.buf + of); ln.sz = mandoc_asprintf(&cp, ".sp\nSee the file %s.\n.sp", ln.buf + of); free(ln.buf); ln.buf = cp; of = 0; mparse_buf_r(curp, ln, of, 0); } pos = 0; continue; default: break; } /* * If input parsers have not been allocated, do so now. * We keep these instanced between parsers, but set them * locally per parse routine since we can use different * parsers with each one. */ if (curp->man == NULL || curp->man->macroset == MACROSET_NONE) choose_parser(curp); /* * Lastly, push down into the parsers themselves. * If libroff returns ROFF_TBL, then add it to the * currently open parse. Since we only get here if * there does exist data (see tbl_data.c), we're * guaranteed that something's been allocated. * Do the same for ROFF_EQN. */ if (rr == ROFF_TBL) while ((span = roff_span(curp->roff)) != NULL) roff_addtbl(curp->man, span); else if (rr == ROFF_EQN) roff_addeqn(curp->man, roff_eqn(curp->roff)); else if ((curp->man->macroset == MACROSET_MDOC ? mdoc_parseln(curp->man, curp->line, ln.buf, of) : man_parseln(curp->man, curp->line, ln.buf, of)) == 2) break; /* Temporary buffers typically are not full. */ if (0 == start && '\0' == blk.buf[i]) break; /* Start the next input line. */ pos = 0; } free(ln.buf); }
/* * Main parse routine for an opened file. This is called for each * opened file and simply loops around the full input file, possibly * nesting (i.e., with `so'). */ static void mparse_buf_r(struct mparse *curp, struct buf blk, int start) { const struct tbl_span *span; struct buf ln; enum rofferr rr; int i, of, rc; int pos; /* byte number in the ln buffer */ int lnn; /* line number in the real file */ unsigned char c; memset(&ln, 0, sizeof(struct buf)); lnn = curp->line; pos = 0; for (i = 0; i < (int)blk.sz; ) { if (0 == pos && '\0' == blk.buf[i]) break; if (start) { curp->line = lnn; curp->reparse_count = 0; } while (i < (int)blk.sz && (start || '\0' != blk.buf[i])) { /* * When finding an unescaped newline character, * leave the character loop to process the line. * Skip a preceding carriage return, if any. */ if ('\r' == blk.buf[i] && i + 1 < (int)blk.sz && '\n' == blk.buf[i + 1]) ++i; if ('\n' == blk.buf[i]) { ++i; ++lnn; break; } /* * Make sure we have space for at least * one backslash and one other character * and the trailing NUL byte. */ if (pos + 2 >= (int)ln.sz) resize_buf(&ln, 256); /* * Warn about bogus characters. If you're using * non-ASCII encoding, you're screwing your * readers. Since I'd rather this not happen, * I'll be helpful and replace these characters * with "?", so we don't display gibberish. * Note to manual writers: use special characters. */ c = (unsigned char) blk.buf[i]; if ( ! (isascii(c) && (isgraph(c) || isblank(c)))) { mandoc_msg(MANDOCERR_BADCHAR, curp, curp->line, pos, NULL); i++; ln.buf[pos++] = '?'; continue; } /* Trailing backslash = a plain char. */ if ('\\' != blk.buf[i] || i + 1 == (int)blk.sz) { ln.buf[pos++] = blk.buf[i++]; continue; } /* * Found escape and at least one other character. * When it's a newline character, skip it. * When there is a carriage return in between, * skip that one as well. */ if ('\r' == blk.buf[i + 1] && i + 2 < (int)blk.sz && '\n' == blk.buf[i + 2]) ++i; if ('\n' == blk.buf[i + 1]) { i += 2; ++lnn; continue; } if ('"' == blk.buf[i + 1] || '#' == blk.buf[i + 1]) { i += 2; /* Comment, skip to end of line */ for (; i < (int)blk.sz; ++i) { if ('\n' == blk.buf[i]) { ++i; ++lnn; break; } } /* Backout trailing whitespaces */ for (; pos > 0; --pos) { if (ln.buf[pos - 1] != ' ') break; if (pos > 2 && ln.buf[pos - 2] == '\\') break; } break; } /* Catch escaped bogus characters. */ c = (unsigned char) blk.buf[i+1]; if ( ! (isascii(c) && (isgraph(c) || isblank(c)))) { mandoc_msg(MANDOCERR_BADCHAR, curp, curp->line, pos, NULL); i += 2; ln.buf[pos++] = '?'; continue; } /* Some other escape sequence, copy & cont. */ ln.buf[pos++] = blk.buf[i++]; ln.buf[pos++] = blk.buf[i++]; } if (pos >= (int)ln.sz) resize_buf(&ln, 256); ln.buf[pos] = '\0'; /* * A significant amount of complexity is contained by * the roff preprocessor. It's line-oriented but can be * expressed on one line, so we need at times to * readjust our starting point and re-run it. The roff * preprocessor can also readjust the buffers with new * data, so we pass them in wholesale. */ of = 0; /* * Maintain a lookaside buffer of all parsed lines. We * only do this if mparse_keep() has been invoked (the * buffer may be accessed with mparse_getkeep()). */ if (curp->secondary) { curp->secondary->buf = mandoc_realloc (curp->secondary->buf, curp->secondary->sz + pos + 2); memcpy(curp->secondary->buf + curp->secondary->sz, ln.buf, pos); curp->secondary->sz += pos; curp->secondary->buf [curp->secondary->sz] = '\n'; curp->secondary->sz++; curp->secondary->buf [curp->secondary->sz] = '\0'; } rerun: rr = roff_parseln (curp->roff, curp->line, &ln.buf, &ln.sz, of, &of); switch (rr) { case (ROFF_REPARSE): if (REPARSE_LIMIT >= ++curp->reparse_count) mparse_buf_r(curp, ln, 0); else mandoc_msg(MANDOCERR_ROFFLOOP, curp, curp->line, pos, NULL); pos = 0; continue; case (ROFF_APPEND): pos = (int)strlen(ln.buf); continue; case (ROFF_RERUN): goto rerun; case (ROFF_IGN): pos = 0; continue; case (ROFF_ERR): assert(MANDOCLEVEL_FATAL <= curp->file_status); break; case (ROFF_SO): /* * We remove `so' clauses from our lookaside * buffer because we're going to descend into * the file recursively. */ if (curp->secondary) curp->secondary->sz -= pos + 1; mparse_readfd(curp, -1, ln.buf + of); if (MANDOCLEVEL_FATAL <= curp->file_status) break; pos = 0; continue; default: break; } /* * If we encounter errors in the recursive parse, make * sure we don't continue parsing. */ if (MANDOCLEVEL_FATAL <= curp->file_status) break; /* * If input parsers have not been allocated, do so now. * We keep these instanced between parsers, but set them * locally per parse routine since we can use different * parsers with each one. */ if ( ! (curp->man || curp->mdoc)) pset(ln.buf + of, pos - of, curp); /* * Lastly, push down into the parsers themselves. One * of these will have already been set in the pset() * routine. * If libroff returns ROFF_TBL, then add it to the * currently open parse. Since we only get here if * there does exist data (see tbl_data.c), we're * guaranteed that something's been allocated. * Do the same for ROFF_EQN. */ rc = -1; if (ROFF_TBL == rr) while (NULL != (span = roff_span(curp->roff))) { rc = curp->man ? man_addspan(curp->man, span) : mdoc_addspan(curp->mdoc, span); if (0 == rc) break; } else if (ROFF_EQN == rr) rc = curp->mdoc ? mdoc_addeqn(curp->mdoc, roff_eqn(curp->roff)) : man_addeqn(curp->man, roff_eqn(curp->roff)); else if (curp->man || curp->mdoc) rc = curp->man ? man_parseln(curp->man, curp->line, ln.buf, of) : mdoc_parseln(curp->mdoc, curp->line, ln.buf, of); if (0 == rc) { assert(MANDOCLEVEL_FATAL <= curp->file_status); break; } /* Temporary buffers typically are not full. */ if (0 == start && '\0' == blk.buf[i]) break; /* Start the next input line. */ pos = 0; } free(ln.buf); }
static void format(const struct req *req, const char *file) { struct mparse *mp; struct mchars *mchars; struct mdoc *mdoc; struct man *man; void *vp; char *opts; enum mandoclevel rc; int fd; int usepath; if (-1 == (fd = open(file, O_RDONLY, 0))) { puts("<P>You specified an invalid manual file.</P>"); return; } mchars = mchars_alloc(); mp = mparse_alloc(MPARSE_SO, MANDOCLEVEL_FATAL, NULL, mchars, req->q.manpath); rc = mparse_readfd(mp, fd, file); close(fd); if (rc >= MANDOCLEVEL_FATAL) { fprintf(stderr, "fatal mandoc error: %s/%s\n", req->q.manpath, file); pg_error_internal(); return; } usepath = strcmp(req->q.manpath, req->p[0]); mandoc_asprintf(&opts, "fragment,man=%s?query=%%N&sec=%%S%s%s%s%s", scriptname, req->q.arch ? "&arch=" : "", req->q.arch ? req->q.arch : "", usepath ? "&manpath=" : "", usepath ? req->q.manpath : ""); mparse_result(mp, &mdoc, &man, NULL); if (NULL == man && NULL == mdoc) { fprintf(stderr, "fatal mandoc error: %s/%s\n", req->q.manpath, file); pg_error_internal(); mparse_free(mp); mchars_free(mchars); return; } vp = html_alloc(mchars, opts); if (NULL != mdoc) html_mdoc(vp, mdoc); else html_man(vp, man); html_free(vp); mparse_free(mp); mchars_free(mchars); free(opts); }
int main(int argc, char *argv[]) { struct mparse *mp; /* parse sequence */ struct mdoc *mdoc; /* resulting mdoc */ char *fn; const char *dir; /* result dir (default: cwd) */ char ibuf[MAXPATHLEN], /* index fname */ ibbuf[MAXPATHLEN], /* index backup fname */ fbuf[MAXPATHLEN], /* btree fname */ fbbuf[MAXPATHLEN]; /* btree backup fname */ int c; DB *index, /* index database */ *db; /* keyword database */ DBT rkey, rval, /* recno entries */ key, val; /* persistent keyword entries */ size_t ksz; /* entry buffer size */ char vbuf[8]; BTREEINFO info; /* btree configuration */ recno_t rec; extern int optind; extern char *optarg; progname = strrchr(argv[0], '/'); if (progname == NULL) progname = argv[0]; else ++progname; dir = ""; while (-1 != (c = getopt(argc, argv, "d:"))) switch (c) { case ('d'): dir = optarg; break; default: usage(); return((int)MANDOCLEVEL_BADARG); } argc -= optind; argv += optind; /* * Set up temporary file-names into which we're going to write * all of our data (both for the index and database). These * will be securely renamed to the real file-names after we've * written all of our data. */ ibuf[0] = ibuf[MAXPATHLEN - 2] = ibbuf[0] = ibbuf[MAXPATHLEN - 2] = fbuf[0] = fbuf[MAXPATHLEN - 2] = fbbuf[0] = fbbuf[MAXPATHLEN - 2] = '\0'; strlcat(fbuf, dir, MAXPATHLEN); strlcat(fbuf, MANDOC_DB, MAXPATHLEN); strlcat(fbbuf, fbuf, MAXPATHLEN); strlcat(fbbuf, "~", MAXPATHLEN); strlcat(ibuf, dir, MAXPATHLEN); strlcat(ibuf, MANDOC_IDX, MAXPATHLEN); strlcat(ibbuf, ibuf, MAXPATHLEN); strlcat(ibbuf, "~", MAXPATHLEN); if ('\0' != fbuf[MAXPATHLEN - 2] || '\0' != fbbuf[MAXPATHLEN - 2] || '\0' != ibuf[MAXPATHLEN - 2] || '\0' != ibbuf[MAXPATHLEN - 2]) { fprintf(stderr, "%s: Path too long\n", progname); exit((int)MANDOCLEVEL_SYSERR); } /* * For the keyword database, open a BTREE database that allows * duplicates. For the index database, use a standard RECNO * database type. */ memset(&info, 0, sizeof(BTREEINFO)); info.flags = R_DUP; db = dbopen(fbbuf, MANDOC_FLAGS, 0644, DB_BTREE, &info); if (NULL == db) { perror(fbbuf); exit((int)MANDOCLEVEL_SYSERR); } index = dbopen(ibbuf, MANDOC_FLAGS, 0644, DB_RECNO, NULL); if (NULL == db) { perror(ibbuf); (*db->close)(db); exit((int)MANDOCLEVEL_SYSERR); } /* * Try parsing the manuals given on the command line. If we * totally fail, then just keep on going. Take resulting trees * and push them down into the database code. * Use the auto-parser and don't report any errors. */ mp = mparse_alloc(MPARSE_AUTO, MANDOCLEVEL_FATAL, NULL, NULL); memset(&key, 0, sizeof(DBT)); memset(&val, 0, sizeof(DBT)); memset(&rkey, 0, sizeof(DBT)); memset(&rval, 0, sizeof(DBT)); val.size = sizeof(vbuf); val.data = vbuf; rkey.size = sizeof(recno_t); rec = 1; ksz = 0; while (NULL != (fn = *argv++)) { mparse_reset(mp); if (mparse_readfd(mp, -1, fn) >= MANDOCLEVEL_FATAL) { fprintf(stderr, "%s: Parse failure\n", fn); continue; } mparse_result(mp, &mdoc, NULL); if (NULL == mdoc) continue; rkey.data = &rec; rval.data = fn; rval.size = strlen(fn) + 1; if (-1 == (*index->put)(index, &rkey, &rval, 0)) { perror(ibbuf); break; } memset(val.data, 0, sizeof(uint32_t)); memcpy(val.data + 4, &rec, sizeof(uint32_t)); pmdoc(db, fbbuf, &key, &ksz, &val, fn, mdoc); rec++; } (*db->close)(db); (*index->close)(index); mparse_free(mp); free(key.data); /* Atomically replace the file with our temporary one. */ if (-1 == rename(fbbuf, fbuf)) perror(fbuf); if (-1 == rename(ibbuf, ibuf)) perror(fbuf); return((int)MANDOCLEVEL_OK); }