]>
git.cameronkatri.com Git - mandoc.git/blob - cgi.c
1 /* $Id: cgi.c,v 1.119 2016/03/18 13:22:27 schwarze Exp $ */
3 * Copyright (c) 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv>
4 * Copyright (c) 2014, 2015, 2016 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 pathgen(struct req
*);
67 static void path_parse(struct req
*req
, const char *path
);
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_copy(const char *);
79 static void resp_end_html(void);
80 static void resp_searchform(const struct req
*);
81 static void resp_show(const struct req
*, const char *);
82 static void set_query_attr(char **, char **);
83 static int validate_filename(const char *);
84 static int validate_manpath(const struct req
*, const char *);
85 static int validate_urifrag(const char *);
87 static const char *scriptname
= SCRIPT_NAME
;
89 static const int sec_prios
[] = {1, 4, 5, 8, 6, 3, 7, 2, 9};
90 static const char *const sec_numbers
[] = {
91 "0", "1", "2", "3", "3p", "4", "5", "6", "7", "8", "9"
93 static const char *const sec_names
[] = {
95 "1 - General Commands",
97 "3 - Library Functions",
102 "7 - Miscellaneous Information",
103 "8 - System Manager\'s Manual",
104 "9 - Kernel Developer\'s Manual"
106 static const int sec_MAX
= sizeof(sec_names
) / sizeof(char *);
108 static const char *const arch_names
[] = {
109 "amd64", "alpha", "armish", "armv7",
110 "aviion", "hppa", "hppa64", "i386",
111 "ia64", "landisk", "loongson", "luna88k",
112 "macppc", "mips64", "octeon", "sgi",
113 "socppc", "solbourne", "sparc", "sparc64",
115 "amiga", "arc", "arm32", "atari",
116 "beagle", "cats", "hp300", "mac68k",
117 "mvme68k", "mvme88k", "mvmeppc", "palm",
118 "pc532", "pegasos", "pmax", "powerpc",
119 "sun3", "wgrisc", "x68k"
121 static const int arch_MAX
= sizeof(arch_names
) / sizeof(char *);
124 * Print a character, escaping HTML along the way.
125 * This will pass non-ASCII straight to output: be warned!
145 putchar((unsigned char)c
);
151 * Call through to html_putchar().
152 * Accepts NULL strings.
155 html_print(const char *p
)
165 * Transfer the responsibility for the allocated string *val
166 * to the query structure.
169 set_query_attr(char **attr
, char **val
)
182 * Parse the QUERY_STRING for key-value pairs
183 * and store the values into the query structure.
186 http_parse(struct req
*req
, const char *qs
)
191 req
->q
.manpath
= NULL
;
198 while (*qs
!= '\0') {
202 keysz
= strcspn(qs
, "=;&");
203 key
= mandoc_strndup(qs
, keysz
);
208 /* Parse one value. */
210 valsz
= strcspn(++qs
, ";&");
211 val
= mandoc_strndup(qs
, valsz
);
214 /* Decode and catch encoding errors. */
216 if ( ! (http_decode(key
) && http_decode(val
)))
219 /* Handle key-value pairs. */
221 if ( ! strcmp(key
, "query"))
222 set_query_attr(&req
->q
.query
, &val
);
224 else if ( ! strcmp(key
, "apropos"))
225 req
->q
.equal
= !strcmp(val
, "0");
227 else if ( ! strcmp(key
, "manpath")) {
229 if ( ! strncmp(val
, "OpenBSD ", 8)) {
235 set_query_attr(&req
->q
.manpath
, &val
);
238 else if ( ! (strcmp(key
, "sec")
240 && strcmp(key
, "sektion")
243 if ( ! strcmp(val
, "0"))
245 set_query_attr(&req
->q
.sec
, &val
);
248 else if ( ! strcmp(key
, "arch")) {
249 if ( ! strcmp(val
, "default"))
251 set_query_attr(&req
->q
.arch
, &val
);
255 * The key must be freed in any case.
256 * The val may have been handed over to the query
257 * structure, in which case it is now NULL.
271 * HTTP-decode a string. The standard explanation is that this turns
272 * "%4e+foo" into "n foo" in the regular way. This is done in-place
273 * over the allocated string.
285 for ( ; '\0' != *p
; p
++, q
++) {
287 if ('\0' == (hex
[0] = *(p
+ 1)))
289 if ('\0' == (hex
[1] = *(p
+ 2)))
291 if (1 != sscanf(hex
, "%x", &c
))
299 *q
= '+' == *p
? ' ' : *p
;
307 resp_begin_http(int code
, const char *msg
)
311 printf("Status: %d %s\r\n", code
, msg
);
313 printf("Content-Type: text/html; charset=utf-8\r\n"
314 "Cache-Control: no-cache\r\n"
315 "Pragma: no-cache\r\n"
322 resp_copy(const char *filename
)
328 if ((fd
= open(filename
, O_RDONLY
)) != -1) {
330 while ((sz
= read(fd
, buf
, sizeof(buf
))) > 0)
331 write(STDOUT_FILENO
, buf
, sz
);
336 resp_begin_html(int code
, const char *msg
)
339 resp_begin_http(code
, msg
);
341 printf("<!DOCTYPE html>\n"
344 "<META CHARSET=\"UTF-8\" />\n"
345 "<LINK REL=\"stylesheet\" HREF=\"%s/mandoc.css\""
346 " TYPE=\"text/css\" media=\"all\">\n"
347 "<TITLE>%s</TITLE>\n"
350 "<!-- Begin page content. //-->\n",
351 CSS_DIR
, CUSTOMIZE_TITLE
);
353 resp_copy(MAN_DIR
"/header.html");
360 resp_copy(MAN_DIR
"/footer.html");
367 resp_searchform(const struct req
*req
)
371 puts("<!-- Begin search form. //-->");
372 printf("<DIV ID=\"mancgi\">\n"
373 "<FORM ACTION=\"/%s\" METHOD=\"get\">\n"
375 "<LEGEND>Manual Page Search Parameters</LEGEND>\n",
378 /* Write query input box. */
380 printf( "<TABLE><TR><TD>\n"
381 "<INPUT TYPE=\"text\" NAME=\"query\" VALUE=\"");
382 if (NULL
!= req
->q
.query
)
383 html_print(req
->q
.query
);
384 puts("\" SIZE=\"40\">");
386 /* Write submission and reset buttons. */
388 printf( "<INPUT TYPE=\"submit\" VALUE=\"Submit\">\n"
389 "<INPUT TYPE=\"reset\" VALUE=\"Reset\">\n");
391 /* Write show radio button */
393 printf( "</TD><TD>\n"
394 "<INPUT TYPE=\"radio\" ");
396 printf("CHECKED=\"checked\" ");
397 printf( "NAME=\"apropos\" ID=\"show\" VALUE=\"0\">\n"
398 "<LABEL FOR=\"show\">Show named manual page</LABEL>\n");
400 /* Write section selector. */
402 puts( "</TD></TR><TR><TD>\n"
403 "<SELECT NAME=\"sec\">");
404 for (i
= 0; i
< sec_MAX
; i
++) {
405 printf("<OPTION VALUE=\"%s\"", sec_numbers
[i
]);
406 if (NULL
!= req
->q
.sec
&&
407 0 == strcmp(sec_numbers
[i
], req
->q
.sec
))
408 printf(" SELECTED=\"selected\"");
409 printf(">%s</OPTION>\n", sec_names
[i
]);
413 /* Write architecture selector. */
415 printf( "<SELECT NAME=\"arch\">\n"
416 "<OPTION VALUE=\"default\"");
417 if (NULL
== req
->q
.arch
)
418 printf(" SELECTED=\"selected\"");
419 puts(">All Architectures</OPTION>");
420 for (i
= 0; i
< arch_MAX
; i
++) {
421 printf("<OPTION VALUE=\"%s\"", arch_names
[i
]);
422 if (NULL
!= req
->q
.arch
&&
423 0 == strcmp(arch_names
[i
], req
->q
.arch
))
424 printf(" SELECTED=\"selected\"");
425 printf(">%s</OPTION>\n", arch_names
[i
]);
429 /* Write manpath selector. */
432 puts("<SELECT NAME=\"manpath\">");
433 for (i
= 0; i
< (int)req
->psz
; i
++) {
435 if (strcmp(req
->q
.manpath
, req
->p
[i
]) == 0)
436 printf("SELECTED=\"selected\" ");
438 html_print(req
->p
[i
]);
440 html_print(req
->p
[i
]);
446 /* Write search radio button */
448 printf( "</TD><TD>\n"
449 "<INPUT TYPE=\"radio\" ");
450 if (0 == req
->q
.equal
)
451 printf("CHECKED=\"checked\" ");
452 printf( "NAME=\"apropos\" ID=\"search\" VALUE=\"1\">\n"
453 "<LABEL FOR=\"search\">Search with apropos query</LABEL>\n");
455 puts("</TD></TR></TABLE>\n"
459 puts("<!-- End search form. //-->");
463 validate_urifrag(const char *frag
)
466 while ('\0' != *frag
) {
467 if ( ! (isalnum((unsigned char)*frag
) ||
468 '-' == *frag
|| '.' == *frag
||
469 '/' == *frag
|| '_' == *frag
))
477 validate_manpath(const struct req
*req
, const char* manpath
)
481 if ( ! strcmp(manpath
, "mandoc"))
484 for (i
= 0; i
< req
->psz
; i
++)
485 if ( ! strcmp(manpath
, req
->p
[i
]))
492 validate_filename(const char *file
)
495 if ('.' == file
[0] && '/' == file
[1])
498 return ! (strstr(file
, "../") || strstr(file
, "/..") ||
499 (strncmp(file
, "man", 3) && strncmp(file
, "cat", 3)));
503 pg_index(const struct req
*req
)
506 resp_begin_html(200, NULL
);
507 resp_searchform(req
);
509 "This web interface is documented in the\n"
510 "<A HREF=\"/%s%smandoc/man8/man.cgi.8\">man.cgi</A>\n"
512 "<A HREF=\"/%s%smandoc/man1/apropos.1\">apropos</A>\n"
513 "manual explains the query syntax.\n"
515 scriptname
, *scriptname
== '\0' ? "" : "/",
516 scriptname
, *scriptname
== '\0' ? "" : "/");
521 pg_noresult(const struct req
*req
, const char *msg
)
523 resp_begin_html(200, NULL
);
524 resp_searchform(req
);
532 pg_error_badrequest(const char *msg
)
535 resp_begin_html(400, "Bad Request");
536 puts("<H1>Bad Request</H1>\n"
539 printf("Try again from the\n"
540 "<A HREF=\"/%s\">main page</A>.\n"
546 pg_error_internal(void)
548 resp_begin_html(500, "Internal Server Error");
549 puts("<P>Internal Server Error</P>");
554 pg_searchres(const struct req
*req
, struct manpage
*r
, size_t sz
)
556 char *arch
, *archend
;
557 size_t i
, iuse
, isec
;
558 int archprio
, archpriouse
;
562 for (i
= 0; i
< sz
; i
++) {
563 if (validate_filename(r
[i
].file
))
565 fprintf(stderr
, "invalid filename %s in %s database\n",
566 r
[i
].file
, req
->q
.manpath
);
573 * If we have just one result, then jump there now
576 printf("Status: 303 See Other\r\n");
577 printf("Location: http://%s/%s%s%s/%s",
578 HTTP_HOST
, scriptname
,
579 *scriptname
== '\0' ? "" : "/",
580 req
->q
.manpath
, r
[0].file
);
582 "Content-Type: text/html; charset=utf-8\r\n"
587 resp_begin_html(200, NULL
);
588 resp_searchform(req
);
589 puts("<DIV CLASS=\"results\">");
592 for (i
= 0; i
< sz
; i
++) {
594 "<TD CLASS=\"title\">\n"
595 "<A HREF=\"/%s%s%s/%s",
596 scriptname
, *scriptname
== '\0' ? "" : "/",
597 req
->q
.manpath
, r
[i
].file
);
599 html_print(r
[i
].names
);
602 "<TD CLASS=\"desc\">");
603 html_print(r
[i
].output
);
612 * In man(1) mode, show one of the pages
613 * even if more than one is found.
621 for (i
= 0; i
< sz
; i
++) {
622 isec
= strcspn(r
[i
].file
, "123456789");
623 sec
= r
[i
].file
[isec
];
626 prio
= sec_prios
[sec
- '1'];
627 if (NULL
== req
->q
.arch
) {
629 (NULL
== (arch
= strchr(
630 r
[i
].file
+ isec
, '/'))) ? 3 :
631 (NULL
== (archend
= strchr(
632 arch
+ 1, '/'))) ? 0 :
633 strncmp(arch
, "amd64/",
634 archend
- arch
) ? 2 : 1;
635 if (archprio
< archpriouse
) {
636 archpriouse
= archprio
;
641 if (archprio
> archpriouse
)
649 resp_show(req
, r
[iuse
].file
);
656 catman(const struct req
*req
, const char *file
)
665 if ((f
= fopen(file
, "r")) == NULL
) {
666 puts("<P>You specified an invalid manual file.</P>");
670 puts("<DIV CLASS=\"catman\">\n"
676 while ((len
= getline(&p
, &sz
, f
)) != -1) {
678 for (i
= 0; i
< len
- 1; i
++) {
680 * This means that the catpage is out of state.
681 * Ignore it and keep going (although the
685 if ('\b' == p
[i
] || '\n' == p
[i
])
689 * Print a regular character.
690 * Close out any bold/italic scopes.
691 * If we're in back-space mode, make sure we'll
692 * have something to enter when we backspace.
695 if ('\b' != p
[i
+ 1]) {
703 } else if (i
+ 2 >= len
)
721 * Handle funny behaviour troff-isms.
722 * These grok'd from the original man2html.c.
725 if (('+' == p
[i
] && 'o' == p
[i
+ 2]) ||
726 ('o' == p
[i
] && '+' == p
[i
+ 2]) ||
727 ('|' == p
[i
] && '=' == p
[i
+ 2]) ||
728 ('=' == p
[i
] && '|' == p
[i
+ 2]) ||
729 ('*' == p
[i
] && '=' == p
[i
+ 2]) ||
730 ('=' == p
[i
] && '*' == p
[i
+ 2]) ||
731 ('*' == p
[i
] && '|' == p
[i
+ 2]) ||
732 ('|' == p
[i
] && '*' == p
[i
+ 2])) {
741 } else if (('|' == p
[i
] && '-' == p
[i
+ 2]) ||
742 ('-' == p
[i
] && '|' == p
[i
+ 1]) ||
743 ('+' == p
[i
] && '-' == p
[i
+ 1]) ||
744 ('-' == p
[i
] && '+' == p
[i
+ 1]) ||
745 ('+' == p
[i
] && '|' == p
[i
+ 1]) ||
746 ('|' == p
[i
] && '+' == p
[i
+ 1])) {
770 * Clean up the last character.
771 * We can get to a newline; don't print that.
779 if (i
== len
- 1 && p
[i
] != '\n')
793 format(const struct req
*req
, const char *file
)
795 struct manoutput conf
;
797 struct roff_man
*man
;
802 if (-1 == (fd
= open(file
, O_RDONLY
, 0))) {
803 puts("<P>You specified an invalid manual file.</P>");
808 mp
= mparse_alloc(MPARSE_SO
, MANDOCLEVEL_BADARG
, NULL
, req
->q
.manpath
);
809 mparse_readfd(mp
, fd
, file
);
812 memset(&conf
, 0, sizeof(conf
));
814 usepath
= strcmp(req
->q
.manpath
, req
->p
[0]);
815 mandoc_asprintf(&conf
.man
, "/%s?query=%%N&sec=%%S%s%s%s%s",
817 req
->q
.arch
? "&arch=" : "",
818 req
->q
.arch
? req
->q
.arch
: "",
819 usepath
? "&manpath=" : "",
820 usepath
? req
->q
.manpath
: "");
822 mparse_result(mp
, &man
, NULL
);
824 fprintf(stderr
, "fatal mandoc error: %s/%s\n",
825 req
->q
.manpath
, file
);
832 vp
= html_alloc(&conf
);
834 if (man
->macroset
== MACROSET_MDOC
) {
849 resp_show(const struct req
*req
, const char *file
)
852 if ('.' == file
[0] && '/' == file
[1])
862 pg_show(struct req
*req
, const char *fullpath
)
867 if ((file
= strchr(fullpath
, '/')) == NULL
) {
869 "You did not specify a page to show.");
872 manpath
= mandoc_strndup(fullpath
, file
- fullpath
);
875 if ( ! validate_manpath(req
, manpath
)) {
877 "You specified an invalid manpath.");
883 * Begin by chdir()ing into the manpath.
884 * This way we can pick up the database files, which are
885 * relative to the manpath root.
888 if (chdir(manpath
) == -1) {
889 fprintf(stderr
, "chdir %s: %s\n",
890 manpath
, strerror(errno
));
896 if (strcmp(manpath
, "mandoc")) {
897 free(req
->q
.manpath
);
898 req
->q
.manpath
= manpath
;
902 if ( ! validate_filename(file
)) {
904 "You specified an invalid manual file.");
908 resp_begin_html(200, NULL
);
909 resp_searchform(req
);
910 resp_show(req
, file
);
915 pg_search(const struct req
*req
)
917 struct mansearch search
;
918 struct manpaths paths
;
921 char *query
, *rp
, *wp
;
926 * Begin by chdir()ing into the root of the manpath.
927 * This way we can pick up the database files, which are
928 * relative to the manpath root.
931 if (-1 == (chdir(req
->q
.manpath
))) {
932 fprintf(stderr
, "chdir %s: %s\n",
933 req
->q
.manpath
, strerror(errno
));
938 search
.arch
= req
->q
.arch
;
939 search
.sec
= req
->q
.sec
;
940 search
.outkey
= "Nd";
941 search
.argmode
= req
->q
.equal
? ARG_NAME
: ARG_EXPR
;
942 search
.firstmatch
= 1;
945 paths
.paths
= mandoc_malloc(sizeof(char *));
946 paths
.paths
[0] = mandoc_strdup(".");
949 * Break apart at spaces with backslash-escaping.
954 rp
= query
= mandoc_strdup(req
->q
.query
);
956 while (isspace((unsigned char)*rp
))
960 argv
= mandoc_reallocarray(argv
, argc
+ 1, sizeof(char *));
961 argv
[argc
++] = wp
= rp
;
963 if (isspace((unsigned char)*rp
)) {
968 if (rp
[0] == '\\' && rp
[1] != '\0')
979 if (0 == mansearch(&search
, &paths
, argc
, argv
, &res
, &ressz
))
980 pg_noresult(req
, "You entered an invalid query.");
982 pg_noresult(req
, "No results found.");
984 pg_searchres(req
, res
, ressz
);
987 mansearch_free(res
, ressz
);
988 free(paths
.paths
[0]);
996 struct itimerval itimer
;
998 const char *querystring
;
1001 /* Poor man's ReDoS mitigation. */
1003 itimer
.it_value
.tv_sec
= 2;
1004 itimer
.it_value
.tv_usec
= 0;
1005 itimer
.it_interval
.tv_sec
= 2;
1006 itimer
.it_interval
.tv_usec
= 0;
1007 if (setitimer(ITIMER_VIRTUAL
, &itimer
, NULL
) == -1) {
1008 fprintf(stderr
, "setitimer: %s\n", strerror(errno
));
1009 pg_error_internal();
1010 return EXIT_FAILURE
;
1014 * First we change directory into the MAN_DIR so that
1015 * subsequent scanning for manpath directories is rooted
1016 * relative to the same position.
1019 if (-1 == chdir(MAN_DIR
)) {
1020 fprintf(stderr
, "MAN_DIR: %s: %s\n",
1021 MAN_DIR
, strerror(errno
));
1022 pg_error_internal();
1023 return EXIT_FAILURE
;
1026 memset(&req
, 0, sizeof(struct req
));
1030 /* Parse the path info and the query string. */
1032 if ((path
= getenv("PATH_INFO")) == NULL
)
1034 else if (*path
== '/')
1037 if (*path
!= '\0' && access(path
, F_OK
) == -1) {
1038 path_parse(&req
, path
);
1040 } else if ((querystring
= getenv("QUERY_STRING")) != NULL
)
1041 http_parse(&req
, querystring
);
1043 /* Validate parsed data and add defaults. */
1045 if (req
.q
.manpath
== NULL
)
1046 req
.q
.manpath
= mandoc_strdup(req
.p
[0]);
1047 else if ( ! validate_manpath(&req
, req
.q
.manpath
)) {
1048 pg_error_badrequest(
1049 "You specified an invalid manpath.");
1050 return EXIT_FAILURE
;
1053 if ( ! (NULL
== req
.q
.arch
|| validate_urifrag(req
.q
.arch
))) {
1054 pg_error_badrequest(
1055 "You specified an invalid architecture.");
1056 return EXIT_FAILURE
;
1059 /* Dispatch to the three different pages. */
1062 pg_show(&req
, path
);
1063 else if (NULL
!= req
.q
.query
)
1068 free(req
.q
.manpath
);
1072 for (i
= 0; i
< (int)req
.psz
; i
++)
1075 return EXIT_SUCCESS
;
1079 * If PATH_INFO is not a file name, translate it to a query.
1082 path_parse(struct req
*req
, const char *path
)
1087 req
->q
.manpath
= mandoc_strdup(path
);
1089 /* Mandatory manual page name. */
1090 if ((req
->q
.query
= strrchr(req
->q
.manpath
, '/')) == NULL
) {
1091 req
->q
.query
= req
->q
.manpath
;
1092 req
->q
.manpath
= NULL
;
1094 *req
->q
.query
++ = '\0';
1096 /* Optional trailing section. */
1097 if ((req
->q
.sec
= strrchr(req
->q
.query
, '.')) != NULL
) {
1098 if(isdigit((unsigned char)req
->q
.sec
[1])) {
1099 *req
->q
.sec
++ = '\0';
1100 req
->q
.sec
= mandoc_strdup(req
->q
.sec
);
1105 /* Handle the case of name[.section] only. */
1106 if (req
->q
.manpath
== NULL
) {
1110 req
->q
.query
= mandoc_strdup(req
->q
.query
);
1112 /* Optional architecture. */
1115 if ((req
->q
.arch
= strrchr(req
->q
.manpath
, '/')) == NULL
)
1117 *req
->q
.arch
++ = '\0';
1118 if (dir_done
|| strncmp(req
->q
.arch
, "man", 3)) {
1119 req
->q
.arch
= mandoc_strdup(req
->q
.arch
);
1123 /* Optional directory name. */
1125 if (*req
->q
.arch
!= '\0') {
1127 req
->q
.sec
= mandoc_strdup(req
->q
.arch
);
1134 * Scan for indexable paths.
1137 pathgen(struct req
*req
)
1144 if (NULL
== (fp
= fopen("manpath.conf", "r"))) {
1145 fprintf(stderr
, "%s/manpath.conf: %s\n",
1146 MAN_DIR
, strerror(errno
));
1147 pg_error_internal();
1154 while ((len
= getline(&dp
, &dpsz
, fp
)) != -1) {
1155 if (dp
[len
- 1] == '\n')
1157 req
->p
= mandoc_realloc(req
->p
,
1158 (req
->psz
+ 1) * sizeof(char *));
1159 if ( ! validate_urifrag(dp
)) {
1160 fprintf(stderr
, "%s/manpath.conf contains "
1161 "unsafe path \"%s\"\n", MAN_DIR
, dp
);
1162 pg_error_internal();
1165 if (NULL
!= strchr(dp
, '/')) {
1166 fprintf(stderr
, "%s/manpath.conf contains "
1167 "path with slash \"%s\"\n", MAN_DIR
, dp
);
1168 pg_error_internal();
1171 req
->p
[req
->psz
++] = dp
;
1177 if ( req
->p
== NULL
) {
1178 fprintf(stderr
, "%s/manpath.conf is empty\n", MAN_DIR
);
1179 pg_error_internal();