]>
git.cameronkatri.com Git - mandoc.git/blob - cgi.c
1 /* $Id: cgi.c,v 1.149 2017/03/15 10:17:29 schwarze Exp $ */
3 * Copyright (c) 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv>
4 * Copyright (c) 2014, 2015, 2016, 2017 Ingo Schwarze <schwarze@usta.de>
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20 #include <sys/types.h>
36 #include "mandoc_aux.h"
43 #include "mansearch.h"
47 * A query as passed to the search function.
50 char *manpath
; /* desired manual directory */
51 char *arch
; /* architecture */
52 char *sec
; /* manual section */
53 char *query
; /* unparsed query expression */
54 int equal
; /* match whole names, not substrings */
59 char **p
; /* array of available manpaths */
60 size_t psz
; /* number of available manpaths */
61 int isquery
; /* QUERY_STRING used, not PATH_INFO */
69 static void html_print(const char *);
70 static void html_putchar(char);
71 static int http_decode(char *);
72 static void parse_manpath_conf(struct req
*);
73 static void parse_path_info(struct req
*req
, const char *path
);
74 static void parse_query_string(struct req
*, const char *);
75 static void pg_error_badrequest(const char *);
76 static void pg_error_internal(void);
77 static void pg_index(const struct req
*);
78 static void pg_noresult(const struct req
*, const char *);
79 static void pg_redirect(const struct req
*, const char *);
80 static void pg_search(const struct req
*);
81 static void pg_searchres(const struct req
*,
82 struct manpage
*, size_t);
83 static void pg_show(struct req
*, const char *);
84 static void resp_begin_html(int, const char *);
85 static void resp_begin_http(int, const char *);
86 static void resp_catman(const struct req
*, const char *);
87 static void resp_copy(const char *);
88 static void resp_end_html(void);
89 static void resp_format(const struct req
*, const char *);
90 static void resp_searchform(const struct req
*, enum focus
);
91 static void resp_show(const struct req
*, const char *);
92 static void set_query_attr(char **, char **);
93 static int validate_filename(const char *);
94 static int validate_manpath(const struct req
*, const char *);
95 static int validate_urifrag(const char *);
97 static const char *scriptname
= SCRIPT_NAME
;
99 static const int sec_prios
[] = {1, 4, 5, 8, 6, 3, 7, 2, 9};
100 static const char *const sec_numbers
[] = {
101 "0", "1", "2", "3", "3p", "4", "5", "6", "7", "8", "9"
103 static const char *const sec_names
[] = {
105 "1 - General Commands",
107 "3 - Library Functions",
109 "4 - Device Drivers",
112 "7 - Miscellaneous Information",
113 "8 - System Manager\'s Manual",
114 "9 - Kernel Developer\'s Manual"
116 static const int sec_MAX
= sizeof(sec_names
) / sizeof(char *);
118 static const char *const arch_names
[] = {
119 "amd64", "alpha", "armv7", "arm64",
120 "hppa", "i386", "landisk",
121 "loongson", "luna88k", "macppc", "mips64",
122 "octeon", "sgi", "socppc", "sparc64",
123 "amiga", "arc", "armish", "arm32",
124 "atari", "aviion", "beagle", "cats",
126 "ia64", "mac68k", "mvme68k", "mvme88k",
127 "mvmeppc", "palm", "pc532", "pegasos",
128 "pmax", "powerpc", "solbourne", "sparc",
129 "sun3", "vax", "wgrisc", "x68k",
132 static const int arch_MAX
= sizeof(arch_names
) / sizeof(char *);
135 * Print a character, escaping HTML along the way.
136 * This will pass non-ASCII straight to output: be warned!
156 putchar((unsigned char)c
);
162 * Call through to html_putchar().
163 * Accepts NULL strings.
166 html_print(const char *p
)
176 * Transfer the responsibility for the allocated string *val
177 * to the query structure.
180 set_query_attr(char **attr
, char **val
)
193 * Parse the QUERY_STRING for key-value pairs
194 * and store the values into the query structure.
197 parse_query_string(struct req
*req
, const char *qs
)
203 req
->q
.manpath
= NULL
;
210 while (*qs
!= '\0') {
214 keysz
= strcspn(qs
, "=;&");
215 key
= mandoc_strndup(qs
, keysz
);
220 /* Parse one value. */
222 valsz
= strcspn(++qs
, ";&");
223 val
= mandoc_strndup(qs
, valsz
);
226 /* Decode and catch encoding errors. */
228 if ( ! (http_decode(key
) && http_decode(val
)))
231 /* Handle key-value pairs. */
233 if ( ! strcmp(key
, "query"))
234 set_query_attr(&req
->q
.query
, &val
);
236 else if ( ! strcmp(key
, "apropos"))
237 req
->q
.equal
= !strcmp(val
, "0");
239 else if ( ! strcmp(key
, "manpath")) {
241 if ( ! strncmp(val
, "OpenBSD ", 8)) {
247 set_query_attr(&req
->q
.manpath
, &val
);
250 else if ( ! (strcmp(key
, "sec")
252 && strcmp(key
, "sektion")
255 if ( ! strcmp(val
, "0"))
257 set_query_attr(&req
->q
.sec
, &val
);
260 else if ( ! strcmp(key
, "arch")) {
261 if ( ! strcmp(val
, "default"))
263 set_query_attr(&req
->q
.arch
, &val
);
267 * The key must be freed in any case.
268 * The val may have been handed over to the query
269 * structure, in which case it is now NULL.
283 * HTTP-decode a string. The standard explanation is that this turns
284 * "%4e+foo" into "n foo" in the regular way. This is done in-place
285 * over the allocated string.
297 for ( ; '\0' != *p
; p
++, q
++) {
299 if ('\0' == (hex
[0] = *(p
+ 1)))
301 if ('\0' == (hex
[1] = *(p
+ 2)))
303 if (1 != sscanf(hex
, "%x", &c
))
311 *q
= '+' == *p
? ' ' : *p
;
319 resp_begin_http(int code
, const char *msg
)
323 printf("Status: %d %s\r\n", code
, msg
);
325 printf("Content-Type: text/html; charset=utf-8\r\n"
326 "Cache-Control: no-cache\r\n"
327 "Pragma: no-cache\r\n"
334 resp_copy(const char *filename
)
340 if ((fd
= open(filename
, O_RDONLY
)) != -1) {
342 while ((sz
= read(fd
, buf
, sizeof(buf
))) > 0)
343 write(STDOUT_FILENO
, buf
, sz
);
349 resp_begin_html(int code
, const char *msg
)
352 resp_begin_http(code
, msg
);
354 printf("<!DOCTYPE html>\n"
357 " <meta charset=\"UTF-8\"/>\n"
358 " <link rel=\"stylesheet\" href=\"%s/mandoc.css\""
359 " type=\"text/css\" media=\"all\">\n"
360 " <title>%s</title>\n"
363 CSS_DIR
, CUSTOMIZE_TITLE
);
365 resp_copy(MAN_DIR
"/header.html");
372 resp_copy(MAN_DIR
"/footer.html");
379 resp_searchform(const struct req
*req
, enum focus focus
)
383 printf("<form action=\"/%s\" method=\"get\">\n"
385 " <legend>Manual Page Search Parameters</legend>\n",
388 /* Write query input box. */
390 printf(" <input type=\"text\" name=\"query\" value=\"");
391 if (req
->q
.query
!= NULL
)
392 html_print(req
->q
.query
);
393 printf( "\" size=\"40\"");
394 if (focus
== FOCUS_QUERY
)
395 printf(" autofocus");
398 /* Write submission buttons. */
400 printf( " <button type=\"submit\" name=\"apropos\" value=\"0\">"
402 " <button type=\"submit\" name=\"apropos\" value=\"1\">"
406 /* Write section selector. */
408 puts(" <select name=\"sec\">");
409 for (i
= 0; i
< sec_MAX
; i
++) {
410 printf(" <option value=\"%s\"", sec_numbers
[i
]);
411 if (NULL
!= req
->q
.sec
&&
412 0 == strcmp(sec_numbers
[i
], req
->q
.sec
))
413 printf(" selected=\"selected\"");
414 printf(">%s</option>\n", sec_names
[i
]);
418 /* Write architecture selector. */
420 printf( " <select name=\"arch\">\n"
421 " <option value=\"default\"");
422 if (NULL
== req
->q
.arch
)
423 printf(" selected=\"selected\"");
424 puts(">All Architectures</option>");
425 for (i
= 0; i
< arch_MAX
; i
++) {
426 printf(" <option value=\"%s\"", arch_names
[i
]);
427 if (NULL
!= req
->q
.arch
&&
428 0 == strcmp(arch_names
[i
], req
->q
.arch
))
429 printf(" selected=\"selected\"");
430 printf(">%s</option>\n", arch_names
[i
]);
434 /* Write manpath selector. */
437 puts(" <select name=\"manpath\">");
438 for (i
= 0; i
< (int)req
->psz
; i
++) {
440 if (strcmp(req
->q
.manpath
, req
->p
[i
]) == 0)
441 printf("selected=\"selected\" ");
443 html_print(req
->p
[i
]);
445 html_print(req
->p
[i
]);
451 puts(" </fieldset>\n"
456 validate_urifrag(const char *frag
)
459 while ('\0' != *frag
) {
460 if ( ! (isalnum((unsigned char)*frag
) ||
461 '-' == *frag
|| '.' == *frag
||
462 '/' == *frag
|| '_' == *frag
))
470 validate_manpath(const struct req
*req
, const char* manpath
)
474 for (i
= 0; i
< req
->psz
; i
++)
475 if ( ! strcmp(manpath
, req
->p
[i
]))
482 validate_filename(const char *file
)
485 if ('.' == file
[0] && '/' == file
[1])
488 return ! (strstr(file
, "../") || strstr(file
, "/..") ||
489 (strncmp(file
, "man", 3) && strncmp(file
, "cat", 3)));
493 pg_index(const struct req
*req
)
496 resp_begin_html(200, NULL
);
497 resp_searchform(req
, FOCUS_QUERY
);
499 "This web interface is documented in the\n"
500 "<a class=\"Xr\" href=\"/%s%sman.cgi.8\">man.cgi(8)</a>\n"
502 "<a class=\"Xr\" href=\"/%s%sapropos.1\">apropos(1)</a>\n"
503 "manual explains the query syntax.\n"
505 scriptname
, *scriptname
== '\0' ? "" : "/",
506 scriptname
, *scriptname
== '\0' ? "" : "/");
511 pg_noresult(const struct req
*req
, const char *msg
)
513 resp_begin_html(200, NULL
);
514 resp_searchform(req
, FOCUS_QUERY
);
522 pg_error_badrequest(const char *msg
)
525 resp_begin_html(400, "Bad Request");
526 puts("<h1>Bad Request</h1>\n"
529 printf("Try again from the\n"
530 "<a href=\"/%s\">main page</a>.\n"
536 pg_error_internal(void)
538 resp_begin_html(500, "Internal Server Error");
539 puts("<p>Internal Server Error</p>");
544 pg_redirect(const struct req
*req
, const char *name
)
546 printf("Status: 303 See Other\r\n");
547 printf("Location: http://%s/", HTTP_HOST
);
548 if (*scriptname
!= '\0')
549 printf("%s/", scriptname
);
550 if (strcmp(req
->q
.manpath
, req
->p
[0]))
551 printf("%s/", req
->q
.manpath
);
552 if (req
->q
.arch
!= NULL
)
553 printf("%s/", req
->q
.arch
);
555 if (req
->q
.sec
!= NULL
)
556 printf(".%s", req
->q
.sec
);
557 printf("\r\nContent-Type: text/html; charset=utf-8\r\n\r\n");
561 pg_searchres(const struct req
*req
, struct manpage
*r
, size_t sz
)
563 char *arch
, *archend
;
566 int archprio
, archpriouse
;
569 for (i
= 0; i
< sz
; i
++) {
570 if (validate_filename(r
[i
].file
))
572 warnx("invalid filename %s in %s database",
573 r
[i
].file
, req
->q
.manpath
);
578 if (req
->isquery
&& sz
== 1) {
580 * If we have just one result, then jump there now
583 printf("Status: 303 See Other\r\n");
584 printf("Location: http://%s/%s%s%s/%s",
585 HTTP_HOST
, scriptname
,
586 *scriptname
== '\0' ? "" : "/",
587 req
->q
.manpath
, r
[0].file
);
589 "Content-Type: text/html; charset=utf-8\r\n"
594 resp_begin_html(200, NULL
);
596 req
->q
.equal
|| sz
== 1 ? FOCUS_NONE
: FOCUS_QUERY
);
599 puts("<table class=\"results\">");
600 for (i
= 0; i
< sz
; i
++) {
603 "<a class=\"Xr\" href=\"/%s%s%s/%s\">",
604 scriptname
, *scriptname
== '\0' ? "" : "/",
605 req
->q
.manpath
, r
[i
].file
);
606 html_print(r
[i
].names
);
608 " <td><span class=\"Nd\">");
609 html_print(r
[i
].output
);
610 puts("</span></td>\n"
617 * In man(1) mode, show one of the pages
618 * even if more than one is found.
621 if (req
->q
.equal
|| sz
== 1) {
626 for (i
= 0; i
< sz
; i
++) {
628 sec
+= strcspn(sec
, "123456789");
631 prio
= sec_prios
[sec
[0] - '1'];
634 if (req
->q
.arch
== NULL
) {
636 ((arch
= strchr(sec
+ 1, '/'))
638 ((archend
= strchr(arch
+ 1, '/'))
640 strncmp(arch
, "amd64/",
641 archend
- arch
) ? 2 : 1;
642 if (archprio
< archpriouse
) {
643 archpriouse
= archprio
;
648 if (archprio
> archpriouse
)
656 resp_show(req
, r
[iuse
].file
);
663 resp_catman(const struct req
*req
, const char *file
)
672 if ((f
= fopen(file
, "r")) == NULL
) {
673 puts("<p>You specified an invalid manual file.</p>");
677 puts("<div class=\"catman\">\n"
683 while ((len
= getline(&p
, &sz
, f
)) != -1) {
685 for (i
= 0; i
< len
- 1; i
++) {
687 * This means that the catpage is out of state.
688 * Ignore it and keep going (although the
692 if ('\b' == p
[i
] || '\n' == p
[i
])
696 * Print a regular character.
697 * Close out any bold/italic scopes.
698 * If we're in back-space mode, make sure we'll
699 * have something to enter when we backspace.
702 if ('\b' != p
[i
+ 1]) {
710 } else if (i
+ 2 >= len
)
728 * Handle funny behaviour troff-isms.
729 * These grok'd from the original man2html.c.
732 if (('+' == p
[i
] && 'o' == p
[i
+ 2]) ||
733 ('o' == p
[i
] && '+' == p
[i
+ 2]) ||
734 ('|' == p
[i
] && '=' == p
[i
+ 2]) ||
735 ('=' == p
[i
] && '|' == p
[i
+ 2]) ||
736 ('*' == p
[i
] && '=' == p
[i
+ 2]) ||
737 ('=' == p
[i
] && '*' == p
[i
+ 2]) ||
738 ('*' == p
[i
] && '|' == p
[i
+ 2]) ||
739 ('|' == p
[i
] && '*' == p
[i
+ 2])) {
748 } else if (('|' == p
[i
] && '-' == p
[i
+ 2]) ||
749 ('-' == p
[i
] && '|' == p
[i
+ 1]) ||
750 ('+' == p
[i
] && '-' == p
[i
+ 1]) ||
751 ('-' == p
[i
] && '+' == p
[i
+ 1]) ||
752 ('+' == p
[i
] && '|' == p
[i
+ 1]) ||
753 ('|' == p
[i
] && '+' == p
[i
+ 1])) {
777 * Clean up the last character.
778 * We can get to a newline; don't print that.
786 if (i
== len
- 1 && p
[i
] != '\n')
800 resp_format(const struct req
*req
, const char *file
)
802 struct manoutput conf
;
804 struct roff_man
*man
;
809 if (-1 == (fd
= open(file
, O_RDONLY
, 0))) {
810 puts("<p>You specified an invalid manual file.</p>");
815 mp
= mparse_alloc(MPARSE_SO
| MPARSE_UTF8
| MPARSE_LATIN1
,
816 MANDOCLEVEL_BADARG
, NULL
, req
->q
.manpath
);
817 mparse_readfd(mp
, fd
, file
);
820 memset(&conf
, 0, sizeof(conf
));
822 conf
.style
= mandoc_strdup(CSS_DIR
"/mandoc.css");
823 usepath
= strcmp(req
->q
.manpath
, req
->p
[0]);
824 mandoc_asprintf(&conf
.man
, "/%s%s%%N.%%S",
825 usepath
? req
->q
.manpath
: "", usepath
? "/" : "");
827 mparse_result(mp
, &man
, NULL
);
829 warnx("fatal mandoc error: %s/%s", req
->q
.manpath
, file
);
836 vp
= html_alloc(&conf
);
838 if (man
->macroset
== MACROSET_MDOC
) {
854 resp_show(const struct req
*req
, const char *file
)
857 if ('.' == file
[0] && '/' == file
[1])
861 resp_catman(req
, file
);
863 resp_format(req
, file
);
867 pg_show(struct req
*req
, const char *fullpath
)
872 if ((file
= strchr(fullpath
, '/')) == NULL
) {
874 "You did not specify a page to show.");
877 manpath
= mandoc_strndup(fullpath
, file
- fullpath
);
880 if ( ! validate_manpath(req
, manpath
)) {
882 "You specified an invalid manpath.");
888 * Begin by chdir()ing into the manpath.
889 * This way we can pick up the database files, which are
890 * relative to the manpath root.
893 if (chdir(manpath
) == -1) {
894 warn("chdir %s", manpath
);
901 if ( ! validate_filename(file
)) {
903 "You specified an invalid manual file.");
907 resp_begin_html(200, NULL
);
908 resp_searchform(req
, FOCUS_NONE
);
909 resp_show(req
, file
);
914 pg_search(const struct req
*req
)
916 struct mansearch search
;
917 struct manpaths paths
;
920 char *query
, *rp
, *wp
;
925 * Begin by chdir()ing into the root of the manpath.
926 * This way we can pick up the database files, which are
927 * relative to the manpath root.
930 if (chdir(req
->q
.manpath
) == -1) {
931 warn("chdir %s", req
->q
.manpath
);
936 search
.arch
= req
->q
.arch
;
937 search
.sec
= req
->q
.sec
;
938 search
.outkey
= "Nd";
939 search
.argmode
= req
->q
.equal
? ARG_NAME
: ARG_EXPR
;
940 search
.firstmatch
= 1;
943 paths
.paths
= mandoc_malloc(sizeof(char *));
944 paths
.paths
[0] = mandoc_strdup(".");
947 * Break apart at spaces with backslash-escaping.
952 rp
= query
= mandoc_strdup(req
->q
.query
);
954 while (isspace((unsigned char)*rp
))
958 argv
= mandoc_reallocarray(argv
, argc
+ 1, sizeof(char *));
959 argv
[argc
++] = wp
= rp
;
961 if (isspace((unsigned char)*rp
)) {
966 if (rp
[0] == '\\' && rp
[1] != '\0')
979 if (req
->isquery
&& req
->q
.equal
&& argc
== 1)
980 pg_redirect(req
, argv
[0]);
981 else if (mansearch(&search
, &paths
, argc
, argv
, &res
, &ressz
) == 0)
982 pg_noresult(req
, "You entered an invalid query.");
984 pg_noresult(req
, "No results found.");
986 pg_searchres(req
, res
, ressz
);
989 mansearch_free(res
, ressz
);
990 free(paths
.paths
[0]);
998 struct itimerval itimer
;
1000 const char *querystring
;
1005 * The "rpath" pledge could be revoked after mparse_readfd()
1006 * if the file desciptor to "/footer.html" would be opened
1007 * up front, but it's probably not worth the complication
1008 * of the code it would cause: it would require scattering
1009 * pledge() calls in multiple low-level resp_*() functions.
1012 if (pledge("stdio rpath", NULL
) == -1) {
1014 pg_error_internal();
1015 return EXIT_FAILURE
;
1019 /* Poor man's ReDoS mitigation. */
1021 itimer
.it_value
.tv_sec
= 2;
1022 itimer
.it_value
.tv_usec
= 0;
1023 itimer
.it_interval
.tv_sec
= 2;
1024 itimer
.it_interval
.tv_usec
= 0;
1025 if (setitimer(ITIMER_VIRTUAL
, &itimer
, NULL
) == -1) {
1027 pg_error_internal();
1028 return EXIT_FAILURE
;
1032 * First we change directory into the MAN_DIR so that
1033 * subsequent scanning for manpath directories is rooted
1034 * relative to the same position.
1037 if (chdir(MAN_DIR
) == -1) {
1038 warn("MAN_DIR: %s", MAN_DIR
);
1039 pg_error_internal();
1040 return EXIT_FAILURE
;
1043 memset(&req
, 0, sizeof(struct req
));
1045 parse_manpath_conf(&req
);
1047 /* Parse the path info and the query string. */
1049 if ((path
= getenv("PATH_INFO")) == NULL
)
1051 else if (*path
== '/')
1054 if (*path
!= '\0') {
1055 parse_path_info(&req
, path
);
1056 if (req
.q
.manpath
== NULL
|| access(path
, F_OK
) == -1)
1058 } else if ((querystring
= getenv("QUERY_STRING")) != NULL
)
1059 parse_query_string(&req
, querystring
);
1061 /* Validate parsed data and add defaults. */
1063 if (req
.q
.manpath
== NULL
)
1064 req
.q
.manpath
= mandoc_strdup(req
.p
[0]);
1065 else if ( ! validate_manpath(&req
, req
.q
.manpath
)) {
1066 pg_error_badrequest(
1067 "You specified an invalid manpath.");
1068 return EXIT_FAILURE
;
1071 if ( ! (NULL
== req
.q
.arch
|| validate_urifrag(req
.q
.arch
))) {
1072 pg_error_badrequest(
1073 "You specified an invalid architecture.");
1074 return EXIT_FAILURE
;
1077 /* Dispatch to the three different pages. */
1080 pg_show(&req
, path
);
1081 else if (NULL
!= req
.q
.query
)
1086 free(req
.q
.manpath
);
1090 for (i
= 0; i
< (int)req
.psz
; i
++)
1093 return EXIT_SUCCESS
;
1097 * If PATH_INFO is not a file name, translate it to a query.
1100 parse_path_info(struct req
*req
, const char *path
)
1107 req
->q
.manpath
= mandoc_strdup(path
);
1110 /* Mandatory manual page name. */
1111 if ((req
->q
.query
= strrchr(req
->q
.manpath
, '/')) == NULL
) {
1112 req
->q
.query
= req
->q
.manpath
;
1113 req
->q
.manpath
= NULL
;
1115 *req
->q
.query
++ = '\0';
1117 /* Optional trailing section. */
1118 if ((req
->q
.sec
= strrchr(req
->q
.query
, '.')) != NULL
) {
1119 if(isdigit((unsigned char)req
->q
.sec
[1])) {
1120 *req
->q
.sec
++ = '\0';
1121 req
->q
.sec
= mandoc_strdup(req
->q
.sec
);
1126 /* Handle the case of name[.section] only. */
1127 if (req
->q
.manpath
== NULL
)
1129 req
->q
.query
= mandoc_strdup(req
->q
.query
);
1131 /* Split directory components. */
1132 dir
[i
= 0] = req
->q
.manpath
;
1133 while ((dir
[i
+ 1] = strchr(dir
[i
], '/')) != NULL
) {
1135 pg_error_badrequest(
1136 "You specified too many directory components.");
1142 /* Optional manpath. */
1143 if ((i
= validate_manpath(req
, req
->q
.manpath
)) == 0)
1144 req
->q
.manpath
= NULL
;
1145 else if (dir
[1] == NULL
)
1148 /* Optional section. */
1149 if (strncmp(dir
[i
], "man", 3) == 0) {
1151 req
->q
.sec
= mandoc_strdup(dir
[i
++] + 3);
1153 if (dir
[i
] == NULL
) {
1154 if (req
->q
.manpath
== NULL
)
1158 if (dir
[i
+ 1] != NULL
) {
1159 pg_error_badrequest(
1160 "You specified an invalid directory component.");
1164 /* Optional architecture. */
1166 req
->q
.arch
= mandoc_strdup(dir
[i
]);
1167 if (req
->q
.manpath
== NULL
)
1170 req
->q
.arch
= dir
[0];
1174 * Scan for indexable paths.
1177 parse_manpath_conf(struct req
*req
)
1184 if ((fp
= fopen("manpath.conf", "r")) == NULL
) {
1185 warn("%s/manpath.conf", MAN_DIR
);
1186 pg_error_internal();
1193 while ((len
= getline(&dp
, &dpsz
, fp
)) != -1) {
1194 if (dp
[len
- 1] == '\n')
1196 req
->p
= mandoc_realloc(req
->p
,
1197 (req
->psz
+ 1) * sizeof(char *));
1198 if ( ! validate_urifrag(dp
)) {
1199 warnx("%s/manpath.conf contains "
1200 "unsafe path \"%s\"", MAN_DIR
, dp
);
1201 pg_error_internal();
1204 if (strchr(dp
, '/') != NULL
) {
1205 warnx("%s/manpath.conf contains "
1206 "path with slash \"%s\"", MAN_DIR
, dp
);
1207 pg_error_internal();
1210 req
->p
[req
->psz
++] = dp
;
1216 if (req
->p
== NULL
) {
1217 warnx("%s/manpath.conf is empty", MAN_DIR
);
1218 pg_error_internal();