]>
git.cameronkatri.com Git - mandoc.git/blob - cgi.c
1 /* $Id: cgi.c,v 1.104 2015/02/10 08:05:30 schwarze Exp $ */
3 * Copyright (c) 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv>
4 * Copyright (c) 2014 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 AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR 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"
37 #include "mansearch.h"
41 * A query as passed to the search function.
44 char *manpath
; /* desired manual directory */
45 char *arch
; /* architecture */
46 char *sec
; /* manual section */
47 char *query
; /* unparsed query expression */
48 int equal
; /* match whole names, not substrings */
53 char **p
; /* array of available manpaths */
54 size_t psz
; /* number of available manpaths */
57 static void catman(const struct req
*, const char *);
58 static void format(const struct req
*, const char *);
59 static void html_print(const char *);
60 static void html_putchar(char);
61 static int http_decode(char *);
62 static void http_parse(struct req
*, const char *);
63 static void http_print(const char *);
64 static void http_putchar(char);
65 static void http_printquery(const struct req
*, const char *);
66 static void pathgen(struct req
*);
67 static void pg_error_badrequest(const char *);
68 static void pg_error_internal(void);
69 static void pg_index(const struct req
*);
70 static void pg_noresult(const struct req
*, const char *);
71 static void pg_search(const struct req
*);
72 static void pg_searchres(const struct req
*,
73 struct manpage
*, size_t);
74 static void pg_show(struct req
*, const char *);
75 static void resp_begin_html(int, const char *);
76 static void resp_begin_http(int, const char *);
77 static void resp_end_html(void);
78 static void resp_searchform(const struct req
*);
79 static void resp_show(const struct req
*, const char *);
80 static void set_query_attr(char **, char **);
81 static int validate_filename(const char *);
82 static int validate_manpath(const struct req
*, const char *);
83 static int validate_urifrag(const char *);
85 static const char *scriptname
; /* CGI script name */
87 static const int sec_prios
[] = {1, 4, 5, 8, 6, 3, 7, 2, 9};
88 static const char *const sec_numbers
[] = {
89 "0", "1", "2", "3", "3p", "4", "5", "6", "7", "8", "9"
91 static const char *const sec_names
[] = {
93 "1 - General Commands",
95 "3 - Library Functions",
100 "7 - Miscellaneous Information",
101 "8 - System Manager\'s Manual",
102 "9 - Kernel Developer\'s Manual"
104 static const int sec_MAX
= sizeof(sec_names
) / sizeof(char *);
106 static const char *const arch_names
[] = {
107 "amd64", "alpha", "armish", "armv7",
108 "aviion", "hppa", "hppa64", "i386",
109 "ia64", "landisk", "loongson", "luna88k",
110 "macppc", "mips64", "octeon", "sgi",
111 "socppc", "solbourne", "sparc", "sparc64",
113 "amiga", "arc", "arm32", "atari",
114 "beagle", "cats", "hp300", "mac68k",
115 "mvme68k", "mvme88k", "mvmeppc", "palm",
116 "pc532", "pegasos", "pmax", "powerpc",
117 "sun3", "wgrisc", "x68k"
119 static const int arch_MAX
= sizeof(arch_names
) / sizeof(char *);
122 * Print a character, escaping HTML along the way.
123 * This will pass non-ASCII straight to output: be warned!
143 putchar((unsigned char)c
);
149 http_printquery(const struct req
*req
, const char *sep
)
152 if (NULL
!= req
->q
.query
) {
154 http_print(req
->q
.query
);
156 if (0 == req
->q
.equal
)
157 printf("%sapropos=1", sep
);
158 if (NULL
!= req
->q
.sec
) {
159 printf("%ssec=", sep
);
160 http_print(req
->q
.sec
);
162 if (NULL
!= req
->q
.arch
) {
163 printf("%sarch=", sep
);
164 http_print(req
->q
.arch
);
166 if (strcmp(req
->q
.manpath
, req
->p
[0])) {
167 printf("%smanpath=", sep
);
168 http_print(req
->q
.manpath
);
173 http_print(const char *p
)
183 * Call through to html_putchar().
184 * Accepts NULL strings.
187 html_print(const char *p
)
197 * Transfer the responsibility for the allocated string *val
198 * to the query structure.
201 set_query_attr(char **attr
, char **val
)
214 * Parse the QUERY_STRING for key-value pairs
215 * and store the values into the query structure.
218 http_parse(struct req
*req
, const char *qs
)
223 req
->q
.manpath
= NULL
;
230 while (*qs
!= '\0') {
234 keysz
= strcspn(qs
, "=;&");
235 key
= mandoc_strndup(qs
, keysz
);
240 /* Parse one value. */
242 valsz
= strcspn(++qs
, ";&");
243 val
= mandoc_strndup(qs
, valsz
);
246 /* Decode and catch encoding errors. */
248 if ( ! (http_decode(key
) && http_decode(val
)))
251 /* Handle key-value pairs. */
253 if ( ! strcmp(key
, "query"))
254 set_query_attr(&req
->q
.query
, &val
);
256 else if ( ! strcmp(key
, "apropos"))
257 req
->q
.equal
= !strcmp(val
, "0");
259 else if ( ! strcmp(key
, "manpath")) {
261 if ( ! strncmp(val
, "OpenBSD ", 8)) {
267 set_query_attr(&req
->q
.manpath
, &val
);
270 else if ( ! (strcmp(key
, "sec")
272 && strcmp(key
, "sektion")
275 if ( ! strcmp(val
, "0"))
277 set_query_attr(&req
->q
.sec
, &val
);
280 else if ( ! strcmp(key
, "arch")) {
281 if ( ! strcmp(val
, "default"))
283 set_query_attr(&req
->q
.arch
, &val
);
287 * The key must be freed in any case.
288 * The val may have been handed over to the query
289 * structure, in which case it is now NULL.
306 if (isalnum((unsigned char)c
)) {
307 putchar((unsigned char)c
);
309 } else if (' ' == c
) {
317 * HTTP-decode a string. The standard explanation is that this turns
318 * "%4e+foo" into "n foo" in the regular way. This is done in-place
319 * over the allocated string.
331 for ( ; '\0' != *p
; p
++, q
++) {
333 if ('\0' == (hex
[0] = *(p
+ 1)))
335 if ('\0' == (hex
[1] = *(p
+ 2)))
337 if (1 != sscanf(hex
, "%x", &c
))
345 *q
= '+' == *p
? ' ' : *p
;
353 resp_begin_http(int code
, const char *msg
)
357 printf("Status: %d %s\r\n", code
, msg
);
359 printf("Content-Type: text/html; charset=utf-8\r\n"
360 "Cache-Control: no-cache\r\n"
361 "Pragma: no-cache\r\n"
368 resp_begin_html(int code
, const char *msg
)
371 resp_begin_http(code
, msg
);
373 printf("<!DOCTYPE html>\n"
376 "<META CHARSET=\"UTF-8\" />\n"
377 "<LINK REL=\"stylesheet\" HREF=\"%s/man-cgi.css\""
378 " TYPE=\"text/css\" media=\"all\">\n"
379 "<LINK REL=\"stylesheet\" HREF=\"%s/man.css\""
380 " TYPE=\"text/css\" media=\"all\">\n"
381 "<TITLE>%s</TITLE>\n"
384 "<!-- Begin page content. //-->\n",
385 CSS_DIR
, CSS_DIR
, CUSTOMIZE_TITLE
);
397 resp_searchform(const struct req
*req
)
401 puts(CUSTOMIZE_BEGIN
);
402 puts("<!-- Begin search form. //-->");
403 printf("<DIV ID=\"mancgi\">\n"
404 "<FORM ACTION=\"%s\" METHOD=\"get\">\n"
406 "<LEGEND>Manual Page Search Parameters</LEGEND>\n",
409 /* Write query input box. */
411 printf( "<TABLE><TR><TD>\n"
412 "<INPUT TYPE=\"text\" NAME=\"query\" VALUE=\"");
413 if (NULL
!= req
->q
.query
)
414 html_print(req
->q
.query
);
415 puts("\" SIZE=\"40\">");
417 /* Write submission and reset buttons. */
419 printf( "<INPUT TYPE=\"submit\" VALUE=\"Submit\">\n"
420 "<INPUT TYPE=\"reset\" VALUE=\"Reset\">\n");
422 /* Write show radio button */
424 printf( "</TD><TD>\n"
425 "<INPUT TYPE=\"radio\" ");
427 printf("CHECKED=\"checked\" ");
428 printf( "NAME=\"apropos\" ID=\"show\" VALUE=\"0\">\n"
429 "<LABEL FOR=\"show\">Show named manual page</LABEL>\n");
431 /* Write section selector. */
433 puts( "</TD></TR><TR><TD>\n"
434 "<SELECT NAME=\"sec\">");
435 for (i
= 0; i
< sec_MAX
; i
++) {
436 printf("<OPTION VALUE=\"%s\"", sec_numbers
[i
]);
437 if (NULL
!= req
->q
.sec
&&
438 0 == strcmp(sec_numbers
[i
], req
->q
.sec
))
439 printf(" SELECTED=\"selected\"");
440 printf(">%s</OPTION>\n", sec_names
[i
]);
444 /* Write architecture selector. */
446 printf( "<SELECT NAME=\"arch\">\n"
447 "<OPTION VALUE=\"default\"");
448 if (NULL
== req
->q
.arch
)
449 printf(" SELECTED=\"selected\"");
450 puts(">All Architectures</OPTION>");
451 for (i
= 0; i
< arch_MAX
; i
++) {
452 printf("<OPTION VALUE=\"%s\"", arch_names
[i
]);
453 if (NULL
!= req
->q
.arch
&&
454 0 == strcmp(arch_names
[i
], req
->q
.arch
))
455 printf(" SELECTED=\"selected\"");
456 printf(">%s</OPTION>\n", arch_names
[i
]);
460 /* Write manpath selector. */
463 puts("<SELECT NAME=\"manpath\">");
464 for (i
= 0; i
< (int)req
->psz
; i
++) {
466 if (strcmp(req
->q
.manpath
, req
->p
[i
]) == 0)
467 printf("SELECTED=\"selected\" ");
469 html_print(req
->p
[i
]);
471 html_print(req
->p
[i
]);
477 /* Write search radio button */
479 printf( "</TD><TD>\n"
480 "<INPUT TYPE=\"radio\" ");
481 if (0 == req
->q
.equal
)
482 printf("CHECKED=\"checked\" ");
483 printf( "NAME=\"apropos\" ID=\"search\" VALUE=\"1\">\n"
484 "<LABEL FOR=\"search\">Search with apropos query</LABEL>\n");
486 puts("</TD></TR></TABLE>\n"
490 puts("<!-- End search form. //-->");
494 validate_urifrag(const char *frag
)
497 while ('\0' != *frag
) {
498 if ( ! (isalnum((unsigned char)*frag
) ||
499 '-' == *frag
|| '.' == *frag
||
500 '/' == *frag
|| '_' == *frag
))
508 validate_manpath(const struct req
*req
, const char* manpath
)
512 if ( ! strcmp(manpath
, "mandoc"))
515 for (i
= 0; i
< req
->psz
; i
++)
516 if ( ! strcmp(manpath
, req
->p
[i
]))
523 validate_filename(const char *file
)
526 if ('.' == file
[0] && '/' == file
[1])
529 return ( ! (strstr(file
, "../") || strstr(file
, "/..") ||
530 (strncmp(file
, "man", 3) && strncmp(file
, "cat", 3))));
534 pg_index(const struct req
*req
)
537 resp_begin_html(200, NULL
);
538 resp_searchform(req
);
540 "This web interface is documented in the\n"
541 "<A HREF=\"%s/mandoc/man8/man.cgi.8\">man.cgi</A>\n"
543 "<A HREF=\"%s/mandoc/man1/apropos.1\">apropos</A>\n"
544 "manual explains the query syntax.\n"
546 scriptname
, scriptname
);
551 pg_noresult(const struct req
*req
, const char *msg
)
553 resp_begin_html(200, NULL
);
554 resp_searchform(req
);
562 pg_error_badrequest(const char *msg
)
565 resp_begin_html(400, "Bad Request");
566 puts("<H1>Bad Request</H1>\n"
569 printf("Try again from the\n"
570 "<A HREF=\"%s\">main page</A>.\n"
576 pg_error_internal(void)
578 resp_begin_html(500, "Internal Server Error");
579 puts("<P>Internal Server Error</P>");
584 pg_searchres(const struct req
*req
, struct manpage
*r
, size_t sz
)
586 char *arch
, *archend
;
587 size_t i
, iuse
, isec
;
588 int archprio
, archpriouse
;
592 for (i
= 0; i
< sz
; i
++) {
593 if (validate_filename(r
[i
].file
))
595 fprintf(stderr
, "invalid filename %s in %s database\n",
596 r
[i
].file
, req
->q
.manpath
);
603 * If we have just one result, then jump there now
606 printf("Status: 303 See Other\r\n");
607 printf("Location: http://%s%s/%s/%s?",
608 HTTP_HOST
, scriptname
, req
->q
.manpath
, r
[0].file
);
609 http_printquery(req
, "&");
611 "Content-Type: text/html; charset=utf-8\r\n"
616 resp_begin_html(200, NULL
);
617 resp_searchform(req
);
618 puts("<DIV CLASS=\"results\">");
621 for (i
= 0; i
< sz
; i
++) {
623 "<TD CLASS=\"title\">\n"
624 "<A HREF=\"%s/%s/%s?",
625 scriptname
, req
->q
.manpath
, r
[i
].file
);
626 http_printquery(req
, "&");
628 html_print(r
[i
].names
);
631 "<TD CLASS=\"desc\">");
632 html_print(r
[i
].output
);
641 * In man(1) mode, show one of the pages
642 * even if more than one is found.
650 for (i
= 0; i
< sz
; i
++) {
651 isec
= strcspn(r
[i
].file
, "123456789");
652 sec
= r
[i
].file
[isec
];
655 prio
= sec_prios
[sec
- '1'];
656 if (NULL
== req
->q
.arch
) {
658 (NULL
== (arch
= strchr(
659 r
[i
].file
+ isec
, '/'))) ? 3 :
660 (NULL
== (archend
= strchr(
661 arch
+ 1, '/'))) ? 0 :
662 strncmp(arch
, "amd64/",
663 archend
- arch
) ? 2 : 1;
664 if (archprio
< archpriouse
) {
665 archpriouse
= archprio
;
670 if (archprio
> archpriouse
)
678 resp_show(req
, r
[iuse
].file
);
685 catman(const struct req
*req
, const char *file
)
693 if (NULL
== (f
= fopen(file
, "r"))) {
694 puts("<P>You specified an invalid manual file.</P>");
698 puts("<DIV CLASS=\"catman\">\n"
701 while (NULL
!= (p
= fgetln(f
, &len
))) {
703 for (i
= 0; i
< (int)len
- 1; i
++) {
705 * This means that the catpage is out of state.
706 * Ignore it and keep going (although the
710 if ('\b' == p
[i
] || '\n' == p
[i
])
714 * Print a regular character.
715 * Close out any bold/italic scopes.
716 * If we're in back-space mode, make sure we'll
717 * have something to enter when we backspace.
720 if ('\b' != p
[i
+ 1]) {
728 } else if (i
+ 2 >= (int)len
)
746 * Handle funny behaviour troff-isms.
747 * These grok'd from the original man2html.c.
750 if (('+' == p
[i
] && 'o' == p
[i
+ 2]) ||
751 ('o' == p
[i
] && '+' == p
[i
+ 2]) ||
752 ('|' == p
[i
] && '=' == p
[i
+ 2]) ||
753 ('=' == p
[i
] && '|' == p
[i
+ 2]) ||
754 ('*' == p
[i
] && '=' == p
[i
+ 2]) ||
755 ('=' == p
[i
] && '*' == p
[i
+ 2]) ||
756 ('*' == p
[i
] && '|' == p
[i
+ 2]) ||
757 ('|' == p
[i
] && '*' == p
[i
+ 2])) {
766 } else if (('|' == p
[i
] && '-' == p
[i
+ 2]) ||
767 ('-' == p
[i
] && '|' == p
[i
+ 1]) ||
768 ('+' == p
[i
] && '-' == p
[i
+ 1]) ||
769 ('-' == p
[i
] && '+' == p
[i
+ 1]) ||
770 ('+' == p
[i
] && '|' == p
[i
+ 1]) ||
771 ('|' == p
[i
] && '+' == p
[i
+ 1])) {
795 * Clean up the last character.
796 * We can get to a newline; don't print that.
804 if (i
== (int)len
- 1 && '\n' != p
[i
])
817 format(const struct req
*req
, const char *file
)
820 struct mchars
*mchars
;
828 if (-1 == (fd
= open(file
, O_RDONLY
, 0))) {
829 puts("<P>You specified an invalid manual file.</P>");
833 mchars
= mchars_alloc();
834 mp
= mparse_alloc(MPARSE_SO
, MANDOCLEVEL_BADARG
, NULL
,
835 mchars
, req
->q
.manpath
);
836 mparse_readfd(mp
, fd
, file
);
839 usepath
= strcmp(req
->q
.manpath
, req
->p
[0]);
840 mandoc_asprintf(&opts
,
841 "fragment,man=%s?query=%%N&sec=%%S%s%s%s%s",
843 req
->q
.arch
? "&arch=" : "",
844 req
->q
.arch
? req
->q
.arch
: "",
845 usepath
? "&manpath=" : "",
846 usepath
? req
->q
.manpath
: "");
848 mparse_result(mp
, &mdoc
, &man
, NULL
);
849 if (NULL
== man
&& NULL
== mdoc
) {
850 fprintf(stderr
, "fatal mandoc error: %s/%s\n",
851 req
->q
.manpath
, file
);
858 vp
= html_alloc(mchars
, opts
);
872 resp_show(const struct req
*req
, const char *file
)
875 if ('.' == file
[0] && '/' == file
[1])
885 pg_show(struct req
*req
, const char *fullpath
)
890 if ((file
= strchr(fullpath
, '/')) == NULL
) {
892 "You did not specify a page to show.");
895 manpath
= mandoc_strndup(fullpath
, file
- fullpath
);
898 if ( ! validate_manpath(req
, manpath
)) {
900 "You specified an invalid manpath.");
906 * Begin by chdir()ing into the manpath.
907 * This way we can pick up the database files, which are
908 * relative to the manpath root.
911 if (chdir(manpath
) == -1) {
912 fprintf(stderr
, "chdir %s: %s\n",
913 manpath
, strerror(errno
));
919 if (strcmp(manpath
, "mandoc")) {
920 free(req
->q
.manpath
);
921 req
->q
.manpath
= manpath
;
925 if ( ! validate_filename(file
)) {
927 "You specified an invalid manual file.");
931 resp_begin_html(200, NULL
);
932 resp_searchform(req
);
933 resp_show(req
, file
);
938 pg_search(const struct req
*req
)
940 struct mansearch search
;
941 struct manpaths paths
;
944 char *query
, *rp
, *wp
;
949 * Begin by chdir()ing into the root of the manpath.
950 * This way we can pick up the database files, which are
951 * relative to the manpath root.
954 if (-1 == (chdir(req
->q
.manpath
))) {
955 fprintf(stderr
, "chdir %s: %s\n",
956 req
->q
.manpath
, strerror(errno
));
961 search
.arch
= req
->q
.arch
;
962 search
.sec
= req
->q
.sec
;
963 search
.outkey
= "Nd";
964 search
.argmode
= req
->q
.equal
? ARG_NAME
: ARG_EXPR
;
965 search
.firstmatch
= 1;
968 paths
.paths
= mandoc_malloc(sizeof(char *));
969 paths
.paths
[0] = mandoc_strdup(".");
972 * Break apart at spaces with backslash-escaping.
977 rp
= query
= mandoc_strdup(req
->q
.query
);
979 while (isspace((unsigned char)*rp
))
983 argv
= mandoc_reallocarray(argv
, argc
+ 1, sizeof(char *));
984 argv
[argc
++] = wp
= rp
;
986 if (isspace((unsigned char)*rp
)) {
991 if (rp
[0] == '\\' && rp
[1] != '\0')
1002 if (0 == mansearch(&search
, &paths
, argc
, argv
, &res
, &ressz
))
1003 pg_noresult(req
, "You entered an invalid query.");
1004 else if (0 == ressz
)
1005 pg_noresult(req
, "No results found.");
1007 pg_searchres(req
, res
, ressz
);
1010 mansearch_free(res
, ressz
);
1011 free(paths
.paths
[0]);
1019 struct itimerval itimer
;
1021 const char *querystring
;
1024 /* Poor man's ReDoS mitigation. */
1026 itimer
.it_value
.tv_sec
= 2;
1027 itimer
.it_value
.tv_usec
= 0;
1028 itimer
.it_interval
.tv_sec
= 2;
1029 itimer
.it_interval
.tv_usec
= 0;
1030 if (setitimer(ITIMER_VIRTUAL
, &itimer
, NULL
) == -1) {
1031 fprintf(stderr
, "setitimer: %s\n", strerror(errno
));
1032 pg_error_internal();
1033 return(EXIT_FAILURE
);
1036 /* Scan our run-time environment. */
1038 if (NULL
== (scriptname
= getenv("SCRIPT_NAME")))
1041 if ( ! validate_urifrag(scriptname
)) {
1042 fprintf(stderr
, "unsafe SCRIPT_NAME \"%s\"\n",
1044 pg_error_internal();
1045 return(EXIT_FAILURE
);
1049 * First we change directory into the MAN_DIR so that
1050 * subsequent scanning for manpath directories is rooted
1051 * relative to the same position.
1054 if (-1 == chdir(MAN_DIR
)) {
1055 fprintf(stderr
, "MAN_DIR: %s: %s\n",
1056 MAN_DIR
, strerror(errno
));
1057 pg_error_internal();
1058 return(EXIT_FAILURE
);
1061 memset(&req
, 0, sizeof(struct req
));
1064 /* Next parse out the query string. */
1066 if (NULL
!= (querystring
= getenv("QUERY_STRING")))
1067 http_parse(&req
, querystring
);
1069 if (req
.q
.manpath
== NULL
)
1070 req
.q
.manpath
= mandoc_strdup(req
.p
[0]);
1071 else if ( ! validate_manpath(&req
, req
.q
.manpath
)) {
1072 pg_error_badrequest(
1073 "You specified an invalid manpath.");
1074 return(EXIT_FAILURE
);
1077 if ( ! (NULL
== req
.q
.arch
|| validate_urifrag(req
.q
.arch
))) {
1078 pg_error_badrequest(
1079 "You specified an invalid architecture.");
1080 return(EXIT_FAILURE
);
1083 /* Dispatch to the three different pages. */
1085 path
= getenv("PATH_INFO");
1088 else if ('/' == *path
)
1092 pg_show(&req
, path
);
1093 else if (NULL
!= req
.q
.query
)
1098 free(req
.q
.manpath
);
1102 for (i
= 0; i
< (int)req
.psz
; i
++)
1105 return(EXIT_SUCCESS
);
1109 * Scan for indexable paths.
1112 pathgen(struct req
*req
)
1118 if (NULL
== (fp
= fopen("manpath.conf", "r"))) {
1119 fprintf(stderr
, "%s/manpath.conf: %s\n",
1120 MAN_DIR
, strerror(errno
));
1121 pg_error_internal();
1125 while (NULL
!= (dp
= fgetln(fp
, &dpsz
))) {
1126 if ('\n' == dp
[dpsz
- 1])
1128 req
->p
= mandoc_realloc(req
->p
,
1129 (req
->psz
+ 1) * sizeof(char *));
1130 dp
= mandoc_strndup(dp
, dpsz
);
1131 if ( ! validate_urifrag(dp
)) {
1132 fprintf(stderr
, "%s/manpath.conf contains "
1133 "unsafe path \"%s\"\n", MAN_DIR
, dp
);
1134 pg_error_internal();
1137 if (NULL
!= strchr(dp
, '/')) {
1138 fprintf(stderr
, "%s/manpath.conf contains "
1139 "path with slash \"%s\"\n", MAN_DIR
, dp
);
1140 pg_error_internal();
1143 req
->p
[req
->psz
++] = dp
;
1146 if ( req
->p
== NULL
) {
1147 fprintf(stderr
, "%s/manpath.conf is empty\n", MAN_DIR
);
1148 pg_error_internal();