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