X-Git-Url: https://git.cameronkatri.com/mandoc.git/blobdiff_plain/a723f03473df54cccbce739fdda9b7d35da84ceb..992470d745901ca26e106a31bebfe62d6d0fccd9:/cgi.c?ds=sidebyside
diff --git a/cgi.c b/cgi.c
index 416197be..c203c671 100644
--- a/cgi.c
+++ b/cgi.c
@@ -1,4 +1,4 @@
-/* $Id: cgi.c,v 1.11 2011/12/07 11:52:36 kristaps Exp $ */
+/* $Id: cgi.c,v 1.31 2011/12/14 13:36:59 kristaps Exp $ */
/*
* Copyright (c) 2011 Kristaps Dzonsons \n"
- " The query your entered was malformed.\n"
- " Try again from the\n"
- " main page\n"
- " \n"
+ "The query your entered was malformed.\n"
+ "Try again from the\n"
+ "main page.\n"
+ " \n"
- " The page you're looking for, ");
- printf(" ");
+ "The page you're looking for, ");
+ printf("");
html_print(page);
- puts(",\n"
- " could not be found.\n"
- " Try searching from the\n"
- " main page\n"
- "Malformed Query
\n"
- "Malformed Query
\n"
+ "Page Not Found
\n"
"
No results found.
"); + qsort(r, sz, sizeof(struct res), cmp); + + resp_begin_html(200, NULL); + resp_searchform(req); + + if (0 == sz) { + printf("\n" + "No %s results found.\n", + req->q.whatis ? "whatis" : "apropos"); + if (req->q.whatis) { + printf("(Try q.expr ? req->q.expr : ""); + printf("&sec="); + html_print(req->q.sec ? req->q.sec : ""); + printf("&arch="); + html_print(req->q.arch ? req->q.arch : ""); + puts("\">apropos?)"); + } + puts("
"); + resp_end_html(); + return; + } + + puts("\n" + "\n", css); - puts(""); while (NULL != (p = fgetln(f, &len))) { bold = italic = 0; for (i = 0; i < (int)len - 1; i++) { @@ -628,10 +719,10 @@ format(const char *file) return; } - snprintf(opts, sizeof(opts), "style=/man.css," + snprintf(opts, sizeof(opts), "style=%s/man.css," "man=%s/search.html?sec=%%S&expr=%%N," /*"includes=/cgi-bin/man.cgi/usr/include/%%I"*/, - progname); + css, progname); mparse_result(mp, &mdoc, &man); vp = html_alloc(opts); @@ -650,42 +741,74 @@ format(const char *file) } static void -pg_show(const struct manpaths *ps, const struct req *req, char *path) +pg_show(const struct req *req, char *path) { + struct manpaths ps; char *sub; char file[MAXPATHLEN]; const char *fn, *cp; int rc; - unsigned int vol, rec; + unsigned int vol, rec, mr; DB *idx; DBT key, val; - if (NULL == path) { + idx = NULL; + + /* Parse out mroot, volume, and record from the path. */ + + if (NULL == path || NULL == (sub = strchr(path, '/'))) { resp_error400(); return; - } else if (NULL == (sub = strrchr(path, '/'))) { + } + *sub++ = '\0'; + if ( ! atou(path, &mr)) { resp_error400(); return; - } else - *sub++ = '\0'; - - if ( ! (atou(path, &vol) && atou(sub, &rec))) { + } + path = sub; + if (NULL == (sub = strchr(path, '/'))) { resp_error400(); return; - } else if (vol >= (unsigned int)ps->sz) { + } + *sub++ = '\0'; + if ( ! atou(path, &vol) || ! atou(sub, &rec)) { + resp_error400(); + return; + } else if (mr >= (unsigned int)req->psz) { resp_error400(); return; } - strlcpy(file, ps->paths[vol], MAXPATHLEN); + /* + * Begin by chdir()ing into the manroot. + * This way we can pick up the database files, which are + * relative to the manpath root. + */ + + if (-1 == chdir(req->p[(int)mr].path)) { + perror(req->p[(int)mr].path); + resp_baddb(); + return; + } + + memset(&ps, 0, sizeof(struct manpaths)); + manpath_manconf(&ps, "etc/catman.conf"); + + if (vol >= (unsigned int)ps.sz) { + resp_error400(); + goto out; + } + + strlcpy(file, ps.paths[vol], MAXPATHLEN); strlcat(file, "/mandoc.index", MAXPATHLEN); /* Open the index recno(3) database. */ idx = dbopen(file, O_RDONLY, 0, DB_RECNO, NULL); if (NULL == idx) { + perror(file); resp_baddb(); - return; + goto out; } key.data = &rec; @@ -705,46 +828,60 @@ pg_show(const struct manpaths *ps, const struct req *req, char *path) else if (NULL == memchr(fn, '\0', val.size - (fn - cp))) resp_baddb(); else { - strlcpy(file, ps->paths[vol], MAXPATHLEN); - strlcat(file, "/", MAXPATHLEN); - strlcat(file, fn, MAXPATHLEN); if (0 == strcmp(cp, "cat")) - catman(file); + catman(fn + 1); else - format(file); + format(fn + 1); } out: - (*idx->close)(idx); + if (idx) + (*idx->close)(idx); + manpath_free(&ps); } static void -pg_search(const struct manpaths *ps, const struct req *req, char *path) +pg_search(const struct req *req, char *path) { size_t tt; + struct manpaths ps; int i, sz, rc; const char *ep, *start; char **cp; struct opts opt; struct expr *expr; - expr = NULL; - cp = NULL; - ep = NULL; - sz = 0; + if (req->q.manroot < 0 || 0 == req->psz) { + resp_search(NULL, 0, (void *)req); + return; + } memset(&opt, 0, sizeof(struct opts)); - for (sz = i = 0; i < (int)req->fieldsz; i++) - if (0 == strcmp(req->fields[i].key, "expr")) - ep = req->fields[i].val; - else if (0 == strcmp(req->fields[i].key, "sec")) - opt.cat = req->fields[i].val; - else if (0 == strcmp(req->fields[i].key, "arch")) - opt.arch = req->fields[i].val; + ep = req->q.expr; + opt.arch = req->q.arch; + opt.cat = req->q.sec; + rc = -1; + sz = 0; + cp = 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. + * Poor man's tokenisation: just break apart by spaces. * Yes, this is half-ass. But it works for now. */ @@ -763,16 +900,17 @@ pg_search(const struct manpaths *ps, const struct req *req, char *path) ep++; } - rc = -1; - /* * Pump down into apropos backend. * The resp_search() function is called with the results. */ - if (NULL != (expr = exprcomp(sz, cp, &tt))) + expr = req->q.whatis ? + termcomp(sz, cp, &tt) : exprcomp(sz, cp, &tt); + + if (NULL != expr) rc = apropos_search - (ps->sz, ps->paths, &opt, + (ps.sz, ps.paths, &opt, expr, tt, (void *)req, resp_search); /* ...unless errors occured. */ @@ -787,41 +925,65 @@ pg_search(const struct manpaths *ps, const struct req *req, char *path) free(cp); exprfree(expr); + manpath_free(&ps); } int main(void) { int i; + char buf[MAXPATHLEN]; + DIR *cwd; struct req req; char *p, *path, *subpath; - struct manpaths paths; - /* HTTP init: read and parse the query string. */ + /* Scan our run-time environment. */ - progname = getenv("SCRIPT_NAME"); - if (NULL == progname) + if (NULL == (cache = getenv("CACHE_DIR"))) + cache = "/cache/man.cgi"; + + if (NULL == (progname = getenv("SCRIPT_NAME"))) progname = ""; - cache = getenv("CACHE_DIR"); - if (NULL == cache) - cache = "/cache/man.cgi"; + if (NULL == (css = getenv("CSS_DIR"))) + css = ""; + + if (NULL == (host = getenv("HTTP_HOST"))) + host = "localhost"; + + /* + * First we change directory into the cache directory so that + * subsequent scanning for manpath directories is rooted + * relative to the same position. + */ if (-1 == chdir(cache)) { + perror(cache); resp_bad(); return(EXIT_FAILURE); - } - - host = getenv("HTTP_HOST"); - if (NULL == host) - host = "localhost"; + } else if (NULL == (cwd = opendir(cache))) { + perror(cache); + resp_bad(); + return(EXIT_FAILURE); + } memset(&req, 0, sizeof(struct req)); + strlcpy(buf, ".", MAXPATHLEN); + pathgen(cwd, buf, &req); + closedir(cwd); + + /* Next parse out the query string. */ + if (NULL != (p = getenv("QUERY_STRING"))) - kval_parse(&req.fields, &req.fieldsz, p); + http_parse(&req, p); - /* Resolve leading subpath component. */ + /* + * Now juggle paths to extract information. + * We want to extract our filetype (the file suffix), the + * initial path component, then the trailing component(s). + * Start with leading subpath component. + */ subpath = path = NULL; req.page = PAGE__MAX; @@ -852,30 +1014,149 @@ main(void) break; } - /* Initialise MANPATH. */ - - memset(&paths, 0, sizeof(struct manpaths)); - manpath_manconf("etc/catman.conf", &paths); - /* Route pages. */ switch (req.page) { case (PAGE_INDEX): - pg_index(&paths, &req, subpath); + pg_index(&req, subpath); break; case (PAGE_SEARCH): - pg_search(&paths, &req, subpath); + pg_search(&req, subpath); break; case (PAGE_SHOW): - pg_show(&paths, &req, subpath); + pg_show(&req, subpath); break; default: resp_error404(path); break; } - manpath_free(&paths); - kval_free(req.fields, req.fieldsz); + for (i = 0; i < (int)req.psz; i++) { + free(req.p[i].path); + free(req.p[i].name); + } + free(req.p); return(EXIT_SUCCESS); } + +static int +cmp(const void *p1, const void *p2) +{ + + return(strcasecmp(((const struct res *)p1)->title, + ((const struct res *)p2)->title)); +} + +/* + * Check to see if an "etc" path consists of a catman.conf file. If it + * does, that means that the path contains a tree created by catman(8) + * and should be used for indexing. + */ +static int +pathstop(DIR *dir) +{ + struct dirent *d; + + while (NULL != (d = readdir(dir))) + if (DT_REG == d->d_type) + if (0 == strcmp(d->d_name, "catman.conf")) + return(1); + + return(0); +} + +/* + * Scan for indexable paths. + * This adds all paths with "etc/catman.conf" to the buffer. + */ +static void +pathgen(DIR *dir, char *path, struct req *req) +{ + struct dirent *d; + char *cp; + DIR *cd; + int rc; + size_t sz, ssz; + + sz = strlcat(path, "/", MAXPATHLEN); + if (sz >= MAXPATHLEN) { + fprintf(stderr, "%s: Path too long", path); + return; + } + + /* + * First, scan for the "etc" directory. + * If it's found, then see if it should cause us to stop. This + * happens when a catman.conf is found in the directory. + */ + + rc = 0; + while (0 == rc && NULL != (d = readdir(dir))) { + if (DT_DIR != d->d_type || strcmp(d->d_name, "etc")) + continue; + + path[(int)sz] = '\0'; + ssz = strlcat(path, d->d_name, MAXPATHLEN); + + if (ssz >= MAXPATHLEN) { + fprintf(stderr, "%s: Path too long", path); + return; + } else if (NULL == (cd = opendir(path))) { + perror(path); + return; + } + + rc = pathstop(cd); + closedir(cd); + } + + if (rc > 0) { + /* This also strips the trailing slash. */ + path[(int)--sz] = '\0'; + req->p = mandoc_realloc + (req->p, + (req->psz + 1) * sizeof(struct paths)); + /* + * Strip out the leading "./" unless we're just a ".", + * in which case use an empty string as our name. + */ + req->p[(int)req->psz].path = mandoc_strdup(path); + req->p[(int)req->psz].name = + cp = mandoc_strdup(path + (1 == sz ? 1 : 2)); + req->psz++; + /* + * The name is just the path with all the slashes taken + * out of it. Simple but effective. + */ + for ( ; '\0' != *cp; cp++) + if ('/' == *cp) + *cp = ' '; + return; + } + + /* + * If no etc/catman.conf was found, recursively enter child + * directory and continue scanning. + */ + + rewinddir(dir); + while (NULL != (d = readdir(dir))) { + if (DT_DIR != d->d_type || '.' == d->d_name[0]) + continue; + + path[(int)sz] = '\0'; + ssz = strlcat(path, d->d_name, MAXPATHLEN); + + if (ssz >= MAXPATHLEN) { + fprintf(stderr, "%s: Path too long", path); + return; + } else if (NULL == (cd = opendir(path))) { + perror(path); + return; + } + + pathgen(cd, path, req); + closedir(cd); + } +}