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(""e;");
16192395e9cSLionel Sambuc break;
16292395e9cSLionel Sambuc case ('&'):
16392395e9cSLionel Sambuc printf("&");
16492395e9cSLionel Sambuc break;
16592395e9cSLionel Sambuc case ('>'):
16692395e9cSLionel Sambuc printf(">");
16792395e9cSLionel Sambuc break;
16892395e9cSLionel Sambuc case ('<'):
16992395e9cSLionel Sambuc printf("<");
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("&expr=");
19492395e9cSLionel Sambuc html_print(req->q.expr ? req->q.expr : "");
19592395e9cSLionel Sambuc printf("&sec=");
19692395e9cSLionel Sambuc html_print(req->q.sec ? req->q.sec : "");
19792395e9cSLionel Sambuc printf("&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