]>
git.cameronkatri.com Git - mandoc.git/blob - cgi.c
1 /* $Id: cgi.c,v 1.112 2015/10/22 22:06:43 schwarze Exp $ */
3 * Copyright (c) 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv>
4 * Copyright (c) 2014, 2015 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 */
60 static void catman(const struct req
*, const char *);
61 static void format(const struct req
*, const char *);
62 static void html_print(const char *);
63 static void html_putchar(char);
64 static int http_decode(char *);
65 static void http_parse(struct req
*, const char *);
66 static void http_print(const char *);
67 static void http_putchar(char);
68 static void http_printquery(const struct req
*, const char *);
69 static void pathgen(struct req
*);
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_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
; /* CGI 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 "aviion", "hppa", "hppa64", "i386",
112 "ia64", "landisk", "loongson", "luna88k",
113 "macppc", "mips64", "octeon", "sgi",
114 "socppc", "solbourne", "sparc", "sparc64",
116 "amiga", "arc", "arm32", "atari",
117 "beagle", "cats", "hp300", "mac68k",
118 "mvme68k", "mvme88k", "mvmeppc", "palm",
119 "pc532", "pegasos", "pmax", "powerpc",
120 "sun3", "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 http_printquery(const struct req
*req
, const char *sep
)
155 if (NULL
!= req
->q
.query
) {
157 http_print(req
->q
.query
);
159 if (0 == req
->q
.equal
)
160 printf("%sapropos=1", sep
);
161 if (NULL
!= req
->q
.sec
) {
162 printf("%ssec=", sep
);
163 http_print(req
->q
.sec
);
165 if (NULL
!= req
->q
.arch
) {
166 printf("%sarch=", sep
);
167 http_print(req
->q
.arch
);
169 if (strcmp(req
->q
.manpath
, req
->p
[0])) {
170 printf("%smanpath=", sep
);
171 http_print(req
->q
.manpath
);
176 http_print(const char *p
)
186 * Call through to html_putchar().
187 * Accepts NULL strings.
190 html_print(const char *p
)
200 * Transfer the responsibility for the allocated string *val
201 * to the query structure.
204 set_query_attr(char **attr
, char **val
)
217 * Parse the QUERY_STRING for key-value pairs
218 * and store the values into the query structure.
221 http_parse(struct req
*req
, const char *qs
)
226 req
->q
.manpath
= NULL
;
233 while (*qs
!= '\0') {
237 keysz
= strcspn(qs
, "=;&");
238 key
= mandoc_strndup(qs
, keysz
);
243 /* Parse one value. */
245 valsz
= strcspn(++qs
, ";&");
246 val
= mandoc_strndup(qs
, valsz
);
249 /* Decode and catch encoding errors. */
251 if ( ! (http_decode(key
) && http_decode(val
)))
254 /* Handle key-value pairs. */
256 if ( ! strcmp(key
, "query"))
257 set_query_attr(&req
->q
.query
, &val
);
259 else if ( ! strcmp(key
, "apropos"))
260 req
->q
.equal
= !strcmp(val
, "0");
262 else if ( ! strcmp(key
, "manpath")) {
264 if ( ! strncmp(val
, "OpenBSD ", 8)) {
270 set_query_attr(&req
->q
.manpath
, &val
);
273 else if ( ! (strcmp(key
, "sec")
275 && strcmp(key
, "sektion")
278 if ( ! strcmp(val
, "0"))
280 set_query_attr(&req
->q
.sec
, &val
);
283 else if ( ! strcmp(key
, "arch")) {
284 if ( ! strcmp(val
, "default"))
286 set_query_attr(&req
->q
.arch
, &val
);
290 * The key must be freed in any case.
291 * The val may have been handed over to the query
292 * structure, in which case it is now NULL.
309 if (isalnum((unsigned char)c
)) {
310 putchar((unsigned char)c
);
312 } else if (' ' == c
) {
320 * HTTP-decode a string. The standard explanation is that this turns
321 * "%4e+foo" into "n foo" in the regular way. This is done in-place
322 * over the allocated string.
334 for ( ; '\0' != *p
; p
++, q
++) {
336 if ('\0' == (hex
[0] = *(p
+ 1)))
338 if ('\0' == (hex
[1] = *(p
+ 2)))
340 if (1 != sscanf(hex
, "%x", &c
))
348 *q
= '+' == *p
? ' ' : *p
;
356 resp_begin_http(int code
, const char *msg
)
360 printf("Status: %d %s\r\n", code
, msg
);
362 printf("Content-Type: text/html; charset=utf-8\r\n"
363 "Cache-Control: no-cache\r\n"
364 "Pragma: no-cache\r\n"
371 resp_begin_html(int code
, const char *msg
)
374 resp_begin_http(code
, msg
);
376 printf("<!DOCTYPE html>\n"
379 "<META CHARSET=\"UTF-8\" />\n"
380 "<LINK REL=\"stylesheet\" HREF=\"%s/man-cgi.css\""
381 " TYPE=\"text/css\" media=\"all\">\n"
382 "<LINK REL=\"stylesheet\" HREF=\"%s/man.css\""
383 " TYPE=\"text/css\" media=\"all\">\n"
384 "<TITLE>%s</TITLE>\n"
387 "<!-- Begin page content. //-->\n",
388 CSS_DIR
, CSS_DIR
, CUSTOMIZE_TITLE
);
400 resp_searchform(const struct req
*req
)
404 puts(CUSTOMIZE_BEGIN
);
405 puts("<!-- Begin search form. //-->");
406 printf("<DIV ID=\"mancgi\">\n"
407 "<FORM ACTION=\"%s\" METHOD=\"get\">\n"
409 "<LEGEND>Manual Page Search Parameters</LEGEND>\n",
412 /* Write query input box. */
414 printf( "<TABLE><TR><TD>\n"
415 "<INPUT TYPE=\"text\" NAME=\"query\" VALUE=\"");
416 if (NULL
!= req
->q
.query
)
417 html_print(req
->q
.query
);
418 puts("\" SIZE=\"40\">");
420 /* Write submission and reset buttons. */
422 printf( "<INPUT TYPE=\"submit\" VALUE=\"Submit\">\n"
423 "<INPUT TYPE=\"reset\" VALUE=\"Reset\">\n");
425 /* Write show radio button */
427 printf( "</TD><TD>\n"
428 "<INPUT TYPE=\"radio\" ");
430 printf("CHECKED=\"checked\" ");
431 printf( "NAME=\"apropos\" ID=\"show\" VALUE=\"0\">\n"
432 "<LABEL FOR=\"show\">Show named manual page</LABEL>\n");
434 /* Write section selector. */
436 puts( "</TD></TR><TR><TD>\n"
437 "<SELECT NAME=\"sec\">");
438 for (i
= 0; i
< sec_MAX
; i
++) {
439 printf("<OPTION VALUE=\"%s\"", sec_numbers
[i
]);
440 if (NULL
!= req
->q
.sec
&&
441 0 == strcmp(sec_numbers
[i
], req
->q
.sec
))
442 printf(" SELECTED=\"selected\"");
443 printf(">%s</OPTION>\n", sec_names
[i
]);
447 /* Write architecture selector. */
449 printf( "<SELECT NAME=\"arch\">\n"
450 "<OPTION VALUE=\"default\"");
451 if (NULL
== req
->q
.arch
)
452 printf(" SELECTED=\"selected\"");
453 puts(">All Architectures</OPTION>");
454 for (i
= 0; i
< arch_MAX
; i
++) {
455 printf("<OPTION VALUE=\"%s\"", arch_names
[i
]);
456 if (NULL
!= req
->q
.arch
&&
457 0 == strcmp(arch_names
[i
], req
->q
.arch
))
458 printf(" SELECTED=\"selected\"");
459 printf(">%s</OPTION>\n", arch_names
[i
]);
463 /* Write manpath selector. */
466 puts("<SELECT NAME=\"manpath\">");
467 for (i
= 0; i
< (int)req
->psz
; i
++) {
469 if (strcmp(req
->q
.manpath
, req
->p
[i
]) == 0)
470 printf("SELECTED=\"selected\" ");
472 html_print(req
->p
[i
]);
474 html_print(req
->p
[i
]);
480 /* Write search radio button */
482 printf( "</TD><TD>\n"
483 "<INPUT TYPE=\"radio\" ");
484 if (0 == req
->q
.equal
)
485 printf("CHECKED=\"checked\" ");
486 printf( "NAME=\"apropos\" ID=\"search\" VALUE=\"1\">\n"
487 "<LABEL FOR=\"search\">Search with apropos query</LABEL>\n");
489 puts("</TD></TR></TABLE>\n"
493 puts("<!-- End search form. //-->");
497 validate_urifrag(const char *frag
)
500 while ('\0' != *frag
) {
501 if ( ! (isalnum((unsigned char)*frag
) ||
502 '-' == *frag
|| '.' == *frag
||
503 '/' == *frag
|| '_' == *frag
))
511 validate_manpath(const struct req
*req
, const char* manpath
)
515 if ( ! strcmp(manpath
, "mandoc"))
518 for (i
= 0; i
< req
->psz
; i
++)
519 if ( ! strcmp(manpath
, req
->p
[i
]))
526 validate_filename(const char *file
)
529 if ('.' == file
[0] && '/' == file
[1])
532 return ! (strstr(file
, "../") || strstr(file
, "/..") ||
533 (strncmp(file
, "man", 3) && strncmp(file
, "cat", 3)));
537 pg_index(const struct req
*req
)
540 resp_begin_html(200, NULL
);
541 resp_searchform(req
);
543 "This web interface is documented in the\n"
544 "<A HREF=\"%s/mandoc/man8/man.cgi.8\">man.cgi</A>\n"
546 "<A HREF=\"%s/mandoc/man1/apropos.1\">apropos</A>\n"
547 "manual explains the query syntax.\n"
549 scriptname
, scriptname
);
554 pg_noresult(const struct req
*req
, const char *msg
)
556 resp_begin_html(200, NULL
);
557 resp_searchform(req
);
565 pg_error_badrequest(const char *msg
)
568 resp_begin_html(400, "Bad Request");
569 puts("<H1>Bad Request</H1>\n"
572 printf("Try again from the\n"
573 "<A HREF=\"%s\">main page</A>.\n"
579 pg_error_internal(void)
581 resp_begin_html(500, "Internal Server Error");
582 puts("<P>Internal Server Error</P>");
587 pg_searchres(const struct req
*req
, struct manpage
*r
, size_t sz
)
589 char *arch
, *archend
;
590 size_t i
, iuse
, isec
;
591 int archprio
, archpriouse
;
595 for (i
= 0; i
< sz
; i
++) {
596 if (validate_filename(r
[i
].file
))
598 fprintf(stderr
, "invalid filename %s in %s database\n",
599 r
[i
].file
, req
->q
.manpath
);
606 * If we have just one result, then jump there now
609 printf("Status: 303 See Other\r\n");
610 printf("Location: http://%s%s/%s/%s?",
611 HTTP_HOST
, scriptname
, req
->q
.manpath
, r
[0].file
);
612 http_printquery(req
, "&");
614 "Content-Type: text/html; charset=utf-8\r\n"
619 resp_begin_html(200, NULL
);
620 resp_searchform(req
);
621 puts("<DIV CLASS=\"results\">");
624 for (i
= 0; i
< sz
; i
++) {
626 "<TD CLASS=\"title\">\n"
627 "<A HREF=\"%s/%s/%s?",
628 scriptname
, req
->q
.manpath
, r
[i
].file
);
629 http_printquery(req
, "&");
631 html_print(r
[i
].names
);
634 "<TD CLASS=\"desc\">");
635 html_print(r
[i
].output
);
644 * In man(1) mode, show one of the pages
645 * even if more than one is found.
653 for (i
= 0; i
< sz
; i
++) {
654 isec
= strcspn(r
[i
].file
, "123456789");
655 sec
= r
[i
].file
[isec
];
658 prio
= sec_prios
[sec
- '1'];
659 if (NULL
== req
->q
.arch
) {
661 (NULL
== (arch
= strchr(
662 r
[i
].file
+ isec
, '/'))) ? 3 :
663 (NULL
== (archend
= strchr(
664 arch
+ 1, '/'))) ? 0 :
665 strncmp(arch
, "amd64/",
666 archend
- arch
) ? 2 : 1;
667 if (archprio
< archpriouse
) {
668 archpriouse
= archprio
;
673 if (archprio
> archpriouse
)
681 resp_show(req
, r
[iuse
].file
);
688 catman(const struct req
*req
, const char *file
)
696 if (NULL
== (f
= fopen(file
, "r"))) {
697 puts("<P>You specified an invalid manual file.</P>");
701 puts("<DIV CLASS=\"catman\">\n"
704 while (NULL
!= (p
= fgetln(f
, &len
))) {
706 for (i
= 0; i
< (int)len
- 1; i
++) {
708 * This means that the catpage is out of state.
709 * Ignore it and keep going (although the
713 if ('\b' == p
[i
] || '\n' == p
[i
])
717 * Print a regular character.
718 * Close out any bold/italic scopes.
719 * If we're in back-space mode, make sure we'll
720 * have something to enter when we backspace.
723 if ('\b' != p
[i
+ 1]) {
731 } else if (i
+ 2 >= (int)len
)
749 * Handle funny behaviour troff-isms.
750 * These grok'd from the original man2html.c.
753 if (('+' == p
[i
] && 'o' == p
[i
+ 2]) ||
754 ('o' == p
[i
] && '+' == p
[i
+ 2]) ||
755 ('|' == p
[i
] && '=' == p
[i
+ 2]) ||
756 ('=' == p
[i
] && '|' == p
[i
+ 2]) ||
757 ('*' == p
[i
] && '=' == p
[i
+ 2]) ||
758 ('=' == p
[i
] && '*' == p
[i
+ 2]) ||
759 ('*' == p
[i
] && '|' == p
[i
+ 2]) ||
760 ('|' == p
[i
] && '*' == p
[i
+ 2])) {
769 } else if (('|' == p
[i
] && '-' == p
[i
+ 2]) ||
770 ('-' == p
[i
] && '|' == p
[i
+ 1]) ||
771 ('+' == p
[i
] && '-' == p
[i
+ 1]) ||
772 ('-' == p
[i
] && '+' == p
[i
+ 1]) ||
773 ('+' == p
[i
] && '|' == p
[i
+ 1]) ||
774 ('|' == p
[i
] && '+' == p
[i
+ 1])) {
798 * Clean up the last character.
799 * We can get to a newline; don't print that.
807 if (i
== (int)len
- 1 && '\n' != p
[i
])
820 format(const struct req
*req
, const char *file
)
822 struct manoutput conf
;
824 struct roff_man
*man
;
829 if (-1 == (fd
= open(file
, O_RDONLY
, 0))) {
830 puts("<P>You specified an invalid manual file.</P>");
835 mp
= mparse_alloc(MPARSE_SO
, MANDOCLEVEL_BADARG
, NULL
, req
->q
.manpath
);
836 mparse_readfd(mp
, fd
, file
);
839 memset(&conf
, 0, sizeof(conf
));
841 usepath
= strcmp(req
->q
.manpath
, req
->p
[0]);
842 mandoc_asprintf(&conf
.man
, "%s?query=%%N&sec=%%S%s%s%s%s",
844 req
->q
.arch
? "&arch=" : "",
845 req
->q
.arch
? req
->q
.arch
: "",
846 usepath
? "&manpath=" : "",
847 usepath
? req
->q
.manpath
: "");
849 mparse_result(mp
, &man
, NULL
);
851 fprintf(stderr
, "fatal mandoc error: %s/%s\n",
852 req
->q
.manpath
, file
);
859 vp
= html_alloc(&conf
);
861 if (man
->macroset
== MACROSET_MDOC
) {
876 resp_show(const struct req
*req
, const char *file
)
879 if ('.' == file
[0] && '/' == file
[1])
889 pg_show(struct req
*req
, const char *fullpath
)
894 if ((file
= strchr(fullpath
, '/')) == NULL
) {
896 "You did not specify a page to show.");
899 manpath
= mandoc_strndup(fullpath
, file
- fullpath
);
902 if ( ! validate_manpath(req
, manpath
)) {
904 "You specified an invalid manpath.");
910 * Begin by chdir()ing into the manpath.
911 * This way we can pick up the database files, which are
912 * relative to the manpath root.
915 if (chdir(manpath
) == -1) {
916 fprintf(stderr
, "chdir %s: %s\n",
917 manpath
, strerror(errno
));
923 if (strcmp(manpath
, "mandoc")) {
924 free(req
->q
.manpath
);
925 req
->q
.manpath
= manpath
;
929 if ( ! validate_filename(file
)) {
931 "You specified an invalid manual file.");
935 resp_begin_html(200, NULL
);
936 resp_searchform(req
);
937 resp_show(req
, file
);
942 pg_search(const struct req
*req
)
944 struct mansearch search
;
945 struct manpaths paths
;
948 char *query
, *rp
, *wp
;
953 * Begin by chdir()ing into the root of the manpath.
954 * This way we can pick up the database files, which are
955 * relative to the manpath root.
958 if (-1 == (chdir(req
->q
.manpath
))) {
959 fprintf(stderr
, "chdir %s: %s\n",
960 req
->q
.manpath
, strerror(errno
));
965 search
.arch
= req
->q
.arch
;
966 search
.sec
= req
->q
.sec
;
967 search
.outkey
= "Nd";
968 search
.argmode
= req
->q
.equal
? ARG_NAME
: ARG_EXPR
;
969 search
.firstmatch
= 1;
972 paths
.paths
= mandoc_malloc(sizeof(char *));
973 paths
.paths
[0] = mandoc_strdup(".");
976 * Break apart at spaces with backslash-escaping.
981 rp
= query
= mandoc_strdup(req
->q
.query
);
983 while (isspace((unsigned char)*rp
))
987 argv
= mandoc_reallocarray(argv
, argc
+ 1, sizeof(char *));
988 argv
[argc
++] = wp
= rp
;
990 if (isspace((unsigned char)*rp
)) {
995 if (rp
[0] == '\\' && rp
[1] != '\0')
1006 if (0 == mansearch(&search
, &paths
, argc
, argv
, &res
, &ressz
))
1007 pg_noresult(req
, "You entered an invalid query.");
1008 else if (0 == ressz
)
1009 pg_noresult(req
, "No results found.");
1011 pg_searchres(req
, res
, ressz
);
1014 mansearch_free(res
, ressz
);
1015 free(paths
.paths
[0]);
1023 struct itimerval itimer
;
1025 const char *querystring
;
1028 /* Poor man's ReDoS mitigation. */
1030 itimer
.it_value
.tv_sec
= 2;
1031 itimer
.it_value
.tv_usec
= 0;
1032 itimer
.it_interval
.tv_sec
= 2;
1033 itimer
.it_interval
.tv_usec
= 0;
1034 if (setitimer(ITIMER_VIRTUAL
, &itimer
, NULL
) == -1) {
1035 fprintf(stderr
, "setitimer: %s\n", strerror(errno
));
1036 pg_error_internal();
1037 return EXIT_FAILURE
;
1040 /* Scan our run-time environment. */
1042 if (NULL
== (scriptname
= getenv("SCRIPT_NAME")))
1045 if ( ! validate_urifrag(scriptname
)) {
1046 fprintf(stderr
, "unsafe SCRIPT_NAME \"%s\"\n",
1048 pg_error_internal();
1049 return EXIT_FAILURE
;
1053 * First we change directory into the MAN_DIR so that
1054 * subsequent scanning for manpath directories is rooted
1055 * relative to the same position.
1058 if (-1 == chdir(MAN_DIR
)) {
1059 fprintf(stderr
, "MAN_DIR: %s: %s\n",
1060 MAN_DIR
, strerror(errno
));
1061 pg_error_internal();
1062 return EXIT_FAILURE
;
1065 memset(&req
, 0, sizeof(struct req
));
1068 /* Next parse out the query string. */
1070 if (NULL
!= (querystring
= getenv("QUERY_STRING")))
1071 http_parse(&req
, querystring
);
1073 if (req
.q
.manpath
== NULL
)
1074 req
.q
.manpath
= mandoc_strdup(req
.p
[0]);
1075 else if ( ! validate_manpath(&req
, req
.q
.manpath
)) {
1076 pg_error_badrequest(
1077 "You specified an invalid manpath.");
1078 return EXIT_FAILURE
;
1081 if ( ! (NULL
== req
.q
.arch
|| validate_urifrag(req
.q
.arch
))) {
1082 pg_error_badrequest(
1083 "You specified an invalid architecture.");
1084 return EXIT_FAILURE
;
1087 /* Dispatch to the three different pages. */
1089 path
= getenv("PATH_INFO");
1092 else if ('/' == *path
)
1096 pg_show(&req
, path
);
1097 else if (NULL
!= req
.q
.query
)
1102 free(req
.q
.manpath
);
1106 for (i
= 0; i
< (int)req
.psz
; i
++)
1109 return EXIT_SUCCESS
;
1113 * Scan for indexable paths.
1116 pathgen(struct req
*req
)
1122 if (NULL
== (fp
= fopen("manpath.conf", "r"))) {
1123 fprintf(stderr
, "%s/manpath.conf: %s\n",
1124 MAN_DIR
, strerror(errno
));
1125 pg_error_internal();
1129 while (NULL
!= (dp
= fgetln(fp
, &dpsz
))) {
1130 if ('\n' == dp
[dpsz
- 1])
1132 req
->p
= mandoc_realloc(req
->p
,
1133 (req
->psz
+ 1) * sizeof(char *));
1134 dp
= mandoc_strndup(dp
, dpsz
);
1135 if ( ! validate_urifrag(dp
)) {
1136 fprintf(stderr
, "%s/manpath.conf contains "
1137 "unsafe path \"%s\"\n", MAN_DIR
, dp
);
1138 pg_error_internal();
1141 if (NULL
!= strchr(dp
, '/')) {
1142 fprintf(stderr
, "%s/manpath.conf contains "
1143 "path with slash \"%s\"\n", MAN_DIR
, dp
);
1144 pg_error_internal();
1147 req
->p
[req
->psz
++] = dp
;
1150 if ( req
->p
== NULL
) {
1151 fprintf(stderr
, "%s/manpath.conf is empty\n", MAN_DIR
);
1152 pg_error_internal();