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