Annotation of mandoc/cgi.c, Revision 1.19
1.19 ! kristaps 1: /* $Id: cgi.c,v 1.18 2011/12/08 18:39:14 kristaps Exp $ */
1.6 kristaps 2: /*
3: * Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv>
4: *
5: * Permission to use, copy, modify, and distribute this software for any
6: * purpose with or without fee is hereby granted, provided that the above
7: * copyright notice and this permission notice appear in all copies.
8: *
9: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16: */
17: #ifdef HAVE_CONFIG_H
18: #include "config.h"
19: #endif
20:
21: #include <sys/param.h>
22: #include <sys/wait.h>
23:
1.1 kristaps 24: #include <assert.h>
1.6 kristaps 25: #include <ctype.h>
26: #include <errno.h>
1.1 kristaps 27: #include <fcntl.h>
1.6 kristaps 28: #include <limits.h>
1.1 kristaps 29: #include <regex.h>
30: #include <stdio.h>
31: #include <stdarg.h>
1.5 kristaps 32: #include <stdint.h>
1.1 kristaps 33: #include <stdlib.h>
34: #include <string.h>
1.6 kristaps 35: #include <unistd.h>
1.1 kristaps 36:
1.6 kristaps 37: #include "apropos_db.h"
1.4 schwarze 38: #include "mandoc.h"
1.8 kristaps 39: #include "mdoc.h"
40: #include "man.h"
41: #include "main.h"
1.6 kristaps 42: #include "manpath.h"
43:
44: #ifdef __linux__
45: # include <db_185.h>
46: #else
47: # include <db.h>
48: #endif
1.1 kristaps 49:
50: enum page {
51: PAGE_INDEX,
52: PAGE_SEARCH,
1.6 kristaps 53: PAGE_SHOW,
1.1 kristaps 54: PAGE__MAX
55: };
56:
57: struct kval {
58: char *key;
59: char *val;
60: };
61:
62: struct req {
1.6 kristaps 63: struct kval *fields;
1.1 kristaps 64: size_t fieldsz;
65: enum page page;
66: };
67:
1.6 kristaps 68: static int atou(const char *, unsigned *);
1.9 kristaps 69: static void catman(const char *);
1.15 kristaps 70: static int cmp(const void *, const void *);
1.8 kristaps 71: static void format(const char *);
1.6 kristaps 72: static void html_print(const char *);
1.10 kristaps 73: static void html_putchar(char);
1.1 kristaps 74: static int kval_decode(char *);
75: static void kval_parse(struct kval **, size_t *, char *);
76: static void kval_free(struct kval *, size_t);
1.6 kristaps 77: static void pg_index(const struct manpaths *,
78: const struct req *, char *);
79: static void pg_search(const struct manpaths *,
80: const struct req *, char *);
81: static void pg_show(const struct manpaths *,
82: const struct req *, char *);
1.7 kristaps 83: static void resp_bad(void);
1.6 kristaps 84: static void resp_baddb(void);
1.10 kristaps 85: static void resp_error400(void);
86: static void resp_error404(const char *);
1.6 kristaps 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(struct res *, size_t, void *);
92: static void resp_searchform(const struct req *);
93:
94: static const char *progname;
1.7 kristaps 95: static const char *cache;
1.6 kristaps 96: static const char *host;
1.1 kristaps 97:
98: static const char * const pages[PAGE__MAX] = {
99: "index", /* PAGE_INDEX */
100: "search", /* PAGE_SEARCH */
1.6 kristaps 101: "show", /* PAGE_SHOW */
1.1 kristaps 102: };
103:
1.6 kristaps 104: /*
105: * This is just OpenBSD's strtol(3) suggestion.
106: * I use it instead of strtonum(3) for portability's sake.
107: */
108: static int
109: atou(const char *buf, unsigned *v)
110: {
111: char *ep;
112: long lval;
113:
114: errno = 0;
115: lval = strtol(buf, &ep, 10);
116: if (buf[0] == '\0' || *ep != '\0')
117: return(0);
118: if ((errno == ERANGE && (lval == LONG_MAX ||
119: lval == LONG_MIN)) ||
120: (lval > UINT_MAX || lval < 0))
121: return(0);
122:
123: *v = (unsigned int)lval;
124: return(1);
125: }
1.1 kristaps 126:
1.10 kristaps 127: static void
128: html_putchar(char c)
129: {
130:
131: switch (c) {
132: case ('"'):
133: printf(""e;");
134: break;
135: case ('&'):
136: printf("&");
137: break;
138: case ('>'):
139: printf(">");
140: break;
141: case ('<'):
142: printf("<");
143: break;
144: default:
145: putchar((unsigned char)c);
146: break;
147: }
148: }
149:
1.6 kristaps 150: /*
151: * Print a word, escaping HTML along the way.
152: * This will pass non-ASCII straight to output: be warned!
153: */
1.1 kristaps 154: static void
1.6 kristaps 155: html_print(const char *p)
1.1 kristaps 156: {
1.6 kristaps 157:
158: if (NULL == p)
159: return;
1.1 kristaps 160: while ('\0' != *p)
1.10 kristaps 161: html_putchar(*p++);
1.1 kristaps 162: }
163:
164: static void
165: kval_free(struct kval *p, size_t sz)
166: {
167: int i;
168:
169: for (i = 0; i < (int)sz; i++) {
170: free(p[i].key);
171: free(p[i].val);
172: }
173: free(p);
174: }
175:
176: /*
177: * Parse out key-value pairs from an HTTP request variable.
1.6 kristaps 178: * This can be either a cookie or a POST/GET string, although man.cgi
179: * uses only GET for simplicity.
1.1 kristaps 180: */
181: static void
182: kval_parse(struct kval **kv, size_t *kvsz, char *p)
183: {
184: char *key, *val;
185: size_t sz, cur;
186:
187: cur = 0;
188:
189: while (p && '\0' != *p) {
190: while (' ' == *p)
191: p++;
192:
193: key = p;
194: val = NULL;
195:
196: if (NULL != (p = strchr(p, '='))) {
197: *p++ = '\0';
198: val = p;
199:
200: sz = strcspn(p, ";&");
201: /* LINTED */
202: p += sz;
203:
204: if ('\0' != *p)
205: *p++ = '\0';
206: } else {
207: p = key;
208: sz = strcspn(p, ";&");
209: /* LINTED */
210: p += sz;
211:
212: if ('\0' != *p)
213: p++;
214: continue;
215: }
216:
217: if ('\0' == *key || '\0' == *val)
218: continue;
219:
220: /* Just abort handling. */
221:
222: if ( ! kval_decode(key))
223: return;
224: if ( ! kval_decode(val))
225: return;
226:
227: if (*kvsz + 1 >= cur) {
228: cur++;
229: *kv = mandoc_realloc
230: (*kv, cur * sizeof(struct kval));
231: }
232:
233: (*kv)[(int)*kvsz].key = mandoc_strdup(key);
234: (*kv)[(int)*kvsz].val = mandoc_strdup(val);
235: (*kvsz)++;
236: }
237: }
238:
239: /*
1.6 kristaps 240: * HTTP-decode a string. The standard explanation is that this turns
241: * "%4e+foo" into "n foo" in the regular way. This is done in-place
242: * over the allocated string.
1.1 kristaps 243: */
244: static int
245: kval_decode(char *p)
246: {
247: char hex[3];
248: int c;
249:
250: hex[2] = '\0';
251:
252: for ( ; '\0' != *p; p++) {
253: if ('%' == *p) {
254: if ('\0' == (hex[0] = *(p + 1)))
255: return(0);
256: if ('\0' == (hex[1] = *(p + 2)))
257: return(0);
258: if (1 != sscanf(hex, "%x", &c))
259: return(0);
260: if ('\0' == c)
261: return(0);
262:
263: *p = (char)c;
264: memmove(p + 1, p + 3, strlen(p + 3) + 1);
265: } else
266: *p = '+' == *p ? ' ' : *p;
267: }
268:
269: *p = '\0';
270: return(1);
271: }
272:
1.6 kristaps 273: static void
274: resp_begin_http(int code, const char *msg)
275: {
276:
277: if (200 != code)
278: printf("Status: %d %s\n", code, msg);
279:
280: puts("Content-Type: text/html; charset=utf-8" "\n"
281: "Cache-Control: no-cache" "\n"
282: "Pragma: no-cache" "\n"
283: "");
284:
285: fflush(stdout);
286: }
287:
288: static void
289: resp_begin_html(int code, const char *msg)
290: {
291:
292: resp_begin_http(code, msg);
293:
294: puts("<!DOCTYPE HTML PUBLIC " "\n"
295: " \"-//W3C//DTD HTML 4.01//EN\"" "\n"
296: " \"http://www.w3.org/TR/html4/strict.dtd\">" "\n"
297: "<HTML>" "\n"
298: " <HEAD>" "\n"
1.14 kristaps 299: " <META HTTP-EQUIV=\"Content-Type\" " "\n"
300: " CONTENT=\"text/html; charset=utf-8\">" "\n"
1.12 kristaps 301: " <LINK REL=\"stylesheet\" HREF=\"/man.cgi.css\"" "\n"
302: " TYPE=\"text/css\" media=\"all\">" "\n"
1.6 kristaps 303: " <TITLE>System Manpage Reference</TITLE>" "\n"
304: " </HEAD>" "\n"
305: " <BODY>" "\n"
306: "<!-- Begin page content. //-->");
307: }
308:
309: static void
310: resp_end_html(void)
311: {
312:
313: puts(" </BODY>\n</HTML>");
314: }
315:
316: static void
317: resp_searchform(const struct req *req)
318: {
319: int i;
320: const char *expr, *sec, *arch;
321:
322: expr = sec = arch = "";
323:
324: for (i = 0; i < (int)req->fieldsz; i++)
325: if (0 == strcmp(req->fields[i].key, "expr"))
326: expr = req->fields[i].val;
1.13 kristaps 327: else if (0 == strcmp(req->fields[i].key, "query"))
328: expr = req->fields[i].val;
1.6 kristaps 329: else if (0 == strcmp(req->fields[i].key, "sec"))
330: sec = req->fields[i].val;
1.13 kristaps 331: else if (0 == strcmp(req->fields[i].key, "sektion"))
332: sec = req->fields[i].val;
1.6 kristaps 333: else if (0 == strcmp(req->fields[i].key, "arch"))
334: arch = req->fields[i].val;
335:
1.13 kristaps 336: if (NULL != sec && 0 == strcmp(sec, "0"))
337: sec = NULL;
338:
1.6 kristaps 339: puts("<!-- Begin search form. //-->");
340: printf("<FORM ACTION=\"");
341: html_print(progname);
1.9 kristaps 342: printf("/search.html\" METHOD=\"get\">\n");
1.14 kristaps 343: printf("<FIELDSET>\n"
1.16 kristaps 344: "<LEGEND>Search Parameters</LEGEND>\n"
1.14 kristaps 345: "<INPUT TYPE=\"submit\" NAME=\"op\" "
346: "VALUE=\"Whatis\"> or \n"
347: "<INPUT TYPE=\"submit\" NAME=\"op\" "
348: "VALUE=\"apropos\"> for manuals satisfying \n"
349: "<INPUT TYPE=\"text\" NAME=\"expr\" VALUE=\"");
1.6 kristaps 350: html_print(expr);
1.14 kristaps 351: printf("\">, section "
352: "<INPUT TYPE=\"text\" "
353: "SIZE=\"4\" NAME=\"sec\" VALUE=\"");
1.6 kristaps 354: html_print(sec);
1.14 kristaps 355: printf("\">, arch "
356: "<INPUT TYPE=\"text\" "
357: "SIZE=\"8\" NAME=\"arch\" VALUE=\"");
1.6 kristaps 358: html_print(arch);
1.12 kristaps 359: puts("\">.\n"
360: "<INPUT TYPE=\"reset\" VALUE=\"Reset\">\n"
361: "</FIELDSET>\n"
362: "</FORM>\n"
363: "<!-- End search form. //-->");
1.6 kristaps 364: }
365:
366: static void
367: resp_index(const struct req *req)
368: {
369:
370: resp_begin_html(200, NULL);
371: resp_searchform(req);
372: resp_end_html();
373: }
374:
375: static void
1.10 kristaps 376: resp_error400(void)
1.9 kristaps 377: {
378:
1.10 kristaps 379: resp_begin_html(400, "Query Malformed");
1.12 kristaps 380: printf("<H1>Malformed Query</H1>\n"
381: "<P>\n"
382: " The query your entered was malformed.\n"
383: " Try again from the\n"
384: " <A HREF=\"%s/index.html\">main page</A>\n"
385: "</P>", progname);
1.9 kristaps 386: resp_end_html();
387: }
388:
389: static void
1.10 kristaps 390: resp_error404(const char *page)
1.6 kristaps 391: {
392:
393: resp_begin_html(404, "Not Found");
1.10 kristaps 394: puts("<H1>Page Not Found</H1>\n"
395: "<P>\n"
396: " The page you're looking for, ");
397: printf(" <B>");
398: html_print(page);
1.12 kristaps 399: printf("</B>,\n"
400: " could not be found.\n"
401: " Try searching from the\n"
402: " <A HREF=\"%s/index.html\">main page</A>\n"
403: "</P>", progname);
1.6 kristaps 404: resp_end_html();
405: }
1.1 kristaps 406:
407: static void
1.7 kristaps 408: resp_bad(void)
409: {
410: resp_begin_html(500, "Internal Server Error");
411: puts("<P>Generic badness happened.</P>");
412: resp_end_html();
413: }
414:
415: static void
1.6 kristaps 416: resp_baddb(void)
1.1 kristaps 417: {
418:
1.6 kristaps 419: resp_begin_html(500, "Internal Server Error");
420: puts("<P>Your database is broken.</P>");
421: resp_end_html();
1.1 kristaps 422: }
423:
424: static void
1.6 kristaps 425: resp_search(struct res *r, size_t sz, void *arg)
1.1 kristaps 426: {
1.19 ! kristaps 427: int i, whatis;
! 428: const char *ep, *sec, *arch;
! 429: const struct req *req;
! 430:
! 431: whatis = 1;
! 432: ep = sec = arch = NULL;
1.1 kristaps 433:
1.6 kristaps 434: if (1 == sz) {
435: /*
436: * If we have just one result, then jump there now
437: * without any delay.
438: */
439: puts("Status: 303 See Other");
440: printf("Location: http://%s%s/show/%u/%u.html\n",
441: host, progname,
442: r[0].volume, r[0].rec);
443: puts("Content-Type: text/html; charset=utf-8\n");
444: return;
445: }
446:
1.19 ! kristaps 447: req = (const struct req *)arg;
! 448:
! 449: for (i = 0; i < (int)req->fieldsz; i++)
! 450: if (0 == strcmp(req->fields[i].key, "expr"))
! 451: ep = req->fields[i].val;
! 452: else if (0 == strcmp(req->fields[i].key, "query"))
! 453: ep = req->fields[i].val;
! 454: else if (0 == strcmp(req->fields[i].key, "sec"))
! 455: sec = req->fields[i].val;
! 456: else if (0 == strcmp(req->fields[i].key, "sektion"))
! 457: sec = req->fields[i].val;
! 458: else if (0 == strcmp(req->fields[i].key, "arch"))
! 459: arch = req->fields[i].val;
! 460: else if (0 == strcmp(req->fields[i].key, "apropos"))
! 461: whatis = 0 == strcmp
! 462: (req->fields[i].val, "0");
! 463: else if (0 == strcmp(req->fields[i].key, "op"))
! 464: whatis = 0 == strcasecmp
! 465: (req->fields[i].val, "whatis");
! 466:
1.15 kristaps 467: qsort(r, sz, sizeof(struct res), cmp);
468:
1.12 kristaps 469: resp_begin_html(200, NULL);
1.19 ! kristaps 470: resp_searchform(req);
1.6 kristaps 471:
1.14 kristaps 472: if (0 == sz) {
1.19 ! kristaps 473: puts("<P>\n"
! 474: "No results found.");
! 475: if (whatis) {
! 476: printf("(Try <A HREF=\"");
! 477: html_print(progname);
! 478: printf("/search.html?op=apropos&expr=");
! 479: html_print(ep ? ep : "");
! 480: printf("&sec=");
! 481: html_print(sec ? sec : "");
! 482: printf("&arch=");
! 483: html_print(arch ? arch : "");
! 484: puts("\">apropos</A>?)");
! 485: }
! 486: puts("</P>");
1.14 kristaps 487: resp_end_html();
488: return;
489: }
490:
491: puts("<P></P>\n"
492: "<TABLE>");
1.1 kristaps 493:
494: for (i = 0; i < (int)sz; i++) {
1.14 kristaps 495: printf("<TR><TD CLASS=\"title\"><A HREF=\"");
1.6 kristaps 496: html_print(progname);
497: printf("/show/%u/%u.html\">", r[i].volume, r[i].rec);
1.15 kristaps 498: html_print(r[i].title);
1.1 kristaps 499: putchar('(');
1.6 kristaps 500: html_print(r[i].cat);
501: if (r[i].arch && '\0' != *r[i].arch) {
502: putchar('/');
503: html_print(r[i].arch);
504: }
1.14 kristaps 505: printf(")</A></TD><TD CLASS=\"desc\">");
1.6 kristaps 506: html_print(r[i].desc);
1.14 kristaps 507: puts("</TD></TR>");
1.1 kristaps 508: }
1.16 kristaps 509:
510: puts("</TABLE>");
1.6 kristaps 511:
512: resp_end_html();
513: }
514:
515: /* ARGSUSED */
516: static void
517: pg_index(const struct manpaths *ps, const struct req *req, char *path)
518: {
519:
520: resp_index(req);
1.1 kristaps 521: }
522:
523: static void
1.9 kristaps 524: catman(const char *file)
525: {
1.10 kristaps 526: FILE *f;
527: size_t len;
528: int i;
529: char *p;
530: int italic, bold;
1.9 kristaps 531:
1.10 kristaps 532: if (NULL == (f = fopen(file, "r"))) {
1.9 kristaps 533: resp_baddb();
534: return;
535: }
536:
1.12 kristaps 537: resp_begin_http(200, NULL);
538: puts("<!DOCTYPE HTML PUBLIC " "\n"
539: " \"-//W3C//DTD HTML 4.01//EN\"" "\n"
540: " \"http://www.w3.org/TR/html4/strict.dtd\">" "\n"
541: "<HTML>" "\n"
542: " <HEAD>" "\n"
543: " <META HTTP-EQUIV=\"Content-Type\" " "\n"
544: " CONTENT=\"text/html; charset=utf-8\">" "\n"
545: " <LINK REL=\"stylesheet\" HREF=\"/catman.css\"" "\n"
546: " TYPE=\"text/css\" media=\"all\">" "\n"
547: " <TITLE>System Manpage Reference</TITLE>" "\n"
548: " </HEAD>" "\n"
549: " <BODY>" "\n"
550: "<!-- Begin page content. //-->");
1.10 kristaps 551:
552: puts("<PRE>");
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: */
1.9 kristaps 650:
1.10 kristaps 651: if (italic)
652: printf("</I>");
653: if (bold)
654: printf("</B>");
1.9 kristaps 655:
1.10 kristaps 656: if (i == (int)len - 1 && '\n' != p[i])
657: html_putchar(p[i]);
1.9 kristaps 658:
1.10 kristaps 659: putchar('\n');
660: }
661:
662: puts("</PRE>\n"
663: "</BODY>\n"
664: "</HTML>");
665:
666: fclose(f);
1.9 kristaps 667: }
668:
669: static void
1.8 kristaps 670: format(const char *file)
1.7 kristaps 671: {
1.8 kristaps 672: struct mparse *mp;
673: int fd;
674: struct mdoc *mdoc;
675: struct man *man;
676: void *vp;
677: enum mandoclevel rc;
1.10 kristaps 678: char opts[MAXPATHLEN + 128];
1.7 kristaps 679:
1.8 kristaps 680: if (-1 == (fd = open(file, O_RDONLY, 0))) {
681: resp_baddb();
1.7 kristaps 682: return;
683: }
684:
1.8 kristaps 685: mp = mparse_alloc(MPARSE_AUTO, MANDOCLEVEL_FATAL, NULL, NULL);
686: rc = mparse_readfd(mp, fd, file);
687: close(fd);
1.7 kristaps 688:
1.8 kristaps 689: if (rc >= MANDOCLEVEL_FATAL) {
1.7 kristaps 690: resp_baddb();
691: return;
692: }
693:
1.11 kristaps 694: snprintf(opts, sizeof(opts), "style=/man.css,"
1.10 kristaps 695: "man=%s/search.html?sec=%%S&expr=%%N,"
1.11 kristaps 696: /*"includes=/cgi-bin/man.cgi/usr/include/%%I"*/,
1.10 kristaps 697: progname);
698:
1.8 kristaps 699: mparse_result(mp, &mdoc, &man);
1.10 kristaps 700: vp = html_alloc(opts);
1.7 kristaps 701:
1.8 kristaps 702: if (NULL != mdoc) {
703: resp_begin_http(200, NULL);
704: html_mdoc(vp, mdoc);
705: } else if (NULL != man) {
706: resp_begin_http(200, NULL);
707: html_man(vp, man);
708: } else
709: resp_baddb();
1.7 kristaps 710:
1.8 kristaps 711: html_free(vp);
712: mparse_free(mp);
1.7 kristaps 713: }
714:
715: static void
1.6 kristaps 716: pg_show(const struct manpaths *ps, const struct req *req, char *path)
1.1 kristaps 717: {
1.6 kristaps 718: char *sub;
1.7 kristaps 719: char file[MAXPATHLEN];
1.9 kristaps 720: const char *fn, *cp;
1.6 kristaps 721: int rc;
722: unsigned int vol, rec;
1.9 kristaps 723: DB *idx;
1.6 kristaps 724: DBT key, val;
725:
726: if (NULL == path) {
1.10 kristaps 727: resp_error400();
1.6 kristaps 728: return;
729: } else if (NULL == (sub = strrchr(path, '/'))) {
1.10 kristaps 730: resp_error400();
1.6 kristaps 731: return;
732: } else
733: *sub++ = '\0';
734:
735: if ( ! (atou(path, &vol) && atou(sub, &rec))) {
1.10 kristaps 736: resp_error400();
1.6 kristaps 737: return;
738: } else if (vol >= (unsigned int)ps->sz) {
1.10 kristaps 739: resp_error400();
1.6 kristaps 740: return;
741: }
742:
743: strlcpy(file, ps->paths[vol], MAXPATHLEN);
744: strlcat(file, "/mandoc.index", MAXPATHLEN);
745:
746: /* Open the index recno(3) database. */
747:
1.9 kristaps 748: idx = dbopen(file, O_RDONLY, 0, DB_RECNO, NULL);
749: if (NULL == idx) {
1.6 kristaps 750: resp_baddb();
751: return;
752: }
753:
754: key.data = &rec;
755: key.size = 4;
756:
1.9 kristaps 757: if (0 != (rc = (*idx->get)(idx, &key, &val, 0))) {
1.10 kristaps 758: rc < 0 ? resp_baddb() : resp_error400();
1.9 kristaps 759: goto out;
1.6 kristaps 760: }
761:
1.9 kristaps 762: cp = (char *)val.data;
1.6 kristaps 763:
1.9 kristaps 764: if (NULL == (fn = memchr(cp, '\0', val.size)))
765: resp_baddb();
766: else if (++fn - cp >= (int)val.size)
767: resp_baddb();
768: else if (NULL == memchr(fn, '\0', val.size - (fn - cp)))
769: resp_baddb();
770: else {
1.17 kristaps 771: strlcpy(file, cache, MAXPATHLEN);
1.18 kristaps 772: strlcat(file, "/", MAXPATHLEN);
1.9 kristaps 773: strlcat(file, fn, MAXPATHLEN);
774: if (0 == strcmp(cp, "cat"))
775: catman(file);
776: else
777: format(file);
778: }
779: out:
780: (*idx->close)(idx);
1.6 kristaps 781: }
782:
783: static void
784: pg_search(const struct manpaths *ps, const struct req *req, char *path)
785: {
786: size_t tt;
1.12 kristaps 787: int i, sz, rc, whatis;
1.6 kristaps 788: const char *ep, *start;
789: char **cp;
790: struct opts opt;
791: struct expr *expr;
792:
793: expr = NULL;
794: cp = NULL;
795: ep = NULL;
796: sz = 0;
1.15 kristaps 797: whatis = 1;
1.1 kristaps 798:
799: memset(&opt, 0, sizeof(struct opts));
1.6 kristaps 800:
801: for (sz = i = 0; i < (int)req->fieldsz; i++)
802: if (0 == strcmp(req->fields[i].key, "expr"))
803: ep = req->fields[i].val;
1.13 kristaps 804: else if (0 == strcmp(req->fields[i].key, "query"))
805: ep = req->fields[i].val;
1.6 kristaps 806: else if (0 == strcmp(req->fields[i].key, "sec"))
807: opt.cat = req->fields[i].val;
1.13 kristaps 808: else if (0 == strcmp(req->fields[i].key, "sektion"))
809: opt.cat = req->fields[i].val;
1.6 kristaps 810: else if (0 == strcmp(req->fields[i].key, "arch"))
811: opt.arch = req->fields[i].val;
1.13 kristaps 812: else if (0 == strcmp(req->fields[i].key, "apropos"))
813: whatis = 0 == strcmp
814: (req->fields[i].val, "0");
1.12 kristaps 815: else if (0 == strcmp(req->fields[i].key, "op"))
816: whatis = 0 == strcasecmp
817: (req->fields[i].val, "whatis");
1.13 kristaps 818:
819: if (NULL != opt.cat && 0 == strcmp(opt.cat, "0"))
820: opt.cat = NULL;
1.6 kristaps 821:
822: /*
823: * Poor man's tokenisation.
824: * Just break apart by spaces.
825: * Yes, this is half-ass. But it works for now.
826: */
827:
828: while (ep && isspace((unsigned char)*ep))
829: ep++;
830:
831: while (ep && '\0' != *ep) {
832: cp = mandoc_realloc(cp, (sz + 1) * sizeof(char *));
833: start = ep;
834: while ('\0' != *ep && ! isspace((unsigned char)*ep))
835: ep++;
836: cp[sz] = mandoc_malloc((ep - start) + 1);
837: memcpy(cp[sz], start, ep - start);
838: cp[sz++][ep - start] = '\0';
839: while (isspace((unsigned char)*ep))
840: ep++;
841: }
842:
843: rc = -1;
844:
845: /*
846: * Pump down into apropos backend.
847: * The resp_search() function is called with the results.
848: */
849:
1.12 kristaps 850: expr = whatis ? termcomp(sz, cp, &tt) :
851: exprcomp(sz, cp, &tt);
852:
853: if (NULL != expr)
1.6 kristaps 854: rc = apropos_search
855: (ps->sz, ps->paths, &opt,
856: expr, tt, (void *)req, resp_search);
857:
858: /* ...unless errors occured. */
859:
860: if (0 == rc)
861: resp_baddb();
862: else if (-1 == rc)
1.10 kristaps 863: resp_search(NULL, 0, (void *)req);
1.6 kristaps 864:
865: for (i = 0; i < sz; i++)
866: free(cp[i]);
867:
868: free(cp);
869: exprfree(expr);
1.1 kristaps 870: }
871:
872: int
873: main(void)
874: {
875: int i;
876: struct req req;
1.6 kristaps 877: char *p, *path, *subpath;
878: struct manpaths paths;
879:
880: /* HTTP init: read and parse the query string. */
881:
882: progname = getenv("SCRIPT_NAME");
883: if (NULL == progname)
884: progname = "";
885:
1.7 kristaps 886: cache = getenv("CACHE_DIR");
887: if (NULL == cache)
888: cache = "/cache/man.cgi";
889:
1.8 kristaps 890: if (-1 == chdir(cache)) {
891: resp_bad();
892: return(EXIT_FAILURE);
1.7 kristaps 893: }
894:
1.6 kristaps 895: host = getenv("HTTP_HOST");
896: if (NULL == host)
897: host = "localhost";
1.1 kristaps 898:
899: memset(&req, 0, sizeof(struct req));
900:
901: if (NULL != (p = getenv("QUERY_STRING")))
902: kval_parse(&req.fields, &req.fieldsz, p);
903:
1.6 kristaps 904: /* Resolve leading subpath component. */
1.1 kristaps 905:
1.6 kristaps 906: subpath = path = NULL;
1.1 kristaps 907: req.page = PAGE__MAX;
908:
909: if (NULL == (path = getenv("PATH_INFO")) || '\0' == *path)
910: req.page = PAGE_INDEX;
1.6 kristaps 911:
1.1 kristaps 912: if (NULL != path && '/' == *path && '\0' == *++path)
913: req.page = PAGE_INDEX;
914:
1.6 kristaps 915: /* Strip file suffix. */
916:
917: if (NULL != path && NULL != (p = strrchr(path, '.')))
918: if (NULL != p && NULL == strchr(p, '/'))
919: *p++ = '\0';
920:
921: /* Resolve subpath component. */
1.1 kristaps 922:
923: if (NULL != path && NULL != (subpath = strchr(path, '/')))
1.6 kristaps 924: *subpath++ = '\0';
1.1 kristaps 925:
1.6 kristaps 926: /* Map path into one we recognise. */
1.1 kristaps 927:
928: if (NULL != path && '\0' != *path)
929: for (i = 0; i < (int)PAGE__MAX; i++)
930: if (0 == strcmp(pages[i], path)) {
931: req.page = (enum page)i;
932: break;
933: }
934:
1.6 kristaps 935: /* Initialise MANPATH. */
936:
937: memset(&paths, 0, sizeof(struct manpaths));
1.9 kristaps 938: manpath_manconf("etc/catman.conf", &paths);
1.6 kristaps 939:
940: /* Route pages. */
941:
1.1 kristaps 942: switch (req.page) {
943: case (PAGE_INDEX):
1.6 kristaps 944: pg_index(&paths, &req, subpath);
1.1 kristaps 945: break;
946: case (PAGE_SEARCH):
1.6 kristaps 947: pg_search(&paths, &req, subpath);
948: break;
949: case (PAGE_SHOW):
950: pg_show(&paths, &req, subpath);
1.1 kristaps 951: break;
952: default:
1.10 kristaps 953: resp_error404(path);
1.1 kristaps 954: break;
955: }
956:
1.6 kristaps 957: manpath_free(&paths);
1.1 kristaps 958: kval_free(req.fields, req.fieldsz);
1.6 kristaps 959:
1.1 kristaps 960: return(EXIT_SUCCESS);
961: }
1.15 kristaps 962:
963: static int
964: cmp(const void *p1, const void *p2)
965: {
966:
967: return(strcasecmp(((const struct res *)p1)->title,
968: ((const struct res *)p2)->title));
969: }
970:
CVSweb