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