Annotation of mandoc/cgi.c, Revision 1.15
1.15 ! kristaps 1: /* $Id: cgi.c,v 1.14 2011/12/07 15:55:06 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"
344: "<INPUT TYPE=\"submit\" NAME=\"op\" "
345: "VALUE=\"Whatis\"> or \n"
346: "<INPUT TYPE=\"submit\" NAME=\"op\" "
347: "VALUE=\"apropos\"> for manuals satisfying \n"
348: "<INPUT TYPE=\"text\" NAME=\"expr\" VALUE=\"");
1.6 kristaps 349: html_print(expr);
1.14 kristaps 350: printf("\">, section "
351: "<INPUT TYPE=\"text\" "
352: "SIZE=\"4\" NAME=\"sec\" VALUE=\"");
1.6 kristaps 353: html_print(sec);
1.14 kristaps 354: printf("\">, arch "
355: "<INPUT TYPE=\"text\" "
356: "SIZE=\"8\" NAME=\"arch\" VALUE=\"");
1.6 kristaps 357: html_print(arch);
1.12 kristaps 358: puts("\">.\n"
359: "<INPUT TYPE=\"reset\" VALUE=\"Reset\">\n"
360: "</FIELDSET>\n"
361: "</FORM>\n"
362: "<!-- End search form. //-->");
1.6 kristaps 363: }
364:
365: static void
366: resp_index(const struct req *req)
367: {
368:
369: resp_begin_html(200, NULL);
370: resp_searchform(req);
371: resp_end_html();
372: }
373:
374: static void
1.10 kristaps 375: resp_error400(void)
1.9 kristaps 376: {
377:
1.10 kristaps 378: resp_begin_html(400, "Query Malformed");
1.12 kristaps 379: printf("<H1>Malformed Query</H1>\n"
380: "<P>\n"
381: " The query your entered was malformed.\n"
382: " Try again from the\n"
383: " <A HREF=\"%s/index.html\">main page</A>\n"
384: "</P>", progname);
1.9 kristaps 385: resp_end_html();
386: }
387:
388: static void
1.10 kristaps 389: resp_error404(const char *page)
1.6 kristaps 390: {
391:
392: resp_begin_html(404, "Not Found");
1.10 kristaps 393: puts("<H1>Page Not Found</H1>\n"
394: "<P>\n"
395: " The page you're looking for, ");
396: printf(" <B>");
397: html_print(page);
1.12 kristaps 398: printf("</B>,\n"
399: " could not be found.\n"
400: " Try searching from the\n"
401: " <A HREF=\"%s/index.html\">main page</A>\n"
402: "</P>", progname);
1.6 kristaps 403: resp_end_html();
404: }
1.1 kristaps 405:
406: static void
1.7 kristaps 407: resp_bad(void)
408: {
409: resp_begin_html(500, "Internal Server Error");
410: puts("<P>Generic badness happened.</P>");
411: resp_end_html();
412: }
413:
414: static void
1.6 kristaps 415: resp_baddb(void)
1.1 kristaps 416: {
417:
1.6 kristaps 418: resp_begin_html(500, "Internal Server Error");
419: puts("<P>Your database is broken.</P>");
420: resp_end_html();
1.1 kristaps 421: }
422:
423: static void
1.6 kristaps 424: resp_search(struct res *r, size_t sz, void *arg)
1.1 kristaps 425: {
426: int i;
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.15 ! kristaps 441: qsort(r, sz, sizeof(struct res), cmp);
! 442:
1.12 kristaps 443: resp_begin_html(200, NULL);
1.6 kristaps 444: resp_searchform((const struct req *)arg);
445:
1.14 kristaps 446: if (0 == sz) {
1.6 kristaps 447: puts("<P>No results found.</P>");
1.14 kristaps 448: resp_end_html();
449: return;
450: }
451:
452: puts("<P></P>\n"
453: "<TABLE>");
1.1 kristaps 454:
455: for (i = 0; i < (int)sz; i++) {
1.14 kristaps 456: printf("<TR><TD CLASS=\"title\"><A HREF=\"");
1.6 kristaps 457: html_print(progname);
458: printf("/show/%u/%u.html\">", r[i].volume, r[i].rec);
1.15 ! kristaps 459: html_print(r[i].title);
1.1 kristaps 460: putchar('(');
1.6 kristaps 461: html_print(r[i].cat);
462: if (r[i].arch && '\0' != *r[i].arch) {
463: putchar('/');
464: html_print(r[i].arch);
465: }
1.14 kristaps 466: printf(")</A></TD><TD CLASS=\"desc\">");
1.6 kristaps 467: html_print(r[i].desc);
1.14 kristaps 468: puts("</TD></TR>");
1.1 kristaps 469: }
1.6 kristaps 470:
471: resp_end_html();
472: }
473:
474: /* ARGSUSED */
475: static void
476: pg_index(const struct manpaths *ps, const struct req *req, char *path)
477: {
478:
479: resp_index(req);
1.1 kristaps 480: }
481:
482: static void
1.9 kristaps 483: catman(const char *file)
484: {
1.10 kristaps 485: FILE *f;
486: size_t len;
487: int i;
488: char *p;
489: int italic, bold;
1.9 kristaps 490:
1.10 kristaps 491: if (NULL == (f = fopen(file, "r"))) {
1.9 kristaps 492: resp_baddb();
493: return;
494: }
495:
1.12 kristaps 496: resp_begin_http(200, NULL);
497: puts("<!DOCTYPE HTML PUBLIC " "\n"
498: " \"-//W3C//DTD HTML 4.01//EN\"" "\n"
499: " \"http://www.w3.org/TR/html4/strict.dtd\">" "\n"
500: "<HTML>" "\n"
501: " <HEAD>" "\n"
502: " <META HTTP-EQUIV=\"Content-Type\" " "\n"
503: " CONTENT=\"text/html; charset=utf-8\">" "\n"
504: " <LINK REL=\"stylesheet\" HREF=\"/catman.css\"" "\n"
505: " TYPE=\"text/css\" media=\"all\">" "\n"
506: " <TITLE>System Manpage Reference</TITLE>" "\n"
507: " </HEAD>" "\n"
508: " <BODY>" "\n"
509: "<!-- Begin page content. //-->");
1.10 kristaps 510:
511: puts("<PRE>");
512: while (NULL != (p = fgetln(f, &len))) {
513: bold = italic = 0;
514: for (i = 0; i < (int)len - 1; i++) {
515: /*
516: * This means that the catpage is out of state.
517: * Ignore it and keep going (although the
518: * catpage is bogus).
519: */
520:
521: if ('\b' == p[i] || '\n' == p[i])
522: continue;
523:
524: /*
525: * Print a regular character.
526: * Close out any bold/italic scopes.
527: * If we're in back-space mode, make sure we'll
528: * have something to enter when we backspace.
529: */
530:
531: if ('\b' != p[i + 1]) {
532: if (italic)
533: printf("</I>");
534: if (bold)
535: printf("</B>");
536: italic = bold = 0;
537: html_putchar(p[i]);
538: continue;
539: } else if (i + 2 >= (int)len)
540: continue;
541:
542: /* Italic mode. */
543:
544: if ('_' == p[i]) {
545: if (bold)
546: printf("</B>");
547: if ( ! italic)
548: printf("<I>");
549: bold = 0;
550: italic = 1;
551: i += 2;
552: html_putchar(p[i]);
553: continue;
554: }
555:
556: /*
557: * Handle funny behaviour troff-isms.
558: * These grok'd from the original man2html.c.
559: */
560:
561: if (('+' == p[i] && 'o' == p[i + 2]) ||
562: ('o' == 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: ('|' == p[i] && '*' == p[i + 2])) {
569: if (italic)
570: printf("</I>");
571: if (bold)
572: printf("</B>");
573: italic = bold = 0;
574: putchar('*');
575: i += 2;
576: continue;
577: } else if (('|' == p[i] && '-' == p[i + 2]) ||
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: ('|' == p[i] && '+' == p[i + 1])) {
583: if (italic)
584: printf("</I>");
585: if (bold)
586: printf("</B>");
587: italic = bold = 0;
588: putchar('+');
589: i += 2;
590: continue;
591: }
592:
593: /* Bold mode. */
594:
595: if (italic)
596: printf("</I>");
597: if ( ! bold)
598: printf("<B>");
599: bold = 1;
600: italic = 0;
601: i += 2;
602: html_putchar(p[i]);
603: }
604:
605: /*
606: * Clean up the last character.
607: * We can get to a newline; don't print that.
608: */
1.9 kristaps 609:
1.10 kristaps 610: if (italic)
611: printf("</I>");
612: if (bold)
613: printf("</B>");
1.9 kristaps 614:
1.10 kristaps 615: if (i == (int)len - 1 && '\n' != p[i])
616: html_putchar(p[i]);
1.9 kristaps 617:
1.10 kristaps 618: putchar('\n');
619: }
620:
621: puts("</PRE>\n"
622: "</BODY>\n"
623: "</HTML>");
624:
625: fclose(f);
1.9 kristaps 626: }
627:
628: static void
1.8 kristaps 629: format(const char *file)
1.7 kristaps 630: {
1.8 kristaps 631: struct mparse *mp;
632: int fd;
633: struct mdoc *mdoc;
634: struct man *man;
635: void *vp;
636: enum mandoclevel rc;
1.10 kristaps 637: char opts[MAXPATHLEN + 128];
1.7 kristaps 638:
1.8 kristaps 639: if (-1 == (fd = open(file, O_RDONLY, 0))) {
640: resp_baddb();
1.7 kristaps 641: return;
642: }
643:
1.8 kristaps 644: mp = mparse_alloc(MPARSE_AUTO, MANDOCLEVEL_FATAL, NULL, NULL);
645: rc = mparse_readfd(mp, fd, file);
646: close(fd);
1.7 kristaps 647:
1.8 kristaps 648: if (rc >= MANDOCLEVEL_FATAL) {
1.7 kristaps 649: resp_baddb();
650: return;
651: }
652:
1.11 kristaps 653: snprintf(opts, sizeof(opts), "style=/man.css,"
1.10 kristaps 654: "man=%s/search.html?sec=%%S&expr=%%N,"
1.11 kristaps 655: /*"includes=/cgi-bin/man.cgi/usr/include/%%I"*/,
1.10 kristaps 656: progname);
657:
1.8 kristaps 658: mparse_result(mp, &mdoc, &man);
1.10 kristaps 659: vp = html_alloc(opts);
1.7 kristaps 660:
1.8 kristaps 661: if (NULL != mdoc) {
662: resp_begin_http(200, NULL);
663: html_mdoc(vp, mdoc);
664: } else if (NULL != man) {
665: resp_begin_http(200, NULL);
666: html_man(vp, man);
667: } else
668: resp_baddb();
1.7 kristaps 669:
1.8 kristaps 670: html_free(vp);
671: mparse_free(mp);
1.7 kristaps 672: }
673:
674: static void
1.6 kristaps 675: pg_show(const struct manpaths *ps, const struct req *req, char *path)
1.1 kristaps 676: {
1.6 kristaps 677: char *sub;
1.7 kristaps 678: char file[MAXPATHLEN];
1.9 kristaps 679: const char *fn, *cp;
1.6 kristaps 680: int rc;
681: unsigned int vol, rec;
1.9 kristaps 682: DB *idx;
1.6 kristaps 683: DBT key, val;
684:
685: if (NULL == path) {
1.10 kristaps 686: resp_error400();
1.6 kristaps 687: return;
688: } else if (NULL == (sub = strrchr(path, '/'))) {
1.10 kristaps 689: resp_error400();
1.6 kristaps 690: return;
691: } else
692: *sub++ = '\0';
693:
694: if ( ! (atou(path, &vol) && atou(sub, &rec))) {
1.10 kristaps 695: resp_error400();
1.6 kristaps 696: return;
697: } else if (vol >= (unsigned int)ps->sz) {
1.10 kristaps 698: resp_error400();
1.6 kristaps 699: return;
700: }
701:
702: strlcpy(file, ps->paths[vol], MAXPATHLEN);
703: strlcat(file, "/mandoc.index", MAXPATHLEN);
704:
705: /* Open the index recno(3) database. */
706:
1.9 kristaps 707: idx = dbopen(file, O_RDONLY, 0, DB_RECNO, NULL);
708: if (NULL == idx) {
1.6 kristaps 709: resp_baddb();
710: return;
711: }
712:
713: key.data = &rec;
714: key.size = 4;
715:
1.9 kristaps 716: if (0 != (rc = (*idx->get)(idx, &key, &val, 0))) {
1.10 kristaps 717: rc < 0 ? resp_baddb() : resp_error400();
1.9 kristaps 718: goto out;
1.6 kristaps 719: }
720:
1.9 kristaps 721: cp = (char *)val.data;
1.6 kristaps 722:
1.9 kristaps 723: if (NULL == (fn = memchr(cp, '\0', val.size)))
724: resp_baddb();
725: else if (++fn - cp >= (int)val.size)
726: resp_baddb();
727: else if (NULL == memchr(fn, '\0', val.size - (fn - cp)))
728: resp_baddb();
729: else {
730: strlcpy(file, ps->paths[vol], MAXPATHLEN);
731: strlcat(file, "/", MAXPATHLEN);
732: strlcat(file, fn, MAXPATHLEN);
733: if (0 == strcmp(cp, "cat"))
734: catman(file);
735: else
736: format(file);
737: }
738: out:
739: (*idx->close)(idx);
1.6 kristaps 740: }
741:
742: static void
743: pg_search(const struct manpaths *ps, const struct req *req, char *path)
744: {
745: size_t tt;
1.12 kristaps 746: int i, sz, rc, whatis;
1.6 kristaps 747: const char *ep, *start;
748: char **cp;
749: struct opts opt;
750: struct expr *expr;
751:
752: expr = NULL;
753: cp = NULL;
754: ep = NULL;
755: sz = 0;
1.15 ! kristaps 756: whatis = 1;
1.1 kristaps 757:
758: memset(&opt, 0, sizeof(struct opts));
1.6 kristaps 759:
760: for (sz = i = 0; i < (int)req->fieldsz; i++)
761: if (0 == strcmp(req->fields[i].key, "expr"))
762: ep = req->fields[i].val;
1.13 kristaps 763: else if (0 == strcmp(req->fields[i].key, "query"))
764: ep = req->fields[i].val;
1.6 kristaps 765: else if (0 == strcmp(req->fields[i].key, "sec"))
766: opt.cat = req->fields[i].val;
1.13 kristaps 767: else if (0 == strcmp(req->fields[i].key, "sektion"))
768: opt.cat = req->fields[i].val;
1.6 kristaps 769: else if (0 == strcmp(req->fields[i].key, "arch"))
770: opt.arch = req->fields[i].val;
1.13 kristaps 771: else if (0 == strcmp(req->fields[i].key, "apropos"))
772: whatis = 0 == strcmp
773: (req->fields[i].val, "0");
1.12 kristaps 774: else if (0 == strcmp(req->fields[i].key, "op"))
775: whatis = 0 == strcasecmp
776: (req->fields[i].val, "whatis");
1.13 kristaps 777:
778: if (NULL != opt.cat && 0 == strcmp(opt.cat, "0"))
779: opt.cat = NULL;
1.6 kristaps 780:
781: /*
782: * Poor man's tokenisation.
783: * Just break apart by spaces.
784: * Yes, this is half-ass. But it works for now.
785: */
786:
787: while (ep && isspace((unsigned char)*ep))
788: ep++;
789:
790: while (ep && '\0' != *ep) {
791: cp = mandoc_realloc(cp, (sz + 1) * sizeof(char *));
792: start = ep;
793: while ('\0' != *ep && ! isspace((unsigned char)*ep))
794: ep++;
795: cp[sz] = mandoc_malloc((ep - start) + 1);
796: memcpy(cp[sz], start, ep - start);
797: cp[sz++][ep - start] = '\0';
798: while (isspace((unsigned char)*ep))
799: ep++;
800: }
801:
802: rc = -1;
803:
804: /*
805: * Pump down into apropos backend.
806: * The resp_search() function is called with the results.
807: */
808:
1.12 kristaps 809: expr = whatis ? termcomp(sz, cp, &tt) :
810: exprcomp(sz, cp, &tt);
811:
812: if (NULL != expr)
1.6 kristaps 813: rc = apropos_search
814: (ps->sz, ps->paths, &opt,
815: expr, tt, (void *)req, resp_search);
816:
817: /* ...unless errors occured. */
818:
819: if (0 == rc)
820: resp_baddb();
821: else if (-1 == rc)
1.10 kristaps 822: resp_search(NULL, 0, (void *)req);
1.6 kristaps 823:
824: for (i = 0; i < sz; i++)
825: free(cp[i]);
826:
827: free(cp);
828: exprfree(expr);
1.1 kristaps 829: }
830:
831: int
832: main(void)
833: {
834: int i;
835: struct req req;
1.6 kristaps 836: char *p, *path, *subpath;
837: struct manpaths paths;
838:
839: /* HTTP init: read and parse the query string. */
840:
841: progname = getenv("SCRIPT_NAME");
842: if (NULL == progname)
843: progname = "";
844:
1.7 kristaps 845: cache = getenv("CACHE_DIR");
846: if (NULL == cache)
847: cache = "/cache/man.cgi";
848:
1.8 kristaps 849: if (-1 == chdir(cache)) {
850: resp_bad();
851: return(EXIT_FAILURE);
1.7 kristaps 852: }
853:
1.6 kristaps 854: host = getenv("HTTP_HOST");
855: if (NULL == host)
856: host = "localhost";
1.1 kristaps 857:
858: memset(&req, 0, sizeof(struct req));
859:
860: if (NULL != (p = getenv("QUERY_STRING")))
861: kval_parse(&req.fields, &req.fieldsz, p);
862:
1.6 kristaps 863: /* Resolve leading subpath component. */
1.1 kristaps 864:
1.6 kristaps 865: subpath = path = NULL;
1.1 kristaps 866: req.page = PAGE__MAX;
867:
868: if (NULL == (path = getenv("PATH_INFO")) || '\0' == *path)
869: req.page = PAGE_INDEX;
1.6 kristaps 870:
1.1 kristaps 871: if (NULL != path && '/' == *path && '\0' == *++path)
872: req.page = PAGE_INDEX;
873:
1.6 kristaps 874: /* Strip file suffix. */
875:
876: if (NULL != path && NULL != (p = strrchr(path, '.')))
877: if (NULL != p && NULL == strchr(p, '/'))
878: *p++ = '\0';
879:
880: /* Resolve subpath component. */
1.1 kristaps 881:
882: if (NULL != path && NULL != (subpath = strchr(path, '/')))
1.6 kristaps 883: *subpath++ = '\0';
1.1 kristaps 884:
1.6 kristaps 885: /* Map path into one we recognise. */
1.1 kristaps 886:
887: if (NULL != path && '\0' != *path)
888: for (i = 0; i < (int)PAGE__MAX; i++)
889: if (0 == strcmp(pages[i], path)) {
890: req.page = (enum page)i;
891: break;
892: }
893:
1.6 kristaps 894: /* Initialise MANPATH. */
895:
896: memset(&paths, 0, sizeof(struct manpaths));
1.9 kristaps 897: manpath_manconf("etc/catman.conf", &paths);
1.6 kristaps 898:
899: /* Route pages. */
900:
1.1 kristaps 901: switch (req.page) {
902: case (PAGE_INDEX):
1.6 kristaps 903: pg_index(&paths, &req, subpath);
1.1 kristaps 904: break;
905: case (PAGE_SEARCH):
1.6 kristaps 906: pg_search(&paths, &req, subpath);
907: break;
908: case (PAGE_SHOW):
909: pg_show(&paths, &req, subpath);
1.1 kristaps 910: break;
911: default:
1.10 kristaps 912: resp_error404(path);
1.1 kristaps 913: break;
914: }
915:
1.6 kristaps 916: manpath_free(&paths);
1.1 kristaps 917: kval_free(req.fields, req.fieldsz);
1.6 kristaps 918:
1.1 kristaps 919: return(EXIT_SUCCESS);
920: }
1.15 ! kristaps 921:
! 922: static int
! 923: cmp(const void *p1, const void *p2)
! 924: {
! 925:
! 926: return(strcasecmp(((const struct res *)p1)->title,
! 927: ((const struct res *)p2)->title));
! 928: }
! 929:
CVSweb