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