-/* $Id: cgi.c,v 1.24 2011/12/10 21:51:07 kristaps Exp $ */
+/* $Id: cgi.c,v 1.31 2011/12/14 13:36:59 kristaps Exp $ */
/*
* Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv>
*
const char *arch; /* architecture */
const char *sec; /* manual section */
const char *expr; /* unparsed expression string */
+ int manroot; /* manroot index (or -1)*/
int whatis; /* whether whatis mode */
int legacy; /* whether legacy mode */
};
static void html_print(const char *);
static void html_putchar(char);
static int http_decode(char *);
-static void http_parse(struct query *, char *);
+static void http_parse(struct req *, char *);
static int pathstop(DIR *);
static void pathgen(DIR *, char *, struct req *);
static void pg_index(const struct req *, char *);
static void resp_search(struct res *, size_t, void *);
static void resp_searchform(const struct req *);
-static const char *progname;
-static const char *cache;
-static const char *host;
+static const char *progname; /* cgi script name */
+static const char *cache; /* cache directory */
+static const char *css; /* css directory */
+static const char *host; /* hostname */
static const char * const pages[PAGE__MAX] = {
"index", /* PAGE_INDEX */
* uses only GET for simplicity.
*/
static void
-http_parse(struct query *q, char *p)
+http_parse(struct req *req, char *p)
{
- char *key, *val;
+ char *key, *val, *manroot;
size_t sz;
- int legacy;
+ int i, legacy;
- memset(q, 0, sizeof(struct query));
+ memset(&req->q, 0, sizeof(struct query));
- q->whatis = 1;
+ req->q.whatis = 1;
legacy = -1;
+ manroot = NULL;
while (p && '\0' != *p) {
while (' ' == *p)
break;
if (0 == strcmp(key, "expr"))
- q->expr = val;
+ req->q.expr = val;
else if (0 == strcmp(key, "query"))
- q->expr = val;
+ req->q.expr = val;
else if (0 == strcmp(key, "sec"))
- q->sec = val;
+ req->q.sec = val;
else if (0 == strcmp(key, "sektion"))
- q->sec = val;
+ req->q.sec = val;
else if (0 == strcmp(key, "arch"))
- q->arch = val;
+ req->q.arch = val;
+ else if (0 == strcmp(key, "manpath"))
+ manroot = val;
else if (0 == strcmp(key, "apropos"))
legacy = 0 == strcmp(val, "0");
else if (0 == strcmp(key, "op"))
- q->whatis = 0 == strcasecmp(val, "whatis");
+ req->q.whatis = 0 == strcasecmp(val, "whatis");
}
/* Test for old man.cgi compatibility mode. */
if (legacy == 0) {
- q->whatis = 0;
- q->legacy = 1;
+ req->q.whatis = 0;
+ req->q.legacy = 1;
} else if (legacy > 0) {
- q->legacy = 1;
- q->whatis = 1;
+ req->q.legacy = 1;
+ req->q.whatis = 1;
}
/*
* For some man.cgi scripts, "default" arch is none.
*/
- if (q->legacy && NULL != q->sec)
- if (0 == strcmp(q->sec, "0"))
- q->sec = NULL;
- if (q->legacy && NULL != q->arch)
- if (0 == strcmp(q->arch, "default"))
- q->arch = NULL;
+ if (req->q.legacy && NULL != req->q.sec)
+ if (0 == strcmp(req->q.sec, "0"))
+ req->q.sec = NULL;
+ if (req->q.legacy && NULL != req->q.arch)
+ if (0 == strcmp(req->q.arch, "default"))
+ req->q.arch = NULL;
+
+ /* Default to first manroot. */
+
+ if (NULL != manroot) {
+ for (i = 0; i < (int)req->psz; i++)
+ if (0 == strcmp(req->p[i].name, manroot))
+ break;
+ req->q.manroot = i < (int)req->psz ? i : -1;
+ }
}
/*
resp_begin_http(code, msg);
- puts("<!DOCTYPE HTML PUBLIC "
- " \"-//W3C//DTD HTML 4.01//EN\""
- " \"http://www.w3.org/TR/html4/strict.dtd\">\n"
- "<HTML>\n"
- "<HEAD>\n"
- "<META HTTP-EQUIV=\"Content-Type\""
- " CONTENT=\"text/html; charset=utf-8\">\n"
- "<LINK REL=\"stylesheet\" HREF=\"/man.cgi.css\""
- " TYPE=\"text/css\" media=\"all\">\n"
- "<TITLE>System Manpage Reference</TITLE>\n"
- "</HEAD>\n"
- "<BODY>\n"
- "<!-- Begin page content. //-->");
+ printf("<!DOCTYPE HTML PUBLIC "
+ " \"-//W3C//DTD HTML 4.01//EN\""
+ " \"http://www.w3.org/TR/html4/strict.dtd\">\n"
+ "<HTML>\n"
+ "<HEAD>\n"
+ "<META HTTP-EQUIV=\"Content-Type\""
+ " CONTENT=\"text/html; charset=utf-8\">\n"
+ "<LINK REL=\"stylesheet\" HREF=\"%s/man.cgi.css\""
+ " TYPE=\"text/css\" media=\"all\">\n"
+ "<TITLE>System Manpage Reference</TITLE>\n"
+ "</HEAD>\n"
+ "<BODY>\n"
+ "<!-- Begin page content. //-->\n", css);
}
static void
static void
resp_searchform(const struct req *req)
{
+ int i;
puts("<!-- Begin search form. //-->");
- printf("<FORM ACTION=\"");
- html_print(progname);
- printf("/search.html\" METHOD=\"get\">\n");
- printf("<FIELDSET>\n"
+ printf("<FORM ACTION=\"%s/search.html\" METHOD=\"get\">\n"
+ "<FIELDSET>\n"
"<LEGEND>Search Parameters</LEGEND>\n"
"<INPUT TYPE=\"submit\" NAME=\"op\""
" VALUE=\"Whatis\"> or \n"
"<INPUT TYPE=\"submit\" NAME=\"op\""
" VALUE=\"apropos\"> for manuals satisfying \n"
- "<INPUT TYPE=\"text\" NAME=\"expr\" VALUE=\"");
+ "<INPUT TYPE=\"text\" NAME=\"expr\" VALUE=\"",
+ progname);
html_print(req->q.expr ? req->q.expr : "");
printf("\">, section "
"<INPUT TYPE=\"text\""
"<INPUT TYPE=\"text\""
" SIZE=\"8\" NAME=\"arch\" VALUE=\"");
html_print(req->q.arch ? req->q.arch : "");
- puts("\">.\n"
+ printf("\">");
+ if (req->psz > 1) {
+ puts(", <SELECT NAME=\"manpath\">");
+ for (i = 0; i < (int)req->psz; i++) {
+ printf("<OPTION %s VALUE=\"",
+ (i == req->q.manroot) ||
+ (0 == i && -1 == req->q.manroot) ?
+ "SELECTED=\"selected\"" : "");
+ html_print(req->p[i].name);
+ printf("\">");
+ html_print(req->p[i].name);
+ puts("</OPTION>");
+ }
+ puts("</SELECT>");
+ }
+ puts(".\n"
"<INPUT TYPE=\"reset\" VALUE=\"Reset\">\n"
"</FIELDSET>\n"
"</FORM>");
int i;
const struct req *req;
+ req = (const struct req *)arg;
+ assert(req->q.manroot >= 0);
+
if (1 == sz) {
/*
* If we have just one result, then jump there now
* without any delay.
*/
puts("Status: 303 See Other");
- printf("Location: http://%s%s/show/%u/%u.html\n",
- host, progname,
+ printf("Location: http://%s%s/show/%d/%u/%u.html\n",
+ host, progname, req->q.manroot,
r[0].volume, r[0].rec);
puts("Content-Type: text/html; charset=utf-8\n");
return;
qsort(r, sz, sizeof(struct res), cmp);
resp_begin_html(200, NULL);
-
- req = (const struct req *)arg;
resp_searchform(req);
if (0 == sz) {
"No %s results found.\n",
req->q.whatis ? "whatis" : "apropos");
if (req->q.whatis) {
- printf("(Try <A HREF=\"");
- html_print(progname);
- printf("/search.html?op=apropos&expr=");
+ printf("(Try <A HREF=\"%s/search.html?"
+ "op=apropos&expr=", progname);
html_print(req->q.expr ? req->q.expr : "");
printf("&sec=");
html_print(req->q.sec ? req->q.sec : "");
for (i = 0; i < (int)sz; i++) {
printf("<TR>\n"
"<TD CLASS=\"title\">\n"
- "<A HREF=\"");
- html_print(progname);
- printf("/show/%u/%u.html\">", r[i].volume, r[i].rec);
+ "<A HREF=\"%s/show/%d/%u/%u.html\">",
+ progname, req->q.manroot,
+ r[i].volume, r[i].rec);
html_print(r[i].title);
putchar('(');
html_print(r[i].cat);
}
resp_begin_http(200, NULL);
- puts("<!DOCTYPE HTML PUBLIC "
- " \"-//W3C//DTD HTML 4.01//EN\""
- " \"http://www.w3.org/TR/html4/strict.dtd\">\n"
- "<HTML>\n"
- "<HEAD>\n"
- "<META HTTP-EQUIV=\"Content-Type\""
- " CONTENT=\"text/html; charset=utf-8\">\n"
- "<LINK REL=\"stylesheet\" HREF=\"/catman.css\""
- " TYPE=\"text/css\" media=\"all\">\n"
- "<TITLE>System Manpage Reference</TITLE>\n"
- "</HEAD>\n"
- "<BODY>\n"
- "<!-- Begin page content. //-->\n"
- "<PRE>");
+ printf("<!DOCTYPE HTML PUBLIC "
+ " \"-//W3C//DTD HTML 4.01//EN\""
+ " \"http://www.w3.org/TR/html4/strict.dtd\">\n"
+ "<HTML>\n"
+ "<HEAD>\n"
+ "<META HTTP-EQUIV=\"Content-Type\""
+ " CONTENT=\"text/html; charset=utf-8\">\n"
+ "<LINK REL=\"stylesheet\" HREF=\"%s/catman.css\""
+ " TYPE=\"text/css\" media=\"all\">\n"
+ "<TITLE>System Manpage Reference</TITLE>\n"
+ "</HEAD>\n"
+ "<BODY>\n"
+ "<!-- Begin page content. //-->\n"
+ "<PRE>\n", css);
while (NULL != (p = fgetln(f, &len))) {
bold = italic = 0;
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);
char file[MAXPATHLEN];
const char *fn, *cp;
int rc;
- unsigned int vol, rec;
+ unsigned int vol, rec, mr;
DB *idx;
DBT key, val;
idx = NULL;
- if (0 == req->psz || NULL == path) {
+ /* Parse out mroot, volume, and record from the path. */
+
+ if (NULL == path || NULL == (sub = strchr(path, '/'))) {
+ resp_error400();
+ return;
+ }
+ *sub++ = '\0';
+ if ( ! atou(path, &mr)) {
resp_error400();
return;
- } else if (NULL == (sub = strrchr(path, '/'))) {
+ }
+ path = sub;
+ if (NULL == (sub = strchr(path, '/'))) {
resp_error400();
return;
- } else
- *sub++ = '\0';
+ }
+ *sub++ = '\0';
+ if ( ! atou(path, &vol) || ! atou(sub, &rec)) {
+ resp_error400();
+ return;
+ } else if (mr >= (unsigned int)req->psz) {
+ resp_error400();
+ return;
+ }
/*
- * Begin by chdir()ing into the root of the manpath.
+ * 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[0].path)) {
- perror(req->p[0].path);
- resp_error400();
+ 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("etc/catman.conf", &ps);
+ manpath_manconf(&ps, "etc/catman.conf");
- if ( ! (atou(path, &vol) && atou(sub, &rec))) {
- resp_error400();
- goto out;
- } else if (vol >= (unsigned int)ps.sz) {
+ if (vol >= (unsigned int)ps.sz) {
resp_error400();
goto out;
}
struct opts opt;
struct expr *expr;
- if (0 == req->psz) {
+ if (req->q.manroot < 0 || 0 == req->psz) {
resp_search(NULL, 0, (void *)req);
return;
}
* relative to the manpath root.
*/
- if (-1 == (chdir(req->p[0].path))) {
- perror(req->p[0].path);
+ 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("etc/catman.conf", &ps);
+ manpath_manconf(&ps, "etc/catman.conf");
/*
* Poor man's tokenisation: just break apart by spaces.
/* 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 = "";
- host = getenv("HTTP_HOST");
- if (NULL == host)
+ if (NULL == (host = getenv("HTTP_HOST")))
host = "localhost";
/*
/* Next parse out the query string. */
if (NULL != (p = getenv("QUERY_STRING")))
- http_parse(&req.q, p);
+ http_parse(&req, p);
/*
* Now juggle paths to extract information.
if (rc > 0) {
/* This also strips the trailing slash. */
- path[(int)sz - 1] = '\0';
+ 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);
- /* And this strips out the leading "./". */
req->p[(int)req->psz].name =
- cp = mandoc_strdup(path + 2);
+ cp = mandoc_strdup(path + (1 == sz ? 1 : 2));
req->psz++;
/*
* The name is just the path with all the slashes taken