]>
git.cameronkatri.com Git - mandoc.git/blob - cgi.c
1 /* $Id: cgi.c,v 1.109 2015/10/06 18:32:19 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"
38 #include "mansearch.h"
42 * A query as passed to the search function.
45 char *manpath
; /* desired manual directory */
46 char *arch
; /* architecture */
47 char *sec
; /* manual section */
48 char *query
; /* unparsed query expression */
49 int equal
; /* match whole names, not substrings */
54 char **p
; /* array of available manpaths */
55 size_t psz
; /* number of available manpaths */
58 static void catman(const struct req
*, const char *);
59 static void format(const struct req
*, const char *);
60 static void html_print(const char *);
61 static void html_putchar(char);
62 static int http_decode(char *);
63 static void http_parse(struct req
*, const char *);
64 static void http_print(const char *);
65 static void http_putchar(char);
66 static void http_printquery(const struct req
*, const char *);
67 static void pathgen(struct req
*);
68 static void pg_error_badrequest(const char *);
69 static void pg_error_internal(void);
70 static void pg_index(const struct req
*);
71 static void pg_noresult(const struct req
*, const char *);
72 static void pg_search(const struct req
*);
73 static void pg_searchres(const struct req
*,
74 struct manpage
*, size_t);
75 static void pg_show(struct req
*, const char *);
76 static void resp_begin_html(int, const char *);
77 static void resp_begin_http(int, const char *);
78 static void resp_end_html(void);
79 static void resp_searchform(const struct req
*);
80 static void resp_show(const struct req
*, const char *);
81 static void set_query_attr(char **, char **);
82 static int validate_filename(const char *);
83 static int validate_manpath(const struct req
*, const char *);
84 static int validate_urifrag(const char *);
86 static const char *scriptname
; /* CGI script name */
88 static const int sec_prios
[] = {1, 4, 5, 8, 6, 3, 7, 2, 9};
89 static const char *const sec_numbers
[] = {
90 "0", "1", "2", "3", "3p", "4", "5", "6", "7", "8", "9"
92 static const char *const sec_names
[] = {
94 "1 - General Commands",
96 "3 - Library Functions",
101 "7 - Miscellaneous Information",
102 "8 - System Manager\'s Manual",
103 "9 - Kernel Developer\'s Manual"
105 static const int sec_MAX
= sizeof(sec_names
) / sizeof(char *);
107 static const char *const arch_names
[] = {
108 "amd64", "alpha", "armish", "armv7",
109 "aviion", "hppa", "hppa64", "i386",
110 "ia64", "landisk", "loongson", "luna88k",
111 "macppc", "mips64", "octeon", "sgi",
112 "socppc", "solbourne", "sparc", "sparc64",
114 "amiga", "arc", "arm32", "atari",
115 "beagle", "cats", "hp300", "mac68k",
116 "mvme68k", "mvme88k", "mvmeppc", "palm",
117 "pc532", "pegasos", "pmax", "powerpc",
118 "sun3", "wgrisc", "x68k"
120 static const int arch_MAX
= sizeof(arch_names
) / sizeof(char *);
123 * Print a character, escaping HTML along the way.
124 * This will pass non-ASCII straight to output: be warned!
144 putchar((unsigned char)c
);
150 http_printquery(const struct req
*req
, const char *sep
)
153 if (NULL
!= req
->q
.query
) {
155 http_print(req
->q
.query
);
157 if (0 == req
->q
.equal
)
158 printf("%sapropos=1", sep
);
159 if (NULL
!= req
->q
.sec
) {
160 printf("%ssec=", sep
);
161 http_print(req
->q
.sec
);
163 if (NULL
!= req
->q
.arch
) {
164 printf("%sarch=", sep
);
165 http_print(req
->q
.arch
);
167 if (strcmp(req
->q
.manpath
, req
->p
[0])) {
168 printf("%smanpath=", sep
);
169 http_print(req
->q
.manpath
);
174 http_print(const char *p
)
184 * Call through to html_putchar().
185 * Accepts NULL strings.
188 html_print(const char *p
)
198 * Transfer the responsibility for the allocated string *val
199 * to the query structure.
202 set_query_attr(char **attr
, char **val
)
215 * Parse the QUERY_STRING for key-value pairs
216 * and store the values into the query structure.
219 http_parse(struct req
*req
, const char *qs
)
224 req
->q
.manpath
= NULL
;
231 while (*qs
!= '\0') {
235 keysz
= strcspn(qs
, "=;&");
236 key
= mandoc_strndup(qs
, keysz
);
241 /* Parse one value. */
243 valsz
= strcspn(++qs
, ";&");
244 val
= mandoc_strndup(qs
, valsz
);
247 /* Decode and catch encoding errors. */
249 if ( ! (http_decode(key
) && http_decode(val
)))
252 /* Handle key-value pairs. */
254 if ( ! strcmp(key
, "query"))
255 set_query_attr(&req
->q
.query
, &val
);
257 else if ( ! strcmp(key
, "apropos"))
258 req
->q
.equal
= !strcmp(val
, "0");
260 else if ( ! strcmp(key
, "manpath")) {
262 if ( ! strncmp(val
, "OpenBSD ", 8)) {
268 set_query_attr(&req
->q
.manpath
, &val
);
271 else if ( ! (strcmp(key
, "sec")
273 && strcmp(key
, "sektion")
276 if ( ! strcmp(val
, "0"))
278 set_query_attr(&req
->q
.sec
, &val
);
281 else if ( ! strcmp(key
, "arch")) {
282 if ( ! strcmp(val
, "default"))
284 set_query_attr(&req
->q
.arch
, &val
);
288 * The key must be freed in any case.
289 * The val may have been handed over to the query
290 * structure, in which case it is now NULL.
307 if (isalnum((unsigned char)c
)) {
308 putchar((unsigned char)c
);
310 } else if (' ' == c
) {
318 * HTTP-decode a string. The standard explanation is that this turns
319 * "%4e+foo" into "n foo" in the regular way. This is done in-place
320 * over the allocated string.
332 for ( ; '\0' != *p
; p
++, q
++) {
334 if ('\0' == (hex
[0] = *(p
+ 1)))
336 if ('\0' == (hex
[1] = *(p
+ 2)))
338 if (1 != sscanf(hex
, "%x", &c
))
346 *q
= '+' == *p
? ' ' : *p
;
354 resp_begin_http(int code
, const char *msg
)
358 printf("Status: %d %s\r\n", code
, msg
);
360 printf("Content-Type: text/html; charset=utf-8\r\n"
361 "Cache-Control: no-cache\r\n"
362 "Pragma: no-cache\r\n"
369 resp_begin_html(int code
, const char *msg
)
372 resp_begin_http(code
, msg
);
374 printf("<!DOCTYPE html>\n"
377 "<META CHARSET=\"UTF-8\" />\n"
378 "<LINK REL=\"stylesheet\" HREF=\"%s/man-cgi.css\""
379 " TYPE=\"text/css\" media=\"all\">\n"
380 "<LINK REL=\"stylesheet\" HREF=\"%s/man.css\""
381 " TYPE=\"text/css\" media=\"all\">\n"
382 "<TITLE>%s</TITLE>\n"
385 "<!-- Begin page content. //-->\n",
386 CSS_DIR
, CSS_DIR
, CUSTOMIZE_TITLE
);
398 resp_searchform(const struct req
*req
)
402 puts(CUSTOMIZE_BEGIN
);
403 puts("<!-- Begin search form. //-->");
404 printf("<DIV ID=\"mancgi\">\n"
405 "<FORM ACTION=\"%s\" METHOD=\"get\">\n"
407 "<LEGEND>Manual Page Search Parameters</LEGEND>\n",
410 /* Write query input box. */
412 printf( "<TABLE><TR><TD>\n"
413 "<INPUT TYPE=\"text\" NAME=\"query\" VALUE=\"");
414 if (NULL
!= req
->q
.query
)
415 html_print(req
->q
.query
);
416 puts("\" SIZE=\"40\">");
418 /* Write submission and reset buttons. */
420 printf( "<INPUT TYPE=\"submit\" VALUE=\"Submit\">\n"
421 "<INPUT TYPE=\"reset\" VALUE=\"Reset\">\n");
423 /* Write show radio button */
425 printf( "</TD><TD>\n"
426 "<INPUT TYPE=\"radio\" ");
428 printf("CHECKED=\"checked\" ");
429 printf( "NAME=\"apropos\" ID=\"show\" VALUE=\"0\">\n"
430 "<LABEL FOR=\"show\">Show named manual page</LABEL>\n");
432 /* Write section selector. */
434 puts( "</TD></TR><TR><TD>\n"
435 "<SELECT NAME=\"sec\">");
436 for (i
= 0; i
< sec_MAX
; i
++) {
437 printf("<OPTION VALUE=\"%s\"", sec_numbers
[i
]);
438 if (NULL
!= req
->q
.sec
&&
439 0 == strcmp(sec_numbers
[i
], req
->q
.sec
))
440 printf(" SELECTED=\"selected\"");
441 printf(">%s</OPTION>\n", sec_names
[i
]);
445 /* Write architecture selector. */
447 printf( "<SELECT NAME=\"arch\">\n"
448 "<OPTION VALUE=\"default\"");
449 if (NULL
== req
->q
.arch
)
450 printf(" SELECTED=\"selected\"");
451 puts(">All Architectures</OPTION>");
452 for (i
= 0; i
< arch_MAX
; i
++) {
453 printf("<OPTION VALUE=\"%s\"", arch_names
[i
]);
454 if (NULL
!= req
->q
.arch
&&
455 0 == strcmp(arch_names
[i
], req
->q
.arch
))
456 printf(" SELECTED=\"selected\"");
457 printf(">%s</OPTION>\n", arch_names
[i
]);
461 /* Write manpath selector. */
464 puts("<SELECT NAME=\"manpath\">");
465 for (i
= 0; i
< (int)req
->psz
; i
++) {
467 if (strcmp(req
->q
.manpath
, req
->p
[i
]) == 0)
468 printf("SELECTED=\"selected\" ");
470 html_print(req
->p
[i
]);
472 html_print(req
->p
[i
]);
478 /* Write search radio button */
480 printf( "</TD><TD>\n"
481 "<INPUT TYPE=\"radio\" ");
482 if (0 == req
->q
.equal
)
483 printf("CHECKED=\"checked\" ");
484 printf( "NAME=\"apropos\" ID=\"search\" VALUE=\"1\">\n"
485 "<LABEL FOR=\"search\">Search with apropos query</LABEL>\n");
487 puts("</TD></TR></TABLE>\n"
491 puts("<!-- End search form. //-->");
495 validate_urifrag(const char *frag
)
498 while ('\0' != *frag
) {
499 if ( ! (isalnum((unsigned char)*frag
) ||
500 '-' == *frag
|| '.' == *frag
||
501 '/' == *frag
|| '_' == *frag
))
509 validate_manpath(const struct req
*req
, const char* manpath
)
513 if ( ! strcmp(manpath
, "mandoc"))
516 for (i
= 0; i
< req
->psz
; i
++)
517 if ( ! strcmp(manpath
, req
->p
[i
]))
524 validate_filename(const char *file
)
527 if ('.' == file
[0] && '/' == file
[1])
530 return ! (strstr(file
, "../") || strstr(file
, "/..") ||
531 (strncmp(file
, "man", 3) && strncmp(file
, "cat", 3)));
535 pg_index(const struct req
*req
)
538 resp_begin_html(200, NULL
);
539 resp_searchform(req
);
541 "This web interface is documented in the\n"
542 "<A HREF=\"%s/mandoc/man8/man.cgi.8\">man.cgi</A>\n"
544 "<A HREF=\"%s/mandoc/man1/apropos.1\">apropos</A>\n"
545 "manual explains the query syntax.\n"
547 scriptname
, scriptname
);
552 pg_noresult(const struct req
*req
, const char *msg
)
554 resp_begin_html(200, NULL
);
555 resp_searchform(req
);
563 pg_error_badrequest(const char *msg
)
566 resp_begin_html(400, "Bad Request");
567 puts("<H1>Bad Request</H1>\n"
570 printf("Try again from the\n"
571 "<A HREF=\"%s\">main page</A>.\n"
577 pg_error_internal(void)
579 resp_begin_html(500, "Internal Server Error");
580 puts("<P>Internal Server Error</P>");
585 pg_searchres(const struct req
*req
, struct manpage
*r
, size_t sz
)
587 char *arch
, *archend
;
588 size_t i
, iuse
, isec
;
589 int archprio
, archpriouse
;
593 for (i
= 0; i
< sz
; i
++) {
594 if (validate_filename(r
[i
].file
))
596 fprintf(stderr
, "invalid filename %s in %s database\n",
597 r
[i
].file
, req
->q
.manpath
);
604 * If we have just one result, then jump there now
607 printf("Status: 303 See Other\r\n");
608 printf("Location: http://%s%s/%s/%s?",
609 HTTP_HOST
, scriptname
, req
->q
.manpath
, r
[0].file
);
610 http_printquery(req
, "&");
612 "Content-Type: text/html; charset=utf-8\r\n"
617 resp_begin_html(200, NULL
);
618 resp_searchform(req
);
619 puts("<DIV CLASS=\"results\">");
622 for (i
= 0; i
< sz
; i
++) {
624 "<TD CLASS=\"title\">\n"
625 "<A HREF=\"%s/%s/%s?",
626 scriptname
, req
->q
.manpath
, r
[i
].file
);
627 http_printquery(req
, "&");
629 html_print(r
[i
].names
);
632 "<TD CLASS=\"desc\">");
633 html_print(r
[i
].output
);
642 * In man(1) mode, show one of the pages
643 * even if more than one is found.
651 for (i
= 0; i
< sz
; i
++) {
652 isec
= strcspn(r
[i
].file
, "123456789");
653 sec
= r
[i
].file
[isec
];
656 prio
= sec_prios
[sec
- '1'];
657 if (NULL
== req
->q
.arch
) {
659 (NULL
== (arch
= strchr(
660 r
[i
].file
+ isec
, '/'))) ? 3 :
661 (NULL
== (archend
= strchr(
662 arch
+ 1, '/'))) ? 0 :
663 strncmp(arch
, "amd64/",
664 archend
- arch
) ? 2 : 1;
665 if (archprio
< archpriouse
) {
666 archpriouse
= archprio
;
671 if (archprio
> archpriouse
)
679 resp_show(req
, r
[iuse
].file
);
686 catman(const struct req
*req
, const char *file
)
694 if (NULL
== (f
= fopen(file
, "r"))) {
695 puts("<P>You specified an invalid manual file.</P>");
699 puts("<DIV CLASS=\"catman\">\n"
702 while (NULL
!= (p
= fgetln(f
, &len
))) {
704 for (i
= 0; i
< (int)len
- 1; i
++) {
706 * This means that the catpage is out of state.
707 * Ignore it and keep going (although the
711 if ('\b' == p
[i
] || '\n' == p
[i
])
715 * Print a regular character.
716 * Close out any bold/italic scopes.
717 * If we're in back-space mode, make sure we'll
718 * have something to enter when we backspace.
721 if ('\b' != p
[i
+ 1]) {
729 } else if (i
+ 2 >= (int)len
)
747 * Handle funny behaviour troff-isms.
748 * These grok'd from the original man2html.c.
751 if (('+' == p
[i
] && 'o' == p
[i
+ 2]) ||
752 ('o' == 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]) ||
758 ('|' == p
[i
] && '*' == p
[i
+ 2])) {
767 } else if (('|' == p
[i
] && '-' == p
[i
+ 2]) ||
768 ('-' == p
[i
] && '|' == p
[i
+ 1]) ||
769 ('+' == p
[i
] && '-' == p
[i
+ 1]) ||
770 ('-' == p
[i
] && '+' == p
[i
+ 1]) ||
771 ('+' == p
[i
] && '|' == p
[i
+ 1]) ||
772 ('|' == p
[i
] && '+' == p
[i
+ 1])) {
796 * Clean up the last character.
797 * We can get to a newline; don't print that.
805 if (i
== (int)len
- 1 && '\n' != p
[i
])
818 format(const struct req
*req
, const char *file
)
820 struct manoutput conf
;
822 struct mchars
*mchars
;
823 struct roff_man
*man
;
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 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(mchars
, &conf
);
861 if (man
->macroset
== MACROSET_MDOC
)
873 resp_show(const struct req
*req
, const char *file
)
876 if ('.' == file
[0] && '/' == file
[1])
886 pg_show(struct req
*req
, const char *fullpath
)
891 if ((file
= strchr(fullpath
, '/')) == NULL
) {
893 "You did not specify a page to show.");
896 manpath
= mandoc_strndup(fullpath
, file
- fullpath
);
899 if ( ! validate_manpath(req
, manpath
)) {
901 "You specified an invalid manpath.");
907 * Begin by chdir()ing into the manpath.
908 * This way we can pick up the database files, which are
909 * relative to the manpath root.
912 if (chdir(manpath
) == -1) {
913 fprintf(stderr
, "chdir %s: %s\n",
914 manpath
, strerror(errno
));
920 if (strcmp(manpath
, "mandoc")) {
921 free(req
->q
.manpath
);
922 req
->q
.manpath
= manpath
;
926 if ( ! validate_filename(file
)) {
928 "You specified an invalid manual file.");
932 resp_begin_html(200, NULL
);
933 resp_searchform(req
);
934 resp_show(req
, file
);
939 pg_search(const struct req
*req
)
941 struct mansearch search
;
942 struct manpaths paths
;
945 char *query
, *rp
, *wp
;
950 * Begin by chdir()ing into the root of the manpath.
951 * This way we can pick up the database files, which are
952 * relative to the manpath root.
955 if (-1 == (chdir(req
->q
.manpath
))) {
956 fprintf(stderr
, "chdir %s: %s\n",
957 req
->q
.manpath
, strerror(errno
));
962 search
.arch
= req
->q
.arch
;
963 search
.sec
= req
->q
.sec
;
964 search
.outkey
= "Nd";
965 search
.argmode
= req
->q
.equal
? ARG_NAME
: ARG_EXPR
;
966 search
.firstmatch
= 1;
969 paths
.paths
= mandoc_malloc(sizeof(char *));
970 paths
.paths
[0] = mandoc_strdup(".");
973 * Break apart at spaces with backslash-escaping.
978 rp
= query
= mandoc_strdup(req
->q
.query
);
980 while (isspace((unsigned char)*rp
))
984 argv
= mandoc_reallocarray(argv
, argc
+ 1, sizeof(char *));
985 argv
[argc
++] = wp
= rp
;
987 if (isspace((unsigned char)*rp
)) {
992 if (rp
[0] == '\\' && rp
[1] != '\0')
1003 if (0 == mansearch(&search
, &paths
, argc
, argv
, &res
, &ressz
))
1004 pg_noresult(req
, "You entered an invalid query.");
1005 else if (0 == ressz
)
1006 pg_noresult(req
, "No results found.");
1008 pg_searchres(req
, res
, ressz
);
1011 mansearch_free(res
, ressz
);
1012 free(paths
.paths
[0]);
1020 struct itimerval itimer
;
1022 const char *querystring
;
1025 /* Poor man's ReDoS mitigation. */
1027 itimer
.it_value
.tv_sec
= 2;
1028 itimer
.it_value
.tv_usec
= 0;
1029 itimer
.it_interval
.tv_sec
= 2;
1030 itimer
.it_interval
.tv_usec
= 0;
1031 if (setitimer(ITIMER_VIRTUAL
, &itimer
, NULL
) == -1) {
1032 fprintf(stderr
, "setitimer: %s\n", strerror(errno
));
1033 pg_error_internal();
1034 return EXIT_FAILURE
;
1037 /* Scan our run-time environment. */
1039 if (NULL
== (scriptname
= getenv("SCRIPT_NAME")))
1042 if ( ! validate_urifrag(scriptname
)) {
1043 fprintf(stderr
, "unsafe SCRIPT_NAME \"%s\"\n",
1045 pg_error_internal();
1046 return EXIT_FAILURE
;
1050 * First we change directory into the MAN_DIR so that
1051 * subsequent scanning for manpath directories is rooted
1052 * relative to the same position.
1055 if (-1 == chdir(MAN_DIR
)) {
1056 fprintf(stderr
, "MAN_DIR: %s: %s\n",
1057 MAN_DIR
, strerror(errno
));
1058 pg_error_internal();
1059 return EXIT_FAILURE
;
1062 memset(&req
, 0, sizeof(struct req
));
1065 /* Next parse out the query string. */
1067 if (NULL
!= (querystring
= getenv("QUERY_STRING")))
1068 http_parse(&req
, querystring
);
1070 if (req
.q
.manpath
== NULL
)
1071 req
.q
.manpath
= mandoc_strdup(req
.p
[0]);
1072 else if ( ! validate_manpath(&req
, req
.q
.manpath
)) {
1073 pg_error_badrequest(
1074 "You specified an invalid manpath.");
1075 return EXIT_FAILURE
;
1078 if ( ! (NULL
== req
.q
.arch
|| validate_urifrag(req
.q
.arch
))) {
1079 pg_error_badrequest(
1080 "You specified an invalid architecture.");
1081 return EXIT_FAILURE
;
1084 /* Dispatch to the three different pages. */
1086 path
= getenv("PATH_INFO");
1089 else if ('/' == *path
)
1093 pg_show(&req
, path
);
1094 else if (NULL
!= req
.q
.query
)
1099 free(req
.q
.manpath
);
1103 for (i
= 0; i
< (int)req
.psz
; i
++)
1106 return EXIT_SUCCESS
;
1110 * Scan for indexable paths.
1113 pathgen(struct req
*req
)
1119 if (NULL
== (fp
= fopen("manpath.conf", "r"))) {
1120 fprintf(stderr
, "%s/manpath.conf: %s\n",
1121 MAN_DIR
, strerror(errno
));
1122 pg_error_internal();
1126 while (NULL
!= (dp
= fgetln(fp
, &dpsz
))) {
1127 if ('\n' == dp
[dpsz
- 1])
1129 req
->p
= mandoc_realloc(req
->p
,
1130 (req
->psz
+ 1) * sizeof(char *));
1131 dp
= mandoc_strndup(dp
, dpsz
);
1132 if ( ! validate_urifrag(dp
)) {
1133 fprintf(stderr
, "%s/manpath.conf contains "
1134 "unsafe path \"%s\"\n", MAN_DIR
, dp
);
1135 pg_error_internal();
1138 if (NULL
!= strchr(dp
, '/')) {
1139 fprintf(stderr
, "%s/manpath.conf contains "
1140 "path with slash \"%s\"\n", MAN_DIR
, dp
);
1141 pg_error_internal();
1144 req
->p
[req
->psz
++] = dp
;
1147 if ( req
->p
== NULL
) {
1148 fprintf(stderr
, "%s/manpath.conf is empty\n", MAN_DIR
);
1149 pg_error_internal();