]>
git.cameronkatri.com Git - mandoc.git/blob - cgi.c
1 /* $Id: cgi.c,v 1.128 2016/04/15 16:42:52 schwarze Exp $ */
3 * Copyright (c) 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv>
4 * Copyright (c) 2014, 2015, 2016 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>
34 #include "mandoc_aux.h"
41 #include "mansearch.h"
45 * A query as passed to the search function.
48 char *manpath
; /* desired manual directory */
49 char *arch
; /* architecture */
50 char *sec
; /* manual section */
51 char *query
; /* unparsed query expression */
52 int equal
; /* match whole names, not substrings */
57 char **p
; /* array of available manpaths */
58 size_t psz
; /* number of available manpaths */
59 int isquery
; /* QUERY_STRING used, not PATH_INFO */
62 static void catman(const struct req
*, const char *);
63 static void format(const struct req
*, const char *);
64 static void html_print(const char *);
65 static void html_putchar(char);
66 static int http_decode(char *);
67 static void http_parse(struct req
*, const char *);
68 static void pathgen(struct req
*);
69 static void path_parse(struct req
*req
, const char *path
);
70 static void pg_error_badrequest(const char *);
71 static void pg_error_internal(void);
72 static void pg_index(const struct req
*);
73 static void pg_noresult(const struct req
*, const char *);
74 static void pg_search(const struct req
*);
75 static void pg_searchres(const struct req
*,
76 struct manpage
*, size_t);
77 static void pg_show(struct req
*, const char *);
78 static void resp_begin_html(int, const char *);
79 static void resp_begin_http(int, const char *);
80 static void resp_copy(const char *);
81 static void resp_end_html(void);
82 static void resp_searchform(const struct req
*);
83 static void resp_show(const struct req
*, const char *);
84 static void set_query_attr(char **, char **);
85 static int validate_filename(const char *);
86 static int validate_manpath(const struct req
*, const char *);
87 static int validate_urifrag(const char *);
89 static const char *scriptname
= SCRIPT_NAME
;
91 static const int sec_prios
[] = {1, 4, 5, 8, 6, 3, 7, 2, 9};
92 static const char *const sec_numbers
[] = {
93 "0", "1", "2", "3", "3p", "4", "5", "6", "7", "8", "9"
95 static const char *const sec_names
[] = {
97 "1 - General Commands",
99 "3 - Library Functions",
101 "4 - Device Drivers",
104 "7 - Miscellaneous Information",
105 "8 - System Manager\'s Manual",
106 "9 - Kernel Developer\'s Manual"
108 static const int sec_MAX
= sizeof(sec_names
) / sizeof(char *);
110 static const char *const arch_names
[] = {
111 "amd64", "alpha", "armish", "armv7",
112 "hppa", "hppa64", "i386", "landisk",
113 "loongson", "luna88k", "macppc", "mips64",
114 "octeon", "sgi", "socppc", "sparc",
116 "amiga", "arc", "arm32", "atari",
117 "aviion", "beagle", "cats", "hp300",
118 "ia64", "mac68k", "mvme68k", "mvme88k",
119 "mvmeppc", "palm", "pc532", "pegasos",
120 "pmax", "powerpc", "solbourne", "sun3",
121 "vax", "wgrisc", "x68k"
123 static const int arch_MAX
= sizeof(arch_names
) / sizeof(char *);
126 * Print a character, escaping HTML along the way.
127 * This will pass non-ASCII straight to output: be warned!
147 putchar((unsigned char)c
);
153 * Call through to html_putchar().
154 * Accepts NULL strings.
157 html_print(const char *p
)
167 * Transfer the responsibility for the allocated string *val
168 * to the query structure.
171 set_query_attr(char **attr
, char **val
)
184 * Parse the QUERY_STRING for key-value pairs
185 * and store the values into the query structure.
188 http_parse(struct req
*req
, const char *qs
)
194 req
->q
.manpath
= NULL
;
201 while (*qs
!= '\0') {
205 keysz
= strcspn(qs
, "=;&");
206 key
= mandoc_strndup(qs
, keysz
);
211 /* Parse one value. */
213 valsz
= strcspn(++qs
, ";&");
214 val
= mandoc_strndup(qs
, valsz
);
217 /* Decode and catch encoding errors. */
219 if ( ! (http_decode(key
) && http_decode(val
)))
222 /* Handle key-value pairs. */
224 if ( ! strcmp(key
, "query"))
225 set_query_attr(&req
->q
.query
, &val
);
227 else if ( ! strcmp(key
, "apropos"))
228 req
->q
.equal
= !strcmp(val
, "0");
230 else if ( ! strcmp(key
, "manpath")) {
232 if ( ! strncmp(val
, "OpenBSD ", 8)) {
238 set_query_attr(&req
->q
.manpath
, &val
);
241 else if ( ! (strcmp(key
, "sec")
243 && strcmp(key
, "sektion")
246 if ( ! strcmp(val
, "0"))
248 set_query_attr(&req
->q
.sec
, &val
);
251 else if ( ! strcmp(key
, "arch")) {
252 if ( ! strcmp(val
, "default"))
254 set_query_attr(&req
->q
.arch
, &val
);
258 * The key must be freed in any case.
259 * The val may have been handed over to the query
260 * structure, in which case it is now NULL.
274 * HTTP-decode a string. The standard explanation is that this turns
275 * "%4e+foo" into "n foo" in the regular way. This is done in-place
276 * over the allocated string.
288 for ( ; '\0' != *p
; p
++, q
++) {
290 if ('\0' == (hex
[0] = *(p
+ 1)))
292 if ('\0' == (hex
[1] = *(p
+ 2)))
294 if (1 != sscanf(hex
, "%x", &c
))
302 *q
= '+' == *p
? ' ' : *p
;
310 resp_begin_http(int code
, const char *msg
)
314 printf("Status: %d %s\r\n", code
, msg
);
316 printf("Content-Type: text/html; charset=utf-8\r\n"
317 "Cache-Control: no-cache\r\n"
318 "Pragma: no-cache\r\n"
325 resp_copy(const char *filename
)
331 if ((fd
= open(filename
, O_RDONLY
)) != -1) {
333 while ((sz
= read(fd
, buf
, sizeof(buf
))) > 0)
334 write(STDOUT_FILENO
, buf
, sz
);
339 resp_begin_html(int code
, const char *msg
)
342 resp_begin_http(code
, msg
);
344 printf("<!DOCTYPE html>\n"
347 "<meta charset=\"UTF-8\"/>\n"
348 "<link rel=\"stylesheet\" href=\"%s/mandoc.css\""
349 " type=\"text/css\" media=\"all\">\n"
350 "<title>%s</title>\n"
353 "<!-- Begin page content. //-->\n",
354 CSS_DIR
, CUSTOMIZE_TITLE
);
356 resp_copy(MAN_DIR
"/header.html");
363 resp_copy(MAN_DIR
"/footer.html");
370 resp_searchform(const struct req
*req
)
374 puts("<!-- Begin search form. //-->");
375 printf("<div id=\"mancgi\">\n"
376 "<form action=\"/%s\" method=\"get\">\n"
378 "<legend>Manual Page Search Parameters</legend>\n",
381 /* Write query input box. */
383 printf( "<table><tr><td>\n"
384 "<input type=\"text\" name=\"query\" value=\"");
385 if (NULL
!= req
->q
.query
)
386 html_print(req
->q
.query
);
387 puts("\" size=\"40\">");
389 /* Write submission and reset buttons. */
391 printf( "<input type=\"submit\" value=\"Submit\">\n"
392 "<input type=\"reset\" value=\"Reset\">\n");
394 /* Write show radio button */
396 printf( "</td><td>\n"
397 "<input type=\"radio\" ");
399 printf("checked=\"checked\" ");
400 printf( "name=\"apropos\" id=\"show\" value=\"0\">\n"
401 "<label for=\"show\">Show named manual page</label>\n");
403 /* Write section selector. */
405 puts( "</td></tr><tr><td>\n"
406 "<select name=\"sec\">");
407 for (i
= 0; i
< sec_MAX
; i
++) {
408 printf("<option value=\"%s\"", sec_numbers
[i
]);
409 if (NULL
!= req
->q
.sec
&&
410 0 == strcmp(sec_numbers
[i
], req
->q
.sec
))
411 printf(" selected=\"selected\"");
412 printf(">%s</option>\n", sec_names
[i
]);
416 /* Write architecture selector. */
418 printf( "<select name=\"arch\">\n"
419 "<option value=\"default\"");
420 if (NULL
== req
->q
.arch
)
421 printf(" selected=\"selected\"");
422 puts(">All Architectures</option>");
423 for (i
= 0; i
< arch_MAX
; i
++) {
424 printf("<option value=\"%s\"", arch_names
[i
]);
425 if (NULL
!= req
->q
.arch
&&
426 0 == strcmp(arch_names
[i
], req
->q
.arch
))
427 printf(" selected=\"selected\"");
428 printf(">%s</option>\n", arch_names
[i
]);
432 /* Write manpath selector. */
435 puts("<select name=\"manpath\">");
436 for (i
= 0; i
< (int)req
->psz
; i
++) {
438 if (strcmp(req
->q
.manpath
, req
->p
[i
]) == 0)
439 printf("selected=\"selected\" ");
441 html_print(req
->p
[i
]);
443 html_print(req
->p
[i
]);
449 /* Write search radio button */
451 printf( "</td><td>\n"
452 "<input type=\"radio\" ");
453 if (0 == req
->q
.equal
)
454 printf("checked=\"checked\" ");
455 printf( "name=\"apropos\" id=\"search\" value=\"1\">\n"
456 "<label for=\"search\">Search with apropos query</label>\n");
458 puts("</td></tr></table>\n"
462 puts("<!-- End search form. //-->");
466 validate_urifrag(const char *frag
)
469 while ('\0' != *frag
) {
470 if ( ! (isalnum((unsigned char)*frag
) ||
471 '-' == *frag
|| '.' == *frag
||
472 '/' == *frag
|| '_' == *frag
))
480 validate_manpath(const struct req
*req
, const char* manpath
)
484 if ( ! strcmp(manpath
, "mandoc"))
487 for (i
= 0; i
< req
->psz
; i
++)
488 if ( ! strcmp(manpath
, req
->p
[i
]))
495 validate_filename(const char *file
)
498 if ('.' == file
[0] && '/' == file
[1])
501 return ! (strstr(file
, "../") || strstr(file
, "/..") ||
502 (strncmp(file
, "man", 3) && strncmp(file
, "cat", 3)));
506 pg_index(const struct req
*req
)
509 resp_begin_html(200, NULL
);
510 resp_searchform(req
);
512 "This web interface is documented in the\n"
513 "<a href=\"/%s%smandoc/man8/man.cgi.8\">man.cgi</a>\n"
515 "<a href=\"/%s%smandoc/man1/apropos.1\">apropos</a>\n"
516 "manual explains the query syntax.\n"
518 scriptname
, *scriptname
== '\0' ? "" : "/",
519 scriptname
, *scriptname
== '\0' ? "" : "/");
524 pg_noresult(const struct req
*req
, const char *msg
)
526 resp_begin_html(200, NULL
);
527 resp_searchform(req
);
535 pg_error_badrequest(const char *msg
)
538 resp_begin_html(400, "Bad Request");
539 puts("<h1>Bad Request</h1>\n"
542 printf("Try again from the\n"
543 "<a href=\"/%s\">main page</a>.\n"
549 pg_error_internal(void)
551 resp_begin_html(500, "Internal Server Error");
552 puts("<p>Internal Server Error</p>");
557 pg_searchres(const struct req
*req
, struct manpage
*r
, size_t sz
)
559 char *arch
, *archend
;
562 int archprio
, archpriouse
;
565 for (i
= 0; i
< sz
; i
++) {
566 if (validate_filename(r
[i
].file
))
568 warnx("invalid filename %s in %s database",
569 r
[i
].file
, req
->q
.manpath
);
574 if (req
->isquery
&& sz
== 1) {
576 * If we have just one result, then jump there now
579 printf("Status: 303 See Other\r\n");
580 printf("Location: http://%s/%s%s%s/%s",
581 HTTP_HOST
, scriptname
,
582 *scriptname
== '\0' ? "" : "/",
583 req
->q
.manpath
, r
[0].file
);
585 "Content-Type: text/html; charset=utf-8\r\n"
590 resp_begin_html(200, NULL
);
591 resp_searchform(req
);
594 puts("<div class=\"results\">");
597 for (i
= 0; i
< sz
; i
++) {
599 "<td class=\"title\">\n"
600 "<a href=\"/%s%s%s/%s",
601 scriptname
, *scriptname
== '\0' ? "" : "/",
602 req
->q
.manpath
, r
[i
].file
);
604 html_print(r
[i
].names
);
607 "<td class=\"desc\">");
608 html_print(r
[i
].output
);
618 * In man(1) mode, show one of the pages
619 * even if more than one is found.
622 if (req
->q
.equal
|| sz
== 1) {
627 for (i
= 0; i
< sz
; i
++) {
629 sec
+= strcspn(sec
, "123456789");
632 prio
= sec_prios
[sec
[0] - '1'];
635 if (req
->q
.arch
== NULL
) {
637 ((arch
= strchr(sec
+ 1, '/'))
639 ((archend
= strchr(arch
+ 1, '/'))
641 strncmp(arch
, "amd64/",
642 archend
- arch
) ? 2 : 1;
643 if (archprio
< archpriouse
) {
644 archpriouse
= archprio
;
649 if (archprio
> archpriouse
)
657 resp_show(req
, r
[iuse
].file
);
664 catman(const struct req
*req
, const char *file
)
673 if ((f
= fopen(file
, "r")) == NULL
) {
674 puts("<p>You specified an invalid manual file.</p>");
678 puts("<div class=\"catman\">\n"
684 while ((len
= getline(&p
, &sz
, f
)) != -1) {
686 for (i
= 0; i
< len
- 1; i
++) {
688 * This means that the catpage is out of state.
689 * Ignore it and keep going (although the
693 if ('\b' == p
[i
] || '\n' == p
[i
])
697 * Print a regular character.
698 * Close out any bold/italic scopes.
699 * If we're in back-space mode, make sure we'll
700 * have something to enter when we backspace.
703 if ('\b' != p
[i
+ 1]) {
711 } else if (i
+ 2 >= len
)
729 * Handle funny behaviour troff-isms.
730 * These grok'd from the original man2html.c.
733 if (('+' == p
[i
] && 'o' == p
[i
+ 2]) ||
734 ('o' == 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]) ||
740 ('|' == p
[i
] && '*' == p
[i
+ 2])) {
749 } else if (('|' == p
[i
] && '-' == p
[i
+ 2]) ||
750 ('-' == p
[i
] && '|' == p
[i
+ 1]) ||
751 ('+' == p
[i
] && '-' == p
[i
+ 1]) ||
752 ('-' == p
[i
] && '+' == p
[i
+ 1]) ||
753 ('+' == p
[i
] && '|' == p
[i
+ 1]) ||
754 ('|' == p
[i
] && '+' == p
[i
+ 1])) {
778 * Clean up the last character.
779 * We can get to a newline; don't print that.
787 if (i
== len
- 1 && p
[i
] != '\n')
801 format(const struct req
*req
, const char *file
)
803 struct manoutput conf
;
805 struct roff_man
*man
;
810 if (-1 == (fd
= open(file
, O_RDONLY
, 0))) {
811 puts("<p>You specified an invalid manual file.</p>");
816 mp
= mparse_alloc(MPARSE_SO
, MANDOCLEVEL_BADARG
, NULL
, req
->q
.manpath
);
817 mparse_readfd(mp
, fd
, file
);
820 memset(&conf
, 0, sizeof(conf
));
822 usepath
= strcmp(req
->q
.manpath
, req
->p
[0]);
823 mandoc_asprintf(&conf
.man
, "/%s%s%%N.%%S",
824 usepath
? req
->q
.manpath
: "", usepath
? "/" : "");
826 mparse_result(mp
, &man
, NULL
);
828 warnx("fatal mandoc error: %s/%s", req
->q
.manpath
, file
);
835 vp
= html_alloc(&conf
);
837 if (man
->macroset
== MACROSET_MDOC
) {
852 resp_show(const struct req
*req
, const char *file
)
855 if ('.' == file
[0] && '/' == file
[1])
865 pg_show(struct req
*req
, const char *fullpath
)
870 if ((file
= strchr(fullpath
, '/')) == NULL
) {
872 "You did not specify a page to show.");
875 manpath
= mandoc_strndup(fullpath
, file
- fullpath
);
878 if ( ! validate_manpath(req
, manpath
)) {
880 "You specified an invalid manpath.");
886 * Begin by chdir()ing into the manpath.
887 * This way we can pick up the database files, which are
888 * relative to the manpath root.
891 if (chdir(manpath
) == -1) {
892 warn("chdir %s", manpath
);
898 if (strcmp(manpath
, "mandoc")) {
899 free(req
->q
.manpath
);
900 req
->q
.manpath
= manpath
;
904 if ( ! validate_filename(file
)) {
906 "You specified an invalid manual file.");
910 resp_begin_html(200, NULL
);
911 resp_searchform(req
);
912 resp_show(req
, file
);
917 pg_search(const struct req
*req
)
919 struct mansearch search
;
920 struct manpaths paths
;
923 char *query
, *rp
, *wp
;
928 * Begin by chdir()ing into the root of the manpath.
929 * This way we can pick up the database files, which are
930 * relative to the manpath root.
933 if (chdir(req
->q
.manpath
) == -1) {
934 warn("chdir %s", req
->q
.manpath
);
939 search
.arch
= req
->q
.arch
;
940 search
.sec
= req
->q
.sec
;
941 search
.outkey
= "Nd";
942 search
.argmode
= req
->q
.equal
? ARG_NAME
: ARG_EXPR
;
943 search
.firstmatch
= 1;
946 paths
.paths
= mandoc_malloc(sizeof(char *));
947 paths
.paths
[0] = mandoc_strdup(".");
950 * Break apart at spaces with backslash-escaping.
955 rp
= query
= mandoc_strdup(req
->q
.query
);
957 while (isspace((unsigned char)*rp
))
961 argv
= mandoc_reallocarray(argv
, argc
+ 1, sizeof(char *));
962 argv
[argc
++] = wp
= rp
;
964 if (isspace((unsigned char)*rp
)) {
969 if (rp
[0] == '\\' && rp
[1] != '\0')
980 if (0 == mansearch(&search
, &paths
, argc
, argv
, &res
, &ressz
))
981 pg_noresult(req
, "You entered an invalid query.");
983 pg_noresult(req
, "No results found.");
985 pg_searchres(req
, res
, ressz
);
988 mansearch_free(res
, ressz
);
989 free(paths
.paths
[0]);
997 struct itimerval itimer
;
999 const char *querystring
;
1002 /* Poor man's ReDoS mitigation. */
1004 itimer
.it_value
.tv_sec
= 2;
1005 itimer
.it_value
.tv_usec
= 0;
1006 itimer
.it_interval
.tv_sec
= 2;
1007 itimer
.it_interval
.tv_usec
= 0;
1008 if (setitimer(ITIMER_VIRTUAL
, &itimer
, NULL
) == -1) {
1010 pg_error_internal();
1011 return EXIT_FAILURE
;
1015 * First we change directory into the MAN_DIR so that
1016 * subsequent scanning for manpath directories is rooted
1017 * relative to the same position.
1020 if (chdir(MAN_DIR
) == -1) {
1021 warn("MAN_DIR: %s", MAN_DIR
);
1022 pg_error_internal();
1023 return EXIT_FAILURE
;
1026 memset(&req
, 0, sizeof(struct req
));
1030 /* Parse the path info and the query string. */
1032 if ((path
= getenv("PATH_INFO")) == NULL
)
1034 else if (*path
== '/')
1037 if (*path
!= '\0') {
1038 path_parse(&req
, path
);
1039 if (access(path
, F_OK
) == -1)
1041 } else if ((querystring
= getenv("QUERY_STRING")) != NULL
)
1042 http_parse(&req
, querystring
);
1044 /* Validate parsed data and add defaults. */
1046 if (req
.q
.manpath
== NULL
)
1047 req
.q
.manpath
= mandoc_strdup(req
.p
[0]);
1048 else if ( ! validate_manpath(&req
, req
.q
.manpath
)) {
1049 pg_error_badrequest(
1050 "You specified an invalid manpath.");
1051 return EXIT_FAILURE
;
1054 if ( ! (NULL
== req
.q
.arch
|| validate_urifrag(req
.q
.arch
))) {
1055 pg_error_badrequest(
1056 "You specified an invalid architecture.");
1057 return EXIT_FAILURE
;
1060 /* Dispatch to the three different pages. */
1063 pg_show(&req
, path
);
1064 else if (NULL
!= req
.q
.query
)
1069 free(req
.q
.manpath
);
1073 for (i
= 0; i
< (int)req
.psz
; i
++)
1076 return EXIT_SUCCESS
;
1080 * If PATH_INFO is not a file name, translate it to a query.
1083 path_parse(struct req
*req
, const char *path
)
1089 req
->q
.manpath
= mandoc_strdup(path
);
1091 /* Mandatory manual page name. */
1092 if ((req
->q
.query
= strrchr(req
->q
.manpath
, '/')) == NULL
) {
1093 req
->q
.query
= req
->q
.manpath
;
1094 req
->q
.manpath
= NULL
;
1096 *req
->q
.query
++ = '\0';
1098 /* Optional trailing section. */
1099 if ((req
->q
.sec
= strrchr(req
->q
.query
, '.')) != NULL
) {
1100 if(isdigit((unsigned char)req
->q
.sec
[1])) {
1101 *req
->q
.sec
++ = '\0';
1102 req
->q
.sec
= mandoc_strdup(req
->q
.sec
);
1107 /* Handle the case of name[.section] only. */
1108 if (req
->q
.manpath
== NULL
) {
1112 req
->q
.query
= mandoc_strdup(req
->q
.query
);
1114 /* Optional architecture. */
1115 dir
= strrchr(req
->q
.manpath
, '/');
1116 if (dir
!= NULL
&& strncmp(dir
+ 1, "man", 3) != 0) {
1118 req
->q
.arch
= mandoc_strdup(dir
);
1119 dir
= strrchr(req
->q
.manpath
, '/');
1123 /* Optional directory name. */
1124 if (dir
!= NULL
&& strncmp(dir
+ 1, "man", 3) == 0) {
1127 req
->q
.sec
= mandoc_strdup(dir
+ 3);
1132 * Scan for indexable paths.
1135 pathgen(struct req
*req
)
1142 if ((fp
= fopen("manpath.conf", "r")) == NULL
) {
1143 warn("%s/manpath.conf", MAN_DIR
);
1144 pg_error_internal();
1151 while ((len
= getline(&dp
, &dpsz
, fp
)) != -1) {
1152 if (dp
[len
- 1] == '\n')
1154 req
->p
= mandoc_realloc(req
->p
,
1155 (req
->psz
+ 1) * sizeof(char *));
1156 if ( ! validate_urifrag(dp
)) {
1157 warnx("%s/manpath.conf contains "
1158 "unsafe path \"%s\"", MAN_DIR
, dp
);
1159 pg_error_internal();
1162 if (strchr(dp
, '/') != NULL
) {
1163 warnx("%s/manpath.conf contains "
1164 "path with slash \"%s\"", MAN_DIR
, dp
);
1165 pg_error_internal();
1168 req
->p
[req
->psz
++] = dp
;
1174 if (req
->p
== NULL
) {
1175 warnx("%s/manpath.conf is empty", MAN_DIR
);
1176 pg_error_internal();