]>
git.cameronkatri.com Git - mandoc.git/blob - cgi.c
1 /* $Id: cgi.c,v 1.89 2014/07/25 19:37:34 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.
32 #include "mandoc_aux.h"
35 #include "mansearch.h"
39 * A query as passed to the search function.
42 char *manpath
; /* desired manual directory */
43 char *arch
; /* architecture */
44 char *sec
; /* manual section */
45 char *query
; /* unparsed query expression */
46 int equal
; /* match whole names, not substrings */
51 char **p
; /* array of available manpaths */
52 size_t psz
; /* number of available manpaths */
55 static void catman(const struct req
*, const char *);
56 static void format(const struct req
*, const char *);
57 static void html_print(const char *);
58 static void html_printquery(const struct req
*);
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
*);
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
)
151 if (NULL
!= req
->q
.query
) {
153 http_print(req
->q
.query
);
155 if (0 == req
->q
.equal
)
156 printf("&apropos=1");
157 if (NULL
!= req
->q
.sec
) {
159 http_print(req
->q
.sec
);
161 if (NULL
!= req
->q
.arch
) {
163 http_print(req
->q
.arch
);
165 if (NULL
!= req
->q
.manpath
&&
166 strcmp(req
->q
.manpath
, req
->p
[0])) {
168 http_print(req
->q
.manpath
);
173 html_printquery(const struct req
*req
)
176 if (NULL
!= req
->q
.query
) {
178 html_print(req
->q
.query
);
180 if (0 == req
->q
.equal
)
181 printf("&apropos=1");
182 if (NULL
!= req
->q
.sec
) {
184 html_print(req
->q
.sec
);
186 if (NULL
!= req
->q
.arch
) {
187 printf("&arch=");
188 html_print(req
->q
.arch
);
190 if (NULL
!= req
->q
.manpath
&&
191 strcmp(req
->q
.manpath
, req
->p
[0])) {
192 printf("&manpath=");
193 html_print(req
->q
.manpath
);
198 http_print(const char *p
)
208 * Call through to html_putchar().
209 * Accepts NULL strings.
212 html_print(const char *p
)
222 * Transfer the responsibility for the allocated string *val
223 * to the query structure.
226 set_query_attr(char **attr
, char **val
)
239 * Parse the QUERY_STRING for key-value pairs
240 * and store the values into the query structure.
243 http_parse(struct req
*req
, const char *qs
)
248 req
->q
.manpath
= NULL
;
255 while (*qs
!= '\0') {
259 keysz
= strcspn(qs
, "=;&");
260 key
= mandoc_strndup(qs
, keysz
);
265 /* Parse one value. */
267 valsz
= strcspn(++qs
, ";&");
268 val
= mandoc_strndup(qs
, valsz
);
271 /* Decode and catch encoding errors. */
273 if ( ! (http_decode(key
) && http_decode(val
)))
276 /* Handle key-value pairs. */
278 if ( ! strcmp(key
, "query"))
279 set_query_attr(&req
->q
.query
, &val
);
281 else if ( ! strcmp(key
, "apropos"))
282 req
->q
.equal
= !strcmp(val
, "0");
284 else if ( ! strcmp(key
, "manpath")) {
286 if ( ! strncmp(val
, "OpenBSD ", 8)) {
292 set_query_attr(&req
->q
.manpath
, &val
);
295 else if ( ! (strcmp(key
, "sec")
297 && strcmp(key
, "sektion")
300 if ( ! strcmp(val
, "0"))
302 set_query_attr(&req
->q
.sec
, &val
);
305 else if ( ! strcmp(key
, "arch")) {
306 if ( ! strcmp(val
, "default"))
308 set_query_attr(&req
->q
.arch
, &val
);
312 * The key must be freed in any case.
313 * The val may have been handed over to the query
314 * structure, in which case it is now NULL.
326 /* Fall back to the default manpath. */
328 if (req
->q
.manpath
== NULL
)
329 req
->q
.manpath
= mandoc_strdup(req
->p
[0]);
336 if (isalnum((unsigned char)c
)) {
337 putchar((unsigned char)c
);
339 } else if (' ' == c
) {
347 * HTTP-decode a string. The standard explanation is that this turns
348 * "%4e+foo" into "n foo" in the regular way. This is done in-place
349 * over the allocated string.
361 for ( ; '\0' != *p
; p
++, q
++) {
363 if ('\0' == (hex
[0] = *(p
+ 1)))
365 if ('\0' == (hex
[1] = *(p
+ 2)))
367 if (1 != sscanf(hex
, "%x", &c
))
375 *q
= '+' == *p
? ' ' : *p
;
383 resp_begin_http(int code
, const char *msg
)
387 printf("Status: %d %s\r\n", code
, msg
);
389 printf("Content-Type: text/html; charset=utf-8\r\n"
390 "Cache-Control: no-cache\r\n"
391 "Pragma: no-cache\r\n"
398 resp_begin_html(int code
, const char *msg
)
401 resp_begin_http(code
, msg
);
403 printf("<!DOCTYPE HTML PUBLIC "
404 " \"-//W3C//DTD HTML 4.01//EN\""
405 " \"http://www.w3.org/TR/html4/strict.dtd\">\n"
408 "<META HTTP-EQUIV=\"Content-Type\""
409 " CONTENT=\"text/html; charset=utf-8\">\n"
410 "<LINK REL=\"stylesheet\" HREF=\"%s/man-cgi.css\""
411 " TYPE=\"text/css\" media=\"all\">\n"
412 "<LINK REL=\"stylesheet\" HREF=\"%s/man.css\""
413 " TYPE=\"text/css\" media=\"all\">\n"
414 "<TITLE>%s</TITLE>\n"
417 "<!-- Begin page content. //-->\n",
418 CSS_DIR
, CSS_DIR
, CUSTOMIZE_TITLE
);
430 resp_searchform(const struct req
*req
)
434 puts(CUSTOMIZE_BEGIN
);
435 puts("<!-- Begin search form. //-->");
436 printf("<DIV ID=\"mancgi\">\n"
437 "<FORM ACTION=\"%s\" METHOD=\"get\">\n"
439 "<LEGEND>Manual Page Search Parameters</LEGEND>\n",
442 /* Write query input box. */
444 printf( "<TABLE><TR><TD>\n"
445 "<INPUT TYPE=\"text\" NAME=\"query\" VALUE=\"");
446 if (NULL
!= req
->q
.query
)
447 html_print(req
->q
.query
);
448 puts("\" SIZE=\"40\">");
450 /* Write submission and reset buttons. */
452 printf( "<INPUT TYPE=\"submit\" VALUE=\"Submit\">\n"
453 "<INPUT TYPE=\"reset\" VALUE=\"Reset\">\n");
455 /* Write show radio button */
457 printf( "</TD><TD>\n"
458 "<INPUT TYPE=\"radio\" ");
460 printf("CHECKED=\"checked\" ");
461 printf( "NAME=\"apropos\" ID=\"show\" VALUE=\"0\">\n"
462 "<LABEL FOR=\"show\">Show named manual page</LABEL>\n");
464 /* Write section selector. */
466 puts( "</TD></TR><TR><TD>\n"
467 "<SELECT NAME=\"sec\">");
468 for (i
= 0; i
< sec_MAX
; i
++) {
469 printf("<OPTION VALUE=\"%s\"", sec_numbers
[i
]);
470 if (NULL
!= req
->q
.sec
&&
471 0 == strcmp(sec_numbers
[i
], req
->q
.sec
))
472 printf(" SELECTED=\"selected\"");
473 printf(">%s</OPTION>\n", sec_names
[i
]);
477 /* Write architecture selector. */
479 printf( "<SELECT NAME=\"arch\">\n"
480 "<OPTION VALUE=\"default\"");
481 if (NULL
== req
->q
.arch
)
482 printf(" SELECTED=\"selected\"");
483 puts(">All Architectures</OPTION>");
484 for (i
= 0; i
< arch_MAX
; i
++) {
485 printf("<OPTION VALUE=\"%s\"", arch_names
[i
]);
486 if (NULL
!= req
->q
.arch
&&
487 0 == strcmp(arch_names
[i
], req
->q
.arch
))
488 printf(" SELECTED=\"selected\"");
489 printf(">%s</OPTION>\n", arch_names
[i
]);
493 /* Write manpath selector. */
496 puts("<SELECT NAME=\"manpath\">");
497 for (i
= 0; i
< (int)req
->psz
; i
++) {
499 if (NULL
== req
->q
.manpath
? 0 == i
:
500 0 == strcmp(req
->q
.manpath
, req
->p
[i
]))
501 printf("SELECTED=\"selected\" ");
503 html_print(req
->p
[i
]);
505 html_print(req
->p
[i
]);
511 /* Write search radio button */
513 printf( "</TD><TD>\n"
514 "<INPUT TYPE=\"radio\" ");
515 if (0 == req
->q
.equal
)
516 printf("CHECKED=\"checked\" ");
517 printf( "NAME=\"apropos\" ID=\"search\" VALUE=\"1\">\n"
518 "<LABEL FOR=\"search\">Search with apropos query</LABEL>\n");
520 puts("</TD></TR></TABLE>\n"
524 puts("<!-- End search form. //-->");
528 validate_urifrag(const char *frag
)
531 while ('\0' != *frag
) {
532 if ( ! (isalnum((unsigned char)*frag
) ||
533 '-' == *frag
|| '.' == *frag
||
534 '/' == *frag
|| '_' == *frag
))
542 validate_manpath(const struct req
*req
, const char* manpath
)
546 if ( ! strcmp(manpath
, "mandoc"))
549 for (i
= 0; i
< req
->psz
; i
++)
550 if ( ! strcmp(manpath
, req
->p
[i
]))
557 validate_filename(const char *file
)
560 if ('.' == file
[0] && '/' == file
[1])
563 return ( ! (strstr(file
, "../") || strstr(file
, "/..") ||
564 (strncmp(file
, "man", 3) && strncmp(file
, "cat", 3))));
568 pg_index(const struct req
*req
)
571 resp_begin_html(200, NULL
);
572 resp_searchform(req
);
574 "This web interface is documented in the\n"
575 "<A HREF=\"%s/mandoc/man8/man.cgi.8\">man.cgi</A>\n"
577 "<A HREF=\"%s/mandoc/man1/apropos.1\">apropos</A>\n"
578 "manual explains the query syntax.\n"
580 scriptname
, scriptname
);
585 pg_noresult(const struct req
*req
, const char *msg
)
587 resp_begin_html(200, NULL
);
588 resp_searchform(req
);
596 pg_error_badrequest(const char *msg
)
599 resp_begin_html(400, "Bad Request");
600 puts("<H1>Bad Request</H1>\n"
603 printf("Try again from the\n"
604 "<A HREF=\"%s\">main page</A>.\n"
610 pg_error_internal(void)
612 resp_begin_html(500, "Internal Server Error");
613 puts("<P>Internal Server Error</P>");
618 pg_searchres(const struct req
*req
, struct manpage
*r
, size_t sz
)
620 char *arch
, *archend
;
621 size_t i
, iuse
, isec
;
622 int archprio
, archpriouse
;
626 for (i
= 0; i
< sz
; i
++) {
627 if (validate_filename(r
[i
].file
))
629 fprintf(stderr
, "invalid filename %s in %s database\n",
630 r
[i
].file
, req
->q
.manpath
);
637 * If we have just one result, then jump there now
640 printf("Status: 303 See Other\r\n");
641 printf("Location: http://%s%s/%s/%s?",
642 HTTP_HOST
, scriptname
, req
->q
.manpath
, r
[0].file
);
643 http_printquery(req
);
645 "Content-Type: text/html; charset=utf-8\r\n"
650 resp_begin_html(200, NULL
);
651 resp_searchform(req
);
652 puts("<DIV CLASS=\"results\">");
655 for (i
= 0; i
< sz
; i
++) {
657 "<TD CLASS=\"title\">\n"
658 "<A HREF=\"%s/%s/%s?",
659 scriptname
, req
->q
.manpath
, r
[i
].file
);
660 html_printquery(req
);
662 html_print(r
[i
].names
);
665 "<TD CLASS=\"desc\">");
666 html_print(r
[i
].output
);
675 * In man(1) mode, show one of the pages
676 * even if more than one is found.
684 for (i
= 0; i
< sz
; i
++) {
685 isec
= strcspn(r
[i
].file
, "123456789");
686 sec
= r
[i
].file
[isec
];
689 prio
= sec_prios
[sec
- '1'];
690 if (NULL
== req
->q
.arch
) {
692 (NULL
== (arch
= strchr(
693 r
[i
].file
+ isec
, '/'))) ? 3 :
694 (NULL
== (archend
= strchr(
695 arch
+ 1, '/'))) ? 0 :
696 strncmp(arch
, "amd64/",
697 archend
- arch
) ? 2 : 1;
698 if (archprio
< archpriouse
) {
699 archpriouse
= archprio
;
704 if (archprio
> archpriouse
)
712 resp_show(req
, r
[iuse
].file
);
719 catman(const struct req
*req
, const char *file
)
727 if (NULL
== (f
= fopen(file
, "r"))) {
728 puts("<P>You specified an invalid manual file.</P>");
732 puts("<DIV CLASS=\"catman\">\n"
735 while (NULL
!= (p
= fgetln(f
, &len
))) {
737 for (i
= 0; i
< (int)len
- 1; i
++) {
739 * This means that the catpage is out of state.
740 * Ignore it and keep going (although the
744 if ('\b' == p
[i
] || '\n' == p
[i
])
748 * Print a regular character.
749 * Close out any bold/italic scopes.
750 * If we're in back-space mode, make sure we'll
751 * have something to enter when we backspace.
754 if ('\b' != p
[i
+ 1]) {
762 } else if (i
+ 2 >= (int)len
)
780 * Handle funny behaviour troff-isms.
781 * These grok'd from the original man2html.c.
784 if (('+' == p
[i
] && 'o' == p
[i
+ 2]) ||
785 ('o' == p
[i
] && '+' == p
[i
+ 2]) ||
786 ('|' == p
[i
] && '=' == p
[i
+ 2]) ||
787 ('=' == p
[i
] && '|' == p
[i
+ 2]) ||
788 ('*' == p
[i
] && '=' == p
[i
+ 2]) ||
789 ('=' == p
[i
] && '*' == p
[i
+ 2]) ||
790 ('*' == p
[i
] && '|' == p
[i
+ 2]) ||
791 ('|' == p
[i
] && '*' == p
[i
+ 2])) {
800 } else if (('|' == p
[i
] && '-' == p
[i
+ 2]) ||
801 ('-' == p
[i
] && '|' == p
[i
+ 1]) ||
802 ('+' == p
[i
] && '-' == p
[i
+ 1]) ||
803 ('-' == p
[i
] && '+' == p
[i
+ 1]) ||
804 ('+' == p
[i
] && '|' == p
[i
+ 1]) ||
805 ('|' == p
[i
] && '+' == p
[i
+ 1])) {
829 * Clean up the last character.
830 * We can get to a newline; don't print that.
838 if (i
== (int)len
- 1 && '\n' != p
[i
])
851 format(const struct req
*req
, const char *file
)
859 char opts
[PATH_MAX
+ 128];
861 if (-1 == (fd
= open(file
, O_RDONLY
, 0))) {
862 puts("<P>You specified an invalid manual file.</P>");
866 mp
= mparse_alloc(MPARSE_SO
, MANDOCLEVEL_FATAL
, NULL
,
868 rc
= mparse_readfd(mp
, fd
, file
);
871 if (rc
>= MANDOCLEVEL_FATAL
) {
872 fprintf(stderr
, "fatal mandoc error: %s/%s\n",
873 req
->q
.manpath
, file
);
878 snprintf(opts
, sizeof(opts
), "fragment,man=%s?"
879 "manpath=%s&query=%%N&sec=%%S%s%s",
880 scriptname
, req
->q
.manpath
,
881 req
->q
.arch
? "&arch=" : "",
882 req
->q
.arch
? req
->q
.arch
: "");
884 mparse_result(mp
, &mdoc
, &man
, NULL
);
885 if (NULL
== man
&& NULL
== mdoc
) {
886 fprintf(stderr
, "fatal mandoc error: %s/%s\n",
887 req
->q
.manpath
, file
);
893 vp
= html_alloc(opts
);
905 resp_show(const struct req
*req
, const char *file
)
908 if ('.' == file
[0] && '/' == file
[1])
918 pg_show(struct req
*req
, const char *fullpath
)
923 if ((file
= strchr(fullpath
, '/')) == NULL
) {
925 "You did not specify a page to show.");
928 manpath
= mandoc_strndup(fullpath
, file
- fullpath
);
931 if ( ! validate_manpath(req
, manpath
)) {
933 "You specified an invalid manpath.");
939 * Begin by chdir()ing into the manpath.
940 * This way we can pick up the database files, which are
941 * relative to the manpath root.
944 if (chdir(manpath
) == -1) {
945 fprintf(stderr
, "chdir %s: %s\n",
946 manpath
, strerror(errno
));
952 if (strcmp(manpath
, "mandoc")) {
953 free(req
->q
.manpath
);
954 req
->q
.manpath
= manpath
;
958 if ( ! validate_filename(file
)) {
960 "You specified an invalid manual file.");
964 resp_begin_html(200, NULL
);
965 resp_searchform(req
);
966 resp_show(req
, file
);
971 pg_search(const struct req
*req
)
973 struct mansearch search
;
974 struct manpaths paths
;
977 const char *ep
, *start
;
982 * Begin by chdir()ing into the root of the manpath.
983 * This way we can pick up the database files, which are
984 * relative to the manpath root.
987 if (-1 == (chdir(req
->q
.manpath
))) {
988 fprintf(stderr
, "chdir %s: %s\n",
989 req
->q
.manpath
, strerror(errno
));
994 search
.arch
= req
->q
.arch
;
995 search
.sec
= req
->q
.sec
;
996 search
.deftype
= req
->q
.equal
? TYPE_Nm
: (TYPE_Nm
| TYPE_Nd
);
997 search
.flags
= req
->q
.equal
? MANSEARCH_MAN
: 0;
1000 paths
.paths
= mandoc_malloc(sizeof(char *));
1001 paths
.paths
[0] = mandoc_strdup(".");
1004 * Poor man's tokenisation: just break apart by spaces.
1005 * Yes, this is half-ass. But it works for now.
1009 while (ep
&& isspace((unsigned char)*ep
))
1014 while (ep
&& '\0' != *ep
) {
1015 cp
= mandoc_reallocarray(cp
, sz
+ 1, sizeof(char *));
1017 while ('\0' != *ep
&& ! isspace((unsigned char)*ep
))
1019 cp
[sz
] = mandoc_malloc((ep
- start
) + 1);
1020 memcpy(cp
[sz
], start
, ep
- start
);
1021 cp
[sz
++][ep
- start
] = '\0';
1022 while (isspace((unsigned char)*ep
))
1026 if (0 == mansearch(&search
, &paths
, sz
, cp
, "Nd", &res
, &ressz
))
1027 pg_noresult(req
, "You entered an invalid query.");
1028 else if (0 == ressz
)
1029 pg_noresult(req
, "No results found.");
1031 pg_searchres(req
, res
, ressz
);
1033 for (i
= 0; i
< sz
; i
++)
1037 for (i
= 0; i
< (int)ressz
; i
++) {
1040 free(res
[i
].output
);
1044 free(paths
.paths
[0]);
1053 const char *querystring
;
1056 /* Scan our run-time environment. */
1058 if (NULL
== (scriptname
= getenv("SCRIPT_NAME")))
1061 if ( ! validate_urifrag(scriptname
)) {
1062 fprintf(stderr
, "unsafe SCRIPT_NAME \"%s\"\n",
1064 pg_error_internal();
1065 return(EXIT_FAILURE
);
1069 * First we change directory into the MAN_DIR so that
1070 * subsequent scanning for manpath directories is rooted
1071 * relative to the same position.
1074 if (-1 == chdir(MAN_DIR
)) {
1075 fprintf(stderr
, "MAN_DIR: %s: %s\n",
1076 MAN_DIR
, strerror(errno
));
1077 pg_error_internal();
1078 return(EXIT_FAILURE
);
1081 memset(&req
, 0, sizeof(struct req
));
1084 /* Next parse out the query string. */
1086 if (NULL
!= (querystring
= getenv("QUERY_STRING")))
1087 http_parse(&req
, querystring
);
1089 if ( ! (NULL
== req
.q
.manpath
||
1090 validate_manpath(&req
, req
.q
.manpath
))) {
1091 pg_error_badrequest(
1092 "You specified an invalid manpath.");
1093 return(EXIT_FAILURE
);
1096 if ( ! (NULL
== req
.q
.arch
|| validate_urifrag(req
.q
.arch
))) {
1097 pg_error_badrequest(
1098 "You specified an invalid architecture.");
1099 return(EXIT_FAILURE
);
1102 /* Dispatch to the three different pages. */
1104 path
= getenv("PATH_INFO");
1107 else if ('/' == *path
)
1111 pg_show(&req
, path
);
1112 else if (NULL
!= req
.q
.query
)
1117 free(req
.q
.manpath
);
1121 for (i
= 0; i
< (int)req
.psz
; i
++)
1124 return(EXIT_SUCCESS
);
1128 * Scan for indexable paths.
1131 pathgen(struct req
*req
)
1137 if (NULL
== (fp
= fopen("manpath.conf", "r"))) {
1138 fprintf(stderr
, "%s/manpath.conf: %s\n",
1139 MAN_DIR
, strerror(errno
));
1140 pg_error_internal();
1144 while (NULL
!= (dp
= fgetln(fp
, &dpsz
))) {
1145 if ('\n' == dp
[dpsz
- 1])
1147 req
->p
= mandoc_realloc(req
->p
,
1148 (req
->psz
+ 1) * sizeof(char *));
1149 dp
= mandoc_strndup(dp
, dpsz
);
1150 if ( ! validate_urifrag(dp
)) {
1151 fprintf(stderr
, "%s/manpath.conf contains "
1152 "unsafe path \"%s\"\n", MAN_DIR
, dp
);
1153 pg_error_internal();
1156 if (NULL
!= strchr(dp
, '/')) {
1157 fprintf(stderr
, "%s/manpath.conf contains "
1158 "path with slash \"%s\"\n", MAN_DIR
, dp
);
1159 pg_error_internal();
1162 req
->p
[req
->psz
++] = dp
;
1165 if ( req
->p
== NULL
) {
1166 fprintf(stderr
, "%s/manpath.conf is empty\n", MAN_DIR
);
1167 pg_error_internal();