]>
git.cameronkatri.com Git - mandoc.git/blob - cgi.c
1 /* $Id: cgi.c,v 1.113 2015/11/05 17:47:51 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/mandoc.css\""
381 " TYPE=\"text/css\" media=\"all\">\n"
382 "<TITLE>%s</TITLE>\n"
385 "<!-- Begin page content. //-->\n",
386 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 roff_man
*man
;
827 if (-1 == (fd
= open(file
, O_RDONLY
, 0))) {
828 puts("<P>You specified an invalid manual file.</P>");
833 mp
= mparse_alloc(MPARSE_SO
, MANDOCLEVEL_BADARG
, NULL
, req
->q
.manpath
);
834 mparse_readfd(mp
, fd
, file
);
837 memset(&conf
, 0, sizeof(conf
));
839 usepath
= strcmp(req
->q
.manpath
, req
->p
[0]);
840 mandoc_asprintf(&conf
.man
, "%s?query=%%N&sec=%%S%s%s%s%s",
842 req
->q
.arch
? "&arch=" : "",
843 req
->q
.arch
? req
->q
.arch
: "",
844 usepath
? "&manpath=" : "",
845 usepath
? req
->q
.manpath
: "");
847 mparse_result(mp
, &man
, NULL
);
849 fprintf(stderr
, "fatal mandoc error: %s/%s\n",
850 req
->q
.manpath
, file
);
857 vp
= html_alloc(&conf
);
859 if (man
->macroset
== MACROSET_MDOC
) {
874 resp_show(const struct req
*req
, const char *file
)
877 if ('.' == file
[0] && '/' == file
[1])
887 pg_show(struct req
*req
, const char *fullpath
)
892 if ((file
= strchr(fullpath
, '/')) == NULL
) {
894 "You did not specify a page to show.");
897 manpath
= mandoc_strndup(fullpath
, file
- fullpath
);
900 if ( ! validate_manpath(req
, manpath
)) {
902 "You specified an invalid manpath.");
908 * Begin by chdir()ing into the manpath.
909 * This way we can pick up the database files, which are
910 * relative to the manpath root.
913 if (chdir(manpath
) == -1) {
914 fprintf(stderr
, "chdir %s: %s\n",
915 manpath
, strerror(errno
));
921 if (strcmp(manpath
, "mandoc")) {
922 free(req
->q
.manpath
);
923 req
->q
.manpath
= manpath
;
927 if ( ! validate_filename(file
)) {
929 "You specified an invalid manual file.");
933 resp_begin_html(200, NULL
);
934 resp_searchform(req
);
935 resp_show(req
, file
);
940 pg_search(const struct req
*req
)
942 struct mansearch search
;
943 struct manpaths paths
;
946 char *query
, *rp
, *wp
;
951 * Begin by chdir()ing into the root of the manpath.
952 * This way we can pick up the database files, which are
953 * relative to the manpath root.
956 if (-1 == (chdir(req
->q
.manpath
))) {
957 fprintf(stderr
, "chdir %s: %s\n",
958 req
->q
.manpath
, strerror(errno
));
963 search
.arch
= req
->q
.arch
;
964 search
.sec
= req
->q
.sec
;
965 search
.outkey
= "Nd";
966 search
.argmode
= req
->q
.equal
? ARG_NAME
: ARG_EXPR
;
967 search
.firstmatch
= 1;
970 paths
.paths
= mandoc_malloc(sizeof(char *));
971 paths
.paths
[0] = mandoc_strdup(".");
974 * Break apart at spaces with backslash-escaping.
979 rp
= query
= mandoc_strdup(req
->q
.query
);
981 while (isspace((unsigned char)*rp
))
985 argv
= mandoc_reallocarray(argv
, argc
+ 1, sizeof(char *));
986 argv
[argc
++] = wp
= rp
;
988 if (isspace((unsigned char)*rp
)) {
993 if (rp
[0] == '\\' && rp
[1] != '\0')
1004 if (0 == mansearch(&search
, &paths
, argc
, argv
, &res
, &ressz
))
1005 pg_noresult(req
, "You entered an invalid query.");
1006 else if (0 == ressz
)
1007 pg_noresult(req
, "No results found.");
1009 pg_searchres(req
, res
, ressz
);
1012 mansearch_free(res
, ressz
);
1013 free(paths
.paths
[0]);
1021 struct itimerval itimer
;
1023 const char *querystring
;
1026 /* Poor man's ReDoS mitigation. */
1028 itimer
.it_value
.tv_sec
= 2;
1029 itimer
.it_value
.tv_usec
= 0;
1030 itimer
.it_interval
.tv_sec
= 2;
1031 itimer
.it_interval
.tv_usec
= 0;
1032 if (setitimer(ITIMER_VIRTUAL
, &itimer
, NULL
) == -1) {
1033 fprintf(stderr
, "setitimer: %s\n", strerror(errno
));
1034 pg_error_internal();
1035 return EXIT_FAILURE
;
1038 /* Scan our run-time environment. */
1040 if (NULL
== (scriptname
= getenv("SCRIPT_NAME")))
1043 if ( ! validate_urifrag(scriptname
)) {
1044 fprintf(stderr
, "unsafe SCRIPT_NAME \"%s\"\n",
1046 pg_error_internal();
1047 return EXIT_FAILURE
;
1051 * First we change directory into the MAN_DIR so that
1052 * subsequent scanning for manpath directories is rooted
1053 * relative to the same position.
1056 if (-1 == chdir(MAN_DIR
)) {
1057 fprintf(stderr
, "MAN_DIR: %s: %s\n",
1058 MAN_DIR
, strerror(errno
));
1059 pg_error_internal();
1060 return EXIT_FAILURE
;
1063 memset(&req
, 0, sizeof(struct req
));
1066 /* Next parse out the query string. */
1068 if (NULL
!= (querystring
= getenv("QUERY_STRING")))
1069 http_parse(&req
, querystring
);
1071 if (req
.q
.manpath
== NULL
)
1072 req
.q
.manpath
= mandoc_strdup(req
.p
[0]);
1073 else if ( ! validate_manpath(&req
, req
.q
.manpath
)) {
1074 pg_error_badrequest(
1075 "You specified an invalid manpath.");
1076 return EXIT_FAILURE
;
1079 if ( ! (NULL
== req
.q
.arch
|| validate_urifrag(req
.q
.arch
))) {
1080 pg_error_badrequest(
1081 "You specified an invalid architecture.");
1082 return EXIT_FAILURE
;
1085 /* Dispatch to the three different pages. */
1087 path
= getenv("PATH_INFO");
1090 else if ('/' == *path
)
1094 pg_show(&req
, path
);
1095 else if (NULL
!= req
.q
.query
)
1100 free(req
.q
.manpath
);
1104 for (i
= 0; i
< (int)req
.psz
; i
++)
1107 return EXIT_SUCCESS
;
1111 * Scan for indexable paths.
1114 pathgen(struct req
*req
)
1120 if (NULL
== (fp
= fopen("manpath.conf", "r"))) {
1121 fprintf(stderr
, "%s/manpath.conf: %s\n",
1122 MAN_DIR
, strerror(errno
));
1123 pg_error_internal();
1127 while (NULL
!= (dp
= fgetln(fp
, &dpsz
))) {
1128 if ('\n' == dp
[dpsz
- 1])
1130 req
->p
= mandoc_realloc(req
->p
,
1131 (req
->psz
+ 1) * sizeof(char *));
1132 dp
= mandoc_strndup(dp
, dpsz
);
1133 if ( ! validate_urifrag(dp
)) {
1134 fprintf(stderr
, "%s/manpath.conf contains "
1135 "unsafe path \"%s\"\n", MAN_DIR
, dp
);
1136 pg_error_internal();
1139 if (NULL
!= strchr(dp
, '/')) {
1140 fprintf(stderr
, "%s/manpath.conf contains "
1141 "path with slash \"%s\"\n", MAN_DIR
, dp
);
1142 pg_error_internal();
1145 req
->p
[req
->psz
++] = dp
;
1148 if ( req
->p
== NULL
) {
1149 fprintf(stderr
, "%s/manpath.conf is empty\n", MAN_DIR
);
1150 pg_error_internal();