1 /* $Id: cgi.c,v 1.92 2014/08/05 15:29: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.
33 #include "mandoc_aux.h"
36 #include "mansearch.h"
40 * A query as passed to the search function.
43 char *manpath
; /* desired manual directory */
44 char *arch
; /* architecture */
45 char *sec
; /* manual section */
46 char *query
; /* unparsed query expression */
47 int equal
; /* match whole names, not substrings */
52 char **p
; /* array of available manpaths */
53 size_t psz
; /* number of available manpaths */
56 static void catman(const struct req
*, const char *);
57 static void format(const struct req
*, const char *);
58 static void html_print(const char *);
59 static void html_putchar(char);
60 static int http_decode(char *);
61 static void http_parse(struct req
*, const char *);
62 static void http_print(const char *);
63 static void http_putchar(char);
64 static void http_printquery(const struct req
*, const char *);
65 static void pathgen(struct req
*);
66 static void pg_error_badrequest(const char *);
67 static void pg_error_internal(void);
68 static void pg_index(const struct req
*);
69 static void pg_noresult(const struct req
*, const char *);
70 static void pg_search(const struct req
*);
71 static void pg_searchres(const struct req
*,
72 struct manpage
*, size_t);
73 static void pg_show(struct req
*, const char *);
74 static void resp_begin_html(int, const char *);
75 static void resp_begin_http(int, const char *);
76 static void resp_end_html(void);
77 static void resp_searchform(const struct req
*);
78 static void resp_show(const struct req
*, const char *);
79 static void set_query_attr(char **, char **);
80 static int validate_filename(const char *);
81 static int validate_manpath(const struct req
*, const char *);
82 static int validate_urifrag(const char *);
84 static const char *scriptname
; /* CGI script name */
86 static const int sec_prios
[] = {1, 4, 5, 8, 6, 3, 7, 2, 9};
87 static const char *const sec_numbers
[] = {
88 "0", "1", "2", "3", "3p", "4", "5", "6", "7", "8", "9"
90 static const char *const sec_names
[] = {
92 "1 - General Commands",
95 "3p - Perl Subroutines",
99 "7 - Macros and Conventions",
100 "8 - Maintenance Commands",
101 "9 - Kernel Interface"
103 static const int sec_MAX
= sizeof(sec_names
) / sizeof(char *);
105 static const char *const arch_names
[] = {
106 "amd64", "alpha", "armish", "armv7",
107 "aviion", "hppa", "hppa64", "i386",
108 "ia64", "landisk", "loongson", "luna88k",
109 "macppc", "mips64", "octeon", "sgi",
110 "socppc", "solbourne", "sparc", "sparc64",
112 "amiga", "arc", "arm32", "atari",
113 "beagle", "cats", "hp300", "mac68k",
114 "mvme68k", "mvme88k", "mvmeppc", "palm",
115 "pc532", "pegasos", "pmax", "powerpc",
116 "sun3", "wgrisc", "x68k"
118 static const int arch_MAX
= sizeof(arch_names
) / sizeof(char *);
121 * Print a character, escaping HTML along the way.
122 * This will pass non-ASCII straight to output: be warned!
142 putchar((unsigned char)c
);
148 http_printquery(const struct req
*req
, const char *sep
)
151 if (NULL
!= req
->q
.query
) {
153 http_print(req
->q
.query
);
155 if (0 == req
->q
.equal
)
156 printf("%sapropos=1", sep
);
157 if (NULL
!= req
->q
.sec
) {
158 printf("%ssec=", sep
);
159 http_print(req
->q
.sec
);
161 if (NULL
!= req
->q
.arch
) {
162 printf("%sarch=", sep
);
163 http_print(req
->q
.arch
);
165 if (NULL
!= req
->q
.manpath
&&
166 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.
301 /* Fall back to the default manpath. */
303 if (req
->q
.manpath
== NULL
)
304 req
->q
.manpath
= mandoc_strdup(req
->p
[0]);
311 if (isalnum((unsigned char)c
)) {
312 putchar((unsigned char)c
);
314 } else if (' ' == c
) {
322 * HTTP-decode a string. The standard explanation is that this turns
323 * "%4e+foo" into "n foo" in the regular way. This is done in-place
324 * over the allocated string.
336 for ( ; '\0' != *p
; p
++, q
++) {
338 if ('\0' == (hex
[0] = *(p
+ 1)))
340 if ('\0' == (hex
[1] = *(p
+ 2)))
342 if (1 != sscanf(hex
, "%x", &c
))
350 *q
= '+' == *p
? ' ' : *p
;
358 resp_begin_http(int code
, const char *msg
)
362 printf("Status: %d %s\r\n", code
, msg
);
364 printf("Content-Type: text/html; charset=utf-8\r\n"
365 "Cache-Control: no-cache\r\n"
366 "Pragma: no-cache\r\n"
373 resp_begin_html(int code
, const char *msg
)
376 resp_begin_http(code
, msg
);
378 printf("<!DOCTYPE HTML PUBLIC "
379 " \"-//W3C//DTD HTML 4.01//EN\""
380 " \"http://www.w3.org/TR/html4/strict.dtd\">\n"
383 "<META HTTP-EQUIV=\"Content-Type\""
384 " CONTENT=\"text/html; charset=utf-8\">\n"
385 "<LINK REL=\"stylesheet\" HREF=\"%s/man-cgi.css\""
386 " TYPE=\"text/css\" media=\"all\">\n"
387 "<LINK REL=\"stylesheet\" HREF=\"%s/man.css\""
388 " TYPE=\"text/css\" media=\"all\">\n"
389 "<TITLE>%s</TITLE>\n"
392 "<!-- Begin page content. //-->\n",
393 CSS_DIR
, CSS_DIR
, CUSTOMIZE_TITLE
);
405 resp_searchform(const struct req
*req
)
409 puts(CUSTOMIZE_BEGIN
);
410 puts("<!-- Begin search form. //-->");
411 printf("<DIV ID=\"mancgi\">\n"
412 "<FORM ACTION=\"%s\" METHOD=\"get\">\n"
414 "<LEGEND>Manual Page Search Parameters</LEGEND>\n",
417 /* Write query input box. */
419 printf( "<TABLE><TR><TD>\n"
420 "<INPUT TYPE=\"text\" NAME=\"query\" VALUE=\"");
421 if (NULL
!= req
->q
.query
)
422 html_print(req
->q
.query
);
423 puts("\" SIZE=\"40\">");
425 /* Write submission and reset buttons. */
427 printf( "<INPUT TYPE=\"submit\" VALUE=\"Submit\">\n"
428 "<INPUT TYPE=\"reset\" VALUE=\"Reset\">\n");
430 /* Write show radio button */
432 printf( "</TD><TD>\n"
433 "<INPUT TYPE=\"radio\" ");
435 printf("CHECKED=\"checked\" ");
436 printf( "NAME=\"apropos\" ID=\"show\" VALUE=\"0\">\n"
437 "<LABEL FOR=\"show\">Show named manual page</LABEL>\n");
439 /* Write section selector. */
441 puts( "</TD></TR><TR><TD>\n"
442 "<SELECT NAME=\"sec\">");
443 for (i
= 0; i
< sec_MAX
; i
++) {
444 printf("<OPTION VALUE=\"%s\"", sec_numbers
[i
]);
445 if (NULL
!= req
->q
.sec
&&
446 0 == strcmp(sec_numbers
[i
], req
->q
.sec
))
447 printf(" SELECTED=\"selected\"");
448 printf(">%s</OPTION>\n", sec_names
[i
]);
452 /* Write architecture selector. */
454 printf( "<SELECT NAME=\"arch\">\n"
455 "<OPTION VALUE=\"default\"");
456 if (NULL
== req
->q
.arch
)
457 printf(" SELECTED=\"selected\"");
458 puts(">All Architectures</OPTION>");
459 for (i
= 0; i
< arch_MAX
; i
++) {
460 printf("<OPTION VALUE=\"%s\"", arch_names
[i
]);
461 if (NULL
!= req
->q
.arch
&&
462 0 == strcmp(arch_names
[i
], req
->q
.arch
))
463 printf(" SELECTED=\"selected\"");
464 printf(">%s</OPTION>\n", arch_names
[i
]);
468 /* Write manpath selector. */
471 puts("<SELECT NAME=\"manpath\">");
472 for (i
= 0; i
< (int)req
->psz
; i
++) {
474 if (NULL
== req
->q
.manpath
? 0 == i
:
475 0 == strcmp(req
->q
.manpath
, req
->p
[i
]))
476 printf("SELECTED=\"selected\" ");
478 html_print(req
->p
[i
]);
480 html_print(req
->p
[i
]);
486 /* Write search radio button */
488 printf( "</TD><TD>\n"
489 "<INPUT TYPE=\"radio\" ");
490 if (0 == req
->q
.equal
)
491 printf("CHECKED=\"checked\" ");
492 printf( "NAME=\"apropos\" ID=\"search\" VALUE=\"1\">\n"
493 "<LABEL FOR=\"search\">Search with apropos query</LABEL>\n");
495 puts("</TD></TR></TABLE>\n"
499 puts("<!-- End search form. //-->");
503 validate_urifrag(const char *frag
)
506 while ('\0' != *frag
) {
507 if ( ! (isalnum((unsigned char)*frag
) ||
508 '-' == *frag
|| '.' == *frag
||
509 '/' == *frag
|| '_' == *frag
))
517 validate_manpath(const struct req
*req
, const char* manpath
)
521 if ( ! strcmp(manpath
, "mandoc"))
524 for (i
= 0; i
< req
->psz
; i
++)
525 if ( ! strcmp(manpath
, req
->p
[i
]))
532 validate_filename(const char *file
)
535 if ('.' == file
[0] && '/' == file
[1])
538 return ( ! (strstr(file
, "../") || strstr(file
, "/..") ||
539 (strncmp(file
, "man", 3) && strncmp(file
, "cat", 3))));
543 pg_index(const struct req
*req
)
546 resp_begin_html(200, NULL
);
547 resp_searchform(req
);
549 "This web interface is documented in the\n"
550 "<A HREF=\"%s/mandoc/man8/man.cgi.8\">man.cgi</A>\n"
552 "<A HREF=\"%s/mandoc/man1/apropos.1\">apropos</A>\n"
553 "manual explains the query syntax.\n"
555 scriptname
, scriptname
);
560 pg_noresult(const struct req
*req
, const char *msg
)
562 resp_begin_html(200, NULL
);
563 resp_searchform(req
);
571 pg_error_badrequest(const char *msg
)
574 resp_begin_html(400, "Bad Request");
575 puts("<H1>Bad Request</H1>\n"
578 printf("Try again from the\n"
579 "<A HREF=\"%s\">main page</A>.\n"
585 pg_error_internal(void)
587 resp_begin_html(500, "Internal Server Error");
588 puts("<P>Internal Server Error</P>");
593 pg_searchres(const struct req
*req
, struct manpage
*r
, size_t sz
)
595 char *arch
, *archend
;
596 size_t i
, iuse
, isec
;
597 int archprio
, archpriouse
;
601 for (i
= 0; i
< sz
; i
++) {
602 if (validate_filename(r
[i
].file
))
604 fprintf(stderr
, "invalid filename %s in %s database\n",
605 r
[i
].file
, req
->q
.manpath
);
612 * If we have just one result, then jump there now
615 printf("Status: 303 See Other\r\n");
616 printf("Location: http://%s%s/%s/%s?",
617 HTTP_HOST
, scriptname
, req
->q
.manpath
, r
[0].file
);
618 http_printquery(req
, "&");
620 "Content-Type: text/html; charset=utf-8\r\n"
625 resp_begin_html(200, NULL
);
626 resp_searchform(req
);
627 puts("<DIV CLASS=\"results\">");
630 for (i
= 0; i
< sz
; i
++) {
632 "<TD CLASS=\"title\">\n"
633 "<A HREF=\"%s/%s/%s?",
634 scriptname
, req
->q
.manpath
, r
[i
].file
);
635 http_printquery(req
, "&");
637 html_print(r
[i
].names
);
640 "<TD CLASS=\"desc\">");
641 html_print(r
[i
].output
);
650 * In man(1) mode, show one of the pages
651 * even if more than one is found.
659 for (i
= 0; i
< sz
; i
++) {
660 isec
= strcspn(r
[i
].file
, "123456789");
661 sec
= r
[i
].file
[isec
];
664 prio
= sec_prios
[sec
- '1'];
665 if (NULL
== req
->q
.arch
) {
667 (NULL
== (arch
= strchr(
668 r
[i
].file
+ isec
, '/'))) ? 3 :
669 (NULL
== (archend
= strchr(
670 arch
+ 1, '/'))) ? 0 :
671 strncmp(arch
, "amd64/",
672 archend
- arch
) ? 2 : 1;
673 if (archprio
< archpriouse
) {
674 archpriouse
= archprio
;
679 if (archprio
> archpriouse
)
687 resp_show(req
, r
[iuse
].file
);
694 catman(const struct req
*req
, const char *file
)
702 if (NULL
== (f
= fopen(file
, "r"))) {
703 puts("<P>You specified an invalid manual file.</P>");
707 puts("<DIV CLASS=\"catman\">\n"
710 while (NULL
!= (p
= fgetln(f
, &len
))) {
712 for (i
= 0; i
< (int)len
- 1; i
++) {
714 * This means that the catpage is out of state.
715 * Ignore it and keep going (although the
719 if ('\b' == p
[i
] || '\n' == p
[i
])
723 * Print a regular character.
724 * Close out any bold/italic scopes.
725 * If we're in back-space mode, make sure we'll
726 * have something to enter when we backspace.
729 if ('\b' != p
[i
+ 1]) {
737 } else if (i
+ 2 >= (int)len
)
755 * Handle funny behaviour troff-isms.
756 * These grok'd from the original man2html.c.
759 if (('+' == p
[i
] && 'o' == p
[i
+ 2]) ||
760 ('o' == p
[i
] && '+' == p
[i
+ 2]) ||
761 ('|' == p
[i
] && '=' == p
[i
+ 2]) ||
762 ('=' == p
[i
] && '|' == p
[i
+ 2]) ||
763 ('*' == p
[i
] && '=' == p
[i
+ 2]) ||
764 ('=' == p
[i
] && '*' == p
[i
+ 2]) ||
765 ('*' == p
[i
] && '|' == p
[i
+ 2]) ||
766 ('|' == p
[i
] && '*' == p
[i
+ 2])) {
775 } else if (('|' == p
[i
] && '-' == p
[i
+ 2]) ||
776 ('-' == p
[i
] && '|' == p
[i
+ 1]) ||
777 ('+' == p
[i
] && '-' == p
[i
+ 1]) ||
778 ('-' == p
[i
] && '+' == p
[i
+ 1]) ||
779 ('+' == p
[i
] && '|' == p
[i
+ 1]) ||
780 ('|' == p
[i
] && '+' == p
[i
+ 1])) {
804 * Clean up the last character.
805 * We can get to a newline; don't print that.
813 if (i
== (int)len
- 1 && '\n' != p
[i
])
826 format(const struct req
*req
, const char *file
)
837 if (-1 == (fd
= open(file
, O_RDONLY
, 0))) {
838 puts("<P>You specified an invalid manual file.</P>");
842 mp
= mparse_alloc(MPARSE_SO
, MANDOCLEVEL_FATAL
, NULL
,
844 rc
= mparse_readfd(mp
, fd
, file
);
847 if (rc
>= MANDOCLEVEL_FATAL
) {
848 fprintf(stderr
, "fatal mandoc error: %s/%s\n",
849 req
->q
.manpath
, file
);
854 usepath
= strcmp(req
->q
.manpath
, req
->p
[0]);
855 mandoc_asprintf(&opts
,
856 "fragment,man=%s?query=%%N&sec=%%S%s%s%s%s",
858 req
->q
.arch
? "&arch=" : "",
859 req
->q
.arch
? req
->q
.arch
: "",
860 usepath
? "&manpath=" : "",
861 usepath
? req
->q
.manpath
: "");
863 mparse_result(mp
, &mdoc
, &man
, NULL
);
864 if (NULL
== man
&& NULL
== mdoc
) {
865 fprintf(stderr
, "fatal mandoc error: %s/%s\n",
866 req
->q
.manpath
, file
);
872 vp
= html_alloc(opts
);
885 resp_show(const struct req
*req
, const char *file
)
888 if ('.' == file
[0] && '/' == file
[1])
898 pg_show(struct req
*req
, const char *fullpath
)
903 if ((file
= strchr(fullpath
, '/')) == NULL
) {
905 "You did not specify a page to show.");
908 manpath
= mandoc_strndup(fullpath
, file
- fullpath
);
911 if ( ! validate_manpath(req
, manpath
)) {
913 "You specified an invalid manpath.");
919 * Begin by chdir()ing into the manpath.
920 * This way we can pick up the database files, which are
921 * relative to the manpath root.
924 if (chdir(manpath
) == -1) {
925 fprintf(stderr
, "chdir %s: %s\n",
926 manpath
, strerror(errno
));
932 if (strcmp(manpath
, "mandoc")) {
933 free(req
->q
.manpath
);
934 req
->q
.manpath
= manpath
;
938 if ( ! validate_filename(file
)) {
940 "You specified an invalid manual file.");
944 resp_begin_html(200, NULL
);
945 resp_searchform(req
);
946 resp_show(req
, file
);
951 pg_search(const struct req
*req
)
953 struct mansearch search
;
954 struct manpaths paths
;
957 const char *ep
, *start
;
962 * Begin by chdir()ing into the root of the manpath.
963 * This way we can pick up the database files, which are
964 * relative to the manpath root.
967 if (-1 == (chdir(req
->q
.manpath
))) {
968 fprintf(stderr
, "chdir %s: %s\n",
969 req
->q
.manpath
, strerror(errno
));
974 search
.arch
= req
->q
.arch
;
975 search
.sec
= req
->q
.sec
;
976 search
.deftype
= req
->q
.equal
? TYPE_Nm
: (TYPE_Nm
| TYPE_Nd
);
977 search
.flags
= req
->q
.equal
? MANSEARCH_MAN
: 0;
980 paths
.paths
= mandoc_malloc(sizeof(char *));
981 paths
.paths
[0] = mandoc_strdup(".");
984 * Poor man's tokenisation: just break apart by spaces.
985 * Yes, this is half-ass. But it works for now.
989 while (ep
&& isspace((unsigned char)*ep
))
994 while (ep
&& '\0' != *ep
) {
995 cp
= mandoc_reallocarray(cp
, sz
+ 1, sizeof(char *));
997 while ('\0' != *ep
&& ! isspace((unsigned char)*ep
))
999 cp
[sz
] = mandoc_malloc((ep
- start
) + 1);
1000 memcpy(cp
[sz
], start
, ep
- start
);
1001 cp
[sz
++][ep
- start
] = '\0';
1002 while (isspace((unsigned char)*ep
))
1006 if (0 == mansearch(&search
, &paths
, sz
, cp
, "Nd", &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
);
1013 for (i
= 0; i
< sz
; i
++)
1017 for (i
= 0; i
< (int)ressz
; i
++) {
1020 free(res
[i
].output
);
1024 free(paths
.paths
[0]);
1033 const char *querystring
;
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 ( ! (NULL
== req
.q
.manpath
||
1070 validate_manpath(&req
, req
.q
.manpath
))) {
1071 pg_error_badrequest(
1072 "You specified an invalid manpath.");
1073 return(EXIT_FAILURE
);
1076 if ( ! (NULL
== req
.q
.arch
|| validate_urifrag(req
.q
.arch
))) {
1077 pg_error_badrequest(
1078 "You specified an invalid architecture.");
1079 return(EXIT_FAILURE
);
1082 /* Dispatch to the three different pages. */
1084 path
= getenv("PATH_INFO");
1087 else if ('/' == *path
)
1091 pg_show(&req
, path
);
1092 else if (NULL
!= req
.q
.query
)
1097 free(req
.q
.manpath
);
1101 for (i
= 0; i
< (int)req
.psz
; i
++)
1104 return(EXIT_SUCCESS
);
1108 * Scan for indexable paths.
1111 pathgen(struct req
*req
)
1117 if (NULL
== (fp
= fopen("manpath.conf", "r"))) {
1118 fprintf(stderr
, "%s/manpath.conf: %s\n",
1119 MAN_DIR
, strerror(errno
));
1120 pg_error_internal();
1124 while (NULL
!= (dp
= fgetln(fp
, &dpsz
))) {
1125 if ('\n' == dp
[dpsz
- 1])
1127 req
->p
= mandoc_realloc(req
->p
,
1128 (req
->psz
+ 1) * sizeof(char *));
1129 dp
= mandoc_strndup(dp
, dpsz
);
1130 if ( ! validate_urifrag(dp
)) {
1131 fprintf(stderr
, "%s/manpath.conf contains "
1132 "unsafe path \"%s\"\n", MAN_DIR
, dp
);
1133 pg_error_internal();
1136 if (NULL
!= strchr(dp
, '/')) {
1137 fprintf(stderr
, "%s/manpath.conf contains "
1138 "path with slash \"%s\"\n", MAN_DIR
, dp
);
1139 pg_error_internal();
1142 req
->p
[req
->psz
++] = dp
;
1145 if ( req
->p
== NULL
) {
1146 fprintf(stderr
, "%s/manpath.conf is empty\n", MAN_DIR
);
1147 pg_error_internal();