]>
git.cameronkatri.com Git - mandoc.git/blob - cgi.c
1 /* $Id: cgi.c,v 1.126 2016/04/15 01:34:51 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>
33 #include "mandoc_aux.h"
40 #include "mansearch.h"
44 * A query as passed to the search function.
47 char *manpath
; /* desired manual directory */
48 char *arch
; /* architecture */
49 char *sec
; /* manual section */
50 char *query
; /* unparsed query expression */
51 int equal
; /* match whole names, not substrings */
56 char **p
; /* array of available manpaths */
57 size_t psz
; /* number of available manpaths */
58 int isquery
; /* QUERY_STRING used, not PATH_INFO */
61 static void catman(const struct req
*, const char *);
62 static void format(const struct req
*, const char *);
63 static void html_print(const char *);
64 static void html_putchar(char);
65 static int http_decode(char *);
66 static void http_parse(struct req
*, const char *);
67 static void pathgen(struct req
*);
68 static void path_parse(struct req
*req
, const char *path
);
69 static void pg_error_badrequest(const char *);
70 static void pg_error_internal(void);
71 static void pg_index(const struct req
*);
72 static void pg_noresult(const struct req
*, const char *);
73 static void pg_search(const struct req
*);
74 static void pg_searchres(const struct req
*,
75 struct manpage
*, size_t);
76 static void pg_show(struct req
*, const char *);
77 static void resp_begin_html(int, const char *);
78 static void resp_begin_http(int, const char *);
79 static void resp_copy(const char *);
80 static void resp_end_html(void);
81 static void resp_searchform(const struct req
*);
82 static void resp_show(const struct req
*, const char *);
83 static void set_query_attr(char **, char **);
84 static int validate_filename(const char *);
85 static int validate_manpath(const struct req
*, const char *);
86 static int validate_urifrag(const char *);
88 static const char *scriptname
= SCRIPT_NAME
;
90 static const int sec_prios
[] = {1, 4, 5, 8, 6, 3, 7, 2, 9};
91 static const char *const sec_numbers
[] = {
92 "0", "1", "2", "3", "3p", "4", "5", "6", "7", "8", "9"
94 static const char *const sec_names
[] = {
96 "1 - General Commands",
98 "3 - Library Functions",
100 "4 - Device Drivers",
103 "7 - Miscellaneous Information",
104 "8 - System Manager\'s Manual",
105 "9 - Kernel Developer\'s Manual"
107 static const int sec_MAX
= sizeof(sec_names
) / sizeof(char *);
109 static const char *const arch_names
[] = {
110 "amd64", "alpha", "armish", "armv7",
111 "hppa", "hppa64", "i386", "landisk",
112 "loongson", "luna88k", "macppc", "mips64",
113 "octeon", "sgi", "socppc", "sparc",
115 "amiga", "arc", "arm32", "atari",
116 "aviion", "beagle", "cats", "hp300",
117 "ia64", "mac68k", "mvme68k", "mvme88k",
118 "mvmeppc", "palm", "pc532", "pegasos",
119 "pmax", "powerpc", "solbourne", "sun3",
120 "vax", "wgrisc", "x68k"
122 static const int arch_MAX
= sizeof(arch_names
) / sizeof(char *);
125 * Print a character, escaping HTML along the way.
126 * This will pass non-ASCII straight to output: be warned!
146 putchar((unsigned char)c
);
152 * Call through to html_putchar().
153 * Accepts NULL strings.
156 html_print(const char *p
)
166 * Transfer the responsibility for the allocated string *val
167 * to the query structure.
170 set_query_attr(char **attr
, char **val
)
183 * Parse the QUERY_STRING for key-value pairs
184 * and store the values into the query structure.
187 http_parse(struct req
*req
, const char *qs
)
193 req
->q
.manpath
= NULL
;
200 while (*qs
!= '\0') {
204 keysz
= strcspn(qs
, "=;&");
205 key
= mandoc_strndup(qs
, keysz
);
210 /* Parse one value. */
212 valsz
= strcspn(++qs
, ";&");
213 val
= mandoc_strndup(qs
, valsz
);
216 /* Decode and catch encoding errors. */
218 if ( ! (http_decode(key
) && http_decode(val
)))
221 /* Handle key-value pairs. */
223 if ( ! strcmp(key
, "query"))
224 set_query_attr(&req
->q
.query
, &val
);
226 else if ( ! strcmp(key
, "apropos"))
227 req
->q
.equal
= !strcmp(val
, "0");
229 else if ( ! strcmp(key
, "manpath")) {
231 if ( ! strncmp(val
, "OpenBSD ", 8)) {
237 set_query_attr(&req
->q
.manpath
, &val
);
240 else if ( ! (strcmp(key
, "sec")
242 && strcmp(key
, "sektion")
245 if ( ! strcmp(val
, "0"))
247 set_query_attr(&req
->q
.sec
, &val
);
250 else if ( ! strcmp(key
, "arch")) {
251 if ( ! strcmp(val
, "default"))
253 set_query_attr(&req
->q
.arch
, &val
);
257 * The key must be freed in any case.
258 * The val may have been handed over to the query
259 * structure, in which case it is now NULL.
273 * HTTP-decode a string. The standard explanation is that this turns
274 * "%4e+foo" into "n foo" in the regular way. This is done in-place
275 * over the allocated string.
287 for ( ; '\0' != *p
; p
++, q
++) {
289 if ('\0' == (hex
[0] = *(p
+ 1)))
291 if ('\0' == (hex
[1] = *(p
+ 2)))
293 if (1 != sscanf(hex
, "%x", &c
))
301 *q
= '+' == *p
? ' ' : *p
;
309 resp_begin_http(int code
, const char *msg
)
313 printf("Status: %d %s\r\n", code
, msg
);
315 printf("Content-Type: text/html; charset=utf-8\r\n"
316 "Cache-Control: no-cache\r\n"
317 "Pragma: no-cache\r\n"
324 resp_copy(const char *filename
)
330 if ((fd
= open(filename
, O_RDONLY
)) != -1) {
332 while ((sz
= read(fd
, buf
, sizeof(buf
))) > 0)
333 write(STDOUT_FILENO
, buf
, sz
);
338 resp_begin_html(int code
, const char *msg
)
341 resp_begin_http(code
, msg
);
343 printf("<!DOCTYPE html>\n"
346 "<meta charset=\"UTF-8\"/>\n"
347 "<link rel=\"stylesheet\" href=\"%s/mandoc.css\""
348 " type=\"text/css\" media=\"all\">\n"
349 "<title>%s</title>\n"
352 "<!-- Begin page content. //-->\n",
353 CSS_DIR
, CUSTOMIZE_TITLE
);
355 resp_copy(MAN_DIR
"/header.html");
362 resp_copy(MAN_DIR
"/footer.html");
369 resp_searchform(const struct req
*req
)
373 puts("<!-- Begin search form. //-->");
374 printf("<div id=\"mancgi\">\n"
375 "<form action=\"/%s\" method=\"get\">\n"
377 "<legend>Manual Page Search Parameters</legend>\n",
380 /* Write query input box. */
382 printf( "<table><tr><td>\n"
383 "<input type=\"text\" name=\"query\" value=\"");
384 if (NULL
!= req
->q
.query
)
385 html_print(req
->q
.query
);
386 puts("\" size=\"40\">");
388 /* Write submission and reset buttons. */
390 printf( "<input type=\"submit\" value=\"Submit\">\n"
391 "<input type=\"reset\" value=\"Reset\">\n");
393 /* Write show radio button */
395 printf( "</td><td>\n"
396 "<input type=\"radio\" ");
398 printf("checked=\"checked\" ");
399 printf( "name=\"apropos\" id=\"show\" value=\"0\">\n"
400 "<label for=\"show\">Show named manual page</label>\n");
402 /* Write section selector. */
404 puts( "</td></tr><tr><td>\n"
405 "<select name=\"sec\">");
406 for (i
= 0; i
< sec_MAX
; i
++) {
407 printf("<option value=\"%s\"", sec_numbers
[i
]);
408 if (NULL
!= req
->q
.sec
&&
409 0 == strcmp(sec_numbers
[i
], req
->q
.sec
))
410 printf(" selected=\"selected\"");
411 printf(">%s</option>\n", sec_names
[i
]);
415 /* Write architecture selector. */
417 printf( "<select name=\"arch\">\n"
418 "<option value=\"default\"");
419 if (NULL
== req
->q
.arch
)
420 printf(" selected=\"selected\"");
421 puts(">All Architectures</option>");
422 for (i
= 0; i
< arch_MAX
; i
++) {
423 printf("<option value=\"%s\"", arch_names
[i
]);
424 if (NULL
!= req
->q
.arch
&&
425 0 == strcmp(arch_names
[i
], req
->q
.arch
))
426 printf(" selected=\"selected\"");
427 printf(">%s</option>\n", arch_names
[i
]);
431 /* Write manpath selector. */
434 puts("<select name=\"manpath\">");
435 for (i
= 0; i
< (int)req
->psz
; i
++) {
437 if (strcmp(req
->q
.manpath
, req
->p
[i
]) == 0)
438 printf("selected=\"selected\" ");
440 html_print(req
->p
[i
]);
442 html_print(req
->p
[i
]);
448 /* Write search radio button */
450 printf( "</td><td>\n"
451 "<input type=\"radio\" ");
452 if (0 == req
->q
.equal
)
453 printf("checked=\"checked\" ");
454 printf( "name=\"apropos\" id=\"search\" value=\"1\">\n"
455 "<label for=\"search\">Search with apropos query</label>\n");
457 puts("</td></tr></table>\n"
461 puts("<!-- End search form. //-->");
465 validate_urifrag(const char *frag
)
468 while ('\0' != *frag
) {
469 if ( ! (isalnum((unsigned char)*frag
) ||
470 '-' == *frag
|| '.' == *frag
||
471 '/' == *frag
|| '_' == *frag
))
479 validate_manpath(const struct req
*req
, const char* manpath
)
483 if ( ! strcmp(manpath
, "mandoc"))
486 for (i
= 0; i
< req
->psz
; i
++)
487 if ( ! strcmp(manpath
, req
->p
[i
]))
494 validate_filename(const char *file
)
497 if ('.' == file
[0] && '/' == file
[1])
500 return ! (strstr(file
, "../") || strstr(file
, "/..") ||
501 (strncmp(file
, "man", 3) && strncmp(file
, "cat", 3)));
505 pg_index(const struct req
*req
)
508 resp_begin_html(200, NULL
);
509 resp_searchform(req
);
511 "This web interface is documented in the\n"
512 "<a href=\"/%s%smandoc/man8/man.cgi.8\">man.cgi</a>\n"
514 "<a href=\"/%s%smandoc/man1/apropos.1\">apropos</a>\n"
515 "manual explains the query syntax.\n"
517 scriptname
, *scriptname
== '\0' ? "" : "/",
518 scriptname
, *scriptname
== '\0' ? "" : "/");
523 pg_noresult(const struct req
*req
, const char *msg
)
525 resp_begin_html(200, NULL
);
526 resp_searchform(req
);
534 pg_error_badrequest(const char *msg
)
537 resp_begin_html(400, "Bad Request");
538 puts("<h1>Bad Request</h1>\n"
541 printf("Try again from the\n"
542 "<a href=\"/%s\">main page</a>.\n"
548 pg_error_internal(void)
550 resp_begin_html(500, "Internal Server Error");
551 puts("<p>Internal Server Error</p>");
556 pg_searchres(const struct req
*req
, struct manpage
*r
, size_t sz
)
558 char *arch
, *archend
;
561 int archprio
, archpriouse
;
564 for (i
= 0; i
< sz
; i
++) {
565 if (validate_filename(r
[i
].file
))
567 fprintf(stderr
, "invalid filename %s in %s database\n",
568 r
[i
].file
, req
->q
.manpath
);
573 if (req
->isquery
&& sz
== 1) {
575 * If we have just one result, then jump there now
578 printf("Status: 303 See Other\r\n");
579 printf("Location: http://%s/%s%s%s/%s",
580 HTTP_HOST
, scriptname
,
581 *scriptname
== '\0' ? "" : "/",
582 req
->q
.manpath
, r
[0].file
);
584 "Content-Type: text/html; charset=utf-8\r\n"
589 resp_begin_html(200, NULL
);
590 resp_searchform(req
);
593 puts("<div class=\"results\">");
596 for (i
= 0; i
< sz
; i
++) {
598 "<td class=\"title\">\n"
599 "<a href=\"/%s%s%s/%s",
600 scriptname
, *scriptname
== '\0' ? "" : "/",
601 req
->q
.manpath
, r
[i
].file
);
603 html_print(r
[i
].names
);
606 "<td class=\"desc\">");
607 html_print(r
[i
].output
);
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 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 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
, MANDOCLEVEL_BADARG
, NULL
, req
->q
.manpath
);
816 mparse_readfd(mp
, fd
, file
);
819 memset(&conf
, 0, sizeof(conf
));
821 usepath
= strcmp(req
->q
.manpath
, req
->p
[0]);
822 mandoc_asprintf(&conf
.man
, "/%s%s%%N.%%S",
823 usepath
? req
->q
.manpath
: "", usepath
? "/" : "");
825 mparse_result(mp
, &man
, NULL
);
827 fprintf(stderr
, "fatal mandoc error: %s/%s\n",
828 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 fprintf(stderr
, "chdir %s: %s\n",
893 manpath
, strerror(errno
));
899 if (strcmp(manpath
, "mandoc")) {
900 free(req
->q
.manpath
);
901 req
->q
.manpath
= manpath
;
905 if ( ! validate_filename(file
)) {
907 "You specified an invalid manual file.");
911 resp_begin_html(200, NULL
);
912 resp_searchform(req
);
913 resp_show(req
, file
);
918 pg_search(const struct req
*req
)
920 struct mansearch search
;
921 struct manpaths paths
;
924 char *query
, *rp
, *wp
;
929 * Begin by chdir()ing into the root of the manpath.
930 * This way we can pick up the database files, which are
931 * relative to the manpath root.
934 if (-1 == (chdir(req
->q
.manpath
))) {
935 fprintf(stderr
, "chdir %s: %s\n",
936 req
->q
.manpath
, strerror(errno
));
941 search
.arch
= req
->q
.arch
;
942 search
.sec
= req
->q
.sec
;
943 search
.outkey
= "Nd";
944 search
.argmode
= req
->q
.equal
? ARG_NAME
: ARG_EXPR
;
945 search
.firstmatch
= 1;
948 paths
.paths
= mandoc_malloc(sizeof(char *));
949 paths
.paths
[0] = mandoc_strdup(".");
952 * Break apart at spaces with backslash-escaping.
957 rp
= query
= mandoc_strdup(req
->q
.query
);
959 while (isspace((unsigned char)*rp
))
963 argv
= mandoc_reallocarray(argv
, argc
+ 1, sizeof(char *));
964 argv
[argc
++] = wp
= rp
;
966 if (isspace((unsigned char)*rp
)) {
971 if (rp
[0] == '\\' && rp
[1] != '\0')
982 if (0 == mansearch(&search
, &paths
, argc
, argv
, &res
, &ressz
))
983 pg_noresult(req
, "You entered an invalid query.");
985 pg_noresult(req
, "No results found.");
987 pg_searchres(req
, res
, ressz
);
990 mansearch_free(res
, ressz
);
991 free(paths
.paths
[0]);
999 struct itimerval itimer
;
1001 const char *querystring
;
1004 /* Poor man's ReDoS mitigation. */
1006 itimer
.it_value
.tv_sec
= 2;
1007 itimer
.it_value
.tv_usec
= 0;
1008 itimer
.it_interval
.tv_sec
= 2;
1009 itimer
.it_interval
.tv_usec
= 0;
1010 if (setitimer(ITIMER_VIRTUAL
, &itimer
, NULL
) == -1) {
1011 fprintf(stderr
, "setitimer: %s\n", strerror(errno
));
1012 pg_error_internal();
1013 return EXIT_FAILURE
;
1017 * First we change directory into the MAN_DIR so that
1018 * subsequent scanning for manpath directories is rooted
1019 * relative to the same position.
1022 if (-1 == chdir(MAN_DIR
)) {
1023 fprintf(stderr
, "MAN_DIR: %s: %s\n",
1024 MAN_DIR
, strerror(errno
));
1025 pg_error_internal();
1026 return EXIT_FAILURE
;
1029 memset(&req
, 0, sizeof(struct req
));
1033 /* Parse the path info and the query string. */
1035 if ((path
= getenv("PATH_INFO")) == NULL
)
1037 else if (*path
== '/')
1040 if (*path
!= '\0') {
1041 path_parse(&req
, path
);
1042 if (access(path
, F_OK
) == -1)
1044 } else if ((querystring
= getenv("QUERY_STRING")) != NULL
)
1045 http_parse(&req
, querystring
);
1047 /* Validate parsed data and add defaults. */
1049 if (req
.q
.manpath
== NULL
)
1050 req
.q
.manpath
= mandoc_strdup(req
.p
[0]);
1051 else if ( ! validate_manpath(&req
, req
.q
.manpath
)) {
1052 pg_error_badrequest(
1053 "You specified an invalid manpath.");
1054 return EXIT_FAILURE
;
1057 if ( ! (NULL
== req
.q
.arch
|| validate_urifrag(req
.q
.arch
))) {
1058 pg_error_badrequest(
1059 "You specified an invalid architecture.");
1060 return EXIT_FAILURE
;
1063 /* Dispatch to the three different pages. */
1066 pg_show(&req
, path
);
1067 else if (NULL
!= req
.q
.query
)
1072 free(req
.q
.manpath
);
1076 for (i
= 0; i
< (int)req
.psz
; i
++)
1079 return EXIT_SUCCESS
;
1083 * If PATH_INFO is not a file name, translate it to a query.
1086 path_parse(struct req
*req
, const char *path
)
1092 req
->q
.manpath
= mandoc_strdup(path
);
1094 /* Mandatory manual page name. */
1095 if ((req
->q
.query
= strrchr(req
->q
.manpath
, '/')) == NULL
) {
1096 req
->q
.query
= req
->q
.manpath
;
1097 req
->q
.manpath
= NULL
;
1099 *req
->q
.query
++ = '\0';
1101 /* Optional trailing section. */
1102 if ((req
->q
.sec
= strrchr(req
->q
.query
, '.')) != NULL
) {
1103 if(isdigit((unsigned char)req
->q
.sec
[1])) {
1104 *req
->q
.sec
++ = '\0';
1105 req
->q
.sec
= mandoc_strdup(req
->q
.sec
);
1110 /* Handle the case of name[.section] only. */
1111 if (req
->q
.manpath
== NULL
) {
1115 req
->q
.query
= mandoc_strdup(req
->q
.query
);
1117 /* Optional architecture. */
1120 if ((req
->q
.arch
= strrchr(req
->q
.manpath
, '/')) == NULL
)
1122 *req
->q
.arch
++ = '\0';
1123 if (dir_done
|| strncmp(req
->q
.arch
, "man", 3)) {
1124 req
->q
.arch
= mandoc_strdup(req
->q
.arch
);
1128 /* Optional directory name. */
1130 if (*req
->q
.arch
!= '\0') {
1132 req
->q
.sec
= mandoc_strdup(req
->q
.arch
);
1139 * Scan for indexable paths.
1142 pathgen(struct req
*req
)
1149 if (NULL
== (fp
= fopen("manpath.conf", "r"))) {
1150 fprintf(stderr
, "%s/manpath.conf: %s\n",
1151 MAN_DIR
, strerror(errno
));
1152 pg_error_internal();
1159 while ((len
= getline(&dp
, &dpsz
, fp
)) != -1) {
1160 if (dp
[len
- 1] == '\n')
1162 req
->p
= mandoc_realloc(req
->p
,
1163 (req
->psz
+ 1) * sizeof(char *));
1164 if ( ! validate_urifrag(dp
)) {
1165 fprintf(stderr
, "%s/manpath.conf contains "
1166 "unsafe path \"%s\"\n", MAN_DIR
, dp
);
1167 pg_error_internal();
1170 if (NULL
!= strchr(dp
, '/')) {
1171 fprintf(stderr
, "%s/manpath.conf contains "
1172 "path with slash \"%s\"\n", MAN_DIR
, dp
);
1173 pg_error_internal();
1176 req
->p
[req
->psz
++] = dp
;
1182 if ( req
->p
== NULL
) {
1183 fprintf(stderr
, "%s/manpath.conf is empty\n", MAN_DIR
);
1184 pg_error_internal();