]> git.cameronkatri.com Git - mandoc.git/blob - cgi.c
set a reasonable default for .Os
[mandoc.git] / cgi.c
1 /* $Id: cgi.c,v 1.56 2014/07/09 11:34:46 schwarze Exp $ */
2 /*
3 * Copyright (c) 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv>
4 * Copyright (c) 2014 Ingo Schwarze <schwarze@usta.de>
5 *
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.
9 *
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.
17 */
18 #ifdef HAVE_CONFIG_H
19 #include "config.h"
20 #endif
21
22 #include <ctype.h>
23 #include <fcntl.h>
24 #include <limits.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <unistd.h>
29
30 #if defined(__sun)
31 /* for stat() */
32 #include <fcntl.h>
33 #include <sys/types.h>
34 #include <sys/stat.h>
35 #endif
36
37 #include "mandoc.h"
38 #include "mandoc_aux.h"
39 #include "main.h"
40 #include "manpath.h"
41 #include "mansearch.h"
42
43 enum page {
44 PAGE_INDEX,
45 PAGE_SEARCH,
46 PAGE_SHOW,
47 PAGE__MAX
48 };
49
50 /*
51 * A query as passed to the search function.
52 */
53 struct query {
54 const char *manroot; /* manual root directory */
55 const char *arch; /* architecture */
56 const char *sec; /* manual section */
57 const char *expr; /* unparsed expression string */
58 int legacy; /* whether legacy mode */
59 };
60
61 struct req {
62 struct query q;
63 char **p; /* array of available manroots */
64 size_t psz;
65 enum page page;
66 };
67
68 static void catman(const struct req *, const char *);
69 static int cmp(const void *, const void *);
70 static void format(const struct req *, const char *);
71 static void html_print(const char *);
72 static void html_printquery(const struct req *);
73 static void html_putchar(char);
74 static int http_decode(char *);
75 static void http_parse(struct req *, char *);
76 static void http_print(const char *);
77 static void http_putchar(char);
78 static void http_printquery(const struct req *);
79 static void pathgen(struct req *);
80 static void pg_index(const struct req *, char *);
81 static void pg_search(const struct req *, char *);
82 static void pg_show(const struct req *, char *);
83 static void resp_bad(void);
84 static void resp_baddb(void);
85 static void resp_error400(void);
86 static void resp_error404(const char *);
87 static void resp_begin_html(int, const char *);
88 static void resp_begin_http(int, const char *);
89 static void resp_end_html(void);
90 static void resp_index(const struct req *);
91 static void resp_search(const struct req *,
92 struct manpage *, size_t);
93 static void resp_searchform(const struct req *);
94
95 static const char *progname; /* cgi script name */
96 static const char *cache; /* cache directory */
97 static const char *css; /* css directory */
98 static const char *host; /* hostname */
99
100 static const char * const pages[PAGE__MAX] = {
101 "index", /* PAGE_INDEX */
102 "search", /* PAGE_SEARCH */
103 "show", /* PAGE_SHOW */
104 };
105
106 /*
107 * Print a character, escaping HTML along the way.
108 * This will pass non-ASCII straight to output: be warned!
109 */
110 static void
111 html_putchar(char c)
112 {
113
114 switch (c) {
115 case ('"'):
116 printf("&quote;");
117 break;
118 case ('&'):
119 printf("&amp;");
120 break;
121 case ('>'):
122 printf("&gt;");
123 break;
124 case ('<'):
125 printf("&lt;");
126 break;
127 default:
128 putchar((unsigned char)c);
129 break;
130 }
131 }
132 static void
133 http_printquery(const struct req *req)
134 {
135
136 if (NULL != req->q.manroot) {
137 printf("&manpath=");
138 http_print(req->q.manroot);
139 }
140 if (NULL != req->q.sec) {
141 printf("&sec=");
142 http_print(req->q.sec);
143 }
144 if (NULL != req->q.arch) {
145 printf("&arch=");
146 http_print(req->q.arch);
147 }
148 if (NULL != req->q.expr) {
149 printf("&expr=");
150 http_print(req->q.expr ? req->q.expr : "");
151 }
152 }
153
154
155 static void
156 html_printquery(const struct req *req)
157 {
158
159 if (NULL != req->q.manroot) {
160 printf("&amp;manpath=");
161 html_print(req->q.manroot);
162 }
163 if (NULL != req->q.sec) {
164 printf("&amp;sec=");
165 html_print(req->q.sec);
166 }
167 if (NULL != req->q.arch) {
168 printf("&amp;arch=");
169 html_print(req->q.arch);
170 }
171 if (NULL != req->q.expr) {
172 printf("&amp;expr=");
173 html_print(req->q.expr ? req->q.expr : "");
174 }
175 }
176
177 static void
178 http_print(const char *p)
179 {
180
181 if (NULL == p)
182 return;
183 while ('\0' != *p)
184 http_putchar(*p++);
185 }
186
187 /*
188 * Call through to html_putchar().
189 * Accepts NULL strings.
190 */
191 static void
192 html_print(const char *p)
193 {
194
195 if (NULL == p)
196 return;
197 while ('\0' != *p)
198 html_putchar(*p++);
199 }
200
201 /*
202 * Parse out key-value pairs from an HTTP request variable.
203 * This can be either a cookie or a POST/GET string, although man.cgi
204 * uses only GET for simplicity.
205 */
206 static void
207 http_parse(struct req *req, char *p)
208 {
209 char *key, *val;
210 int legacy;
211
212 memset(&req->q, 0, sizeof(struct query));
213 req->q.manroot = req->p[0];
214
215 legacy = -1;
216 while ('\0' != *p) {
217 key = p;
218 val = NULL;
219
220 p += (int)strcspn(p, ";&");
221 if ('\0' != *p)
222 *p++ = '\0';
223 if (NULL != (val = strchr(key, '=')))
224 *val++ = '\0';
225
226 if ('\0' == *key || NULL == val || '\0' == *val)
227 continue;
228
229 /* Just abort handling. */
230
231 if ( ! http_decode(key))
232 break;
233 if (NULL != val && ! http_decode(val))
234 break;
235
236 if (0 == strcmp(key, "expr"))
237 req->q.expr = val;
238 else if (0 == strcmp(key, "query"))
239 req->q.expr = val;
240 else if (0 == strcmp(key, "sec"))
241 req->q.sec = val;
242 else if (0 == strcmp(key, "sektion"))
243 req->q.sec = val;
244 else if (0 == strcmp(key, "arch"))
245 req->q.arch = val;
246 else if (0 == strcmp(key, "manpath"))
247 req->q.manroot = val;
248 else if (0 == strcmp(key, "apropos"))
249 legacy = 0 == strcmp(val, "0");
250 }
251
252 /* Test for old man.cgi compatibility mode. */
253
254 req->q.legacy = legacy > 0;
255
256 /*
257 * Section "0" means no section when in legacy mode.
258 * For some man.cgi scripts, "default" arch is none.
259 */
260
261 if (req->q.legacy && NULL != req->q.sec)
262 if (0 == strcmp(req->q.sec, "0"))
263 req->q.sec = NULL;
264 if (req->q.legacy && NULL != req->q.arch)
265 if (0 == strcmp(req->q.arch, "default"))
266 req->q.arch = NULL;
267 }
268
269 static void
270 http_putchar(char c)
271 {
272
273 if (isalnum((unsigned char)c)) {
274 putchar((unsigned char)c);
275 return;
276 } else if (' ' == c) {
277 putchar('+');
278 return;
279 }
280 printf("%%%.2x", c);
281 }
282
283 /*
284 * HTTP-decode a string. The standard explanation is that this turns
285 * "%4e+foo" into "n foo" in the regular way. This is done in-place
286 * over the allocated string.
287 */
288 static int
289 http_decode(char *p)
290 {
291 char hex[3];
292 int c;
293
294 hex[2] = '\0';
295
296 for ( ; '\0' != *p; p++) {
297 if ('%' == *p) {
298 if ('\0' == (hex[0] = *(p + 1)))
299 return(0);
300 if ('\0' == (hex[1] = *(p + 2)))
301 return(0);
302 if (1 != sscanf(hex, "%x", &c))
303 return(0);
304 if ('\0' == c)
305 return(0);
306
307 *p = (char)c;
308 memmove(p + 1, p + 3, strlen(p + 3) + 1);
309 } else
310 *p = '+' == *p ? ' ' : *p;
311 }
312
313 *p = '\0';
314 return(1);
315 }
316
317 static void
318 resp_begin_http(int code, const char *msg)
319 {
320
321 if (200 != code)
322 printf("Status: %d %s\n", code, msg);
323
324 puts("Content-Type: text/html; charset=utf-8\n"
325 "Cache-Control: no-cache\n"
326 "Pragma: no-cache\n"
327 "");
328
329 fflush(stdout);
330 }
331
332 static void
333 resp_begin_html(int code, const char *msg)
334 {
335
336 resp_begin_http(code, msg);
337
338 printf("<!DOCTYPE HTML PUBLIC "
339 " \"-//W3C//DTD HTML 4.01//EN\""
340 " \"http://www.w3.org/TR/html4/strict.dtd\">\n"
341 "<HTML>\n"
342 "<HEAD>\n"
343 "<META HTTP-EQUIV=\"Content-Type\""
344 " CONTENT=\"text/html; charset=utf-8\">\n"
345 "<LINK REL=\"stylesheet\" HREF=\"%s/man-cgi.css\""
346 " TYPE=\"text/css\" media=\"all\">\n"
347 "<LINK REL=\"stylesheet\" HREF=\"%s/man.css\""
348 " TYPE=\"text/css\" media=\"all\">\n"
349 "<TITLE>System Manpage Reference</TITLE>\n"
350 "</HEAD>\n"
351 "<BODY>\n"
352 "<!-- Begin page content. //-->\n", css, css);
353 }
354
355 static void
356 resp_end_html(void)
357 {
358
359 puts("</BODY>\n"
360 "</HTML>");
361 }
362
363 static void
364 resp_searchform(const struct req *req)
365 {
366 int i;
367
368 puts("<!-- Begin search form. //-->");
369 printf("<DIV ID=\"mancgi\">\n"
370 "<FORM ACTION=\"%s/search\" METHOD=\"get\">\n"
371 "<FIELDSET>\n"
372 "<LEGEND>Search Parameters</LEGEND>\n"
373 "<INPUT TYPE=\"submit\" "
374 " VALUE=\"Search\"> for manuals satisfying \n"
375 "<INPUT TYPE=\"text\" NAME=\"expr\" VALUE=\"",
376 progname);
377 html_print(req->q.expr ? req->q.expr : "");
378 printf("\">, section "
379 "<INPUT TYPE=\"text\""
380 " SIZE=\"4\" NAME=\"sec\" VALUE=\"");
381 html_print(req->q.sec ? req->q.sec : "");
382 printf("\">, arch "
383 "<INPUT TYPE=\"text\""
384 " SIZE=\"8\" NAME=\"arch\" VALUE=\"");
385 html_print(req->q.arch ? req->q.arch : "");
386 printf("\">");
387 if (req->psz > 1) {
388 puts(", <SELECT NAME=\"manpath\">");
389 for (i = 0; i < (int)req->psz; i++) {
390 printf("<OPTION ");
391 if (NULL == req->q.manroot ? 0 == i :
392 0 == strcmp(req->q.manroot, req->p[i]))
393 printf("SELECTED=\"selected\" ");
394 printf("VALUE=\"");
395 html_print(req->p[i]);
396 printf("\">");
397 html_print(req->p[i]);
398 puts("</OPTION>");
399 }
400 puts("</SELECT>");
401 }
402 puts(".\n"
403 "<INPUT TYPE=\"reset\" VALUE=\"Reset\">\n"
404 "</FIELDSET>\n"
405 "</FORM>\n"
406 "</DIV>");
407 puts("<!-- End search form. //-->");
408 }
409
410 static void
411 resp_index(const struct req *req)
412 {
413
414 resp_begin_html(200, NULL);
415 resp_searchform(req);
416 resp_end_html();
417 }
418
419 static void
420 resp_error400(void)
421 {
422
423 resp_begin_html(400, "Query Malformed");
424 printf("<H1>Malformed Query</H1>\n"
425 "<P>\n"
426 "The query your entered was malformed.\n"
427 "Try again from the\n"
428 "<A HREF=\"%s/index.html\">main page</A>.\n"
429 "</P>", progname);
430 resp_end_html();
431 }
432
433 static void
434 resp_error404(const char *page)
435 {
436
437 resp_begin_html(404, "Not Found");
438 puts("<H1>Page Not Found</H1>\n"
439 "<P>\n"
440 "The page you're looking for, ");
441 printf("<B>");
442 html_print(page);
443 printf("</B>,\n"
444 "could not be found.\n"
445 "Try searching from the\n"
446 "<A HREF=\"%s/index.html\">main page</A>.\n"
447 "</P>", progname);
448 resp_end_html();
449 }
450
451 static void
452 resp_bad(void)
453 {
454 resp_begin_html(500, "Internal Server Error");
455 puts("<P>Generic badness happened.</P>");
456 resp_end_html();
457 }
458
459 static void
460 resp_baddb(void)
461 {
462
463 resp_begin_html(500, "Internal Server Error");
464 puts("<P>Your database is broken.</P>");
465 resp_end_html();
466 }
467
468 static void
469 resp_search(const struct req *req, struct manpage *r, size_t sz)
470 {
471 size_t i;
472
473 if (1 == sz) {
474 /*
475 * If we have just one result, then jump there now
476 * without any delay.
477 */
478 puts("Status: 303 See Other");
479 printf("Location: http://%s%s/show/%s/%s?",
480 host, progname, req->q.manroot, r[0].file);
481 http_printquery(req);
482 puts("\n"
483 "Content-Type: text/html; charset=utf-8\n");
484 return;
485 }
486
487 resp_begin_html(200, NULL);
488 resp_searchform(req);
489
490 puts("<DIV CLASS=\"results\">");
491
492 if (0 == sz) {
493 puts("<P>\n"
494 "No results found.\n"
495 "</P>\n"
496 "</DIV>");
497 resp_end_html();
498 return;
499 }
500
501 qsort(r, sz, sizeof(struct manpage), cmp);
502
503 puts("<TABLE>");
504
505 for (i = 0; i < sz; i++) {
506 printf("<TR>\n"
507 "<TD CLASS=\"title\">\n"
508 "<A HREF=\"%s/show/%s/%s?",
509 progname, req->q.manroot, r[i].file);
510 html_printquery(req);
511 printf("\">");
512 html_print(r[i].names);
513 printf("</A>\n"
514 "</TD>\n"
515 "<TD CLASS=\"desc\">");
516 html_print(r[i].output);
517 puts("</TD>\n"
518 "</TR>");
519 }
520
521 puts("</TABLE>\n"
522 "</DIV>");
523 resp_end_html();
524 }
525
526 /* ARGSUSED */
527 static void
528 pg_index(const struct req *req, char *path)
529 {
530
531 resp_index(req);
532 }
533
534 static void
535 catman(const struct req *req, const char *file)
536 {
537 FILE *f;
538 size_t len;
539 int i;
540 char *p;
541 int italic, bold;
542
543 if (NULL == (f = fopen(file, "r"))) {
544 resp_baddb();
545 return;
546 }
547
548 resp_begin_html(200, NULL);
549 resp_searchform(req);
550 puts("<DIV CLASS=\"catman\">\n"
551 "<PRE>");
552
553 while (NULL != (p = fgetln(f, &len))) {
554 bold = italic = 0;
555 for (i = 0; i < (int)len - 1; i++) {
556 /*
557 * This means that the catpage is out of state.
558 * Ignore it and keep going (although the
559 * catpage is bogus).
560 */
561
562 if ('\b' == p[i] || '\n' == p[i])
563 continue;
564
565 /*
566 * Print a regular character.
567 * Close out any bold/italic scopes.
568 * If we're in back-space mode, make sure we'll
569 * have something to enter when we backspace.
570 */
571
572 if ('\b' != p[i + 1]) {
573 if (italic)
574 printf("</I>");
575 if (bold)
576 printf("</B>");
577 italic = bold = 0;
578 html_putchar(p[i]);
579 continue;
580 } else if (i + 2 >= (int)len)
581 continue;
582
583 /* Italic mode. */
584
585 if ('_' == p[i]) {
586 if (bold)
587 printf("</B>");
588 if ( ! italic)
589 printf("<I>");
590 bold = 0;
591 italic = 1;
592 i += 2;
593 html_putchar(p[i]);
594 continue;
595 }
596
597 /*
598 * Handle funny behaviour troff-isms.
599 * These grok'd from the original man2html.c.
600 */
601
602 if (('+' == p[i] && 'o' == p[i + 2]) ||
603 ('o' == p[i] && '+' == p[i + 2]) ||
604 ('|' == p[i] && '=' == p[i + 2]) ||
605 ('=' == p[i] && '|' == p[i + 2]) ||
606 ('*' == p[i] && '=' == p[i + 2]) ||
607 ('=' == p[i] && '*' == p[i + 2]) ||
608 ('*' == p[i] && '|' == p[i + 2]) ||
609 ('|' == p[i] && '*' == p[i + 2])) {
610 if (italic)
611 printf("</I>");
612 if (bold)
613 printf("</B>");
614 italic = bold = 0;
615 putchar('*');
616 i += 2;
617 continue;
618 } else if (('|' == p[i] && '-' == p[i + 2]) ||
619 ('-' == p[i] && '|' == p[i + 1]) ||
620 ('+' == p[i] && '-' == p[i + 1]) ||
621 ('-' == p[i] && '+' == p[i + 1]) ||
622 ('+' == p[i] && '|' == p[i + 1]) ||
623 ('|' == p[i] && '+' == p[i + 1])) {
624 if (italic)
625 printf("</I>");
626 if (bold)
627 printf("</B>");
628 italic = bold = 0;
629 putchar('+');
630 i += 2;
631 continue;
632 }
633
634 /* Bold mode. */
635
636 if (italic)
637 printf("</I>");
638 if ( ! bold)
639 printf("<B>");
640 bold = 1;
641 italic = 0;
642 i += 2;
643 html_putchar(p[i]);
644 }
645
646 /*
647 * Clean up the last character.
648 * We can get to a newline; don't print that.
649 */
650
651 if (italic)
652 printf("</I>");
653 if (bold)
654 printf("</B>");
655
656 if (i == (int)len - 1 && '\n' != p[i])
657 html_putchar(p[i]);
658
659 putchar('\n');
660 }
661
662 puts("</PRE>\n"
663 "</DIV>\n"
664 "</BODY>\n"
665 "</HTML>");
666
667 fclose(f);
668 }
669
670 static void
671 format(const struct req *req, const char *file)
672 {
673 struct mparse *mp;
674 int fd;
675 struct mdoc *mdoc;
676 struct man *man;
677 void *vp;
678 enum mandoclevel rc;
679 char opts[PATH_MAX + 128];
680
681 if (-1 == (fd = open(file, O_RDONLY, 0))) {
682 resp_baddb();
683 return;
684 }
685
686 mp = mparse_alloc(MPARSE_SO, MANDOCLEVEL_FATAL, NULL,
687 req->q.manroot);
688 rc = mparse_readfd(mp, fd, file);
689 close(fd);
690
691 if (rc >= MANDOCLEVEL_FATAL) {
692 resp_baddb();
693 return;
694 }
695
696 snprintf(opts, sizeof(opts),
697 "fragment,man=%s/search?sec=%%S&expr=Nm~^%%N$",
698 progname);
699
700 mparse_result(mp, &mdoc, &man, NULL);
701 if (NULL == man && NULL == mdoc) {
702 resp_baddb();
703 mparse_free(mp);
704 return;
705 }
706
707 resp_begin_html(200, NULL);
708 resp_searchform(req);
709
710 vp = html_alloc(opts);
711
712 if (NULL != mdoc)
713 html_mdoc(vp, mdoc);
714 else
715 html_man(vp, man);
716
717 puts("</BODY>\n"
718 "</HTML>");
719
720 html_free(vp);
721 mparse_free(mp);
722 }
723
724 static void
725 pg_show(const struct req *req, char *path)
726 {
727 char *sub;
728
729 if (NULL == path || NULL == (sub = strchr(path, '/'))) {
730 resp_error400();
731 return;
732 }
733 *sub++ = '\0';
734
735 /*
736 * Begin by chdir()ing into the manroot.
737 * This way we can pick up the database files, which are
738 * relative to the manpath root.
739 */
740
741 if (-1 == chdir(path)) {
742 perror(path);
743 resp_baddb();
744 return;
745 }
746
747 if ('c' == *sub)
748 catman(req, sub);
749 else
750 format(req, sub);
751 }
752
753 static void
754 pg_search(const struct req *req, char *path)
755 {
756 struct mansearch search;
757 struct manpaths paths;
758 struct manpage *res;
759 char **cp;
760 const char *ep, *start;
761 size_t ressz;
762 int i, sz;
763
764 /*
765 * Begin by chdir()ing into the root of the manpath.
766 * This way we can pick up the database files, which are
767 * relative to the manpath root.
768 */
769
770 if (-1 == (chdir(req->q.manroot))) {
771 perror(req->q.manroot);
772 resp_search(req, NULL, 0);
773 return;
774 }
775
776 search.arch = req->q.arch;
777 search.sec = req->q.sec;
778 search.deftype = TYPE_Nm | TYPE_Nd;
779 search.flags = 0;
780
781 paths.sz = 1;
782 paths.paths = mandoc_malloc(sizeof(char *));
783 paths.paths[0] = mandoc_strdup(".");
784
785 /*
786 * Poor man's tokenisation: just break apart by spaces.
787 * Yes, this is half-ass. But it works for now.
788 */
789
790 ep = req->q.expr;
791 while (ep && isspace((unsigned char)*ep))
792 ep++;
793
794 sz = 0;
795 cp = NULL;
796 while (ep && '\0' != *ep) {
797 cp = mandoc_reallocarray(cp, sz + 1, sizeof(char *));
798 start = ep;
799 while ('\0' != *ep && ! isspace((unsigned char)*ep))
800 ep++;
801 cp[sz] = mandoc_malloc((ep - start) + 1);
802 memcpy(cp[sz], start, ep - start);
803 cp[sz++][ep - start] = '\0';
804 while (isspace((unsigned char)*ep))
805 ep++;
806 }
807
808 if (mansearch(&search, &paths, sz, cp, "Nd", &res, &ressz))
809 resp_search(req, res, ressz);
810 else
811 resp_baddb();
812
813 for (i = 0; i < sz; i++)
814 free(cp[i]);
815 free(cp);
816
817 for (i = 0; i < (int)ressz; i++) {
818 free(res[i].file);
819 free(res[i].names);
820 free(res[i].output);
821 }
822 free(res);
823
824 free(paths.paths[0]);
825 free(paths.paths);
826 }
827
828 int
829 main(void)
830 {
831 int i;
832 struct req req;
833 char *p, *path, *subpath;
834
835 /* Scan our run-time environment. */
836
837 if (NULL == (cache = getenv("CACHE_DIR")))
838 cache = "/cache/man.cgi";
839
840 if (NULL == (progname = getenv("SCRIPT_NAME")))
841 progname = "";
842
843 if (NULL == (css = getenv("CSS_DIR")))
844 css = "";
845
846 if (NULL == (host = getenv("HTTP_HOST")))
847 host = "localhost";
848
849 /*
850 * First we change directory into the cache directory so that
851 * subsequent scanning for manpath directories is rooted
852 * relative to the same position.
853 */
854
855 if (-1 == chdir(cache)) {
856 perror(cache);
857 resp_bad();
858 return(EXIT_FAILURE);
859 }
860
861 memset(&req, 0, sizeof(struct req));
862 pathgen(&req);
863
864 /* Next parse out the query string. */
865
866 if (NULL != (p = getenv("QUERY_STRING")))
867 http_parse(&req, p);
868
869 /*
870 * Now juggle paths to extract information.
871 * We want to extract our filetype (the file suffix), the
872 * initial path component, then the trailing component(s).
873 * Start with leading subpath component.
874 */
875
876 subpath = path = NULL;
877 req.page = PAGE__MAX;
878
879 if (NULL == (path = getenv("PATH_INFO")) || '\0' == *path)
880 req.page = PAGE_INDEX;
881
882 if (NULL != path && '/' == *path && '\0' == *++path)
883 req.page = PAGE_INDEX;
884
885 /* Resolve subpath component. */
886
887 if (NULL != path && NULL != (subpath = strchr(path, '/')))
888 *subpath++ = '\0';
889
890 /* Map path into one we recognise. */
891
892 if (NULL != path && '\0' != *path)
893 for (i = 0; i < (int)PAGE__MAX; i++)
894 if (0 == strcmp(pages[i], path)) {
895 req.page = (enum page)i;
896 break;
897 }
898
899 /* Route pages. */
900
901 switch (req.page) {
902 case (PAGE_INDEX):
903 pg_index(&req, subpath);
904 break;
905 case (PAGE_SEARCH):
906 pg_search(&req, subpath);
907 break;
908 case (PAGE_SHOW):
909 pg_show(&req, subpath);
910 break;
911 default:
912 resp_error404(path);
913 break;
914 }
915
916 for (i = 0; i < (int)req.psz; i++)
917 free(req.p[i]);
918 free(req.p);
919 return(EXIT_SUCCESS);
920 }
921
922 static int
923 cmp(const void *p1, const void *p2)
924 {
925
926 return(strcasecmp(((const struct manpage *)p1)->names,
927 ((const struct manpage *)p2)->names));
928 }
929
930 /*
931 * Scan for indexable paths.
932 */
933 static void
934 pathgen(struct req *req)
935 {
936 FILE *fp;
937 char *dp;
938 size_t dpsz;
939
940 if (NULL == (fp = fopen("manpath.conf", "r")))
941 return;
942
943 while (NULL != (dp = fgetln(fp, &dpsz))) {
944 if ('\n' == dp[dpsz - 1])
945 dpsz--;
946 req->p = mandoc_realloc(req->p,
947 (req->psz + 1) * sizeof(char *));
948 req->p[req->psz++] = mandoc_strndup(dp, dpsz);
949 }
950 }