Annotation of mandoc/cgi.c, Revision 1.14
1.14 ! kristaps 1: /* $Id: cgi.c,v 1.13 2011/12/07 15:12:34 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.8 kristaps 70: static void format(const char *);
1.6 kristaps 71: static void html_print(const char *);
1.10 kristaps 72: static void html_putchar(char);
1.1 kristaps 73: static int kval_decode(char *);
74: static void kval_parse(struct kval **, size_t *, char *);
75: static void kval_free(struct kval *, size_t);
1.6 kristaps 76: static void pg_index(const struct manpaths *,
77: const struct req *, char *);
78: static void pg_search(const struct manpaths *,
79: const struct req *, char *);
80: static void pg_show(const struct manpaths *,
81: const struct req *, char *);
1.7 kristaps 82: static void resp_bad(void);
1.6 kristaps 83: static void resp_baddb(void);
1.10 kristaps 84: static void resp_error400(void);
85: static void resp_error404(const char *);
1.6 kristaps 86: static void resp_begin_html(int, const char *);
87: static void resp_begin_http(int, const char *);
88: static void resp_end_html(void);
89: static void resp_index(const struct req *);
90: static void resp_search(struct res *, size_t, void *);
91: static void resp_searchform(const struct req *);
92:
93: static const char *progname;
1.7 kristaps 94: static const char *cache;
1.6 kristaps 95: static const char *host;
1.1 kristaps 96:
97: static const char * const pages[PAGE__MAX] = {
98: "index", /* PAGE_INDEX */
99: "search", /* PAGE_SEARCH */
1.6 kristaps 100: "show", /* PAGE_SHOW */
1.1 kristaps 101: };
102:
1.6 kristaps 103: /*
104: * This is just OpenBSD's strtol(3) suggestion.
105: * I use it instead of strtonum(3) for portability's sake.
106: */
107: static int
108: atou(const char *buf, unsigned *v)
109: {
110: char *ep;
111: long lval;
112:
113: errno = 0;
114: lval = strtol(buf, &ep, 10);
115: if (buf[0] == '\0' || *ep != '\0')
116: return(0);
117: if ((errno == ERANGE && (lval == LONG_MAX ||
118: lval == LONG_MIN)) ||
119: (lval > UINT_MAX || lval < 0))
120: return(0);
121:
122: *v = (unsigned int)lval;
123: return(1);
124: }
1.1 kristaps 125:
1.10 kristaps 126: static void
127: html_putchar(char c)
128: {
129:
130: switch (c) {
131: case ('"'):
132: printf(""e;");
133: break;
134: case ('&'):
135: printf("&");
136: break;
137: case ('>'):
138: printf(">");
139: break;
140: case ('<'):
141: printf("<");
142: break;
143: default:
144: putchar((unsigned char)c);
145: break;
146: }
147: }
148:
1.6 kristaps 149: /*
150: * Print a word, escaping HTML along the way.
151: * This will pass non-ASCII straight to output: be warned!
152: */
1.1 kristaps 153: static void
1.6 kristaps 154: html_print(const char *p)
1.1 kristaps 155: {
1.6 kristaps 156:
157: if (NULL == p)
158: return;
1.1 kristaps 159: while ('\0' != *p)
1.10 kristaps 160: html_putchar(*p++);
1.1 kristaps 161: }
162:
163: static void
164: kval_free(struct kval *p, size_t sz)
165: {
166: int i;
167:
168: for (i = 0; i < (int)sz; i++) {
169: free(p[i].key);
170: free(p[i].val);
171: }
172: free(p);
173: }
174:
175: /*
176: * Parse out key-value pairs from an HTTP request variable.
1.6 kristaps 177: * This can be either a cookie or a POST/GET string, although man.cgi
178: * uses only GET for simplicity.
1.1 kristaps 179: */
180: static void
181: kval_parse(struct kval **kv, size_t *kvsz, char *p)
182: {
183: char *key, *val;
184: size_t sz, cur;
185:
186: cur = 0;
187:
188: while (p && '\0' != *p) {
189: while (' ' == *p)
190: p++;
191:
192: key = p;
193: val = NULL;
194:
195: if (NULL != (p = strchr(p, '='))) {
196: *p++ = '\0';
197: val = p;
198:
199: sz = strcspn(p, ";&");
200: /* LINTED */
201: p += sz;
202:
203: if ('\0' != *p)
204: *p++ = '\0';
205: } else {
206: p = key;
207: sz = strcspn(p, ";&");
208: /* LINTED */
209: p += sz;
210:
211: if ('\0' != *p)
212: p++;
213: continue;
214: }
215:
216: if ('\0' == *key || '\0' == *val)
217: continue;
218:
219: /* Just abort handling. */
220:
221: if ( ! kval_decode(key))
222: return;
223: if ( ! kval_decode(val))
224: return;
225:
226: if (*kvsz + 1 >= cur) {
227: cur++;
228: *kv = mandoc_realloc
229: (*kv, cur * sizeof(struct kval));
230: }
231:
232: (*kv)[(int)*kvsz].key = mandoc_strdup(key);
233: (*kv)[(int)*kvsz].val = mandoc_strdup(val);
234: (*kvsz)++;
235: }
236: }
237:
238: /*
1.6 kristaps 239: * HTTP-decode a string. The standard explanation is that this turns
240: * "%4e+foo" into "n foo" in the regular way. This is done in-place
241: * over the allocated string.
1.1 kristaps 242: */
243: static int
244: kval_decode(char *p)
245: {
246: char hex[3];
247: int c;
248:
249: hex[2] = '\0';
250:
251: for ( ; '\0' != *p; p++) {
252: if ('%' == *p) {
253: if ('\0' == (hex[0] = *(p + 1)))
254: return(0);
255: if ('\0' == (hex[1] = *(p + 2)))
256: return(0);
257: if (1 != sscanf(hex, "%x", &c))
258: return(0);
259: if ('\0' == c)
260: return(0);
261:
262: *p = (char)c;
263: memmove(p + 1, p + 3, strlen(p + 3) + 1);
264: } else
265: *p = '+' == *p ? ' ' : *p;
266: }
267:
268: *p = '\0';
269: return(1);
270: }
271:
1.6 kristaps 272: static void
273: resp_begin_http(int code, const char *msg)
274: {
275:
276: if (200 != code)
277: printf("Status: %d %s\n", code, msg);
278:
279: puts("Content-Type: text/html; charset=utf-8" "\n"
280: "Cache-Control: no-cache" "\n"
281: "Pragma: no-cache" "\n"
282: "");
283:
284: fflush(stdout);
285: }
286:
287: static void
288: resp_begin_html(int code, const char *msg)
289: {
290:
291: resp_begin_http(code, msg);
292:
293: puts("<!DOCTYPE HTML PUBLIC " "\n"
294: " \"-//W3C//DTD HTML 4.01//EN\"" "\n"
295: " \"http://www.w3.org/TR/html4/strict.dtd\">" "\n"
296: "<HTML>" "\n"
297: " <HEAD>" "\n"
1.14 ! kristaps 298: " <META HTTP-EQUIV=\"Content-Type\" " "\n"
! 299: " CONTENT=\"text/html; charset=utf-8\">" "\n"
1.12 kristaps 300: " <LINK REL=\"stylesheet\" HREF=\"/man.cgi.css\"" "\n"
301: " TYPE=\"text/css\" media=\"all\">" "\n"
1.6 kristaps 302: " <TITLE>System Manpage Reference</TITLE>" "\n"
303: " </HEAD>" "\n"
304: " <BODY>" "\n"
305: "<!-- Begin page content. //-->");
306: }
307:
308: static void
309: resp_end_html(void)
310: {
311:
312: puts(" </BODY>\n</HTML>");
313: }
314:
315: static void
316: resp_searchform(const struct req *req)
317: {
318: int i;
319: const char *expr, *sec, *arch;
320:
321: expr = sec = arch = "";
322:
323: for (i = 0; i < (int)req->fieldsz; i++)
324: if (0 == strcmp(req->fields[i].key, "expr"))
325: expr = req->fields[i].val;
1.13 kristaps 326: else if (0 == strcmp(req->fields[i].key, "query"))
327: expr = req->fields[i].val;
1.6 kristaps 328: else if (0 == strcmp(req->fields[i].key, "sec"))
329: sec = req->fields[i].val;
1.13 kristaps 330: else if (0 == strcmp(req->fields[i].key, "sektion"))
331: sec = req->fields[i].val;
1.6 kristaps 332: else if (0 == strcmp(req->fields[i].key, "arch"))
333: arch = req->fields[i].val;
334:
1.13 kristaps 335: if (NULL != sec && 0 == strcmp(sec, "0"))
336: sec = NULL;
337:
1.6 kristaps 338: puts("<!-- Begin search form. //-->");
339: printf("<FORM ACTION=\"");
340: html_print(progname);
1.9 kristaps 341: printf("/search.html\" METHOD=\"get\">\n");
1.14 ! kristaps 342: printf("<FIELDSET>\n"
! 343: "<INPUT TYPE=\"submit\" NAME=\"op\" "
! 344: "VALUE=\"Whatis\"> or \n"
! 345: "<INPUT TYPE=\"submit\" NAME=\"op\" "
! 346: "VALUE=\"apropos\"> for manuals satisfying \n"
! 347: "<INPUT TYPE=\"text\" NAME=\"expr\" VALUE=\"");
1.6 kristaps 348: html_print(expr);
1.14 ! kristaps 349: printf("\">, section "
! 350: "<INPUT TYPE=\"text\" "
! 351: "SIZE=\"4\" NAME=\"sec\" VALUE=\"");
1.6 kristaps 352: html_print(sec);
1.14 ! kristaps 353: printf("\">, arch "
! 354: "<INPUT TYPE=\"text\" "
! 355: "SIZE=\"8\" NAME=\"arch\" VALUE=\"");
1.6 kristaps 356: html_print(arch);
1.12 kristaps 357: puts("\">.\n"
358: "<INPUT TYPE=\"reset\" VALUE=\"Reset\">\n"
359: "</FIELDSET>\n"
360: "</FORM>\n"
361: "<!-- End search form. //-->");
1.6 kristaps 362: }
363:
364: static void
365: resp_index(const struct req *req)
366: {
367:
368: resp_begin_html(200, NULL);
369: resp_searchform(req);
370: resp_end_html();
371: }
372:
373: static void
1.10 kristaps 374: resp_error400(void)
1.9 kristaps 375: {
376:
1.10 kristaps 377: resp_begin_html(400, "Query Malformed");
1.12 kristaps 378: printf("<H1>Malformed Query</H1>\n"
379: "<P>\n"
380: " The query your entered was malformed.\n"
381: " Try again from the\n"
382: " <A HREF=\"%s/index.html\">main page</A>\n"
383: "</P>", progname);
1.9 kristaps 384: resp_end_html();
385: }
386:
387: static void
1.10 kristaps 388: resp_error404(const char *page)
1.6 kristaps 389: {
390:
391: resp_begin_html(404, "Not Found");
1.10 kristaps 392: puts("<H1>Page Not Found</H1>\n"
393: "<P>\n"
394: " The page you're looking for, ");
395: printf(" <B>");
396: html_print(page);
1.12 kristaps 397: printf("</B>,\n"
398: " could not be found.\n"
399: " Try searching from the\n"
400: " <A HREF=\"%s/index.html\">main page</A>\n"
401: "</P>", progname);
1.6 kristaps 402: resp_end_html();
403: }
1.1 kristaps 404:
405: static void
1.7 kristaps 406: resp_bad(void)
407: {
408: resp_begin_html(500, "Internal Server Error");
409: puts("<P>Generic badness happened.</P>");
410: resp_end_html();
411: }
412:
413: static void
1.6 kristaps 414: resp_baddb(void)
1.1 kristaps 415: {
416:
1.6 kristaps 417: resp_begin_html(500, "Internal Server Error");
418: puts("<P>Your database is broken.</P>");
419: resp_end_html();
1.1 kristaps 420: }
421:
422: static void
1.6 kristaps 423: resp_search(struct res *r, size_t sz, void *arg)
1.1 kristaps 424: {
425: int i;
1.14 ! kristaps 426: char *cp;
1.1 kristaps 427:
1.6 kristaps 428: if (1 == sz) {
429: /*
430: * If we have just one result, then jump there now
431: * without any delay.
432: */
433: puts("Status: 303 See Other");
434: printf("Location: http://%s%s/show/%u/%u.html\n",
435: host, progname,
436: r[0].volume, r[0].rec);
437: puts("Content-Type: text/html; charset=utf-8\n");
438: return;
439: }
440:
1.12 kristaps 441: resp_begin_html(200, NULL);
1.6 kristaps 442: resp_searchform((const struct req *)arg);
443:
1.14 ! kristaps 444: if (0 == sz) {
1.6 kristaps 445: puts("<P>No results found.</P>");
1.14 ! kristaps 446: resp_end_html();
! 447: return;
! 448: }
! 449:
! 450: puts("<P></P>\n"
! 451: "<TABLE>");
1.1 kristaps 452:
453: for (i = 0; i < (int)sz; i++) {
1.14 ! kristaps 454: printf("<TR><TD CLASS=\"title\"><A HREF=\"");
1.6 kristaps 455: html_print(progname);
456: printf("/show/%u/%u.html\">", r[i].volume, r[i].rec);
1.14 ! kristaps 457: for (cp = r[i].title; '\0' != *cp; cp++)
! 458: html_putchar(toupper((unsigned char)*cp));
1.1 kristaps 459: putchar('(');
1.6 kristaps 460: html_print(r[i].cat);
461: if (r[i].arch && '\0' != *r[i].arch) {
462: putchar('/');
463: html_print(r[i].arch);
464: }
1.14 ! kristaps 465: printf(")</A></TD><TD CLASS=\"desc\">");
1.6 kristaps 466: html_print(r[i].desc);
1.14 ! kristaps 467: puts("</TD></TR>");
1.1 kristaps 468: }
1.6 kristaps 469:
470: resp_end_html();
471: }
472:
473: /* ARGSUSED */
474: static void
475: pg_index(const struct manpaths *ps, const struct req *req, char *path)
476: {
477:
478: resp_index(req);
1.1 kristaps 479: }
480:
481: static void
1.9 kristaps 482: catman(const char *file)
483: {
1.10 kristaps 484: FILE *f;
485: size_t len;
486: int i;
487: char *p;
488: int italic, bold;
1.9 kristaps 489:
1.10 kristaps 490: if (NULL == (f = fopen(file, "r"))) {
1.9 kristaps 491: resp_baddb();
492: return;
493: }
494:
1.12 kristaps 495: resp_begin_http(200, NULL);
496: puts("<!DOCTYPE HTML PUBLIC " "\n"
497: " \"-//W3C//DTD HTML 4.01//EN\"" "\n"
498: " \"http://www.w3.org/TR/html4/strict.dtd\">" "\n"
499: "<HTML>" "\n"
500: " <HEAD>" "\n"
501: " <META HTTP-EQUIV=\"Content-Type\" " "\n"
502: " CONTENT=\"text/html; charset=utf-8\">" "\n"
503: " <LINK REL=\"stylesheet\" HREF=\"/catman.css\"" "\n"
504: " TYPE=\"text/css\" media=\"all\">" "\n"
505: " <TITLE>System Manpage Reference</TITLE>" "\n"
506: " </HEAD>" "\n"
507: " <BODY>" "\n"
508: "<!-- Begin page content. //-->");
1.10 kristaps 509:
510: puts("<PRE>");
511: while (NULL != (p = fgetln(f, &len))) {
512: bold = italic = 0;
513: for (i = 0; i < (int)len - 1; i++) {
514: /*
515: * This means that the catpage is out of state.
516: * Ignore it and keep going (although the
517: * catpage is bogus).
518: */
519:
520: if ('\b' == p[i] || '\n' == p[i])
521: continue;
522:
523: /*
524: * Print a regular character.
525: * Close out any bold/italic scopes.
526: * If we're in back-space mode, make sure we'll
527: * have something to enter when we backspace.
528: */
529:
530: if ('\b' != p[i + 1]) {
531: if (italic)
532: printf("</I>");
533: if (bold)
534: printf("</B>");
535: italic = bold = 0;
536: html_putchar(p[i]);
537: continue;
538: } else if (i + 2 >= (int)len)
539: continue;
540:
541: /* Italic mode. */
542:
543: if ('_' == p[i]) {
544: if (bold)
545: printf("</B>");
546: if ( ! italic)
547: printf("<I>");
548: bold = 0;
549: italic = 1;
550: i += 2;
551: html_putchar(p[i]);
552: continue;
553: }
554:
555: /*
556: * Handle funny behaviour troff-isms.
557: * These grok'd from the original man2html.c.
558: */
559:
560: if (('+' == p[i] && 'o' == p[i + 2]) ||
561: ('o' == p[i] && '+' == p[i + 2]) ||
562: ('|' == p[i] && '=' == p[i + 2]) ||
563: ('=' == p[i] && '|' == p[i + 2]) ||
564: ('*' == p[i] && '=' == p[i + 2]) ||
565: ('=' == p[i] && '*' == p[i + 2]) ||
566: ('*' == p[i] && '|' == p[i + 2]) ||
567: ('|' == p[i] && '*' == p[i + 2])) {
568: if (italic)
569: printf("</I>");
570: if (bold)
571: printf("</B>");
572: italic = bold = 0;
573: putchar('*');
574: i += 2;
575: continue;
576: } else if (('|' == p[i] && '-' == p[i + 2]) ||
577: ('-' == p[i] && '|' == p[i + 1]) ||
578: ('+' == p[i] && '-' == p[i + 1]) ||
579: ('-' == p[i] && '+' == p[i + 1]) ||
580: ('+' == p[i] && '|' == p[i + 1]) ||
581: ('|' == p[i] && '+' == p[i + 1])) {
582: if (italic)
583: printf("</I>");
584: if (bold)
585: printf("</B>");
586: italic = bold = 0;
587: putchar('+');
588: i += 2;
589: continue;
590: }
591:
592: /* Bold mode. */
593:
594: if (italic)
595: printf("</I>");
596: if ( ! bold)
597: printf("<B>");
598: bold = 1;
599: italic = 0;
600: i += 2;
601: html_putchar(p[i]);
602: }
603:
604: /*
605: * Clean up the last character.
606: * We can get to a newline; don't print that.
607: */
1.9 kristaps 608:
1.10 kristaps 609: if (italic)
610: printf("</I>");
611: if (bold)
612: printf("</B>");
1.9 kristaps 613:
1.10 kristaps 614: if (i == (int)len - 1 && '\n' != p[i])
615: html_putchar(p[i]);
1.9 kristaps 616:
1.10 kristaps 617: putchar('\n');
618: }
619:
620: puts("</PRE>\n"
621: "</BODY>\n"
622: "</HTML>");
623:
624: fclose(f);
1.9 kristaps 625: }
626:
627: static void
1.8 kristaps 628: format(const char *file)
1.7 kristaps 629: {
1.8 kristaps 630: struct mparse *mp;
631: int fd;
632: struct mdoc *mdoc;
633: struct man *man;
634: void *vp;
635: enum mandoclevel rc;
1.10 kristaps 636: char opts[MAXPATHLEN + 128];
1.7 kristaps 637:
1.8 kristaps 638: if (-1 == (fd = open(file, O_RDONLY, 0))) {
639: resp_baddb();
1.7 kristaps 640: return;
641: }
642:
1.8 kristaps 643: mp = mparse_alloc(MPARSE_AUTO, MANDOCLEVEL_FATAL, NULL, NULL);
644: rc = mparse_readfd(mp, fd, file);
645: close(fd);
1.7 kristaps 646:
1.8 kristaps 647: if (rc >= MANDOCLEVEL_FATAL) {
1.7 kristaps 648: resp_baddb();
649: return;
650: }
651:
1.11 kristaps 652: snprintf(opts, sizeof(opts), "style=/man.css,"
1.10 kristaps 653: "man=%s/search.html?sec=%%S&expr=%%N,"
1.11 kristaps 654: /*"includes=/cgi-bin/man.cgi/usr/include/%%I"*/,
1.10 kristaps 655: progname);
656:
1.8 kristaps 657: mparse_result(mp, &mdoc, &man);
1.10 kristaps 658: vp = html_alloc(opts);
1.7 kristaps 659:
1.8 kristaps 660: if (NULL != mdoc) {
661: resp_begin_http(200, NULL);
662: html_mdoc(vp, mdoc);
663: } else if (NULL != man) {
664: resp_begin_http(200, NULL);
665: html_man(vp, man);
666: } else
667: resp_baddb();
1.7 kristaps 668:
1.8 kristaps 669: html_free(vp);
670: mparse_free(mp);
1.7 kristaps 671: }
672:
673: static void
1.6 kristaps 674: pg_show(const struct manpaths *ps, const struct req *req, char *path)
1.1 kristaps 675: {
1.6 kristaps 676: char *sub;
1.7 kristaps 677: char file[MAXPATHLEN];
1.9 kristaps 678: const char *fn, *cp;
1.6 kristaps 679: int rc;
680: unsigned int vol, rec;
1.9 kristaps 681: DB *idx;
1.6 kristaps 682: DBT key, val;
683:
684: if (NULL == path) {
1.10 kristaps 685: resp_error400();
1.6 kristaps 686: return;
687: } else if (NULL == (sub = strrchr(path, '/'))) {
1.10 kristaps 688: resp_error400();
1.6 kristaps 689: return;
690: } else
691: *sub++ = '\0';
692:
693: if ( ! (atou(path, &vol) && atou(sub, &rec))) {
1.10 kristaps 694: resp_error400();
1.6 kristaps 695: return;
696: } else if (vol >= (unsigned int)ps->sz) {
1.10 kristaps 697: resp_error400();
1.6 kristaps 698: return;
699: }
700:
701: strlcpy(file, ps->paths[vol], MAXPATHLEN);
702: strlcat(file, "/mandoc.index", MAXPATHLEN);
703:
704: /* Open the index recno(3) database. */
705:
1.9 kristaps 706: idx = dbopen(file, O_RDONLY, 0, DB_RECNO, NULL);
707: if (NULL == idx) {
1.6 kristaps 708: resp_baddb();
709: return;
710: }
711:
712: key.data = &rec;
713: key.size = 4;
714:
1.9 kristaps 715: if (0 != (rc = (*idx->get)(idx, &key, &val, 0))) {
1.10 kristaps 716: rc < 0 ? resp_baddb() : resp_error400();
1.9 kristaps 717: goto out;
1.6 kristaps 718: }
719:
1.9 kristaps 720: cp = (char *)val.data;
1.6 kristaps 721:
1.9 kristaps 722: if (NULL == (fn = memchr(cp, '\0', val.size)))
723: resp_baddb();
724: else if (++fn - cp >= (int)val.size)
725: resp_baddb();
726: else if (NULL == memchr(fn, '\0', val.size - (fn - cp)))
727: resp_baddb();
728: else {
729: strlcpy(file, ps->paths[vol], MAXPATHLEN);
730: strlcat(file, "/", MAXPATHLEN);
731: strlcat(file, fn, MAXPATHLEN);
732: if (0 == strcmp(cp, "cat"))
733: catman(file);
734: else
735: format(file);
736: }
737: out:
738: (*idx->close)(idx);
1.6 kristaps 739: }
740:
741: static void
742: pg_search(const struct manpaths *ps, const struct req *req, char *path)
743: {
744: size_t tt;
1.12 kristaps 745: int i, sz, rc, whatis;
1.6 kristaps 746: const char *ep, *start;
747: char **cp;
748: struct opts opt;
749: struct expr *expr;
750:
751: expr = NULL;
752: cp = NULL;
753: ep = NULL;
754: sz = 0;
1.12 kristaps 755: whatis = 0;
1.1 kristaps 756:
757: memset(&opt, 0, sizeof(struct opts));
1.6 kristaps 758:
759: for (sz = i = 0; i < (int)req->fieldsz; i++)
760: if (0 == strcmp(req->fields[i].key, "expr"))
761: ep = req->fields[i].val;
1.13 kristaps 762: else if (0 == strcmp(req->fields[i].key, "query"))
763: ep = req->fields[i].val;
1.6 kristaps 764: else if (0 == strcmp(req->fields[i].key, "sec"))
765: opt.cat = req->fields[i].val;
1.13 kristaps 766: else if (0 == strcmp(req->fields[i].key, "sektion"))
767: opt.cat = req->fields[i].val;
1.6 kristaps 768: else if (0 == strcmp(req->fields[i].key, "arch"))
769: opt.arch = req->fields[i].val;
1.13 kristaps 770: else if (0 == strcmp(req->fields[i].key, "apropos"))
771: whatis = 0 == strcmp
772: (req->fields[i].val, "0");
1.12 kristaps 773: else if (0 == strcmp(req->fields[i].key, "op"))
774: whatis = 0 == strcasecmp
775: (req->fields[i].val, "whatis");
1.13 kristaps 776:
777: if (NULL != opt.cat && 0 == strcmp(opt.cat, "0"))
778: opt.cat = NULL;
1.6 kristaps 779:
780: /*
781: * Poor man's tokenisation.
782: * Just break apart by spaces.
783: * Yes, this is half-ass. But it works for now.
784: */
785:
786: while (ep && isspace((unsigned char)*ep))
787: ep++;
788:
789: while (ep && '\0' != *ep) {
790: cp = mandoc_realloc(cp, (sz + 1) * sizeof(char *));
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:
801: rc = -1;
802:
803: /*
804: * Pump down into apropos backend.
805: * The resp_search() function is called with the results.
806: */
807:
1.12 kristaps 808: expr = whatis ? termcomp(sz, cp, &tt) :
809: exprcomp(sz, cp, &tt);
810:
811: if (NULL != expr)
1.6 kristaps 812: rc = apropos_search
813: (ps->sz, ps->paths, &opt,
814: expr, tt, (void *)req, resp_search);
815:
816: /* ...unless errors occured. */
817:
818: if (0 == rc)
819: resp_baddb();
820: else if (-1 == rc)
1.10 kristaps 821: resp_search(NULL, 0, (void *)req);
1.6 kristaps 822:
823: for (i = 0; i < sz; i++)
824: free(cp[i]);
825:
826: free(cp);
827: exprfree(expr);
1.1 kristaps 828: }
829:
830: int
831: main(void)
832: {
833: int i;
834: struct req req;
1.6 kristaps 835: char *p, *path, *subpath;
836: struct manpaths paths;
837:
838: /* HTTP init: read and parse the query string. */
839:
840: progname = getenv("SCRIPT_NAME");
841: if (NULL == progname)
842: progname = "";
843:
1.7 kristaps 844: cache = getenv("CACHE_DIR");
845: if (NULL == cache)
846: cache = "/cache/man.cgi";
847:
1.8 kristaps 848: if (-1 == chdir(cache)) {
849: resp_bad();
850: return(EXIT_FAILURE);
1.7 kristaps 851: }
852:
1.6 kristaps 853: host = getenv("HTTP_HOST");
854: if (NULL == host)
855: host = "localhost";
1.1 kristaps 856:
857: memset(&req, 0, sizeof(struct req));
858:
859: if (NULL != (p = getenv("QUERY_STRING")))
860: kval_parse(&req.fields, &req.fieldsz, p);
861:
1.6 kristaps 862: /* Resolve leading subpath component. */
1.1 kristaps 863:
1.6 kristaps 864: subpath = path = NULL;
1.1 kristaps 865: req.page = PAGE__MAX;
866:
867: if (NULL == (path = getenv("PATH_INFO")) || '\0' == *path)
868: req.page = PAGE_INDEX;
1.6 kristaps 869:
1.1 kristaps 870: if (NULL != path && '/' == *path && '\0' == *++path)
871: req.page = PAGE_INDEX;
872:
1.6 kristaps 873: /* Strip file suffix. */
874:
875: if (NULL != path && NULL != (p = strrchr(path, '.')))
876: if (NULL != p && NULL == strchr(p, '/'))
877: *p++ = '\0';
878:
879: /* Resolve subpath component. */
1.1 kristaps 880:
881: if (NULL != path && NULL != (subpath = strchr(path, '/')))
1.6 kristaps 882: *subpath++ = '\0';
1.1 kristaps 883:
1.6 kristaps 884: /* Map path into one we recognise. */
1.1 kristaps 885:
886: if (NULL != path && '\0' != *path)
887: for (i = 0; i < (int)PAGE__MAX; i++)
888: if (0 == strcmp(pages[i], path)) {
889: req.page = (enum page)i;
890: break;
891: }
892:
1.6 kristaps 893: /* Initialise MANPATH. */
894:
895: memset(&paths, 0, sizeof(struct manpaths));
1.9 kristaps 896: manpath_manconf("etc/catman.conf", &paths);
1.6 kristaps 897:
898: /* Route pages. */
899:
1.1 kristaps 900: switch (req.page) {
901: case (PAGE_INDEX):
1.6 kristaps 902: pg_index(&paths, &req, subpath);
1.1 kristaps 903: break;
904: case (PAGE_SEARCH):
1.6 kristaps 905: pg_search(&paths, &req, subpath);
906: break;
907: case (PAGE_SHOW):
908: pg_show(&paths, &req, subpath);
1.1 kristaps 909: break;
910: default:
1.10 kristaps 911: resp_error404(path);
1.1 kristaps 912: break;
913: }
914:
1.6 kristaps 915: manpath_free(&paths);
1.1 kristaps 916: kval_free(req.fields, req.fieldsz);
1.6 kristaps 917:
1.1 kristaps 918: return(EXIT_SUCCESS);
919: }
CVSweb