Annotation of mandoc/cgi.c, Revision 1.66
1.66 ! schwarze 1: /* $Id: cgi.c,v 1.65 2014/07/12 16:14:35 schwarze Exp $ */
1.6 kristaps 2: /*
1.42 kristaps 3: * Copyright (c) 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv>
1.52 schwarze 4: * Copyright (c) 2014 Ingo Schwarze <schwarze@usta.de>
1.6 kristaps 5: *
6: * Permission to use, copy, modify, and distribute this software for any
7: * purpose with or without fee is hereby granted, provided that the above
8: * copyright notice and this permission notice appear in all copies.
9: *
10: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17: */
18: #ifdef HAVE_CONFIG_H
19: #include "config.h"
20: #endif
21:
22: #include <ctype.h>
1.58 schwarze 23: #include <errno.h>
1.1 kristaps 24: #include <fcntl.h>
1.6 kristaps 25: #include <limits.h>
1.1 kristaps 26: #include <stdio.h>
27: #include <stdlib.h>
28: #include <string.h>
1.6 kristaps 29: #include <unistd.h>
1.1 kristaps 30:
1.4 schwarze 31: #include "mandoc.h"
1.50 schwarze 32: #include "mandoc_aux.h"
1.8 kristaps 33: #include "main.h"
1.6 kristaps 34: #include "manpath.h"
1.52 schwarze 35: #include "mansearch.h"
1.1 kristaps 36:
1.20 kristaps 37: /*
38: * A query as passed to the search function.
39: */
40: struct query {
1.58 schwarze 41: const char *manpath; /* desired manual directory */
1.20 kristaps 42: const char *arch; /* architecture */
43: const char *sec; /* manual section */
44: const char *expr; /* unparsed expression string */
1.65 schwarze 45: int equal; /* match whole names, not substrings */
1.20 kristaps 46: };
47:
1.1 kristaps 48: struct req {
1.58 schwarze 49: struct query q;
50: char **p; /* array of available manpaths */
51: size_t psz; /* number of available manpaths */
1.1 kristaps 52: };
53:
1.32 kristaps 54: static void catman(const struct req *, const char *);
1.15 kristaps 55: static int cmp(const void *, const void *);
1.32 kristaps 56: static void format(const struct req *, const char *);
1.6 kristaps 57: static void html_print(const char *);
1.36 kristaps 58: static void html_printquery(const struct req *);
1.10 kristaps 59: static void html_putchar(char);
1.24 kristaps 60: static int http_decode(char *);
1.26 kristaps 61: static void http_parse(struct req *, char *);
1.36 kristaps 62: static void http_print(const char *);
63: static void http_putchar(char);
64: static void http_printquery(const struct req *);
1.54 schwarze 65: static void pathgen(struct req *);
1.66 ! schwarze 66: static void pg_search(const struct req *);
! 67: static void pg_show(const struct req *, const char *);
1.6 kristaps 68: static void resp_begin_html(int, const char *);
69: static void resp_begin_http(int, const char *);
70: static void resp_end_html(void);
1.59 schwarze 71: static void resp_error_badrequest(const char *);
72: static void resp_error_internal(void);
1.6 kristaps 73: static void resp_index(const struct req *);
1.59 schwarze 74: static void resp_noresult(const struct req *,
75: const char *);
1.52 schwarze 76: static void resp_search(const struct req *,
77: struct manpage *, size_t);
1.6 kristaps 78: static void resp_searchform(const struct req *);
79:
1.58 schwarze 80: static const char *scriptname; /* CGI script name */
81: static const char *mandir; /* contains all manpath directories */
82: static const char *cssdir; /* css directory */
83: static const char *httphost; /* hostname used in the URIs */
1.1 kristaps 84:
1.6 kristaps 85: /*
1.20 kristaps 86: * Print a character, escaping HTML along the way.
87: * This will pass non-ASCII straight to output: be warned!
88: */
1.10 kristaps 89: static void
90: html_putchar(char c)
91: {
92:
93: switch (c) {
94: case ('"'):
95: printf(""e;");
96: break;
97: case ('&'):
98: printf("&");
99: break;
100: case ('>'):
101: printf(">");
102: break;
103: case ('<'):
104: printf("<");
105: break;
106: default:
107: putchar((unsigned char)c);
108: break;
109: }
110: }
1.57 schwarze 111:
1.36 kristaps 112: static void
113: http_printquery(const struct req *req)
114: {
115:
1.58 schwarze 116: if (NULL != req->q.manpath) {
1.53 schwarze 117: printf("&manpath=");
1.58 schwarze 118: http_print(req->q.manpath);
1.53 schwarze 119: }
120: if (NULL != req->q.sec) {
121: printf("&sec=");
122: http_print(req->q.sec);
123: }
124: if (NULL != req->q.arch) {
125: printf("&arch=");
126: http_print(req->q.arch);
127: }
128: if (NULL != req->q.expr) {
1.65 schwarze 129: printf("&query=");
130: http_print(req->q.expr);
1.53 schwarze 131: }
1.65 schwarze 132: if (0 == req->q.equal)
133: printf("&apropos=1");
1.36 kristaps 134: }
135:
136: static void
137: html_printquery(const struct req *req)
138: {
139:
1.58 schwarze 140: if (NULL != req->q.manpath) {
1.53 schwarze 141: printf("&manpath=");
1.58 schwarze 142: html_print(req->q.manpath);
1.53 schwarze 143: }
144: if (NULL != req->q.sec) {
145: printf("&sec=");
146: html_print(req->q.sec);
147: }
148: if (NULL != req->q.arch) {
149: printf("&arch=");
150: html_print(req->q.arch);
151: }
152: if (NULL != req->q.expr) {
1.65 schwarze 153: printf("&query=");
1.63 schwarze 154: html_print(req->q.expr);
1.53 schwarze 155: }
1.65 schwarze 156: if (0 == req->q.equal)
157: printf("&apropos=1");
1.36 kristaps 158: }
159:
160: static void
161: http_print(const char *p)
162: {
163:
164: if (NULL == p)
165: return;
166: while ('\0' != *p)
167: http_putchar(*p++);
168: }
1.10 kristaps 169:
1.6 kristaps 170: /*
1.20 kristaps 171: * Call through to html_putchar().
172: * Accepts NULL strings.
1.6 kristaps 173: */
1.1 kristaps 174: static void
1.6 kristaps 175: html_print(const char *p)
1.1 kristaps 176: {
1.6 kristaps 177:
178: if (NULL == p)
179: return;
1.1 kristaps 180: while ('\0' != *p)
1.10 kristaps 181: html_putchar(*p++);
1.1 kristaps 182: }
183:
184: /*
185: * Parse out key-value pairs from an HTTP request variable.
1.6 kristaps 186: * This can be either a cookie or a POST/GET string, although man.cgi
187: * uses only GET for simplicity.
1.1 kristaps 188: */
189: static void
1.26 kristaps 190: http_parse(struct req *req, char *p)
1.1 kristaps 191: {
1.52 schwarze 192: char *key, *val;
1.1 kristaps 193:
1.26 kristaps 194: memset(&req->q, 0, sizeof(struct query));
1.58 schwarze 195: req->q.manpath = req->p[0];
1.65 schwarze 196: req->q.equal = 1;
1.24 kristaps 197:
1.36 kristaps 198: while ('\0' != *p) {
1.1 kristaps 199: key = p;
200: val = NULL;
201:
1.36 kristaps 202: p += (int)strcspn(p, ";&");
203: if ('\0' != *p)
1.1 kristaps 204: *p++ = '\0';
1.36 kristaps 205: if (NULL != (val = strchr(key, '=')))
206: *val++ = '\0';
1.1 kristaps 207:
1.36 kristaps 208: if ('\0' == *key || NULL == val || '\0' == *val)
1.1 kristaps 209: continue;
210:
211: /* Just abort handling. */
212:
1.24 kristaps 213: if ( ! http_decode(key))
214: break;
1.36 kristaps 215: if (NULL != val && ! http_decode(val))
1.24 kristaps 216: break;
217:
1.65 schwarze 218: if (0 == strcmp(key, "query"))
1.26 kristaps 219: req->q.expr = val;
220: else if (0 == strcmp(key, "manpath"))
1.58 schwarze 221: req->q.manpath = val;
1.24 kristaps 222: else if (0 == strcmp(key, "apropos"))
1.65 schwarze 223: req->q.equal = !strcmp(val, "0");
224: else if (0 == strcmp(key, "sec") ||
225: 0 == strcmp(key, "sektion")) {
226: if (strcmp(val, "0"))
227: req->q.sec = val;
228: } else if (0 == strcmp(key, "arch")) {
229: if (strcmp(val, "default"))
230: req->q.arch = val;
231: }
1.24 kristaps 232: }
1.1 kristaps 233: }
234:
1.36 kristaps 235: static void
236: http_putchar(char c)
237: {
238:
239: if (isalnum((unsigned char)c)) {
240: putchar((unsigned char)c);
241: return;
242: } else if (' ' == c) {
243: putchar('+');
244: return;
245: }
246: printf("%%%.2x", c);
247: }
248:
1.1 kristaps 249: /*
1.6 kristaps 250: * HTTP-decode a string. The standard explanation is that this turns
251: * "%4e+foo" into "n foo" in the regular way. This is done in-place
252: * over the allocated string.
1.1 kristaps 253: */
254: static int
1.24 kristaps 255: http_decode(char *p)
1.1 kristaps 256: {
257: char hex[3];
1.63 schwarze 258: char *q;
1.1 kristaps 259: int c;
260:
261: hex[2] = '\0';
262:
1.63 schwarze 263: q = p;
264: for ( ; '\0' != *p; p++, q++) {
1.1 kristaps 265: if ('%' == *p) {
266: if ('\0' == (hex[0] = *(p + 1)))
267: return(0);
268: if ('\0' == (hex[1] = *(p + 2)))
269: return(0);
270: if (1 != sscanf(hex, "%x", &c))
271: return(0);
272: if ('\0' == c)
273: return(0);
274:
1.63 schwarze 275: *q = (char)c;
276: p += 2;
1.1 kristaps 277: } else
1.63 schwarze 278: *q = '+' == *p ? ' ' : *p;
1.1 kristaps 279: }
280:
1.63 schwarze 281: *q = '\0';
1.1 kristaps 282: return(1);
283: }
284:
1.6 kristaps 285: static void
286: resp_begin_http(int code, const char *msg)
287: {
288:
289: if (200 != code)
1.62 schwarze 290: printf("Status: %d %s\r\n", code, msg);
1.6 kristaps 291:
1.62 schwarze 292: printf("Content-Type: text/html; charset=utf-8\r\n"
293: "Cache-Control: no-cache\r\n"
294: "Pragma: no-cache\r\n"
295: "\r\n");
1.6 kristaps 296:
297: fflush(stdout);
298: }
299:
300: static void
301: resp_begin_html(int code, const char *msg)
302: {
303:
304: resp_begin_http(code, msg);
305:
1.29 kristaps 306: printf("<!DOCTYPE HTML PUBLIC "
307: " \"-//W3C//DTD HTML 4.01//EN\""
308: " \"http://www.w3.org/TR/html4/strict.dtd\">\n"
309: "<HTML>\n"
310: "<HEAD>\n"
311: "<META HTTP-EQUIV=\"Content-Type\""
312: " CONTENT=\"text/html; charset=utf-8\">\n"
1.32 kristaps 313: "<LINK REL=\"stylesheet\" HREF=\"%s/man-cgi.css\""
314: " TYPE=\"text/css\" media=\"all\">\n"
315: "<LINK REL=\"stylesheet\" HREF=\"%s/man.css\""
1.29 kristaps 316: " TYPE=\"text/css\" media=\"all\">\n"
317: "<TITLE>System Manpage Reference</TITLE>\n"
318: "</HEAD>\n"
319: "<BODY>\n"
1.58 schwarze 320: "<!-- Begin page content. //-->\n",
321: cssdir, cssdir);
1.6 kristaps 322: }
323:
324: static void
325: resp_end_html(void)
326: {
327:
1.20 kristaps 328: puts("</BODY>\n"
329: "</HTML>");
1.6 kristaps 330: }
331:
332: static void
333: resp_searchform(const struct req *req)
334: {
1.27 kristaps 335: int i;
1.13 kristaps 336:
1.6 kristaps 337: puts("<!-- Begin search form. //-->");
1.32 kristaps 338: printf("<DIV ID=\"mancgi\">\n"
1.66 ! schwarze 339: "<FORM ACTION=\"%s\" METHOD=\"get\">\n"
1.29 kristaps 340: "<FIELDSET>\n"
1.16 kristaps 341: "<LEGEND>Search Parameters</LEGEND>\n"
1.65 schwarze 342: "<INPUT TYPE=\"submit\" VALUE=\"Search\"> "
343: "for manuals \n",
1.58 schwarze 344: scriptname);
1.65 schwarze 345: printf("<SELECT NAME=\"apropos\">\n"
346: "<OPTION VALUE=\"0\"");
347: if (req->q.equal)
348: printf(" SELECTED=\"selected\"");
349: printf(">named</OPTION>\n"
350: "<OPTION VALUE=\"1\"");
351: if (0 == req->q.equal)
352: printf(" SELECTED=\"selected\"");
353: printf(">matching</OPTION>\n"
354: "</SELECT>\n"
355: "<INPUT TYPE=\"text\" NAME=\"query\" VALUE=\"");
1.24 kristaps 356: html_print(req->q.expr ? req->q.expr : "");
1.14 kristaps 357: printf("\">, section "
1.20 kristaps 358: "<INPUT TYPE=\"text\""
359: " SIZE=\"4\" NAME=\"sec\" VALUE=\"");
1.24 kristaps 360: html_print(req->q.sec ? req->q.sec : "");
1.14 kristaps 361: printf("\">, arch "
1.20 kristaps 362: "<INPUT TYPE=\"text\""
363: " SIZE=\"8\" NAME=\"arch\" VALUE=\"");
1.24 kristaps 364: html_print(req->q.arch ? req->q.arch : "");
1.27 kristaps 365: printf("\">");
366: if (req->psz > 1) {
1.60 schwarze 367: puts(", in <SELECT NAME=\"manpath\">");
1.27 kristaps 368: for (i = 0; i < (int)req->psz; i++) {
1.52 schwarze 369: printf("<OPTION ");
1.58 schwarze 370: if (NULL == req->q.manpath ? 0 == i :
371: 0 == strcmp(req->q.manpath, req->p[i]))
1.52 schwarze 372: printf("SELECTED=\"selected\" ");
373: printf("VALUE=\"");
374: html_print(req->p[i]);
1.27 kristaps 375: printf("\">");
1.52 schwarze 376: html_print(req->p[i]);
1.27 kristaps 377: puts("</OPTION>");
378: }
379: puts("</SELECT>");
380: }
1.60 schwarze 381: puts("—\n"
1.12 kristaps 382: "<INPUT TYPE=\"reset\" VALUE=\"Reset\">\n"
383: "</FIELDSET>\n"
1.32 kristaps 384: "</FORM>\n"
385: "</DIV>");
1.20 kristaps 386: puts("<!-- End search form. //-->");
1.6 kristaps 387: }
388:
389: static void
390: resp_index(const struct req *req)
391: {
392:
393: resp_begin_html(200, NULL);
1.60 schwarze 394: puts("<H1>\n"
395: "Online manuals with "
396: "<A HREF=\"http://mdocml.bsd.lv/\">mandoc</A>\n"
397: "</H1>");
1.6 kristaps 398: resp_searchform(req);
1.64 schwarze 399: printf("<P>\n"
400: "This web interface is documented in the "
1.66 ! schwarze 401: "<A HREF=\"%s?query=man.cgi&sec=8\">"
1.64 schwarze 402: "man.cgi</A> manual, and the "
1.66 ! schwarze 403: "<A HREF=\"%s?query=apropos&sec=1\">"
1.64 schwarze 404: "apropos</A> manual explains the query syntax.\n"
405: "</P>\n",
406: scriptname, scriptname);
1.6 kristaps 407: resp_end_html();
408: }
409:
410: static void
1.59 schwarze 411: resp_noresult(const struct req *req, const char *msg)
412: {
413: resp_begin_html(200, NULL);
414: resp_searchform(req);
415: puts("<P>");
416: puts(msg);
417: puts("</P>");
418: resp_end_html();
419: }
420:
421: static void
422: resp_error_badrequest(const char *msg)
1.9 kristaps 423: {
424:
1.59 schwarze 425: resp_begin_html(400, "Bad Request");
426: puts("<H1>Bad Request</H1>\n"
427: "<P>\n");
428: puts(msg);
429: printf("Try again from the\n"
430: "<A HREF=\"%s\">main page</A>.\n"
1.58 schwarze 431: "</P>", scriptname);
1.9 kristaps 432: resp_end_html();
433: }
434:
435: static void
1.59 schwarze 436: resp_error_internal(void)
1.7 kristaps 437: {
438: resp_begin_html(500, "Internal Server Error");
1.58 schwarze 439: puts("<P>Internal Server Error</P>");
1.7 kristaps 440: resp_end_html();
441: }
442:
443: static void
1.52 schwarze 444: resp_search(const struct req *req, struct manpage *r, size_t sz)
1.1 kristaps 445: {
1.52 schwarze 446: size_t i;
1.19 kristaps 447:
1.52 schwarze 448: if (1 == sz) {
1.6 kristaps 449: /*
450: * If we have just one result, then jump there now
451: * without any delay.
452: */
1.62 schwarze 453: printf("Status: 303 See Other\r\n");
1.66 ! schwarze 454: printf("Location: http://%s%s/%s/%s?",
1.58 schwarze 455: httphost, scriptname, req->q.manpath, r[0].file);
1.36 kristaps 456: http_printquery(req);
1.62 schwarze 457: printf("\r\n"
458: "Content-Type: text/html; charset=utf-8\r\n"
459: "\r\n");
1.6 kristaps 460: return;
461: }
462:
1.59 schwarze 463: qsort(r, sz, sizeof(struct manpage), cmp);
464:
1.12 kristaps 465: resp_begin_html(200, NULL);
1.19 kristaps 466: resp_searchform(req);
1.33 kristaps 467: puts("<DIV CLASS=\"results\">");
468: puts("<TABLE>");
1.1 kristaps 469:
1.41 kristaps 470: for (i = 0; i < sz; i++) {
1.20 kristaps 471: printf("<TR>\n"
472: "<TD CLASS=\"title\">\n"
1.66 ! schwarze 473: "<A HREF=\"%s/%s/%s?",
1.58 schwarze 474: scriptname, req->q.manpath, r[i].file);
1.36 kristaps 475: html_printquery(req);
476: printf("\">");
1.52 schwarze 477: html_print(r[i].names);
478: printf("</A>\n"
1.20 kristaps 479: "</TD>\n"
480: "<TD CLASS=\"desc\">");
1.52 schwarze 481: html_print(r[i].output);
1.20 kristaps 482: puts("</TD>\n"
483: "</TR>");
1.1 kristaps 484: }
1.16 kristaps 485:
1.33 kristaps 486: puts("</TABLE>\n"
487: "</DIV>");
1.6 kristaps 488: resp_end_html();
489: }
490:
1.1 kristaps 491: static void
1.32 kristaps 492: catman(const struct req *req, const char *file)
1.9 kristaps 493: {
1.10 kristaps 494: FILE *f;
495: size_t len;
496: int i;
497: char *p;
498: int italic, bold;
1.9 kristaps 499:
1.10 kristaps 500: if (NULL == (f = fopen(file, "r"))) {
1.59 schwarze 501: resp_error_badrequest(
502: "You specified an invalid manual file.");
1.9 kristaps 503: return;
504: }
505:
1.32 kristaps 506: resp_begin_html(200, NULL);
507: resp_searchform(req);
508: puts("<DIV CLASS=\"catman\">\n"
509: "<PRE>");
1.10 kristaps 510:
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"
1.32 kristaps 621: "</DIV>\n"
1.10 kristaps 622: "</BODY>\n"
623: "</HTML>");
624:
625: fclose(f);
1.9 kristaps 626: }
627:
628: static void
1.32 kristaps 629: format(const struct req *req, 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.45 schwarze 637: char opts[PATH_MAX + 128];
1.7 kristaps 638:
1.8 kristaps 639: if (-1 == (fd = open(file, O_RDONLY, 0))) {
1.59 schwarze 640: resp_error_badrequest(
641: "You specified an invalid manual file.");
1.7 kristaps 642: return;
643: }
644:
1.56 schwarze 645: mp = mparse_alloc(MPARSE_SO, MANDOCLEVEL_FATAL, NULL,
1.58 schwarze 646: req->q.manpath);
1.8 kristaps 647: rc = mparse_readfd(mp, fd, file);
648: close(fd);
1.7 kristaps 649:
1.8 kristaps 650: if (rc >= MANDOCLEVEL_FATAL) {
1.59 schwarze 651: fprintf(stderr, "fatal mandoc error: %s/%s\n",
652: req->q.manpath, file);
653: resp_error_internal();
1.7 kristaps 654: return;
655: }
656:
1.52 schwarze 657: snprintf(opts, sizeof(opts),
1.66 ! schwarze 658: "fragment,man=%s?query=%%N&sec=%%S",
1.58 schwarze 659: scriptname);
1.10 kristaps 660:
1.49 schwarze 661: mparse_result(mp, &mdoc, &man, NULL);
1.32 kristaps 662: if (NULL == man && NULL == mdoc) {
1.59 schwarze 663: fprintf(stderr, "fatal mandoc error: %s/%s\n",
664: req->q.manpath, file);
665: resp_error_internal();
1.32 kristaps 666: mparse_free(mp);
667: return;
668: }
669:
670: resp_begin_html(200, NULL);
671: resp_searchform(req);
672:
1.10 kristaps 673: vp = html_alloc(opts);
1.7 kristaps 674:
1.32 kristaps 675: if (NULL != mdoc)
1.8 kristaps 676: html_mdoc(vp, mdoc);
1.32 kristaps 677: else
1.8 kristaps 678: html_man(vp, man);
1.32 kristaps 679:
680: puts("</BODY>\n"
681: "</HTML>");
1.7 kristaps 682:
1.8 kristaps 683: html_free(vp);
684: mparse_free(mp);
1.7 kristaps 685: }
686:
687: static void
1.66 ! schwarze 688: pg_show(const struct req *req, const char *path)
1.1 kristaps 689: {
1.6 kristaps 690: char *sub;
1.25 kristaps 691:
692: if (NULL == path || NULL == (sub = strchr(path, '/'))) {
1.59 schwarze 693: resp_error_badrequest(
694: "You did not specify a page to show.");
1.25 kristaps 695: return;
696: }
697: *sub++ = '\0';
1.6 kristaps 698:
1.24 kristaps 699: /*
1.58 schwarze 700: * Begin by chdir()ing into the manpath.
1.24 kristaps 701: * This way we can pick up the database files, which are
702: * relative to the manpath root.
703: */
704:
1.52 schwarze 705: if (-1 == chdir(path)) {
1.59 schwarze 706: resp_error_badrequest(
707: "You specified an invalid manpath.");
1.24 kristaps 708: return;
709: }
710:
1.52 schwarze 711: if ('c' == *sub)
712: catman(req, sub);
713: else
714: format(req, sub);
1.6 kristaps 715: }
716:
717: static void
1.66 ! schwarze 718: pg_search(const struct req *req)
1.6 kristaps 719: {
1.52 schwarze 720: struct mansearch search;
721: struct manpaths paths;
722: struct manpage *res;
723: char **cp;
724: const char *ep, *start;
725: size_t ressz;
726: int i, sz;
1.6 kristaps 727:
728: /*
1.24 kristaps 729: * Begin by chdir()ing into the root of the manpath.
730: * This way we can pick up the database files, which are
731: * relative to the manpath root.
732: */
733:
1.58 schwarze 734: if (-1 == (chdir(req->q.manpath))) {
1.59 schwarze 735: resp_error_badrequest(
736: "You specified an invalid manpath.");
1.24 kristaps 737: return;
738: }
739:
1.52 schwarze 740: search.arch = req->q.arch;
741: search.sec = req->q.sec;
1.65 schwarze 742: search.deftype = req->q.equal ? TYPE_Nm : (TYPE_Nm | TYPE_Nd);
743: search.flags = req->q.equal ? MANSEARCH_MAN : 0;
1.52 schwarze 744:
745: paths.sz = 1;
746: paths.paths = mandoc_malloc(sizeof(char *));
747: paths.paths[0] = mandoc_strdup(".");
1.24 kristaps 748:
749: /*
750: * Poor man's tokenisation: just break apart by spaces.
1.6 kristaps 751: * Yes, this is half-ass. But it works for now.
752: */
753:
1.52 schwarze 754: ep = req->q.expr;
1.6 kristaps 755: while (ep && isspace((unsigned char)*ep))
756: ep++;
757:
1.52 schwarze 758: sz = 0;
759: cp = NULL;
1.6 kristaps 760: while (ep && '\0' != *ep) {
1.51 schwarze 761: cp = mandoc_reallocarray(cp, sz + 1, sizeof(char *));
1.6 kristaps 762: start = ep;
763: while ('\0' != *ep && ! isspace((unsigned char)*ep))
764: ep++;
765: cp[sz] = mandoc_malloc((ep - start) + 1);
766: memcpy(cp[sz], start, ep - start);
767: cp[sz++][ep - start] = '\0';
768: while (isspace((unsigned char)*ep))
769: ep++;
770: }
771:
1.59 schwarze 772: if (0 == mansearch(&search, &paths, sz, cp, "Nd", &res, &ressz))
773: resp_noresult(req, "You entered an invalid query.");
774: else if (0 == ressz)
775: resp_noresult(req, "No results found.");
776: else
1.52 schwarze 777: resp_search(req, res, ressz);
1.6 kristaps 778:
779: for (i = 0; i < sz; i++)
780: free(cp[i]);
1.52 schwarze 781: free(cp);
782:
783: for (i = 0; i < (int)ressz; i++) {
784: free(res[i].file);
785: free(res[i].names);
786: free(res[i].output);
787: }
788: free(res);
1.6 kristaps 789:
1.52 schwarze 790: free(paths.paths[0]);
791: free(paths.paths);
1.1 kristaps 792: }
793:
794: int
795: main(void)
796: {
1.66 ! schwarze 797: struct req req;
! 798: const char *path;
! 799: char *querystring;
1.1 kristaps 800: int i;
1.6 kristaps 801:
1.24 kristaps 802: /* Scan our run-time environment. */
1.6 kristaps 803:
1.58 schwarze 804: if (NULL == (mandir = getenv("MAN_DIR")))
805: mandir = "/man";
1.29 kristaps 806:
1.58 schwarze 807: if (NULL == (scriptname = getenv("SCRIPT_NAME")))
808: scriptname = "";
1.6 kristaps 809:
1.58 schwarze 810: if (NULL == (cssdir = getenv("CSS_DIR")))
811: cssdir = "";
1.7 kristaps 812:
1.58 schwarze 813: if (NULL == (httphost = getenv("HTTP_HOST")))
814: httphost = "localhost";
1.24 kristaps 815:
816: /*
1.58 schwarze 817: * First we change directory into the mandir so that
1.24 kristaps 818: * subsequent scanning for manpath directories is rooted
819: * relative to the same position.
820: */
821:
1.58 schwarze 822: if (-1 == chdir(mandir)) {
823: fprintf(stderr, "MAN_DIR: %s: %s\n",
824: mandir, strerror(errno));
1.59 schwarze 825: resp_error_internal();
1.24 kristaps 826: return(EXIT_FAILURE);
827: }
828:
829: memset(&req, 0, sizeof(struct req));
1.54 schwarze 830: pathgen(&req);
1.1 kristaps 831:
1.24 kristaps 832: /* Next parse out the query string. */
1.1 kristaps 833:
1.58 schwarze 834: if (NULL != (querystring = getenv("QUERY_STRING")))
835: http_parse(&req, querystring);
1.1 kristaps 836:
1.66 ! schwarze 837: /* Dispatch to the three different pages. */
1.1 kristaps 838:
1.66 ! schwarze 839: path = getenv("PATH_INFO");
! 840: if (NULL == path)
! 841: path = "";
! 842: else if ('/' == *path)
! 843: path++;
! 844:
! 845: if ('\0' != *path)
! 846: pg_show(&req, path);
! 847: else if (NULL != req.q.expr)
! 848: pg_search(&req);
! 849: else
! 850: resp_index(&req);
1.1 kristaps 851:
1.52 schwarze 852: for (i = 0; i < (int)req.psz; i++)
853: free(req.p[i]);
1.24 kristaps 854: free(req.p);
1.1 kristaps 855: return(EXIT_SUCCESS);
856: }
1.15 kristaps 857:
858: static int
859: cmp(const void *p1, const void *p2)
860: {
861:
1.52 schwarze 862: return(strcasecmp(((const struct manpage *)p1)->names,
863: ((const struct manpage *)p2)->names));
1.24 kristaps 864: }
865:
866: /*
867: * Scan for indexable paths.
868: */
869: static void
1.54 schwarze 870: pathgen(struct req *req)
1.24 kristaps 871: {
1.54 schwarze 872: FILE *fp;
873: char *dp;
874: size_t dpsz;
875:
876: if (NULL == (fp = fopen("manpath.conf", "r")))
877: return;
1.24 kristaps 878:
1.54 schwarze 879: while (NULL != (dp = fgetln(fp, &dpsz))) {
1.55 schwarze 880: if ('\n' == dp[dpsz - 1])
881: dpsz--;
1.54 schwarze 882: req->p = mandoc_realloc(req->p,
883: (req->psz + 1) * sizeof(char *));
884: req->p[req->psz++] = mandoc_strndup(dp, dpsz);
1.24 kristaps 885: }
886: }
CVSweb