X-Git-Url: https://git.cameronkatri.com/mandoc.git/blobdiff_plain/ea48e55b5108c43c1f8c8c5e6e85758eb4ed5ae0..db42cb7f0b926817aff8c41cac4ad993a3db85aa:/cgi.c
diff --git a/cgi.c b/cgi.c
index e20ad201..6d0e3293 100644
--- a/cgi.c
+++ b/cgi.c
@@ -1,102 +1,141 @@
-/* $Id: cgi.c,v 1.60 2014/07/09 17:03:07 schwarze Exp $ */
+/* $Id: cgi.c,v 1.172 2020/04/03 11:35:01 schwarze Exp $ */
/*
+ * Copyright (c) 2014-2019 Ingo Schwarze
* Copyright (c) 2011, 2012 Kristaps Dzonsons
- * Copyright (c) 2014 Ingo Schwarze
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Implementation of the man.cgi(8) program.
*/
-#ifdef HAVE_CONFIG_H
#include "config.h"
-#endif
+
+#include
+#include
#include
+#if HAVE_ERR
+#include
+#endif
#include
#include
#include
+#include
#include
#include
#include
#include
-#include "mandoc.h"
#include "mandoc_aux.h"
+#include "mandoc.h"
+#include "roff.h"
+#include "mdoc.h"
+#include "man.h"
+#include "mandoc_parse.h"
#include "main.h"
-#include "manpath.h"
+#include "manconf.h"
#include "mansearch.h"
-
-enum page {
- PAGE_INDEX,
- PAGE_SEARCH,
- PAGE_SHOW,
- PAGE__MAX
-};
+#include "cgi.h"
/*
* A query as passed to the search function.
*/
struct query {
- const char *manpath; /* desired manual directory */
- const char *arch; /* architecture */
- const char *sec; /* manual section */
- const char *expr; /* unparsed expression string */
- int legacy; /* whether legacy mode */
+ char *manpath; /* desired manual directory */
+ char *arch; /* architecture */
+ char *sec; /* manual section */
+ char *query; /* unparsed query expression */
+ int equal; /* match whole names, not substrings */
};
struct req {
struct query q;
char **p; /* array of available manpaths */
size_t psz; /* number of available manpaths */
- enum page page;
+ int isquery; /* QUERY_STRING used, not PATH_INFO */
+};
+
+enum focus {
+ FOCUS_NONE = 0,
+ FOCUS_QUERY
};
-static void catman(const struct req *, const char *);
-static int cmp(const void *, const void *);
-static void format(const struct req *, const char *);
static void html_print(const char *);
-static void html_printquery(const struct req *);
static void html_putchar(char);
-static int http_decode(char *);
-static void http_parse(struct req *, char *);
-static void http_print(const char *);
-static void http_putchar(char);
-static void http_printquery(const struct req *);
-static void pathgen(struct req *);
-static void pg_index(const struct req *, char *);
-static void pg_search(const struct req *, char *);
-static void pg_show(const struct req *, char *);
-static void resp_begin_html(int, const char *);
-static void resp_begin_http(int, const char *);
-static void resp_end_html(void);
-static void resp_error_badrequest(const char *);
-static void resp_error_internal(void);
-static void resp_error_notfound(const char *);
-static void resp_index(const struct req *);
-static void resp_noresult(const struct req *,
+static int http_decode(char *);
+static void http_encode(const char *);
+static void parse_manpath_conf(struct req *);
+static void parse_path_info(struct req *, const char *);
+static void parse_query_string(struct req *, const char *);
+static void pg_error_badrequest(const char *);
+static void pg_error_internal(void);
+static void pg_index(const struct req *);
+static void pg_noresult(const struct req *, int, const char *,
const char *);
-static void resp_search(const struct req *,
+static void pg_redirect(const struct req *, const char *);
+static void pg_search(const struct req *);
+static void pg_searchres(const struct req *,
struct manpage *, size_t);
-static void resp_searchform(const struct req *);
-
-static const char *scriptname; /* CGI script name */
-static const char *mandir; /* contains all manpath directories */
-static const char *cssdir; /* css directory */
-static const char *httphost; /* hostname used in the URIs */
-
-static const char * const pages[PAGE__MAX] = {
- "index", /* PAGE_INDEX */
- "search", /* PAGE_SEARCH */
- "show", /* PAGE_SHOW */
+static void pg_show(struct req *, const char *);
+static void resp_begin_html(int, const char *, const char *);
+static void resp_begin_http(int, const char *);
+static void resp_catman(const struct req *, const char *);
+static void resp_copy(const char *);
+static void resp_end_html(void);
+static void resp_format(const struct req *, const char *);
+static void resp_searchform(const struct req *, enum focus);
+static void resp_show(const struct req *, const char *);
+static void set_query_attr(char **, char **);
+static int validate_arch(const char *);
+static int validate_filename(const char *);
+static int validate_manpath(const struct req *, const char *);
+static int validate_urifrag(const char *);
+
+static const char *scriptname = SCRIPT_NAME;
+
+static const int sec_prios[] = {1, 4, 5, 8, 6, 3, 7, 2, 9};
+static const char *const sec_numbers[] = {
+ "0", "1", "2", "3", "3p", "4", "5", "6", "7", "8", "9"
};
+static const char *const sec_names[] = {
+ "All Sections",
+ "1 - General Commands",
+ "2 - System Calls",
+ "3 - Library Functions",
+ "3p - Perl Library",
+ "4 - Device Drivers",
+ "5 - File Formats",
+ "6 - Games",
+ "7 - Miscellaneous Information",
+ "8 - System Manager\'s Manual",
+ "9 - Kernel Developer\'s Manual"
+};
+static const int sec_MAX = sizeof(sec_names) / sizeof(char *);
+
+static const char *const arch_names[] = {
+ "amd64", "alpha", "armv7", "arm64",
+ "hppa", "i386", "landisk",
+ "loongson", "luna88k", "macppc", "mips64",
+ "octeon", "sgi", "socppc", "sparc64",
+ "amiga", "arc", "armish", "arm32",
+ "atari", "aviion", "beagle", "cats",
+ "hppa64", "hp300",
+ "ia64", "mac68k", "mvme68k", "mvme88k",
+ "mvmeppc", "palm", "pc532", "pegasos",
+ "pmax", "powerpc", "solbourne", "sparc",
+ "sun3", "vax", "wgrisc", "x68k",
+ "zaurus"
+};
+static const int arch_MAX = sizeof(arch_names) / sizeof(char *);
/*
* Print a character, escaping HTML along the way.
@@ -107,16 +146,16 @@ html_putchar(char c)
{
switch (c) {
- case ('"'):
- printf(""e;");
+ case '"':
+ printf(""");
break;
- case ('&'):
+ case '&':
printf("&");
break;
- case ('>'):
+ case '>':
printf(">");
break;
- case ('<'):
+ case '<':
printf("<");
break;
default:
@@ -125,154 +164,125 @@ html_putchar(char c)
}
}
+/*
+ * Call through to html_putchar().
+ * Accepts NULL strings.
+ */
static void
-http_printquery(const struct req *req)
-{
-
- if (NULL != req->q.manpath) {
- printf("&manpath=");
- http_print(req->q.manpath);
- }
- if (NULL != req->q.sec) {
- printf("&sec=");
- http_print(req->q.sec);
- }
- if (NULL != req->q.arch) {
- printf("&arch=");
- http_print(req->q.arch);
- }
- if (NULL != req->q.expr) {
- printf("&expr=");
- http_print(req->q.expr ? req->q.expr : "");
- }
-}
-
-static void
-html_printquery(const struct req *req)
-{
-
- if (NULL != req->q.manpath) {
- printf("&manpath=");
- html_print(req->q.manpath);
- }
- if (NULL != req->q.sec) {
- printf("&sec=");
- html_print(req->q.sec);
- }
- if (NULL != req->q.arch) {
- printf("&arch=");
- html_print(req->q.arch);
- }
- if (NULL != req->q.expr) {
- printf("&expr=");
- html_print(req->q.expr ? req->q.expr : "");
- }
-}
-
-static void
-http_print(const char *p)
+html_print(const char *p)
{
if (NULL == p)
return;
while ('\0' != *p)
- http_putchar(*p++);
+ html_putchar(*p++);
}
/*
- * Call through to html_putchar().
- * Accepts NULL strings.
+ * Transfer the responsibility for the allocated string *val
+ * to the query structure.
*/
static void
-html_print(const char *p)
+set_query_attr(char **attr, char **val)
{
-
- if (NULL == p)
- return;
- while ('\0' != *p)
- html_putchar(*p++);
+
+ free(*attr);
+ if (**val == '\0') {
+ *attr = NULL;
+ free(*val);
+ } else
+ *attr = *val;
+ *val = NULL;
}
/*
- * Parse out key-value pairs from an HTTP request variable.
- * This can be either a cookie or a POST/GET string, although man.cgi
- * uses only GET for simplicity.
+ * Parse the QUERY_STRING for key-value pairs
+ * and store the values into the query structure.
*/
static void
-http_parse(struct req *req, char *p)
+parse_query_string(struct req *req, const char *qs)
{
- char *key, *val;
- int legacy;
+ char *key, *val;
+ size_t keysz, valsz;
- memset(&req->q, 0, sizeof(struct query));
- req->q.manpath = req->p[0];
+ req->isquery = 1;
+ req->q.manpath = NULL;
+ req->q.arch = NULL;
+ req->q.sec = NULL;
+ req->q.query = NULL;
+ req->q.equal = 1;
- legacy = -1;
- while ('\0' != *p) {
- key = p;
- val = NULL;
+ key = val = NULL;
+ while (*qs != '\0') {
- p += (int)strcspn(p, ";&");
- if ('\0' != *p)
- *p++ = '\0';
- if (NULL != (val = strchr(key, '=')))
- *val++ = '\0';
+ /* Parse one key. */
- if ('\0' == *key || NULL == val || '\0' == *val)
- continue;
+ keysz = strcspn(qs, "=;&");
+ key = mandoc_strndup(qs, keysz);
+ qs += keysz;
+ if (*qs != '=')
+ goto next;
- /* Just abort handling. */
+ /* Parse one value. */
- if ( ! http_decode(key))
- break;
- if (NULL != val && ! http_decode(val))
- break;
+ valsz = strcspn(++qs, ";&");
+ val = mandoc_strndup(qs, valsz);
+ qs += valsz;
- if (0 == strcmp(key, "expr"))
- req->q.expr = val;
- else if (0 == strcmp(key, "query"))
- req->q.expr = val;
- else if (0 == strcmp(key, "sec"))
- req->q.sec = val;
- else if (0 == strcmp(key, "sektion"))
- req->q.sec = val;
- else if (0 == strcmp(key, "arch"))
- req->q.arch = val;
- else if (0 == strcmp(key, "manpath"))
- req->q.manpath = val;
- else if (0 == strcmp(key, "apropos"))
- legacy = 0 == strcmp(val, "0");
- }
-
- /* Test for old man.cgi compatibility mode. */
-
- req->q.legacy = legacy > 0;
-
- /*
- * Section "0" means no section when in legacy mode.
- * For some man.cgi scripts, "default" arch is none.
- */
+ /* Decode and catch encoding errors. */
- 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;
-}
+ if ( ! (http_decode(key) && http_decode(val)))
+ goto next;
-static void
-http_putchar(char c)
-{
+ /* Handle key-value pairs. */
- if (isalnum((unsigned char)c)) {
- putchar((unsigned char)c);
- return;
- } else if (' ' == c) {
- putchar('+');
- return;
+ 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++;
}
- printf("%%%.2x", c);
}
/*
@@ -284,29 +294,43 @@ static int
http_decode(char *p)
{
char hex[3];
+ char *q;
int c;
hex[2] = '\0';
- for ( ; '\0' != *p; p++) {
+ q = p;
+ for ( ; '\0' != *p; p++, q++) {
if ('%' == *p) {
if ('\0' == (hex[0] = *(p + 1)))
- return(0);
+ return 0;
if ('\0' == (hex[1] = *(p + 2)))
- return(0);
+ return 0;
if (1 != sscanf(hex, "%x", &c))
- return(0);
+ return 0;
if ('\0' == c)
- return(0);
+ return 0;
- *p = (char)c;
- memmove(p + 1, p + 3, strlen(p + 3) + 1);
+ *q = (char)c;
+ p += 2;
} else
- *p = '+' == *p ? ' ' : *p;
+ *q = '+' == *p ? ' ' : *p;
}
- *p = '\0';
- return(1);
+ *q = '\0';
+ return 1;
+}
+
+static void
+http_encode(const char *p)
+{
+ for (; *p != '\0'; p++) {
+ if (isalnum((unsigned char)*p) == 0 &&
+ strchr("-._~", *p) == NULL)
+ printf("%%%2.2X", (unsigned char)*p);
+ else
+ putchar(*p);
+ }
}
static void
@@ -314,242 +338,407 @@ resp_begin_http(int code, const char *msg)
{
if (200 != code)
- printf("Status: %d %s\n", code, msg);
+ printf("Status: %d %s\r\n", code, msg);
- puts("Content-Type: text/html; charset=utf-8\n"
- "Cache-Control: no-cache\n"
- "Pragma: no-cache\n"
- "");
+ printf("Content-Type: text/html; charset=utf-8\r\n"
+ "Cache-Control: no-cache\r\n"
+ "Content-Security-Policy: default-src 'none'; "
+ "style-src 'self' 'unsafe-inline'\r\n"
+ "Pragma: no-cache\r\n"
+ "\r\n");
fflush(stdout);
}
static void
-resp_begin_html(int code, const char *msg)
+resp_copy(const char *filename)
+{
+ char buf[4096];
+ ssize_t sz;
+ int fd;
+
+ if ((fd = open(filename, O_RDONLY)) != -1) {
+ fflush(stdout);
+ while ((sz = read(fd, buf, sizeof(buf))) > 0)
+ write(STDOUT_FILENO, buf, sz);
+ close(fd);
+ }
+}
+
+static void
+resp_begin_html(int code, const char *msg, const char *file)
{
+ char *cp;
resp_begin_http(code, msg);
- printf("\n"
- "\n"
- "\n"
- "\n"
- "\n"
- "\n"
- "System Manpage Reference\n"
- "\n"
- "\n"
- "\n",
- cssdir, cssdir);
+ printf("\n"
+ "\n"
+ "\n"
+ " \n"
+ " \n"
+ " \n"
+ " ",
+ CSS_DIR);
+ if (file != NULL) {
+ if ((cp = strrchr(file, '/')) != NULL)
+ file = cp + 1;
+ if ((cp = strrchr(file, '.')) != NULL) {
+ printf("%.*s(%s) - ", (int)(cp - file), file, cp + 1);
+ } else
+ printf("%s - ", file);
+ }
+ printf("%s\n"
+ "\n"
+ "\n",
+ CUSTOMIZE_TITLE);
+
+ resp_copy(MAN_DIR "/header.html");
}
static void
resp_end_html(void)
{
- puts("\n"
- "");
+ resp_copy(MAN_DIR "/footer.html");
+
+ puts("\n"
+ "");
}
static void
-resp_searchform(const struct req *req)
+resp_searchform(const struct req *req, enum focus focus)
{
int i;
- puts("");
- printf("\n"
- "\n"
- "
");
- puts("");
+ puts(" ");
+ }
+
+ puts(" \n"
+ "");
+}
+
+static int
+validate_urifrag(const char *frag)
+{
+
+ while ('\0' != *frag) {
+ if ( ! (isalnum((unsigned char)*frag) ||
+ '-' == *frag || '.' == *frag ||
+ '/' == *frag || '_' == *frag))
+ return 0;
+ frag++;
+ }
+ return 1;
+}
+
+static int
+validate_manpath(const struct req *req, const char* manpath)
+{
+ size_t i;
+
+ for (i = 0; i < req->psz; i++)
+ if ( ! strcmp(manpath, req->p[i]))
+ return 1;
+
+ return 0;
+}
+
+static int
+validate_arch(const char *arch)
+{
+ int i;
+
+ for (i = 0; i < arch_MAX; i++)
+ if (strcmp(arch, arch_names[i]) == 0)
+ return 1;
+
+ return 0;
+}
+
+static int
+validate_filename(const char *file)
+{
+
+ if ('.' == file[0] && '/' == file[1])
+ file += 2;
+
+ return ! (strstr(file, "../") || strstr(file, "/..") ||
+ (strncmp(file, "man", 3) && strncmp(file, "cat", 3)));
}
static void
-resp_index(const struct req *req)
+pg_index(const struct req *req)
{
- resp_begin_html(200, NULL);
- puts("\n"
- "Online manuals with "
- "mandoc\n"
- "
");
- resp_searchform(req);
- puts("\n"
- "The "
- "apropos manual explains the query syntax.\n"
- "
");
+ resp_begin_html(200, NULL, NULL);
+ resp_searchform(req, FOCUS_QUERY);
+ printf("\n"
+ "This web interface is documented in the\n"
+ "man.cgi(8)\n"
+ "manual, and the\n"
+ "apropos(1)\n"
+ "manual explains the query syntax.\n"
+ "
\n",
+ scriptname, *scriptname == '\0' ? "" : "/",
+ scriptname, *scriptname == '\0' ? "" : "/");
resp_end_html();
}
static void
-resp_noresult(const struct req *req, const char *msg)
+pg_noresult(const struct req *req, int code, const char *http_msg,
+ const char *user_msg)
{
- resp_begin_html(200, NULL);
- resp_searchform(req);
- puts("");
- puts(msg);
- puts("
");
+ resp_begin_html(code, http_msg, NULL);
+ resp_searchform(req, FOCUS_QUERY);
+ puts("");
+ puts(user_msg);
+ puts("
");
resp_end_html();
}
static void
-resp_error_badrequest(const char *msg)
+pg_error_badrequest(const char *msg)
{
- resp_begin_html(400, "Bad Request");
- puts("Bad Request
\n"
- "\n");
+ resp_begin_html(400, "Bad Request", NULL);
+ puts("
Bad Request
\n"
+ "\n");
puts(msg);
printf("Try again from the\n"
- "main page.\n"
- "
", scriptname);
+ "main page.\n"
+ "
", scriptname);
resp_end_html();
}
static void
-resp_error_notfound(const char *page)
+pg_error_internal(void)
{
-
- resp_begin_html(404, "Not Found");
- puts("Page Not Found
\n"
- "\n"
- "The page you're looking for, ");
- printf("");
- html_print(page);
- printf(",\n"
- "could not be found.\n"
- "Try searching from the\n"
- "main page.\n"
- "
", scriptname);
+ resp_begin_html(500, "Internal Server Error", NULL);
+ puts("Internal Server Error
");
resp_end_html();
}
static void
-resp_error_internal(void)
+pg_redirect(const struct req *req, const char *name)
{
- resp_begin_html(500, "Internal Server Error");
- puts("Internal Server Error
");
- resp_end_html();
+ printf("Status: 303 See Other\r\n"
+ "Location: /");
+ if (*scriptname != '\0')
+ printf("%s/", scriptname);
+ if (strcmp(req->q.manpath, req->p[0]))
+ printf("%s/", req->q.manpath);
+ if (req->q.arch != NULL)
+ printf("%s/", req->q.arch);
+ http_encode(name);
+ if (req->q.sec != NULL) {
+ putchar('.');
+ http_encode(req->q.sec);
+ }
+ printf("\r\nContent-Type: text/html; charset=utf-8\r\n\r\n");
}
static void
-resp_search(const struct req *req, struct manpage *r, size_t sz)
+pg_searchres(const struct req *req, struct manpage *r, size_t sz)
{
- size_t i;
+ char *arch, *archend;
+ const char *sec;
+ size_t i, iuse;
+ int archprio, archpriouse;
+ int prio, priouse;
- if (1 == sz) {
+ for (i = 0; i < sz; i++) {
+ if (validate_filename(r[i].file))
+ continue;
+ warnx("invalid filename %s in %s database",
+ r[i].file, req->q.manpath);
+ pg_error_internal();
+ return;
+ }
+
+ if (req->isquery && sz == 1) {
/*
* If we have just one result, then jump there now
* without any delay.
*/
- puts("Status: 303 See Other");
- printf("Location: http://%s%s/show/%s/%s?",
- httphost, scriptname, req->q.manpath, r[0].file);
- http_printquery(req);
- puts("\n"
- "Content-Type: text/html; charset=utf-8\n");
+ printf("Status: 303 See Other\r\n"
+ "Location: /");
+ if (*scriptname != '\0')
+ printf("%s/", scriptname);
+ if (strcmp(req->q.manpath, req->p[0]))
+ printf("%s/", req->q.manpath);
+ printf("%s\r\n"
+ "Content-Type: text/html; charset=utf-8\r\n\r\n",
+ r[0].file);
return;
}
- qsort(r, sz, sizeof(struct manpage), cmp);
-
- resp_begin_html(200, NULL);
- resp_searchform(req);
- puts("");
- resp_end_html();
-}
+ iuse = 0;
+ if (req->q.equal || sz == 1) {
+ priouse = 20;
+ archpriouse = 3;
+ for (i = 0; i < sz; i++) {
+ sec = r[i].file;
+ sec += strcspn(sec, "123456789");
+ if (sec[0] == '\0')
+ continue;
+ prio = sec_prios[sec[0] - '1'];
+ if (sec[1] != '/')
+ prio += 10;
+ if (req->q.arch == NULL) {
+ archprio =
+ ((arch = strchr(sec + 1, '/'))
+ == NULL) ? 3 :
+ ((archend = strchr(arch + 1, '/'))
+ == NULL) ? 0 :
+ strncmp(arch, "amd64/",
+ archend - arch) ? 2 : 1;
+ if (archprio < archpriouse) {
+ archpriouse = archprio;
+ priouse = prio;
+ iuse = i;
+ continue;
+ }
+ if (archprio > archpriouse)
+ continue;
+ }
+ if (prio >= priouse)
+ continue;
+ priouse = prio;
+ iuse = i;
+ }
+ resp_begin_html(200, NULL, r[iuse].file);
+ } else
+ resp_begin_html(200, NULL, NULL);
+
+ resp_searchform(req,
+ req->q.equal || sz == 1 ? FOCUS_NONE : FOCUS_QUERY);
+
+ if (sz > 1) {
+ puts("");
+ }
-/* ARGSUSED */
-static void
-pg_index(const struct req *req, char *path)
-{
+ if (req->q.equal || sz == 1) {
+ puts("
");
+ resp_show(req, r[iuse].file);
+ }
- resp_index(req);
+ resp_end_html();
}
static void
-catman(const struct req *req, const char *file)
+resp_catman(const struct req *req, const char *file)
{
FILE *f;
- size_t len;
- int i;
char *p;
+ size_t sz;
+ ssize_t len;
+ int i;
int italic, bold;
- if (NULL == (f = fopen(file, "r"))) {
- resp_error_badrequest(
- "You specified an invalid manual file.");
+ if ((f = fopen(file, "r")) == NULL) {
+ puts("You specified an invalid manual file.
");
return;
}
- resp_begin_html(200, NULL);
- resp_searchform(req);
- puts("\n"
- "
");
+ puts("\n"
+ "
");
- while (NULL != (p = fgetln(f, &len))) {
+ p = NULL;
+ sz = 0;
+
+ while ((len = getline(&p, &sz, f)) != -1) {
bold = italic = 0;
- for (i = 0; i < (int)len - 1; i++) {
- /*
+ for (i = 0; i < len - 1; i++) {
+ /*
* This means that the catpage is out of state.
* Ignore it and keep going (although the
* catpage is bogus).
@@ -567,22 +756,22 @@ catman(const struct req *req, const char *file)
if ('\b' != p[i + 1]) {
if (italic)
- printf("");
+ printf("");
if (bold)
- printf("");
+ printf("");
italic = bold = 0;
html_putchar(p[i]);
continue;
- } else if (i + 2 >= (int)len)
+ } else if (i + 2 >= len)
continue;
/* Italic mode. */
if ('_' == p[i]) {
if (bold)
- printf("");
+ printf("");
if ( ! italic)
- printf("");
+ printf("");
bold = 0;
italic = 1;
i += 2;
@@ -590,7 +779,7 @@ catman(const struct req *req, const char *file)
continue;
}
- /*
+ /*
* Handle funny behaviour troff-isms.
* These grok'd from the original man2html.c.
*/
@@ -604,9 +793,9 @@ catman(const struct req *req, const char *file)
('*' == p[i] && '|' == p[i + 2]) ||
('|' == p[i] && '*' == p[i + 2])) {
if (italic)
- printf("");
+ printf("");
if (bold)
- printf("");
+ printf("");
italic = bold = 0;
putchar('*');
i += 2;
@@ -618,9 +807,9 @@ catman(const struct req *req, const char *file)
('+' == p[i] && '|' == p[i + 1]) ||
('|' == p[i] && '+' == p[i + 1])) {
if (italic)
- printf("");
+ printf("");
if (bold)
- printf("");
+ printf("");
italic = bold = 0;
putchar('+');
i += 2;
@@ -628,111 +817,116 @@ catman(const struct req *req, const char *file)
}
/* Bold mode. */
-
+
if (italic)
- printf("");
+ printf("");
if ( ! bold)
- printf("");
+ printf("");
bold = 1;
italic = 0;
i += 2;
html_putchar(p[i]);
}
- /*
+ /*
* Clean up the last character.
- * We can get to a newline; don't print that.
+ * We can get to a newline; don't print that.
*/
if (italic)
- printf("");
+ printf("");
if (bold)
- printf("");
+ printf("");
- if (i == (int)len - 1 && '\n' != p[i])
+ if (i == len - 1 && p[i] != '\n')
html_putchar(p[i]);
putchar('\n');
}
+ free(p);
- puts("
\n"
- "
\n"
- "