]>
git.cameronkatri.com Git - mandoc.git/blob - cgi.c
1 /* $Id: cgi.c,v 1.99 2014/10/07 18:20:06 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 (NULL
!= req
->q
.manpath
&&
167 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.
302 /* Fall back to the default manpath. */
304 if (req
->q
.manpath
== NULL
)
305 req
->q
.manpath
= mandoc_strdup(req
->p
[0]);
312 if (isalnum((unsigned char)c
)) {
313 putchar((unsigned char)c
);
315 } else if (' ' == c
) {
323 * HTTP-decode a string. The standard explanation is that this turns
324 * "%4e+foo" into "n foo" in the regular way. This is done in-place
325 * over the allocated string.
337 for ( ; '\0' != *p
; p
++, q
++) {
339 if ('\0' == (hex
[0] = *(p
+ 1)))
341 if ('\0' == (hex
[1] = *(p
+ 2)))
343 if (1 != sscanf(hex
, "%x", &c
))
351 *q
= '+' == *p
? ' ' : *p
;
359 resp_begin_http(int code
, const char *msg
)
363 printf("Status: %d %s\r\n", code
, msg
);
365 printf("Content-Type: text/html; charset=utf-8\r\n"
366 "Cache-Control: no-cache\r\n"
367 "Pragma: no-cache\r\n"
374 resp_begin_html(int code
, const char *msg
)
377 resp_begin_http(code
, msg
);
379 printf("<!DOCTYPE html>\n"
382 "<META CHARSET=\"UTF-8\" />\n"
383 "<LINK REL=\"stylesheet\" HREF=\"%s/man-cgi.css\""
384 " TYPE=\"text/css\" media=\"all\">\n"
385 "<LINK REL=\"stylesheet\" HREF=\"%s/man.css\""
386 " TYPE=\"text/css\" media=\"all\">\n"
387 "<TITLE>%s</TITLE>\n"
390 "<!-- Begin page content. //-->\n",
391 CSS_DIR
, CSS_DIR
, CUSTOMIZE_TITLE
);
403 resp_searchform(const struct req
*req
)
407 puts(CUSTOMIZE_BEGIN
);
408 puts("<!-- Begin search form. //-->");
409 printf("<DIV ID=\"mancgi\">\n"
410 "<FORM ACTION=\"%s\" METHOD=\"get\">\n"
412 "<LEGEND>Manual Page Search Parameters</LEGEND>\n",
415 /* Write query input box. */
417 printf( "<TABLE><TR><TD>\n"
418 "<INPUT TYPE=\"text\" NAME=\"query\" VALUE=\"");
419 if (NULL
!= req
->q
.query
)
420 html_print(req
->q
.query
);
421 puts("\" SIZE=\"40\">");
423 /* Write submission and reset buttons. */
425 printf( "<INPUT TYPE=\"submit\" VALUE=\"Submit\">\n"
426 "<INPUT TYPE=\"reset\" VALUE=\"Reset\">\n");
428 /* Write show radio button */
430 printf( "</TD><TD>\n"
431 "<INPUT TYPE=\"radio\" ");
433 printf("CHECKED=\"checked\" ");
434 printf( "NAME=\"apropos\" ID=\"show\" VALUE=\"0\">\n"
435 "<LABEL FOR=\"show\">Show named manual page</LABEL>\n");
437 /* Write section selector. */
439 puts( "</TD></TR><TR><TD>\n"
440 "<SELECT NAME=\"sec\">");
441 for (i
= 0; i
< sec_MAX
; i
++) {
442 printf("<OPTION VALUE=\"%s\"", sec_numbers
[i
]);
443 if (NULL
!= req
->q
.sec
&&
444 0 == strcmp(sec_numbers
[i
], req
->q
.sec
))
445 printf(" SELECTED=\"selected\"");
446 printf(">%s</OPTION>\n", sec_names
[i
]);
450 /* Write architecture selector. */
452 printf( "<SELECT NAME=\"arch\">\n"
453 "<OPTION VALUE=\"default\"");
454 if (NULL
== req
->q
.arch
)
455 printf(" SELECTED=\"selected\"");
456 puts(">All Architectures</OPTION>");
457 for (i
= 0; i
< arch_MAX
; i
++) {
458 printf("<OPTION VALUE=\"%s\"", arch_names
[i
]);
459 if (NULL
!= req
->q
.arch
&&
460 0 == strcmp(arch_names
[i
], req
->q
.arch
))
461 printf(" SELECTED=\"selected\"");
462 printf(">%s</OPTION>\n", arch_names
[i
]);
466 /* Write manpath selector. */
469 puts("<SELECT NAME=\"manpath\">");
470 for (i
= 0; i
< (int)req
->psz
; i
++) {
472 if (NULL
== req
->q
.manpath
? 0 == i
:
473 0 == strcmp(req
->q
.manpath
, req
->p
[i
]))
474 printf("SELECTED=\"selected\" ");
476 html_print(req
->p
[i
]);
478 html_print(req
->p
[i
]);
484 /* Write search radio button */
486 printf( "</TD><TD>\n"
487 "<INPUT TYPE=\"radio\" ");
488 if (0 == req
->q
.equal
)
489 printf("CHECKED=\"checked\" ");
490 printf( "NAME=\"apropos\" ID=\"search\" VALUE=\"1\">\n"
491 "<LABEL FOR=\"search\">Search with apropos query</LABEL>\n");
493 puts("</TD></TR></TABLE>\n"
497 puts("<!-- End search form. //-->");
501 validate_urifrag(const char *frag
)
504 while ('\0' != *frag
) {
505 if ( ! (isalnum((unsigned char)*frag
) ||
506 '-' == *frag
|| '.' == *frag
||
507 '/' == *frag
|| '_' == *frag
))
515 validate_manpath(const struct req
*req
, const char* manpath
)
519 if ( ! strcmp(manpath
, "mandoc"))
522 for (i
= 0; i
< req
->psz
; i
++)
523 if ( ! strcmp(manpath
, req
->p
[i
]))
530 validate_filename(const char *file
)
533 if ('.' == file
[0] && '/' == file
[1])
536 return ( ! (strstr(file
, "../") || strstr(file
, "/..") ||
537 (strncmp(file
, "man", 3) && strncmp(file
, "cat", 3))));
541 pg_index(const struct req
*req
)
544 resp_begin_html(200, NULL
);
545 resp_searchform(req
);
547 "This web interface is documented in the\n"
548 "<A HREF=\"%s/mandoc/man8/man.cgi.8\">man.cgi</A>\n"
550 "<A HREF=\"%s/mandoc/man1/apropos.1\">apropos</A>\n"
551 "manual explains the query syntax.\n"
553 scriptname
, scriptname
);
558 pg_noresult(const struct req
*req
, const char *msg
)
560 resp_begin_html(200, NULL
);
561 resp_searchform(req
);
569 pg_error_badrequest(const char *msg
)
572 resp_begin_html(400, "Bad Request");
573 puts("<H1>Bad Request</H1>\n"
576 printf("Try again from the\n"
577 "<A HREF=\"%s\">main page</A>.\n"
583 pg_error_internal(void)
585 resp_begin_html(500, "Internal Server Error");
586 puts("<P>Internal Server Error</P>");
591 pg_searchres(const struct req
*req
, struct manpage
*r
, size_t sz
)
593 char *arch
, *archend
;
594 size_t i
, iuse
, isec
;
595 int archprio
, archpriouse
;
599 for (i
= 0; i
< sz
; i
++) {
600 if (validate_filename(r
[i
].file
))
602 fprintf(stderr
, "invalid filename %s in %s database\n",
603 r
[i
].file
, req
->q
.manpath
);
610 * If we have just one result, then jump there now
613 printf("Status: 303 See Other\r\n");
614 printf("Location: http://%s%s/%s/%s?",
615 HTTP_HOST
, scriptname
, req
->q
.manpath
, r
[0].file
);
616 http_printquery(req
, "&");
618 "Content-Type: text/html; charset=utf-8\r\n"
623 resp_begin_html(200, NULL
);
624 resp_searchform(req
);
625 puts("<DIV CLASS=\"results\">");
628 for (i
= 0; i
< sz
; i
++) {
630 "<TD CLASS=\"title\">\n"
631 "<A HREF=\"%s/%s/%s?",
632 scriptname
, req
->q
.manpath
, r
[i
].file
);
633 http_printquery(req
, "&");
635 html_print(r
[i
].names
);
638 "<TD CLASS=\"desc\">");
639 html_print(r
[i
].output
);
648 * In man(1) mode, show one of the pages
649 * even if more than one is found.
657 for (i
= 0; i
< sz
; i
++) {
658 isec
= strcspn(r
[i
].file
, "123456789");
659 sec
= r
[i
].file
[isec
];
662 prio
= sec_prios
[sec
- '1'];
663 if (NULL
== req
->q
.arch
) {
665 (NULL
== (arch
= strchr(
666 r
[i
].file
+ isec
, '/'))) ? 3 :
667 (NULL
== (archend
= strchr(
668 arch
+ 1, '/'))) ? 0 :
669 strncmp(arch
, "amd64/",
670 archend
- arch
) ? 2 : 1;
671 if (archprio
< archpriouse
) {
672 archpriouse
= archprio
;
677 if (archprio
> archpriouse
)
685 resp_show(req
, r
[iuse
].file
);
692 catman(const struct req
*req
, const char *file
)
700 if (NULL
== (f
= fopen(file
, "r"))) {
701 puts("<P>You specified an invalid manual file.</P>");
705 puts("<DIV CLASS=\"catman\">\n"
708 while (NULL
!= (p
= fgetln(f
, &len
))) {
710 for (i
= 0; i
< (int)len
- 1; i
++) {
712 * This means that the catpage is out of state.
713 * Ignore it and keep going (although the
717 if ('\b' == p
[i
] || '\n' == p
[i
])
721 * Print a regular character.
722 * Close out any bold/italic scopes.
723 * If we're in back-space mode, make sure we'll
724 * have something to enter when we backspace.
727 if ('\b' != p
[i
+ 1]) {
735 } else if (i
+ 2 >= (int)len
)
753 * Handle funny behaviour troff-isms.
754 * These grok'd from the original man2html.c.
757 if (('+' == p
[i
] && 'o' == p
[i
+ 2]) ||
758 ('o' == p
[i
] && '+' == p
[i
+ 2]) ||
759 ('|' == p
[i
] && '=' == p
[i
+ 2]) ||
760 ('=' == 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])) {
773 } else if (('|' == p
[i
] && '-' == p
[i
+ 2]) ||
774 ('-' == p
[i
] && '|' == p
[i
+ 1]) ||
775 ('+' == p
[i
] && '-' == p
[i
+ 1]) ||
776 ('-' == p
[i
] && '+' == p
[i
+ 1]) ||
777 ('+' == p
[i
] && '|' == p
[i
+ 1]) ||
778 ('|' == p
[i
] && '+' == p
[i
+ 1])) {
802 * Clean up the last character.
803 * We can get to a newline; don't print that.
811 if (i
== (int)len
- 1 && '\n' != p
[i
])
824 format(const struct req
*req
, const char *file
)
835 if (-1 == (fd
= open(file
, O_RDONLY
, 0))) {
836 puts("<P>You specified an invalid manual file.</P>");
840 mp
= mparse_alloc(MPARSE_SO
, MANDOCLEVEL_FATAL
, NULL
,
842 rc
= mparse_readfd(mp
, fd
, file
);
845 if (rc
>= MANDOCLEVEL_FATAL
) {
846 fprintf(stderr
, "fatal mandoc error: %s/%s\n",
847 req
->q
.manpath
, file
);
852 usepath
= strcmp(req
->q
.manpath
, req
->p
[0]);
853 mandoc_asprintf(&opts
,
854 "fragment,man=%s?query=%%N&sec=%%S%s%s%s%s",
856 req
->q
.arch
? "&arch=" : "",
857 req
->q
.arch
? req
->q
.arch
: "",
858 usepath
? "&manpath=" : "",
859 usepath
? req
->q
.manpath
: "");
861 mparse_result(mp
, &mdoc
, &man
, NULL
);
862 if (NULL
== man
&& NULL
== mdoc
) {
863 fprintf(stderr
, "fatal mandoc error: %s/%s\n",
864 req
->q
.manpath
, file
);
870 vp
= html_alloc(opts
);
883 resp_show(const struct req
*req
, const char *file
)
886 if ('.' == file
[0] && '/' == file
[1])
896 pg_show(struct req
*req
, const char *fullpath
)
901 if ((file
= strchr(fullpath
, '/')) == NULL
) {
903 "You did not specify a page to show.");
906 manpath
= mandoc_strndup(fullpath
, file
- fullpath
);
909 if ( ! validate_manpath(req
, manpath
)) {
911 "You specified an invalid manpath.");
917 * Begin by chdir()ing into the manpath.
918 * This way we can pick up the database files, which are
919 * relative to the manpath root.
922 if (chdir(manpath
) == -1) {
923 fprintf(stderr
, "chdir %s: %s\n",
924 manpath
, strerror(errno
));
930 if (strcmp(manpath
, "mandoc")) {
931 free(req
->q
.manpath
);
932 req
->q
.manpath
= manpath
;
936 if ( ! validate_filename(file
)) {
938 "You specified an invalid manual file.");
942 resp_begin_html(200, NULL
);
943 resp_searchform(req
);
944 resp_show(req
, file
);
949 pg_search(const struct req
*req
)
951 struct mansearch search
;
952 struct manpaths paths
;
955 char *query
, *rp
, *wp
;
960 * Begin by chdir()ing into the root of the manpath.
961 * This way we can pick up the database files, which are
962 * relative to the manpath root.
965 if (-1 == (chdir(req
->q
.manpath
))) {
966 fprintf(stderr
, "chdir %s: %s\n",
967 req
->q
.manpath
, strerror(errno
));
972 search
.arch
= req
->q
.arch
;
973 search
.sec
= req
->q
.sec
;
974 search
.outkey
= "Nd";
975 search
.argmode
= req
->q
.equal
? ARG_NAME
: ARG_EXPR
;
978 paths
.paths
= mandoc_malloc(sizeof(char *));
979 paths
.paths
[0] = mandoc_strdup(".");
982 * Break apart at spaces with backslash-escaping.
987 rp
= query
= mandoc_strdup(req
->q
.query
);
989 while (isspace((unsigned char)*rp
))
993 argv
= mandoc_reallocarray(argv
, argc
+ 1, sizeof(char *));
994 argv
[argc
++] = wp
= rp
;
996 if (isspace((unsigned char)*rp
)) {
1001 if (rp
[0] == '\\' && rp
[1] != '\0')
1012 if (0 == mansearch(&search
, &paths
, argc
, argv
, &res
, &ressz
))
1013 pg_noresult(req
, "You entered an invalid query.");
1014 else if (0 == ressz
)
1015 pg_noresult(req
, "No results found.");
1017 pg_searchres(req
, res
, ressz
);
1020 mansearch_free(res
, ressz
);
1021 free(paths
.paths
[0]);
1029 struct itimerval itimer
;
1031 const char *querystring
;
1034 /* Poor man's ReDoS mitigation. */
1036 itimer
.it_value
.tv_sec
= 2;
1037 itimer
.it_value
.tv_usec
= 0;
1038 itimer
.it_interval
.tv_sec
= 2;
1039 itimer
.it_interval
.tv_usec
= 0;
1040 if (setitimer(ITIMER_VIRTUAL
, &itimer
, NULL
) == -1) {
1041 fprintf(stderr
, "setitimer: %s\n", strerror(errno
));
1042 pg_error_internal();
1043 return(EXIT_FAILURE
);
1046 /* Scan our run-time environment. */
1048 if (NULL
== (scriptname
= getenv("SCRIPT_NAME")))
1051 if ( ! validate_urifrag(scriptname
)) {
1052 fprintf(stderr
, "unsafe SCRIPT_NAME \"%s\"\n",
1054 pg_error_internal();
1055 return(EXIT_FAILURE
);
1059 * First we change directory into the MAN_DIR so that
1060 * subsequent scanning for manpath directories is rooted
1061 * relative to the same position.
1064 if (-1 == chdir(MAN_DIR
)) {
1065 fprintf(stderr
, "MAN_DIR: %s: %s\n",
1066 MAN_DIR
, strerror(errno
));
1067 pg_error_internal();
1068 return(EXIT_FAILURE
);
1071 memset(&req
, 0, sizeof(struct req
));
1074 /* Next parse out the query string. */
1076 if (NULL
!= (querystring
= getenv("QUERY_STRING")))
1077 http_parse(&req
, querystring
);
1079 if ( ! (NULL
== req
.q
.manpath
||
1080 validate_manpath(&req
, req
.q
.manpath
))) {
1081 pg_error_badrequest(
1082 "You specified an invalid manpath.");
1083 return(EXIT_FAILURE
);
1086 if ( ! (NULL
== req
.q
.arch
|| validate_urifrag(req
.q
.arch
))) {
1087 pg_error_badrequest(
1088 "You specified an invalid architecture.");
1089 return(EXIT_FAILURE
);
1092 /* Dispatch to the three different pages. */
1094 path
= getenv("PATH_INFO");
1097 else if ('/' == *path
)
1101 pg_show(&req
, path
);
1102 else if (NULL
!= req
.q
.query
)
1107 free(req
.q
.manpath
);
1111 for (i
= 0; i
< (int)req
.psz
; i
++)
1114 return(EXIT_SUCCESS
);
1118 * Scan for indexable paths.
1121 pathgen(struct req
*req
)
1127 if (NULL
== (fp
= fopen("manpath.conf", "r"))) {
1128 fprintf(stderr
, "%s/manpath.conf: %s\n",
1129 MAN_DIR
, strerror(errno
));
1130 pg_error_internal();
1134 while (NULL
!= (dp
= fgetln(fp
, &dpsz
))) {
1135 if ('\n' == dp
[dpsz
- 1])
1137 req
->p
= mandoc_realloc(req
->p
,
1138 (req
->psz
+ 1) * sizeof(char *));
1139 dp
= mandoc_strndup(dp
, dpsz
);
1140 if ( ! validate_urifrag(dp
)) {
1141 fprintf(stderr
, "%s/manpath.conf contains "
1142 "unsafe path \"%s\"\n", MAN_DIR
, dp
);
1143 pg_error_internal();
1146 if (NULL
!= strchr(dp
, '/')) {
1147 fprintf(stderr
, "%s/manpath.conf contains "
1148 "path with slash \"%s\"\n", MAN_DIR
, dp
);
1149 pg_error_internal();
1152 req
->p
[req
->psz
++] = dp
;
1155 if ( req
->p
== NULL
) {
1156 fprintf(stderr
, "%s/manpath.conf is empty\n", MAN_DIR
);
1157 pg_error_internal();