xref: /minix3/external/bsd/mdocml/dist/cgi.c (revision 0a6a1f1d05b60e214de2f05a7310ddd1f0e590e7)
1*0a6a1f1dSLionel Sambuc /*	Id: cgi.c,v 1.46 2013/10/11 00:06:48 schwarze Exp  */
292395e9cSLionel Sambuc /*
384d9c625SLionel Sambuc  * Copyright (c) 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv>
492395e9cSLionel Sambuc  *
592395e9cSLionel Sambuc  * Permission to use, copy, modify, and distribute this software for any
692395e9cSLionel Sambuc  * purpose with or without fee is hereby granted, provided that the above
792395e9cSLionel Sambuc  * copyright notice and this permission notice appear in all copies.
892395e9cSLionel Sambuc  *
992395e9cSLionel Sambuc  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1092395e9cSLionel Sambuc  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1192395e9cSLionel Sambuc  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1292395e9cSLionel Sambuc  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1392395e9cSLionel Sambuc  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1492395e9cSLionel Sambuc  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1592395e9cSLionel Sambuc  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1692395e9cSLionel Sambuc  */
1792395e9cSLionel Sambuc #ifdef HAVE_CONFIG_H
1892395e9cSLionel Sambuc #include "config.h"
1992395e9cSLionel Sambuc #endif
2092395e9cSLionel Sambuc 
2192395e9cSLionel Sambuc #include <sys/wait.h>
2292395e9cSLionel Sambuc 
2392395e9cSLionel Sambuc #include <assert.h>
2492395e9cSLionel Sambuc #include <ctype.h>
2592395e9cSLionel Sambuc #include <errno.h>
2692395e9cSLionel Sambuc #include <dirent.h>
2792395e9cSLionel Sambuc #include <fcntl.h>
2892395e9cSLionel Sambuc #include <limits.h>
2992395e9cSLionel Sambuc #include <regex.h>
3092395e9cSLionel Sambuc #include <stdio.h>
3192395e9cSLionel Sambuc #include <stdarg.h>
3292395e9cSLionel Sambuc #include <stdint.h>
3392395e9cSLionel Sambuc #include <stdlib.h>
3492395e9cSLionel Sambuc #include <string.h>
3592395e9cSLionel Sambuc #include <unistd.h>
3692395e9cSLionel Sambuc 
37*0a6a1f1dSLionel Sambuc #if defined(__sun)
38*0a6a1f1dSLionel Sambuc /* for stat() */
39*0a6a1f1dSLionel Sambuc #include <fcntl.h>
40*0a6a1f1dSLionel Sambuc #include <sys/types.h>
41*0a6a1f1dSLionel Sambuc #include <sys/stat.h>
42*0a6a1f1dSLionel Sambuc #endif
43*0a6a1f1dSLionel Sambuc 
4492395e9cSLionel Sambuc #include "apropos_db.h"
4592395e9cSLionel Sambuc #include "mandoc.h"
4692395e9cSLionel Sambuc #include "mdoc.h"
4792395e9cSLionel Sambuc #include "man.h"
4892395e9cSLionel Sambuc #include "main.h"
4992395e9cSLionel Sambuc #include "manpath.h"
5092395e9cSLionel Sambuc #include "mandocdb.h"
5192395e9cSLionel Sambuc 
52*0a6a1f1dSLionel Sambuc #if defined(__linux__) || defined(__sun)
5392395e9cSLionel Sambuc # include <db_185.h>
5492395e9cSLionel Sambuc #else
5592395e9cSLionel Sambuc # include <db.h>
5692395e9cSLionel Sambuc #endif
5792395e9cSLionel Sambuc 
5892395e9cSLionel Sambuc enum	page {
5992395e9cSLionel Sambuc 	PAGE_INDEX,
6092395e9cSLionel Sambuc 	PAGE_SEARCH,
6192395e9cSLionel Sambuc 	PAGE_SHOW,
6292395e9cSLionel Sambuc 	PAGE__MAX
6392395e9cSLionel Sambuc };
6492395e9cSLionel Sambuc 
6592395e9cSLionel Sambuc struct	paths {
6692395e9cSLionel Sambuc 	char		*name;
6792395e9cSLionel Sambuc 	char		*path;
6892395e9cSLionel Sambuc };
6992395e9cSLionel Sambuc 
7092395e9cSLionel Sambuc /*
7192395e9cSLionel Sambuc  * A query as passed to the search function.
7292395e9cSLionel Sambuc  */
7392395e9cSLionel Sambuc struct	query {
7492395e9cSLionel Sambuc 	const char	*arch; /* architecture */
7592395e9cSLionel Sambuc 	const char	*sec; /* manual section */
7692395e9cSLionel Sambuc 	const char	*expr; /* unparsed expression string */
7792395e9cSLionel Sambuc 	int		 manroot; /* manroot index (or -1)*/
7892395e9cSLionel Sambuc 	int		 legacy; /* whether legacy mode */
7992395e9cSLionel Sambuc };
8092395e9cSLionel Sambuc 
8192395e9cSLionel Sambuc struct	req {
8292395e9cSLionel Sambuc 	struct query	 q;
8392395e9cSLionel Sambuc 	struct paths	*p;
8492395e9cSLionel Sambuc 	size_t		 psz;
8592395e9cSLionel Sambuc 	enum page	 page;
8692395e9cSLionel Sambuc };
8792395e9cSLionel Sambuc 
8892395e9cSLionel Sambuc static	int		 atou(const char *, unsigned *);
8992395e9cSLionel Sambuc static	void		 catman(const struct req *, const char *);
9092395e9cSLionel Sambuc static	int	 	 cmp(const void *, const void *);
9192395e9cSLionel Sambuc static	void		 format(const struct req *, const char *);
9292395e9cSLionel Sambuc static	void		 html_print(const char *);
9392395e9cSLionel Sambuc static	void		 html_printquery(const struct req *);
9492395e9cSLionel Sambuc static	void		 html_putchar(char);
9592395e9cSLionel Sambuc static	int 		 http_decode(char *);
9692395e9cSLionel Sambuc static	void		 http_parse(struct req *, char *);
9792395e9cSLionel Sambuc static	void		 http_print(const char *);
9892395e9cSLionel Sambuc static	void 		 http_putchar(char);
9992395e9cSLionel Sambuc static	void		 http_printquery(const struct req *);
10092395e9cSLionel Sambuc static	int		 pathstop(DIR *);
10192395e9cSLionel Sambuc static	void		 pathgen(DIR *, char *, struct req *);
10292395e9cSLionel Sambuc static	void		 pg_index(const struct req *, char *);
10392395e9cSLionel Sambuc static	void		 pg_search(const struct req *, char *);
10492395e9cSLionel Sambuc static	void		 pg_show(const struct req *, char *);
10592395e9cSLionel Sambuc static	void		 resp_bad(void);
10692395e9cSLionel Sambuc static	void		 resp_baddb(void);
10792395e9cSLionel Sambuc static	void		 resp_error400(void);
10892395e9cSLionel Sambuc static	void		 resp_error404(const char *);
10992395e9cSLionel Sambuc static	void		 resp_begin_html(int, const char *);
11092395e9cSLionel Sambuc static	void		 resp_begin_http(int, const char *);
11192395e9cSLionel Sambuc static	void		 resp_end_html(void);
11292395e9cSLionel Sambuc static	void		 resp_index(const struct req *);
11392395e9cSLionel Sambuc static	void		 resp_search(struct res *, size_t, void *);
11492395e9cSLionel Sambuc static	void		 resp_searchform(const struct req *);
11592395e9cSLionel Sambuc 
11692395e9cSLionel Sambuc static	const char	 *progname; /* cgi script name */
11792395e9cSLionel Sambuc static	const char	 *cache; /* cache directory */
11892395e9cSLionel Sambuc static	const char	 *css; /* css directory */
11992395e9cSLionel Sambuc static	const char	 *host; /* hostname */
12092395e9cSLionel Sambuc 
12192395e9cSLionel Sambuc static	const char * const pages[PAGE__MAX] = {
12292395e9cSLionel Sambuc 	"index", /* PAGE_INDEX */
12392395e9cSLionel Sambuc 	"search", /* PAGE_SEARCH */
12492395e9cSLionel Sambuc 	"show", /* PAGE_SHOW */
12592395e9cSLionel Sambuc };
12692395e9cSLionel Sambuc 
12792395e9cSLionel Sambuc /*
12892395e9cSLionel Sambuc  * This is just OpenBSD's strtol(3) suggestion.
12992395e9cSLionel Sambuc  * I use it instead of strtonum(3) for portability's sake.
13092395e9cSLionel Sambuc  */
13192395e9cSLionel Sambuc static int
atou(const char * buf,unsigned * v)13292395e9cSLionel Sambuc atou(const char *buf, unsigned *v)
13392395e9cSLionel Sambuc {
13492395e9cSLionel Sambuc 	char		*ep;
13592395e9cSLionel Sambuc 	long		 lval;
13692395e9cSLionel Sambuc 
13792395e9cSLionel Sambuc 	errno = 0;
13892395e9cSLionel Sambuc 	lval = strtol(buf, &ep, 10);
13992395e9cSLionel Sambuc 	if (buf[0] == '\0' || *ep != '\0')
14092395e9cSLionel Sambuc 		return(0);
14192395e9cSLionel Sambuc 	if ((errno == ERANGE && (lval == LONG_MAX ||
14292395e9cSLionel Sambuc 					lval == LONG_MIN)) ||
14392395e9cSLionel Sambuc 			(lval > INT_MAX || lval < 0))
14492395e9cSLionel Sambuc 		return(0);
14592395e9cSLionel Sambuc 
14692395e9cSLionel Sambuc 	*v = (unsigned int)lval;
14792395e9cSLionel Sambuc 	return(1);
14892395e9cSLionel Sambuc }
14992395e9cSLionel Sambuc 
15092395e9cSLionel Sambuc /*
15192395e9cSLionel Sambuc  * Print a character, escaping HTML along the way.
15292395e9cSLionel Sambuc  * This will pass non-ASCII straight to output: be warned!
15392395e9cSLionel Sambuc  */
15492395e9cSLionel Sambuc static void
html_putchar(char c)15592395e9cSLionel Sambuc html_putchar(char c)
15692395e9cSLionel Sambuc {
15792395e9cSLionel Sambuc 
15892395e9cSLionel Sambuc 	switch (c) {
15992395e9cSLionel Sambuc 	case ('"'):
16092395e9cSLionel Sambuc 		printf("&quote;");
16192395e9cSLionel Sambuc 		break;
16292395e9cSLionel Sambuc 	case ('&'):
16392395e9cSLionel Sambuc 		printf("&amp;");
16492395e9cSLionel Sambuc 		break;
16592395e9cSLionel Sambuc 	case ('>'):
16692395e9cSLionel Sambuc 		printf("&gt;");
16792395e9cSLionel Sambuc 		break;
16892395e9cSLionel Sambuc 	case ('<'):
16992395e9cSLionel Sambuc 		printf("&lt;");
17092395e9cSLionel Sambuc 		break;
17192395e9cSLionel Sambuc 	default:
17292395e9cSLionel Sambuc 		putchar((unsigned char)c);
17392395e9cSLionel Sambuc 		break;
17492395e9cSLionel Sambuc 	}
17592395e9cSLionel Sambuc }
17692395e9cSLionel Sambuc static void
http_printquery(const struct req * req)17792395e9cSLionel Sambuc http_printquery(const struct req *req)
17892395e9cSLionel Sambuc {
17992395e9cSLionel Sambuc 
18092395e9cSLionel Sambuc 	printf("&expr=");
18192395e9cSLionel Sambuc 	http_print(req->q.expr ? req->q.expr : "");
18292395e9cSLionel Sambuc 	printf("&sec=");
18392395e9cSLionel Sambuc 	http_print(req->q.sec ? req->q.sec : "");
18492395e9cSLionel Sambuc 	printf("&arch=");
18592395e9cSLionel Sambuc 	http_print(req->q.arch ? req->q.arch : "");
18692395e9cSLionel Sambuc }
18792395e9cSLionel Sambuc 
18892395e9cSLionel Sambuc 
18992395e9cSLionel Sambuc static void
html_printquery(const struct req * req)19092395e9cSLionel Sambuc html_printquery(const struct req *req)
19192395e9cSLionel Sambuc {
19292395e9cSLionel Sambuc 
19392395e9cSLionel Sambuc 	printf("&amp;expr=");
19492395e9cSLionel Sambuc 	html_print(req->q.expr ? req->q.expr : "");
19592395e9cSLionel Sambuc 	printf("&amp;sec=");
19692395e9cSLionel Sambuc 	html_print(req->q.sec ? req->q.sec : "");
19792395e9cSLionel Sambuc 	printf("&amp;arch=");
19892395e9cSLionel Sambuc 	html_print(req->q.arch ? req->q.arch : "");
19992395e9cSLionel Sambuc }
20092395e9cSLionel Sambuc 
20192395e9cSLionel Sambuc static void
http_print(const char * p)20292395e9cSLionel Sambuc http_print(const char *p)
20392395e9cSLionel Sambuc {
20492395e9cSLionel Sambuc 
20592395e9cSLionel Sambuc 	if (NULL == p)
20692395e9cSLionel Sambuc 		return;
20792395e9cSLionel Sambuc 	while ('\0' != *p)
20892395e9cSLionel Sambuc 		http_putchar(*p++);
20992395e9cSLionel Sambuc }
21092395e9cSLionel Sambuc 
21192395e9cSLionel Sambuc /*
21292395e9cSLionel Sambuc  * Call through to html_putchar().
21392395e9cSLionel Sambuc  * Accepts NULL strings.
21492395e9cSLionel Sambuc  */
21592395e9cSLionel Sambuc static void
html_print(const char * p)21692395e9cSLionel Sambuc html_print(const char *p)
21792395e9cSLionel Sambuc {
21892395e9cSLionel Sambuc 
21992395e9cSLionel Sambuc 	if (NULL == p)
22092395e9cSLionel Sambuc 		return;
22192395e9cSLionel Sambuc 	while ('\0' != *p)
22292395e9cSLionel Sambuc 		html_putchar(*p++);
22392395e9cSLionel Sambuc }
22492395e9cSLionel Sambuc 
22592395e9cSLionel Sambuc /*
22692395e9cSLionel Sambuc  * Parse out key-value pairs from an HTTP request variable.
22792395e9cSLionel Sambuc  * This can be either a cookie or a POST/GET string, although man.cgi
22892395e9cSLionel Sambuc  * uses only GET for simplicity.
22992395e9cSLionel Sambuc  */
23092395e9cSLionel Sambuc static void
http_parse(struct req * req,char * p)23192395e9cSLionel Sambuc http_parse(struct req *req, char *p)
23292395e9cSLionel Sambuc {
23392395e9cSLionel Sambuc 	char            *key, *val, *manroot;
23492395e9cSLionel Sambuc 	int		 i, legacy;
23592395e9cSLionel Sambuc 
23692395e9cSLionel Sambuc 	memset(&req->q, 0, sizeof(struct query));
23792395e9cSLionel Sambuc 
23892395e9cSLionel Sambuc 	legacy = -1;
23992395e9cSLionel Sambuc 	manroot = NULL;
24092395e9cSLionel Sambuc 
24192395e9cSLionel Sambuc 	while ('\0' != *p) {
24292395e9cSLionel Sambuc 		key = p;
24392395e9cSLionel Sambuc 		val = NULL;
24492395e9cSLionel Sambuc 
24592395e9cSLionel Sambuc 		p += (int)strcspn(p, ";&");
24692395e9cSLionel Sambuc 		if ('\0' != *p)
24792395e9cSLionel Sambuc 			*p++ = '\0';
24892395e9cSLionel Sambuc 		if (NULL != (val = strchr(key, '=')))
24992395e9cSLionel Sambuc 			*val++ = '\0';
25092395e9cSLionel Sambuc 
25192395e9cSLionel Sambuc 		if ('\0' == *key || NULL == val || '\0' == *val)
25292395e9cSLionel Sambuc 			continue;
25392395e9cSLionel Sambuc 
25492395e9cSLionel Sambuc 		/* Just abort handling. */
25592395e9cSLionel Sambuc 
25692395e9cSLionel Sambuc 		if ( ! http_decode(key))
25792395e9cSLionel Sambuc 			break;
25892395e9cSLionel Sambuc 		if (NULL != val && ! http_decode(val))
25992395e9cSLionel Sambuc 			break;
26092395e9cSLionel Sambuc 
26192395e9cSLionel Sambuc 		if (0 == strcmp(key, "expr"))
26292395e9cSLionel Sambuc 			req->q.expr = val;
26392395e9cSLionel Sambuc 		else if (0 == strcmp(key, "query"))
26492395e9cSLionel Sambuc 			req->q.expr = val;
26592395e9cSLionel Sambuc 		else if (0 == strcmp(key, "sec"))
26692395e9cSLionel Sambuc 			req->q.sec = val;
26792395e9cSLionel Sambuc 		else if (0 == strcmp(key, "sektion"))
26892395e9cSLionel Sambuc 			req->q.sec = val;
26992395e9cSLionel Sambuc 		else if (0 == strcmp(key, "arch"))
27092395e9cSLionel Sambuc 			req->q.arch = val;
27192395e9cSLionel Sambuc 		else if (0 == strcmp(key, "manpath"))
27292395e9cSLionel Sambuc 			manroot = val;
27392395e9cSLionel Sambuc 		else if (0 == strcmp(key, "apropos"))
27492395e9cSLionel Sambuc 			legacy = 0 == strcmp(val, "0");
27592395e9cSLionel Sambuc 	}
27692395e9cSLionel Sambuc 
27792395e9cSLionel Sambuc 	/* Test for old man.cgi compatibility mode. */
27892395e9cSLionel Sambuc 
27984d9c625SLionel Sambuc 	req->q.legacy = legacy > 0;
28092395e9cSLionel Sambuc 
28192395e9cSLionel Sambuc 	/*
28292395e9cSLionel Sambuc 	 * Section "0" means no section when in legacy mode.
28392395e9cSLionel Sambuc 	 * For some man.cgi scripts, "default" arch is none.
28492395e9cSLionel Sambuc 	 */
28592395e9cSLionel Sambuc 
28692395e9cSLionel Sambuc 	if (req->q.legacy && NULL != req->q.sec)
28792395e9cSLionel Sambuc 		if (0 == strcmp(req->q.sec, "0"))
28892395e9cSLionel Sambuc 			req->q.sec = NULL;
28992395e9cSLionel Sambuc 	if (req->q.legacy && NULL != req->q.arch)
29092395e9cSLionel Sambuc 		if (0 == strcmp(req->q.arch, "default"))
29192395e9cSLionel Sambuc 			req->q.arch = NULL;
29292395e9cSLionel Sambuc 
29392395e9cSLionel Sambuc 	/* Default to first manroot. */
29492395e9cSLionel Sambuc 
29592395e9cSLionel Sambuc 	if (NULL != manroot) {
29692395e9cSLionel Sambuc 		for (i = 0; i < (int)req->psz; i++)
29792395e9cSLionel Sambuc 			if (0 == strcmp(req->p[i].name, manroot))
29892395e9cSLionel Sambuc 				break;
29992395e9cSLionel Sambuc 		req->q.manroot = i < (int)req->psz ? i : -1;
30092395e9cSLionel Sambuc 	}
30192395e9cSLionel Sambuc }
30292395e9cSLionel Sambuc 
30392395e9cSLionel Sambuc static void
http_putchar(char c)30492395e9cSLionel Sambuc http_putchar(char c)
30592395e9cSLionel Sambuc {
30692395e9cSLionel Sambuc 
30792395e9cSLionel Sambuc 	if (isalnum((unsigned char)c)) {
30892395e9cSLionel Sambuc 		putchar((unsigned char)c);
30992395e9cSLionel Sambuc 		return;
31092395e9cSLionel Sambuc 	} else if (' ' == c) {
31192395e9cSLionel Sambuc 		putchar('+');
31292395e9cSLionel Sambuc 		return;
31392395e9cSLionel Sambuc 	}
31492395e9cSLionel Sambuc 	printf("%%%.2x", c);
31592395e9cSLionel Sambuc }
31692395e9cSLionel Sambuc 
31792395e9cSLionel Sambuc /*
31892395e9cSLionel Sambuc  * HTTP-decode a string.  The standard explanation is that this turns
31992395e9cSLionel Sambuc  * "%4e+foo" into "n foo" in the regular way.  This is done in-place
32092395e9cSLionel Sambuc  * over the allocated string.
32192395e9cSLionel Sambuc  */
32292395e9cSLionel Sambuc static int
http_decode(char * p)32392395e9cSLionel Sambuc http_decode(char *p)
32492395e9cSLionel Sambuc {
32592395e9cSLionel Sambuc 	char             hex[3];
32692395e9cSLionel Sambuc 	int              c;
32792395e9cSLionel Sambuc 
32892395e9cSLionel Sambuc 	hex[2] = '\0';
32992395e9cSLionel Sambuc 
33092395e9cSLionel Sambuc 	for ( ; '\0' != *p; p++) {
33192395e9cSLionel Sambuc 		if ('%' == *p) {
33292395e9cSLionel Sambuc 			if ('\0' == (hex[0] = *(p + 1)))
33392395e9cSLionel Sambuc 				return(0);
33492395e9cSLionel Sambuc 			if ('\0' == (hex[1] = *(p + 2)))
33592395e9cSLionel Sambuc 				return(0);
33692395e9cSLionel Sambuc 			if (1 != sscanf(hex, "%x", &c))
33792395e9cSLionel Sambuc 				return(0);
33892395e9cSLionel Sambuc 			if ('\0' == c)
33992395e9cSLionel Sambuc 				return(0);
34092395e9cSLionel Sambuc 
34192395e9cSLionel Sambuc 			*p = (char)c;
34292395e9cSLionel Sambuc 			memmove(p + 1, p + 3, strlen(p + 3) + 1);
34392395e9cSLionel Sambuc 		} else
34492395e9cSLionel Sambuc 			*p = '+' == *p ? ' ' : *p;
34592395e9cSLionel Sambuc 	}
34692395e9cSLionel Sambuc 
34792395e9cSLionel Sambuc 	*p = '\0';
34892395e9cSLionel Sambuc 	return(1);
34992395e9cSLionel Sambuc }
35092395e9cSLionel Sambuc 
35192395e9cSLionel Sambuc static void
resp_begin_http(int code,const char * msg)35292395e9cSLionel Sambuc resp_begin_http(int code, const char *msg)
35392395e9cSLionel Sambuc {
35492395e9cSLionel Sambuc 
35592395e9cSLionel Sambuc 	if (200 != code)
35692395e9cSLionel Sambuc 		printf("Status: %d %s\n", code, msg);
35792395e9cSLionel Sambuc 
35892395e9cSLionel Sambuc 	puts("Content-Type: text/html; charset=utf-8\n"
35992395e9cSLionel Sambuc 	     "Cache-Control: no-cache\n"
36092395e9cSLionel Sambuc 	     "Pragma: no-cache\n"
36192395e9cSLionel Sambuc 	     "");
36292395e9cSLionel Sambuc 
36392395e9cSLionel Sambuc 	fflush(stdout);
36492395e9cSLionel Sambuc }
36592395e9cSLionel Sambuc 
36692395e9cSLionel Sambuc static void
resp_begin_html(int code,const char * msg)36792395e9cSLionel Sambuc resp_begin_html(int code, const char *msg)
36892395e9cSLionel Sambuc {
36992395e9cSLionel Sambuc 
37092395e9cSLionel Sambuc 	resp_begin_http(code, msg);
37192395e9cSLionel Sambuc 
37292395e9cSLionel Sambuc 	printf("<!DOCTYPE HTML PUBLIC "
37392395e9cSLionel Sambuc 	       " \"-//W3C//DTD HTML 4.01//EN\""
37492395e9cSLionel Sambuc 	       " \"http://www.w3.org/TR/html4/strict.dtd\">\n"
37592395e9cSLionel Sambuc 	       "<HTML>\n"
37692395e9cSLionel Sambuc 	       "<HEAD>\n"
37792395e9cSLionel Sambuc 	       "<META HTTP-EQUIV=\"Content-Type\""
37892395e9cSLionel Sambuc 	       " CONTENT=\"text/html; charset=utf-8\">\n"
37992395e9cSLionel Sambuc 	       "<LINK REL=\"stylesheet\" HREF=\"%s/man-cgi.css\""
38092395e9cSLionel Sambuc 	       " TYPE=\"text/css\" media=\"all\">\n"
38192395e9cSLionel Sambuc 	       "<LINK REL=\"stylesheet\" HREF=\"%s/man.css\""
38292395e9cSLionel Sambuc 	       " TYPE=\"text/css\" media=\"all\">\n"
38392395e9cSLionel Sambuc 	       "<TITLE>System Manpage Reference</TITLE>\n"
38492395e9cSLionel Sambuc 	       "</HEAD>\n"
38592395e9cSLionel Sambuc 	       "<BODY>\n"
38692395e9cSLionel Sambuc 	       "<!-- Begin page content. //-->\n", css, css);
38792395e9cSLionel Sambuc }
38892395e9cSLionel Sambuc 
38992395e9cSLionel Sambuc static void
resp_end_html(void)39092395e9cSLionel Sambuc resp_end_html(void)
39192395e9cSLionel Sambuc {
39292395e9cSLionel Sambuc 
39392395e9cSLionel Sambuc 	puts("</BODY>\n"
39492395e9cSLionel Sambuc 	     "</HTML>");
39592395e9cSLionel Sambuc }
39692395e9cSLionel Sambuc 
39792395e9cSLionel Sambuc static void
resp_searchform(const struct req * req)39892395e9cSLionel Sambuc resp_searchform(const struct req *req)
39992395e9cSLionel Sambuc {
40092395e9cSLionel Sambuc 	int		 i;
40192395e9cSLionel Sambuc 
40292395e9cSLionel Sambuc 	puts("<!-- Begin search form. //-->");
40392395e9cSLionel Sambuc 	printf("<DIV ID=\"mancgi\">\n"
40492395e9cSLionel Sambuc 	       "<FORM ACTION=\"%s/search.html\" METHOD=\"get\">\n"
40592395e9cSLionel Sambuc 	       "<FIELDSET>\n"
40692395e9cSLionel Sambuc 	       "<LEGEND>Search Parameters</LEGEND>\n"
40784d9c625SLionel Sambuc 	       "<INPUT TYPE=\"submit\" "
40884d9c625SLionel Sambuc 	       " VALUE=\"Search\"> for manuals satisfying \n"
40992395e9cSLionel Sambuc 	       "<INPUT TYPE=\"text\" NAME=\"expr\" VALUE=\"",
41092395e9cSLionel Sambuc 	       progname);
41192395e9cSLionel Sambuc 	html_print(req->q.expr ? req->q.expr : "");
41292395e9cSLionel Sambuc 	printf("\">, section "
41392395e9cSLionel Sambuc 	       "<INPUT TYPE=\"text\""
41492395e9cSLionel Sambuc 	       " SIZE=\"4\" NAME=\"sec\" VALUE=\"");
41592395e9cSLionel Sambuc 	html_print(req->q.sec ? req->q.sec : "");
41692395e9cSLionel Sambuc 	printf("\">, arch "
41792395e9cSLionel Sambuc 	       "<INPUT TYPE=\"text\""
41892395e9cSLionel Sambuc 	       " SIZE=\"8\" NAME=\"arch\" VALUE=\"");
41992395e9cSLionel Sambuc 	html_print(req->q.arch ? req->q.arch : "");
42092395e9cSLionel Sambuc 	printf("\">");
42192395e9cSLionel Sambuc 	if (req->psz > 1) {
42292395e9cSLionel Sambuc 		puts(", <SELECT NAME=\"manpath\">");
42392395e9cSLionel Sambuc 		for (i = 0; i < (int)req->psz; i++) {
42492395e9cSLionel Sambuc 			printf("<OPTION %s VALUE=\"",
42592395e9cSLionel Sambuc 				(i == req->q.manroot) ||
42692395e9cSLionel Sambuc 				(0 == i && -1 == req->q.manroot) ?
42792395e9cSLionel Sambuc 				"SELECTED=\"selected\"" : "");
42892395e9cSLionel Sambuc 			html_print(req->p[i].name);
42992395e9cSLionel Sambuc 			printf("\">");
43092395e9cSLionel Sambuc 			html_print(req->p[i].name);
43192395e9cSLionel Sambuc 			puts("</OPTION>");
43292395e9cSLionel Sambuc 		}
43392395e9cSLionel Sambuc 		puts("</SELECT>");
43492395e9cSLionel Sambuc 	}
43592395e9cSLionel Sambuc 	puts(".\n"
43692395e9cSLionel Sambuc 	     "<INPUT TYPE=\"reset\" VALUE=\"Reset\">\n"
43792395e9cSLionel Sambuc 	     "</FIELDSET>\n"
43892395e9cSLionel Sambuc 	     "</FORM>\n"
43992395e9cSLionel Sambuc 	     "</DIV>");
44092395e9cSLionel Sambuc 	puts("<!-- End search form. //-->");
44192395e9cSLionel Sambuc }
44292395e9cSLionel Sambuc 
44392395e9cSLionel Sambuc static void
resp_index(const struct req * req)44492395e9cSLionel Sambuc resp_index(const struct req *req)
44592395e9cSLionel Sambuc {
44692395e9cSLionel Sambuc 
44792395e9cSLionel Sambuc 	resp_begin_html(200, NULL);
44892395e9cSLionel Sambuc 	resp_searchform(req);
44992395e9cSLionel Sambuc 	resp_end_html();
45092395e9cSLionel Sambuc }
45192395e9cSLionel Sambuc 
45292395e9cSLionel Sambuc static void
resp_error400(void)45392395e9cSLionel Sambuc resp_error400(void)
45492395e9cSLionel Sambuc {
45592395e9cSLionel Sambuc 
45692395e9cSLionel Sambuc 	resp_begin_html(400, "Query Malformed");
45792395e9cSLionel Sambuc 	printf("<H1>Malformed Query</H1>\n"
45892395e9cSLionel Sambuc 	       "<P>\n"
45992395e9cSLionel Sambuc 	       "The query your entered was malformed.\n"
46092395e9cSLionel Sambuc 	       "Try again from the\n"
46192395e9cSLionel Sambuc 	       "<A HREF=\"%s/index.html\">main page</A>.\n"
46292395e9cSLionel Sambuc 	       "</P>", progname);
46392395e9cSLionel Sambuc 	resp_end_html();
46492395e9cSLionel Sambuc }
46592395e9cSLionel Sambuc 
46692395e9cSLionel Sambuc static void
resp_error404(const char * page)46792395e9cSLionel Sambuc resp_error404(const char *page)
46892395e9cSLionel Sambuc {
46992395e9cSLionel Sambuc 
47092395e9cSLionel Sambuc 	resp_begin_html(404, "Not Found");
47192395e9cSLionel Sambuc 	puts("<H1>Page Not Found</H1>\n"
47292395e9cSLionel Sambuc 	     "<P>\n"
47392395e9cSLionel Sambuc 	     "The page you're looking for, ");
47492395e9cSLionel Sambuc 	printf("<B>");
47592395e9cSLionel Sambuc 	html_print(page);
47692395e9cSLionel Sambuc 	printf("</B>,\n"
47792395e9cSLionel Sambuc 	       "could not be found.\n"
47892395e9cSLionel Sambuc 	       "Try searching from the\n"
47992395e9cSLionel Sambuc 	       "<A HREF=\"%s/index.html\">main page</A>.\n"
48092395e9cSLionel Sambuc 	       "</P>", progname);
48192395e9cSLionel Sambuc 	resp_end_html();
48292395e9cSLionel Sambuc }
48392395e9cSLionel Sambuc 
48492395e9cSLionel Sambuc static void
resp_bad(void)48592395e9cSLionel Sambuc resp_bad(void)
48692395e9cSLionel Sambuc {
48792395e9cSLionel Sambuc 	resp_begin_html(500, "Internal Server Error");
48892395e9cSLionel Sambuc 	puts("<P>Generic badness happened.</P>");
48992395e9cSLionel Sambuc 	resp_end_html();
49092395e9cSLionel Sambuc }
49192395e9cSLionel Sambuc 
49292395e9cSLionel Sambuc static void
resp_baddb(void)49392395e9cSLionel Sambuc resp_baddb(void)
49492395e9cSLionel Sambuc {
49592395e9cSLionel Sambuc 
49692395e9cSLionel Sambuc 	resp_begin_html(500, "Internal Server Error");
49792395e9cSLionel Sambuc 	puts("<P>Your database is broken.</P>");
49892395e9cSLionel Sambuc 	resp_end_html();
49992395e9cSLionel Sambuc }
50092395e9cSLionel Sambuc 
50192395e9cSLionel Sambuc static void
resp_search(struct res * r,size_t sz,void * arg)50292395e9cSLionel Sambuc resp_search(struct res *r, size_t sz, void *arg)
50392395e9cSLionel Sambuc {
50484d9c625SLionel Sambuc 	size_t		 i, matched;
50592395e9cSLionel Sambuc 	const struct req *req;
50692395e9cSLionel Sambuc 
50792395e9cSLionel Sambuc 	req = (const struct req *)arg;
50892395e9cSLionel Sambuc 
50992395e9cSLionel Sambuc 	if (sz > 0)
51092395e9cSLionel Sambuc 		assert(req->q.manroot >= 0);
51192395e9cSLionel Sambuc 
51284d9c625SLionel Sambuc 	for (matched = i = 0; i < sz; i++)
51384d9c625SLionel Sambuc 		if (r[i].matched)
51484d9c625SLionel Sambuc 			matched++;
51584d9c625SLionel Sambuc 
51684d9c625SLionel Sambuc 	if (1 == matched) {
51784d9c625SLionel Sambuc 		for (i = 0; i < sz; i++)
51884d9c625SLionel Sambuc 			if (r[i].matched)
51984d9c625SLionel Sambuc 				break;
52092395e9cSLionel Sambuc 		/*
52192395e9cSLionel Sambuc 		 * If we have just one result, then jump there now
52292395e9cSLionel Sambuc 		 * without any delay.
52392395e9cSLionel Sambuc 		 */
52492395e9cSLionel Sambuc 		puts("Status: 303 See Other");
52592395e9cSLionel Sambuc 		printf("Location: http://%s%s/show/%d/%u/%u.html?",
52692395e9cSLionel Sambuc 				host, progname, req->q.manroot,
52784d9c625SLionel Sambuc 				r[i].volume, r[i].rec);
52892395e9cSLionel Sambuc 		http_printquery(req);
52992395e9cSLionel Sambuc 		puts("\n"
53092395e9cSLionel Sambuc 		     "Content-Type: text/html; charset=utf-8\n");
53192395e9cSLionel Sambuc 		return;
53292395e9cSLionel Sambuc 	}
53392395e9cSLionel Sambuc 
53492395e9cSLionel Sambuc 	resp_begin_html(200, NULL);
53592395e9cSLionel Sambuc 	resp_searchform(req);
53692395e9cSLionel Sambuc 
53792395e9cSLionel Sambuc 	puts("<DIV CLASS=\"results\">");
53892395e9cSLionel Sambuc 
53984d9c625SLionel Sambuc 	if (0 == matched) {
54084d9c625SLionel Sambuc 		puts("<P>\n"
54184d9c625SLionel Sambuc 		     "No results found.\n"
54284d9c625SLionel Sambuc 		     "</P>\n"
54384d9c625SLionel Sambuc 		     "</DIV>");
54492395e9cSLionel Sambuc 		resp_end_html();
54592395e9cSLionel Sambuc 		return;
54692395e9cSLionel Sambuc 	}
54792395e9cSLionel Sambuc 
54884d9c625SLionel Sambuc 	qsort(r, sz, sizeof(struct res), cmp);
54984d9c625SLionel Sambuc 
55092395e9cSLionel Sambuc 	puts("<TABLE>");
55192395e9cSLionel Sambuc 
55284d9c625SLionel Sambuc 	for (i = 0; i < sz; i++) {
55384d9c625SLionel Sambuc 		if ( ! r[i].matched)
55484d9c625SLionel Sambuc 			continue;
55592395e9cSLionel Sambuc 		printf("<TR>\n"
55692395e9cSLionel Sambuc 		       "<TD CLASS=\"title\">\n"
55792395e9cSLionel Sambuc 		       "<A HREF=\"%s/show/%d/%u/%u.html?",
55892395e9cSLionel Sambuc 				progname, req->q.manroot,
55992395e9cSLionel Sambuc 				r[i].volume, r[i].rec);
56092395e9cSLionel Sambuc 		html_printquery(req);
56192395e9cSLionel Sambuc 		printf("\">");
56292395e9cSLionel Sambuc 		html_print(r[i].title);
56392395e9cSLionel Sambuc 		putchar('(');
56492395e9cSLionel Sambuc 		html_print(r[i].cat);
56592395e9cSLionel Sambuc 		if (r[i].arch && '\0' != *r[i].arch) {
56692395e9cSLionel Sambuc 			putchar('/');
56792395e9cSLionel Sambuc 			html_print(r[i].arch);
56892395e9cSLionel Sambuc 		}
56992395e9cSLionel Sambuc 		printf(")</A>\n"
57092395e9cSLionel Sambuc 		       "</TD>\n"
57192395e9cSLionel Sambuc 		       "<TD CLASS=\"desc\">");
57292395e9cSLionel Sambuc 		html_print(r[i].desc);
57392395e9cSLionel Sambuc 		puts("</TD>\n"
57492395e9cSLionel Sambuc 		     "</TR>");
57592395e9cSLionel Sambuc 	}
57692395e9cSLionel Sambuc 
57792395e9cSLionel Sambuc 	puts("</TABLE>\n"
57892395e9cSLionel Sambuc 	     "</DIV>");
57992395e9cSLionel Sambuc 	resp_end_html();
58092395e9cSLionel Sambuc }
58192395e9cSLionel Sambuc 
58292395e9cSLionel Sambuc /* ARGSUSED */
58392395e9cSLionel Sambuc static void
pg_index(const struct req * req,char * path)58492395e9cSLionel Sambuc pg_index(const struct req *req, char *path)
58592395e9cSLionel Sambuc {
58692395e9cSLionel Sambuc 
58792395e9cSLionel Sambuc 	resp_index(req);
58892395e9cSLionel Sambuc }
58992395e9cSLionel Sambuc 
59092395e9cSLionel Sambuc static void
catman(const struct req * req,const char * file)59192395e9cSLionel Sambuc catman(const struct req *req, const char *file)
59292395e9cSLionel Sambuc {
59392395e9cSLionel Sambuc 	FILE		*f;
59492395e9cSLionel Sambuc 	size_t		 len;
59592395e9cSLionel Sambuc 	int		 i;
59692395e9cSLionel Sambuc 	char		*p;
59792395e9cSLionel Sambuc 	int		 italic, bold;
59892395e9cSLionel Sambuc 
59992395e9cSLionel Sambuc 	if (NULL == (f = fopen(file, "r"))) {
60092395e9cSLionel Sambuc 		resp_baddb();
60192395e9cSLionel Sambuc 		return;
60292395e9cSLionel Sambuc 	}
60392395e9cSLionel Sambuc 
60492395e9cSLionel Sambuc 	resp_begin_html(200, NULL);
60592395e9cSLionel Sambuc 	resp_searchform(req);
60692395e9cSLionel Sambuc 	puts("<DIV CLASS=\"catman\">\n"
60792395e9cSLionel Sambuc 	     "<PRE>");
60892395e9cSLionel Sambuc 
60992395e9cSLionel Sambuc 	while (NULL != (p = fgetln(f, &len))) {
61092395e9cSLionel Sambuc 		bold = italic = 0;
61192395e9cSLionel Sambuc 		for (i = 0; i < (int)len - 1; i++) {
61292395e9cSLionel Sambuc 			/*
61392395e9cSLionel Sambuc 			 * This means that the catpage is out of state.
61492395e9cSLionel Sambuc 			 * Ignore it and keep going (although the
61592395e9cSLionel Sambuc 			 * catpage is bogus).
61692395e9cSLionel Sambuc 			 */
61792395e9cSLionel Sambuc 
61892395e9cSLionel Sambuc 			if ('\b' == p[i] || '\n' == p[i])
61992395e9cSLionel Sambuc 				continue;
62092395e9cSLionel Sambuc 
62192395e9cSLionel Sambuc 			/*
62292395e9cSLionel Sambuc 			 * Print a regular character.
62392395e9cSLionel Sambuc 			 * Close out any bold/italic scopes.
62492395e9cSLionel Sambuc 			 * If we're in back-space mode, make sure we'll
62592395e9cSLionel Sambuc 			 * have something to enter when we backspace.
62692395e9cSLionel Sambuc 			 */
62792395e9cSLionel Sambuc 
62892395e9cSLionel Sambuc 			if ('\b' != p[i + 1]) {
62992395e9cSLionel Sambuc 				if (italic)
63092395e9cSLionel Sambuc 					printf("</I>");
63192395e9cSLionel Sambuc 				if (bold)
63292395e9cSLionel Sambuc 					printf("</B>");
63392395e9cSLionel Sambuc 				italic = bold = 0;
63492395e9cSLionel Sambuc 				html_putchar(p[i]);
63592395e9cSLionel Sambuc 				continue;
63692395e9cSLionel Sambuc 			} else if (i + 2 >= (int)len)
63792395e9cSLionel Sambuc 				continue;
63892395e9cSLionel Sambuc 
63992395e9cSLionel Sambuc 			/* Italic mode. */
64092395e9cSLionel Sambuc 
64192395e9cSLionel Sambuc 			if ('_' == p[i]) {
64292395e9cSLionel Sambuc 				if (bold)
64392395e9cSLionel Sambuc 					printf("</B>");
64492395e9cSLionel Sambuc 				if ( ! italic)
64592395e9cSLionel Sambuc 					printf("<I>");
64692395e9cSLionel Sambuc 				bold = 0;
64792395e9cSLionel Sambuc 				italic = 1;
64892395e9cSLionel Sambuc 				i += 2;
64992395e9cSLionel Sambuc 				html_putchar(p[i]);
65092395e9cSLionel Sambuc 				continue;
65192395e9cSLionel Sambuc 			}
65292395e9cSLionel Sambuc 
65392395e9cSLionel Sambuc 			/*
65492395e9cSLionel Sambuc 			 * Handle funny behaviour troff-isms.
65592395e9cSLionel Sambuc 			 * These grok'd from the original man2html.c.
65692395e9cSLionel Sambuc 			 */
65792395e9cSLionel Sambuc 
65892395e9cSLionel Sambuc 			if (('+' == p[i] && 'o' == p[i + 2]) ||
65992395e9cSLionel Sambuc 					('o' == p[i] && '+' == p[i + 2]) ||
66092395e9cSLionel Sambuc 					('|' == p[i] && '=' == p[i + 2]) ||
66192395e9cSLionel Sambuc 					('=' == p[i] && '|' == p[i + 2]) ||
66292395e9cSLionel Sambuc 					('*' == p[i] && '=' == p[i + 2]) ||
66392395e9cSLionel Sambuc 					('=' == p[i] && '*' == p[i + 2]) ||
66492395e9cSLionel Sambuc 					('*' == p[i] && '|' == p[i + 2]) ||
66592395e9cSLionel Sambuc 					('|' == p[i] && '*' == p[i + 2]))  {
66692395e9cSLionel Sambuc 				if (italic)
66792395e9cSLionel Sambuc 					printf("</I>");
66892395e9cSLionel Sambuc 				if (bold)
66992395e9cSLionel Sambuc 					printf("</B>");
67092395e9cSLionel Sambuc 				italic = bold = 0;
67192395e9cSLionel Sambuc 				putchar('*');
67292395e9cSLionel Sambuc 				i += 2;
67392395e9cSLionel Sambuc 				continue;
67492395e9cSLionel Sambuc 			} else if (('|' == p[i] && '-' == p[i + 2]) ||
67592395e9cSLionel Sambuc 					('-' == p[i] && '|' == p[i + 1]) ||
67692395e9cSLionel Sambuc 					('+' == p[i] && '-' == p[i + 1]) ||
67792395e9cSLionel Sambuc 					('-' == p[i] && '+' == p[i + 1]) ||
67892395e9cSLionel Sambuc 					('+' == p[i] && '|' == p[i + 1]) ||
67992395e9cSLionel Sambuc 					('|' == p[i] && '+' == p[i + 1]))  {
68092395e9cSLionel Sambuc 				if (italic)
68192395e9cSLionel Sambuc 					printf("</I>");
68292395e9cSLionel Sambuc 				if (bold)
68392395e9cSLionel Sambuc 					printf("</B>");
68492395e9cSLionel Sambuc 				italic = bold = 0;
68592395e9cSLionel Sambuc 				putchar('+');
68692395e9cSLionel Sambuc 				i += 2;
68792395e9cSLionel Sambuc 				continue;
68892395e9cSLionel Sambuc 			}
68992395e9cSLionel Sambuc 
69092395e9cSLionel Sambuc 			/* Bold mode. */
69192395e9cSLionel Sambuc 
69292395e9cSLionel Sambuc 			if (italic)
69392395e9cSLionel Sambuc 				printf("</I>");
69492395e9cSLionel Sambuc 			if ( ! bold)
69592395e9cSLionel Sambuc 				printf("<B>");
69692395e9cSLionel Sambuc 			bold = 1;
69792395e9cSLionel Sambuc 			italic = 0;
69892395e9cSLionel Sambuc 			i += 2;
69992395e9cSLionel Sambuc 			html_putchar(p[i]);
70092395e9cSLionel Sambuc 		}
70192395e9cSLionel Sambuc 
70292395e9cSLionel Sambuc 		/*
70392395e9cSLionel Sambuc 		 * Clean up the last character.
70492395e9cSLionel Sambuc 		 * We can get to a newline; don't print that.
70592395e9cSLionel Sambuc 		 */
70692395e9cSLionel Sambuc 
70792395e9cSLionel Sambuc 		if (italic)
70892395e9cSLionel Sambuc 			printf("</I>");
70992395e9cSLionel Sambuc 		if (bold)
71092395e9cSLionel Sambuc 			printf("</B>");
71192395e9cSLionel Sambuc 
71292395e9cSLionel Sambuc 		if (i == (int)len - 1 && '\n' != p[i])
71392395e9cSLionel Sambuc 			html_putchar(p[i]);
71492395e9cSLionel Sambuc 
71592395e9cSLionel Sambuc 		putchar('\n');
71692395e9cSLionel Sambuc 	}
71792395e9cSLionel Sambuc 
71892395e9cSLionel Sambuc 	puts("</PRE>\n"
71992395e9cSLionel Sambuc 	     "</DIV>\n"
72092395e9cSLionel Sambuc 	     "</BODY>\n"
72192395e9cSLionel Sambuc 	     "</HTML>");
72292395e9cSLionel Sambuc 
72392395e9cSLionel Sambuc 	fclose(f);
72492395e9cSLionel Sambuc }
72592395e9cSLionel Sambuc 
72692395e9cSLionel Sambuc static void
format(const struct req * req,const char * file)72792395e9cSLionel Sambuc format(const struct req *req, const char *file)
72892395e9cSLionel Sambuc {
72992395e9cSLionel Sambuc 	struct mparse	*mp;
73092395e9cSLionel Sambuc 	int		 fd;
73192395e9cSLionel Sambuc 	struct mdoc	*mdoc;
73292395e9cSLionel Sambuc 	struct man	*man;
73392395e9cSLionel Sambuc 	void		*vp;
73492395e9cSLionel Sambuc 	enum mandoclevel rc;
735*0a6a1f1dSLionel Sambuc 	char		 opts[PATH_MAX + 128];
73692395e9cSLionel Sambuc 
73792395e9cSLionel Sambuc 	if (-1 == (fd = open(file, O_RDONLY, 0))) {
73892395e9cSLionel Sambuc 		resp_baddb();
73992395e9cSLionel Sambuc 		return;
74092395e9cSLionel Sambuc 	}
74192395e9cSLionel Sambuc 
742*0a6a1f1dSLionel Sambuc 	mp = mparse_alloc(MPARSE_AUTO, MANDOCLEVEL_FATAL, NULL, NULL, NULL);
74392395e9cSLionel Sambuc 	rc = mparse_readfd(mp, fd, file);
74492395e9cSLionel Sambuc 	close(fd);
74592395e9cSLionel Sambuc 
74692395e9cSLionel Sambuc 	if (rc >= MANDOCLEVEL_FATAL) {
74792395e9cSLionel Sambuc 		resp_baddb();
74892395e9cSLionel Sambuc 		return;
74992395e9cSLionel Sambuc 	}
75092395e9cSLionel Sambuc 
75192395e9cSLionel Sambuc 	snprintf(opts, sizeof(opts), "fragment,"
752*0a6a1f1dSLionel Sambuc 			"man=%s/search.html?sec=%%S&expr=Nm~^%%N$,"
75392395e9cSLionel Sambuc 			/*"includes=/cgi-bin/man.cgi/usr/include/%%I"*/,
75492395e9cSLionel Sambuc 			progname);
75592395e9cSLionel Sambuc 
75692395e9cSLionel Sambuc 	mparse_result(mp, &mdoc, &man);
75792395e9cSLionel Sambuc 	if (NULL == man && NULL == mdoc) {
75892395e9cSLionel Sambuc 		resp_baddb();
75992395e9cSLionel Sambuc 		mparse_free(mp);
76092395e9cSLionel Sambuc 		return;
76192395e9cSLionel Sambuc 	}
76292395e9cSLionel Sambuc 
76392395e9cSLionel Sambuc 	resp_begin_html(200, NULL);
76492395e9cSLionel Sambuc 	resp_searchform(req);
76592395e9cSLionel Sambuc 
76692395e9cSLionel Sambuc 	vp = html_alloc(opts);
76792395e9cSLionel Sambuc 
76892395e9cSLionel Sambuc 	if (NULL != mdoc)
76992395e9cSLionel Sambuc 		html_mdoc(vp, mdoc);
77092395e9cSLionel Sambuc 	else
77192395e9cSLionel Sambuc 		html_man(vp, man);
77292395e9cSLionel Sambuc 
77392395e9cSLionel Sambuc 	puts("</BODY>\n"
77492395e9cSLionel Sambuc 	     "</HTML>");
77592395e9cSLionel Sambuc 
77692395e9cSLionel Sambuc 	html_free(vp);
77792395e9cSLionel Sambuc 	mparse_free(mp);
77892395e9cSLionel Sambuc }
77992395e9cSLionel Sambuc 
78092395e9cSLionel Sambuc static void
pg_show(const struct req * req,char * path)78192395e9cSLionel Sambuc pg_show(const struct req *req, char *path)
78292395e9cSLionel Sambuc {
78392395e9cSLionel Sambuc 	struct manpaths	 ps;
78492395e9cSLionel Sambuc 	size_t		 sz;
78592395e9cSLionel Sambuc 	char		*sub;
786*0a6a1f1dSLionel Sambuc 	char		 file[PATH_MAX];
78792395e9cSLionel Sambuc 	const char	*cp;
78892395e9cSLionel Sambuc 	int		 rc, catm;
78992395e9cSLionel Sambuc 	unsigned int	 vol, rec, mr;
79092395e9cSLionel Sambuc 	DB		*idx;
79192395e9cSLionel Sambuc 	DBT		 key, val;
79292395e9cSLionel Sambuc 
79392395e9cSLionel Sambuc 	idx = NULL;
79492395e9cSLionel Sambuc 
79592395e9cSLionel Sambuc 	/* Parse out mroot, volume, and record from the path. */
79692395e9cSLionel Sambuc 
79792395e9cSLionel Sambuc 	if (NULL == path || NULL == (sub = strchr(path, '/'))) {
79892395e9cSLionel Sambuc 		resp_error400();
79992395e9cSLionel Sambuc 		return;
80092395e9cSLionel Sambuc 	}
80192395e9cSLionel Sambuc 	*sub++ = '\0';
80292395e9cSLionel Sambuc 	if ( ! atou(path, &mr)) {
80392395e9cSLionel Sambuc 		resp_error400();
80492395e9cSLionel Sambuc 		return;
80592395e9cSLionel Sambuc 	}
80692395e9cSLionel Sambuc 	path = sub;
80792395e9cSLionel Sambuc 	if (NULL == (sub = strchr(path, '/'))) {
80892395e9cSLionel Sambuc 		resp_error400();
80992395e9cSLionel Sambuc 		return;
81092395e9cSLionel Sambuc 	}
81192395e9cSLionel Sambuc 	*sub++ = '\0';
81292395e9cSLionel Sambuc 	if ( ! atou(path, &vol) || ! atou(sub, &rec)) {
81392395e9cSLionel Sambuc 		resp_error400();
81492395e9cSLionel Sambuc 		return;
81592395e9cSLionel Sambuc 	} else if (mr >= (unsigned int)req->psz) {
81692395e9cSLionel Sambuc 		resp_error400();
81792395e9cSLionel Sambuc 		return;
81892395e9cSLionel Sambuc 	}
81992395e9cSLionel Sambuc 
82092395e9cSLionel Sambuc 	/*
82192395e9cSLionel Sambuc 	 * Begin by chdir()ing into the manroot.
82292395e9cSLionel Sambuc 	 * This way we can pick up the database files, which are
82392395e9cSLionel Sambuc 	 * relative to the manpath root.
82492395e9cSLionel Sambuc 	 */
82592395e9cSLionel Sambuc 
82692395e9cSLionel Sambuc 	if (-1 == chdir(req->p[(int)mr].path)) {
82792395e9cSLionel Sambuc 		perror(req->p[(int)mr].path);
82892395e9cSLionel Sambuc 		resp_baddb();
82992395e9cSLionel Sambuc 		return;
83092395e9cSLionel Sambuc 	}
83192395e9cSLionel Sambuc 
83292395e9cSLionel Sambuc 	memset(&ps, 0, sizeof(struct manpaths));
83392395e9cSLionel Sambuc 	manpath_manconf(&ps, "etc/catman.conf");
83492395e9cSLionel Sambuc 
83592395e9cSLionel Sambuc 	if (vol >= (unsigned int)ps.sz) {
83692395e9cSLionel Sambuc 		resp_error400();
83792395e9cSLionel Sambuc 		goto out;
83892395e9cSLionel Sambuc 	}
83992395e9cSLionel Sambuc 
840*0a6a1f1dSLionel Sambuc 	sz = strlcpy(file, ps.paths[vol], PATH_MAX);
841*0a6a1f1dSLionel Sambuc 	assert(sz < PATH_MAX);
842*0a6a1f1dSLionel Sambuc 	strlcat(file, "/", PATH_MAX);
843*0a6a1f1dSLionel Sambuc 	strlcat(file, MANDOC_IDX, PATH_MAX);
84492395e9cSLionel Sambuc 
84592395e9cSLionel Sambuc 	/* Open the index recno(3) database. */
84692395e9cSLionel Sambuc 
84792395e9cSLionel Sambuc 	idx = dbopen(file, O_RDONLY, 0, DB_RECNO, NULL);
84892395e9cSLionel Sambuc 	if (NULL == idx) {
84992395e9cSLionel Sambuc 		perror(file);
85092395e9cSLionel Sambuc 		resp_baddb();
85192395e9cSLionel Sambuc 		goto out;
85292395e9cSLionel Sambuc 	}
85392395e9cSLionel Sambuc 
85492395e9cSLionel Sambuc 	key.data = &rec;
85592395e9cSLionel Sambuc 	key.size = 4;
85692395e9cSLionel Sambuc 
85792395e9cSLionel Sambuc 	if (0 != (rc = (*idx->get)(idx, &key, &val, 0))) {
85892395e9cSLionel Sambuc 		rc < 0 ? resp_baddb() : resp_error400();
85992395e9cSLionel Sambuc 		goto out;
86092395e9cSLionel Sambuc 	} else if (0 == val.size) {
86192395e9cSLionel Sambuc 		resp_baddb();
86292395e9cSLionel Sambuc 		goto out;
86392395e9cSLionel Sambuc 	}
86492395e9cSLionel Sambuc 
86592395e9cSLionel Sambuc 	cp = (char *)val.data;
86692395e9cSLionel Sambuc 	catm = 'c' == *cp++;
86792395e9cSLionel Sambuc 
86892395e9cSLionel Sambuc 	if (NULL == memchr(cp, '\0', val.size - 1))
86992395e9cSLionel Sambuc 		resp_baddb();
87092395e9cSLionel Sambuc 	else {
87192395e9cSLionel Sambuc  		file[(int)sz] = '\0';
872*0a6a1f1dSLionel Sambuc  		strlcat(file, "/", PATH_MAX);
873*0a6a1f1dSLionel Sambuc  		strlcat(file, cp, PATH_MAX);
87492395e9cSLionel Sambuc 		if (catm)
87592395e9cSLionel Sambuc 			catman(req, file);
87692395e9cSLionel Sambuc 		else
87792395e9cSLionel Sambuc 			format(req, file);
87892395e9cSLionel Sambuc 	}
87992395e9cSLionel Sambuc out:
88092395e9cSLionel Sambuc 	if (idx)
88192395e9cSLionel Sambuc 		(*idx->close)(idx);
88292395e9cSLionel Sambuc 	manpath_free(&ps);
88392395e9cSLionel Sambuc }
88492395e9cSLionel Sambuc 
88592395e9cSLionel Sambuc static void
pg_search(const struct req * req,char * path)88692395e9cSLionel Sambuc pg_search(const struct req *req, char *path)
88792395e9cSLionel Sambuc {
88884d9c625SLionel Sambuc 	size_t		  tt, ressz;
88992395e9cSLionel Sambuc 	struct manpaths	  ps;
89092395e9cSLionel Sambuc 	int		  i, sz, rc;
89192395e9cSLionel Sambuc 	const char	 *ep, *start;
89284d9c625SLionel Sambuc 	struct res	*res;
89392395e9cSLionel Sambuc 	char		**cp;
89492395e9cSLionel Sambuc 	struct opts	  opt;
89592395e9cSLionel Sambuc 	struct expr	 *expr;
89692395e9cSLionel Sambuc 
89792395e9cSLionel Sambuc 	if (req->q.manroot < 0 || 0 == req->psz) {
89892395e9cSLionel Sambuc 		resp_search(NULL, 0, (void *)req);
89992395e9cSLionel Sambuc 		return;
90092395e9cSLionel Sambuc 	}
90192395e9cSLionel Sambuc 
90292395e9cSLionel Sambuc 	memset(&opt, 0, sizeof(struct opts));
90392395e9cSLionel Sambuc 
90492395e9cSLionel Sambuc 	ep 	 = req->q.expr;
90592395e9cSLionel Sambuc 	opt.arch = req->q.arch;
90692395e9cSLionel Sambuc 	opt.cat  = req->q.sec;
90792395e9cSLionel Sambuc 	rc 	 = -1;
90892395e9cSLionel Sambuc 	sz 	 = 0;
90992395e9cSLionel Sambuc 	cp	 = NULL;
91084d9c625SLionel Sambuc 	ressz	 = 0;
91184d9c625SLionel Sambuc 	res	 = NULL;
91292395e9cSLionel Sambuc 
91392395e9cSLionel Sambuc 	/*
91492395e9cSLionel Sambuc 	 * Begin by chdir()ing into the root of the manpath.
91592395e9cSLionel Sambuc 	 * This way we can pick up the database files, which are
91692395e9cSLionel Sambuc 	 * relative to the manpath root.
91792395e9cSLionel Sambuc 	 */
91892395e9cSLionel Sambuc 
91992395e9cSLionel Sambuc 	assert(req->q.manroot < (int)req->psz);
92092395e9cSLionel Sambuc 	if (-1 == (chdir(req->p[req->q.manroot].path))) {
92192395e9cSLionel Sambuc 		perror(req->p[req->q.manroot].path);
92292395e9cSLionel Sambuc 		resp_search(NULL, 0, (void *)req);
92392395e9cSLionel Sambuc 		return;
92492395e9cSLionel Sambuc 	}
92592395e9cSLionel Sambuc 
92692395e9cSLionel Sambuc 	memset(&ps, 0, sizeof(struct manpaths));
92792395e9cSLionel Sambuc 	manpath_manconf(&ps, "etc/catman.conf");
92892395e9cSLionel Sambuc 
92992395e9cSLionel Sambuc 	/*
93092395e9cSLionel Sambuc 	 * Poor man's tokenisation: just break apart by spaces.
93192395e9cSLionel Sambuc 	 * Yes, this is half-ass.  But it works for now.
93292395e9cSLionel Sambuc 	 */
93392395e9cSLionel Sambuc 
93492395e9cSLionel Sambuc 	while (ep && isspace((unsigned char)*ep))
93592395e9cSLionel Sambuc 		ep++;
93692395e9cSLionel Sambuc 
93792395e9cSLionel Sambuc 	while (ep && '\0' != *ep) {
93892395e9cSLionel Sambuc 		cp = mandoc_realloc(cp, (sz + 1) * sizeof(char *));
93992395e9cSLionel Sambuc 		start = ep;
94092395e9cSLionel Sambuc 		while ('\0' != *ep && ! isspace((unsigned char)*ep))
94192395e9cSLionel Sambuc 			ep++;
94292395e9cSLionel Sambuc 		cp[sz] = mandoc_malloc((ep - start) + 1);
94392395e9cSLionel Sambuc 		memcpy(cp[sz], start, ep - start);
94492395e9cSLionel Sambuc 		cp[sz++][ep - start] = '\0';
94592395e9cSLionel Sambuc 		while (isspace((unsigned char)*ep))
94692395e9cSLionel Sambuc 			ep++;
94792395e9cSLionel Sambuc 	}
94892395e9cSLionel Sambuc 
94992395e9cSLionel Sambuc 	/*
95092395e9cSLionel Sambuc 	 * Pump down into apropos backend.
95192395e9cSLionel Sambuc 	 * The resp_search() function is called with the results.
95292395e9cSLionel Sambuc 	 */
95392395e9cSLionel Sambuc 
95484d9c625SLionel Sambuc 	expr = req->q.legacy ?
95592395e9cSLionel Sambuc 		termcomp(sz, cp, &tt) : exprcomp(sz, cp, &tt);
95692395e9cSLionel Sambuc 
95792395e9cSLionel Sambuc 	if (NULL != expr)
95892395e9cSLionel Sambuc 		rc = apropos_search
95984d9c625SLionel Sambuc 			(ps.sz, ps.paths, &opt, expr, tt,
96084d9c625SLionel Sambuc 			 (void *)req, &ressz, &res, resp_search);
96192395e9cSLionel Sambuc 
96292395e9cSLionel Sambuc 	/* ...unless errors occured. */
96392395e9cSLionel Sambuc 
96492395e9cSLionel Sambuc 	if (0 == rc)
96592395e9cSLionel Sambuc 		resp_baddb();
96692395e9cSLionel Sambuc 	else if (-1 == rc)
96784d9c625SLionel Sambuc 		resp_search(NULL, 0, NULL);
96892395e9cSLionel Sambuc 
96992395e9cSLionel Sambuc 	for (i = 0; i < sz; i++)
97092395e9cSLionel Sambuc 		free(cp[i]);
97192395e9cSLionel Sambuc 
97292395e9cSLionel Sambuc 	free(cp);
97384d9c625SLionel Sambuc 	resfree(res, ressz);
97492395e9cSLionel Sambuc 	exprfree(expr);
97592395e9cSLionel Sambuc 	manpath_free(&ps);
97692395e9cSLionel Sambuc }
97792395e9cSLionel Sambuc 
97892395e9cSLionel Sambuc int
main(void)97992395e9cSLionel Sambuc main(void)
98092395e9cSLionel Sambuc {
98192395e9cSLionel Sambuc 	int		 i;
982*0a6a1f1dSLionel Sambuc 	char		 buf[PATH_MAX];
98392395e9cSLionel Sambuc 	DIR		*cwd;
98492395e9cSLionel Sambuc 	struct req	 req;
98592395e9cSLionel Sambuc 	char		*p, *path, *subpath;
98692395e9cSLionel Sambuc 
98792395e9cSLionel Sambuc 	/* Scan our run-time environment. */
98892395e9cSLionel Sambuc 
98992395e9cSLionel Sambuc 	if (NULL == (cache = getenv("CACHE_DIR")))
99092395e9cSLionel Sambuc 		cache = "/cache/man.cgi";
99192395e9cSLionel Sambuc 
99292395e9cSLionel Sambuc 	if (NULL == (progname = getenv("SCRIPT_NAME")))
99392395e9cSLionel Sambuc 		progname = "";
99492395e9cSLionel Sambuc 
99592395e9cSLionel Sambuc 	if (NULL == (css = getenv("CSS_DIR")))
99692395e9cSLionel Sambuc 		css = "";
99792395e9cSLionel Sambuc 
99892395e9cSLionel Sambuc 	if (NULL == (host = getenv("HTTP_HOST")))
99992395e9cSLionel Sambuc 		host = "localhost";
100092395e9cSLionel Sambuc 
100192395e9cSLionel Sambuc 	/*
100292395e9cSLionel Sambuc 	 * First we change directory into the cache directory so that
100392395e9cSLionel Sambuc 	 * subsequent scanning for manpath directories is rooted
100492395e9cSLionel Sambuc 	 * relative to the same position.
100592395e9cSLionel Sambuc 	 */
100692395e9cSLionel Sambuc 
100792395e9cSLionel Sambuc 	if (-1 == chdir(cache)) {
100892395e9cSLionel Sambuc 		perror(cache);
100992395e9cSLionel Sambuc 		resp_bad();
101092395e9cSLionel Sambuc 		return(EXIT_FAILURE);
101192395e9cSLionel Sambuc 	} else if (NULL == (cwd = opendir(cache))) {
101292395e9cSLionel Sambuc 		perror(cache);
101392395e9cSLionel Sambuc 		resp_bad();
101492395e9cSLionel Sambuc 		return(EXIT_FAILURE);
101592395e9cSLionel Sambuc 	}
101692395e9cSLionel Sambuc 
101792395e9cSLionel Sambuc 	memset(&req, 0, sizeof(struct req));
101892395e9cSLionel Sambuc 
1019*0a6a1f1dSLionel Sambuc 	strlcpy(buf, ".", PATH_MAX);
102092395e9cSLionel Sambuc 	pathgen(cwd, buf, &req);
102192395e9cSLionel Sambuc 	closedir(cwd);
102292395e9cSLionel Sambuc 
102392395e9cSLionel Sambuc 	/* Next parse out the query string. */
102492395e9cSLionel Sambuc 
102592395e9cSLionel Sambuc 	if (NULL != (p = getenv("QUERY_STRING")))
102692395e9cSLionel Sambuc 		http_parse(&req, p);
102792395e9cSLionel Sambuc 
102892395e9cSLionel Sambuc 	/*
102992395e9cSLionel Sambuc 	 * Now juggle paths to extract information.
103092395e9cSLionel Sambuc 	 * We want to extract our filetype (the file suffix), the
103192395e9cSLionel Sambuc 	 * initial path component, then the trailing component(s).
103292395e9cSLionel Sambuc 	 * Start with leading subpath component.
103392395e9cSLionel Sambuc 	 */
103492395e9cSLionel Sambuc 
103592395e9cSLionel Sambuc 	subpath = path = NULL;
103692395e9cSLionel Sambuc 	req.page = PAGE__MAX;
103792395e9cSLionel Sambuc 
103892395e9cSLionel Sambuc 	if (NULL == (path = getenv("PATH_INFO")) || '\0' == *path)
103992395e9cSLionel Sambuc 		req.page = PAGE_INDEX;
104092395e9cSLionel Sambuc 
104192395e9cSLionel Sambuc 	if (NULL != path && '/' == *path && '\0' == *++path)
104292395e9cSLionel Sambuc 		req.page = PAGE_INDEX;
104392395e9cSLionel Sambuc 
104492395e9cSLionel Sambuc 	/* Strip file suffix. */
104592395e9cSLionel Sambuc 
104692395e9cSLionel Sambuc 	if (NULL != path && NULL != (p = strrchr(path, '.')))
104792395e9cSLionel Sambuc 		if (NULL != p && NULL == strchr(p, '/'))
104892395e9cSLionel Sambuc 			*p++ = '\0';
104992395e9cSLionel Sambuc 
105092395e9cSLionel Sambuc 	/* Resolve subpath component. */
105192395e9cSLionel Sambuc 
105292395e9cSLionel Sambuc 	if (NULL != path && NULL != (subpath = strchr(path, '/')))
105392395e9cSLionel Sambuc 		*subpath++ = '\0';
105492395e9cSLionel Sambuc 
105592395e9cSLionel Sambuc 	/* Map path into one we recognise. */
105692395e9cSLionel Sambuc 
105792395e9cSLionel Sambuc 	if (NULL != path && '\0' != *path)
105892395e9cSLionel Sambuc 		for (i = 0; i < (int)PAGE__MAX; i++)
105992395e9cSLionel Sambuc 			if (0 == strcmp(pages[i], path)) {
106092395e9cSLionel Sambuc 				req.page = (enum page)i;
106192395e9cSLionel Sambuc 				break;
106292395e9cSLionel Sambuc 			}
106392395e9cSLionel Sambuc 
106492395e9cSLionel Sambuc 	/* Route pages. */
106592395e9cSLionel Sambuc 
106692395e9cSLionel Sambuc 	switch (req.page) {
106792395e9cSLionel Sambuc 	case (PAGE_INDEX):
106892395e9cSLionel Sambuc 		pg_index(&req, subpath);
106992395e9cSLionel Sambuc 		break;
107092395e9cSLionel Sambuc 	case (PAGE_SEARCH):
107192395e9cSLionel Sambuc 		pg_search(&req, subpath);
107292395e9cSLionel Sambuc 		break;
107392395e9cSLionel Sambuc 	case (PAGE_SHOW):
107492395e9cSLionel Sambuc 		pg_show(&req, subpath);
107592395e9cSLionel Sambuc 		break;
107692395e9cSLionel Sambuc 	default:
107792395e9cSLionel Sambuc 		resp_error404(path);
107892395e9cSLionel Sambuc 		break;
107992395e9cSLionel Sambuc 	}
108092395e9cSLionel Sambuc 
108192395e9cSLionel Sambuc 	for (i = 0; i < (int)req.psz; i++) {
108292395e9cSLionel Sambuc 		free(req.p[i].path);
108392395e9cSLionel Sambuc 		free(req.p[i].name);
108492395e9cSLionel Sambuc 	}
108592395e9cSLionel Sambuc 
108692395e9cSLionel Sambuc 	free(req.p);
108792395e9cSLionel Sambuc 	return(EXIT_SUCCESS);
108892395e9cSLionel Sambuc }
108992395e9cSLionel Sambuc 
109092395e9cSLionel Sambuc static int
cmp(const void * p1,const void * p2)109192395e9cSLionel Sambuc cmp(const void *p1, const void *p2)
109292395e9cSLionel Sambuc {
109392395e9cSLionel Sambuc 
109492395e9cSLionel Sambuc 	return(strcasecmp(((const struct res *)p1)->title,
109592395e9cSLionel Sambuc 				((const struct res *)p2)->title));
109692395e9cSLionel Sambuc }
109792395e9cSLionel Sambuc 
109892395e9cSLionel Sambuc /*
109992395e9cSLionel Sambuc  * Check to see if an "etc" path consists of a catman.conf file.  If it
110092395e9cSLionel Sambuc  * does, that means that the path contains a tree created by catman(8)
110192395e9cSLionel Sambuc  * and should be used for indexing.
110292395e9cSLionel Sambuc  */
110392395e9cSLionel Sambuc static int
pathstop(DIR * dir)110492395e9cSLionel Sambuc pathstop(DIR *dir)
110592395e9cSLionel Sambuc {
110692395e9cSLionel Sambuc 	struct dirent	*d;
1107*0a6a1f1dSLionel Sambuc #if defined(__sun)
1108*0a6a1f1dSLionel Sambuc 	struct stat	 sb;
1109*0a6a1f1dSLionel Sambuc #endif
111092395e9cSLionel Sambuc 
1111*0a6a1f1dSLionel Sambuc 	while (NULL != (d = readdir(dir))) {
1112*0a6a1f1dSLionel Sambuc #if defined(__sun)
1113*0a6a1f1dSLionel Sambuc 		stat(d->d_name, &sb);
1114*0a6a1f1dSLionel Sambuc 		if (S_IFREG & sb.st_mode)
1115*0a6a1f1dSLionel Sambuc #else
111692395e9cSLionel Sambuc 		if (DT_REG == d->d_type)
1117*0a6a1f1dSLionel Sambuc #endif
111892395e9cSLionel Sambuc 			if (0 == strcmp(d->d_name, "catman.conf"))
111992395e9cSLionel Sambuc 				return(1);
1120*0a6a1f1dSLionel Sambuc   }
112192395e9cSLionel Sambuc 
112292395e9cSLionel Sambuc 	return(0);
112392395e9cSLionel Sambuc }
112492395e9cSLionel Sambuc 
112592395e9cSLionel Sambuc /*
112692395e9cSLionel Sambuc  * Scan for indexable paths.
112792395e9cSLionel Sambuc  * This adds all paths with "etc/catman.conf" to the buffer.
112892395e9cSLionel Sambuc  */
112992395e9cSLionel Sambuc static void
pathgen(DIR * dir,char * path,struct req * req)113092395e9cSLionel Sambuc pathgen(DIR *dir, char *path, struct req *req)
113192395e9cSLionel Sambuc {
113292395e9cSLionel Sambuc 	struct dirent	*d;
113392395e9cSLionel Sambuc 	char		*cp;
113492395e9cSLionel Sambuc 	DIR		*cd;
113592395e9cSLionel Sambuc 	int		 rc;
113692395e9cSLionel Sambuc 	size_t		 sz, ssz;
1137*0a6a1f1dSLionel Sambuc #if defined(__sun)
1138*0a6a1f1dSLionel Sambuc 	struct stat	 sb;
1139*0a6a1f1dSLionel Sambuc #endif
114092395e9cSLionel Sambuc 
1141*0a6a1f1dSLionel Sambuc 	sz = strlcat(path, "/", PATH_MAX);
1142*0a6a1f1dSLionel Sambuc 	if (sz >= PATH_MAX) {
114392395e9cSLionel Sambuc 		fprintf(stderr, "%s: Path too long", path);
114492395e9cSLionel Sambuc 		return;
114592395e9cSLionel Sambuc 	}
114692395e9cSLionel Sambuc 
114792395e9cSLionel Sambuc 	/*
114892395e9cSLionel Sambuc 	 * First, scan for the "etc" directory.
114992395e9cSLionel Sambuc 	 * If it's found, then see if it should cause us to stop.  This
115092395e9cSLionel Sambuc 	 * happens when a catman.conf is found in the directory.
115192395e9cSLionel Sambuc 	 */
115292395e9cSLionel Sambuc 
115392395e9cSLionel Sambuc 	rc = 0;
115492395e9cSLionel Sambuc 	while (0 == rc && NULL != (d = readdir(dir))) {
1155*0a6a1f1dSLionel Sambuc #if defined(__sun)
1156*0a6a1f1dSLionel Sambuc 		stat(d->d_name, &sb);
1157*0a6a1f1dSLionel Sambuc 		if (!(S_IFDIR & sb.st_mode)
1158*0a6a1f1dSLionel Sambuc #else
1159*0a6a1f1dSLionel Sambuc 		if (DT_DIR != d->d_type
1160*0a6a1f1dSLionel Sambuc #endif
1161*0a6a1f1dSLionel Sambuc         || strcmp(d->d_name, "etc"))
116292395e9cSLionel Sambuc 			continue;
116392395e9cSLionel Sambuc 
116492395e9cSLionel Sambuc 		path[(int)sz] = '\0';
1165*0a6a1f1dSLionel Sambuc 		ssz = strlcat(path, d->d_name, PATH_MAX);
116692395e9cSLionel Sambuc 
1167*0a6a1f1dSLionel Sambuc 		if (ssz >= PATH_MAX) {
116892395e9cSLionel Sambuc 			fprintf(stderr, "%s: Path too long", path);
116992395e9cSLionel Sambuc 			return;
117092395e9cSLionel Sambuc 		} else if (NULL == (cd = opendir(path))) {
117192395e9cSLionel Sambuc 			perror(path);
117292395e9cSLionel Sambuc 			return;
117392395e9cSLionel Sambuc 		}
117492395e9cSLionel Sambuc 
117592395e9cSLionel Sambuc 		rc = pathstop(cd);
117692395e9cSLionel Sambuc 		closedir(cd);
117792395e9cSLionel Sambuc 	}
117892395e9cSLionel Sambuc 
117992395e9cSLionel Sambuc 	if (rc > 0) {
118092395e9cSLionel Sambuc 		/* This also strips the trailing slash. */
118192395e9cSLionel Sambuc 		path[(int)--sz] = '\0';
118292395e9cSLionel Sambuc 		req->p = mandoc_realloc
118392395e9cSLionel Sambuc 			(req->p,
118492395e9cSLionel Sambuc 			 (req->psz + 1) * sizeof(struct paths));
118592395e9cSLionel Sambuc 		/*
118692395e9cSLionel Sambuc 		 * Strip out the leading "./" unless we're just a ".",
118792395e9cSLionel Sambuc 		 * in which case use an empty string as our name.
118892395e9cSLionel Sambuc 		 */
118992395e9cSLionel Sambuc 		req->p[(int)req->psz].path = mandoc_strdup(path);
119092395e9cSLionel Sambuc 		req->p[(int)req->psz].name =
119192395e9cSLionel Sambuc 			cp = mandoc_strdup(path + (1 == sz ? 1 : 2));
119292395e9cSLionel Sambuc 		req->psz++;
119392395e9cSLionel Sambuc 		/*
119492395e9cSLionel Sambuc 		 * The name is just the path with all the slashes taken
119592395e9cSLionel Sambuc 		 * out of it.  Simple but effective.
119692395e9cSLionel Sambuc 		 */
119792395e9cSLionel Sambuc 		for ( ; '\0' != *cp; cp++)
119892395e9cSLionel Sambuc 			if ('/' == *cp)
119992395e9cSLionel Sambuc 				*cp = ' ';
120092395e9cSLionel Sambuc 		return;
120192395e9cSLionel Sambuc 	}
120292395e9cSLionel Sambuc 
120392395e9cSLionel Sambuc 	/*
120492395e9cSLionel Sambuc 	 * If no etc/catman.conf was found, recursively enter child
120592395e9cSLionel Sambuc 	 * directory and continue scanning.
120692395e9cSLionel Sambuc 	 */
120792395e9cSLionel Sambuc 
120892395e9cSLionel Sambuc 	rewinddir(dir);
120992395e9cSLionel Sambuc 	while (NULL != (d = readdir(dir))) {
1210*0a6a1f1dSLionel Sambuc #if defined(__sun)
1211*0a6a1f1dSLionel Sambuc 		stat(d->d_name, &sb);
1212*0a6a1f1dSLionel Sambuc 		if (!(S_IFDIR & sb.st_mode)
1213*0a6a1f1dSLionel Sambuc #else
1214*0a6a1f1dSLionel Sambuc 		if (DT_DIR != d->d_type
1215*0a6a1f1dSLionel Sambuc #endif
1216*0a6a1f1dSLionel Sambuc         || '.' == d->d_name[0])
121792395e9cSLionel Sambuc 			continue;
121892395e9cSLionel Sambuc 
121992395e9cSLionel Sambuc 		path[(int)sz] = '\0';
1220*0a6a1f1dSLionel Sambuc 		ssz = strlcat(path, d->d_name, PATH_MAX);
122192395e9cSLionel Sambuc 
1222*0a6a1f1dSLionel Sambuc 		if (ssz >= PATH_MAX) {
122392395e9cSLionel Sambuc 			fprintf(stderr, "%s: Path too long", path);
122492395e9cSLionel Sambuc 			return;
122592395e9cSLionel Sambuc 		} else if (NULL == (cd = opendir(path))) {
122692395e9cSLionel Sambuc 			perror(path);
122792395e9cSLionel Sambuc 			return;
122892395e9cSLionel Sambuc 		}
122992395e9cSLionel Sambuc 
123092395e9cSLionel Sambuc 		pathgen(cd, path, req);
123192395e9cSLionel Sambuc 		closedir(cd);
123292395e9cSLionel Sambuc 	}
123392395e9cSLionel Sambuc }
1234