Annotation of mandoc/cgi.c, Revision 1.69
1.69 ! schwarze 1: /* $Id: cgi.c,v 1.68 2014/07/12 23:46:44 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.67 schwarze 36: #include "cgi.h"
1.1 kristaps 37:
1.20 kristaps 38: /*
39: * A query as passed to the search function.
40: */
41: struct query {
1.58 schwarze 42: const char *manpath; /* desired manual directory */
1.20 kristaps 43: const char *arch; /* architecture */
44: const char *sec; /* manual section */
45: const char *expr; /* unparsed expression string */
1.65 schwarze 46: int equal; /* match whole names, not substrings */
1.20 kristaps 47: };
48:
1.1 kristaps 49: struct req {
1.58 schwarze 50: struct query q;
51: char **p; /* array of available manpaths */
52: size_t psz; /* number of available manpaths */
1.1 kristaps 53: };
54:
1.32 kristaps 55: static void catman(const struct req *, const char *);
1.15 kristaps 56: static int cmp(const void *, const void *);
1.32 kristaps 57: static void format(const struct req *, const char *);
1.6 kristaps 58: static void html_print(const char *);
1.36 kristaps 59: static void html_printquery(const struct req *);
1.10 kristaps 60: static void html_putchar(char);
1.24 kristaps 61: static int http_decode(char *);
1.26 kristaps 62: static void http_parse(struct req *, char *);
1.36 kristaps 63: static void http_print(const char *);
64: static void http_putchar(char);
65: static void http_printquery(const struct req *);
1.54 schwarze 66: static void pathgen(struct req *);
1.66 schwarze 67: static void pg_search(const struct req *);
68: static void pg_show(const struct req *, const char *);
1.6 kristaps 69: static void resp_begin_html(int, const char *);
70: static void resp_begin_http(int, const char *);
71: static void resp_end_html(void);
1.59 schwarze 72: static void resp_error_badrequest(const char *);
73: static void resp_error_internal(void);
1.6 kristaps 74: static void resp_index(const struct req *);
1.59 schwarze 75: static void resp_noresult(const struct req *,
76: const char *);
1.52 schwarze 77: static void resp_search(const struct req *,
78: struct manpage *, size_t);
1.6 kristaps 79: static void resp_searchform(const struct req *);
80:
1.58 schwarze 81: static const char *scriptname; /* CGI script name */
82: static const char *httphost; /* hostname used in the URIs */
1.1 kristaps 83:
1.68 schwarze 84: static const char *const sec_numbers[] = {
85: "0", "1", "2", "3", "3p", "4", "5", "6", "7", "8", "9"
86: };
87: static const char *const sec_names[] = {
88: "All Sections",
89: "1 - General Commands",
90: "2 - System Calls",
91: "3 - Subroutines",
92: "3p - Perl Subroutines",
93: "4 - Special Files",
94: "5 - File Formats",
95: "6 - Games",
96: "7 - Macros and Conventions",
97: "8 - Maintenance Commands",
98: "9 - Kernel Interface"
99: };
100: static const int sec_MAX = sizeof(sec_names) / sizeof(char *);
101:
102: static const char *const arch_names[] = {
103: "amd64", "alpha", "armish", "armv7",
104: "aviion", "hppa", "hppa64", "i386",
105: "ia64", "landisk", "loongson", "luna88k",
106: "macppc", "mips64", "octeon", "sgi",
107: "socppc", "solbourne", "sparc", "sparc64",
108: "vax", "zaurus",
109: "amiga", "arc", "arm32", "atari",
110: "beagle", "cats", "hp300", "mac68k",
111: "mvme68k", "mvme88k", "mvmeppc", "palm",
112: "pc532", "pegasos", "pmax", "powerpc",
113: "sun3", "wgrisc", "x68k"
114: };
115: static const int arch_MAX = sizeof(arch_names) / sizeof(char *);
116:
1.6 kristaps 117: /*
1.20 kristaps 118: * Print a character, escaping HTML along the way.
119: * This will pass non-ASCII straight to output: be warned!
120: */
1.10 kristaps 121: static void
122: html_putchar(char c)
123: {
124:
125: switch (c) {
126: case ('"'):
127: printf(""e;");
128: break;
129: case ('&'):
130: printf("&");
131: break;
132: case ('>'):
133: printf(">");
134: break;
135: case ('<'):
136: printf("<");
137: break;
138: default:
139: putchar((unsigned char)c);
140: break;
141: }
142: }
1.57 schwarze 143:
1.36 kristaps 144: static void
145: http_printquery(const struct req *req)
146: {
147:
1.58 schwarze 148: if (NULL != req->q.manpath) {
1.53 schwarze 149: printf("&manpath=");
1.58 schwarze 150: http_print(req->q.manpath);
1.53 schwarze 151: }
152: if (NULL != req->q.sec) {
153: printf("&sec=");
154: http_print(req->q.sec);
155: }
156: if (NULL != req->q.arch) {
157: printf("&arch=");
158: http_print(req->q.arch);
159: }
160: if (NULL != req->q.expr) {
1.65 schwarze 161: printf("&query=");
162: http_print(req->q.expr);
1.53 schwarze 163: }
1.65 schwarze 164: if (0 == req->q.equal)
165: printf("&apropos=1");
1.36 kristaps 166: }
167:
168: static void
169: html_printquery(const struct req *req)
170: {
171:
1.58 schwarze 172: if (NULL != req->q.manpath) {
1.53 schwarze 173: printf("&manpath=");
1.58 schwarze 174: html_print(req->q.manpath);
1.53 schwarze 175: }
176: if (NULL != req->q.sec) {
177: printf("&sec=");
178: html_print(req->q.sec);
179: }
180: if (NULL != req->q.arch) {
181: printf("&arch=");
182: html_print(req->q.arch);
183: }
184: if (NULL != req->q.expr) {
1.65 schwarze 185: printf("&query=");
1.63 schwarze 186: html_print(req->q.expr);
1.53 schwarze 187: }
1.65 schwarze 188: if (0 == req->q.equal)
189: printf("&apropos=1");
1.36 kristaps 190: }
191:
192: static void
193: http_print(const char *p)
194: {
195:
196: if (NULL == p)
197: return;
198: while ('\0' != *p)
199: http_putchar(*p++);
200: }
1.10 kristaps 201:
1.6 kristaps 202: /*
1.20 kristaps 203: * Call through to html_putchar().
204: * Accepts NULL strings.
1.6 kristaps 205: */
1.1 kristaps 206: static void
1.6 kristaps 207: html_print(const char *p)
1.1 kristaps 208: {
1.6 kristaps 209:
210: if (NULL == p)
211: return;
1.1 kristaps 212: while ('\0' != *p)
1.10 kristaps 213: html_putchar(*p++);
1.1 kristaps 214: }
215:
216: /*
217: * Parse out key-value pairs from an HTTP request variable.
1.6 kristaps 218: * This can be either a cookie or a POST/GET string, although man.cgi
219: * uses only GET for simplicity.
1.1 kristaps 220: */
221: static void
1.26 kristaps 222: http_parse(struct req *req, char *p)
1.1 kristaps 223: {
1.52 schwarze 224: char *key, *val;
1.1 kristaps 225:
1.26 kristaps 226: memset(&req->q, 0, sizeof(struct query));
1.58 schwarze 227: req->q.manpath = req->p[0];
1.65 schwarze 228: req->q.equal = 1;
1.24 kristaps 229:
1.36 kristaps 230: while ('\0' != *p) {
1.1 kristaps 231: key = p;
232: val = NULL;
233:
1.36 kristaps 234: p += (int)strcspn(p, ";&");
235: if ('\0' != *p)
1.1 kristaps 236: *p++ = '\0';
1.36 kristaps 237: if (NULL != (val = strchr(key, '=')))
238: *val++ = '\0';
1.1 kristaps 239:
1.36 kristaps 240: if ('\0' == *key || NULL == val || '\0' == *val)
1.1 kristaps 241: continue;
242:
243: /* Just abort handling. */
244:
1.24 kristaps 245: if ( ! http_decode(key))
246: break;
1.36 kristaps 247: if (NULL != val && ! http_decode(val))
1.24 kristaps 248: break;
249:
1.65 schwarze 250: if (0 == strcmp(key, "query"))
1.26 kristaps 251: req->q.expr = val;
252: else if (0 == strcmp(key, "manpath"))
1.58 schwarze 253: req->q.manpath = val;
1.24 kristaps 254: else if (0 == strcmp(key, "apropos"))
1.65 schwarze 255: req->q.equal = !strcmp(val, "0");
256: else if (0 == strcmp(key, "sec") ||
257: 0 == strcmp(key, "sektion")) {
258: if (strcmp(val, "0"))
259: req->q.sec = val;
260: } else if (0 == strcmp(key, "arch")) {
261: if (strcmp(val, "default"))
262: req->q.arch = val;
263: }
1.24 kristaps 264: }
1.1 kristaps 265: }
266:
1.36 kristaps 267: static void
268: http_putchar(char c)
269: {
270:
271: if (isalnum((unsigned char)c)) {
272: putchar((unsigned char)c);
273: return;
274: } else if (' ' == c) {
275: putchar('+');
276: return;
277: }
278: printf("%%%.2x", c);
279: }
280:
1.1 kristaps 281: /*
1.6 kristaps 282: * HTTP-decode a string. The standard explanation is that this turns
283: * "%4e+foo" into "n foo" in the regular way. This is done in-place
284: * over the allocated string.
1.1 kristaps 285: */
286: static int
1.24 kristaps 287: http_decode(char *p)
1.1 kristaps 288: {
289: char hex[3];
1.63 schwarze 290: char *q;
1.1 kristaps 291: int c;
292:
293: hex[2] = '\0';
294:
1.63 schwarze 295: q = p;
296: for ( ; '\0' != *p; p++, q++) {
1.1 kristaps 297: if ('%' == *p) {
298: if ('\0' == (hex[0] = *(p + 1)))
299: return(0);
300: if ('\0' == (hex[1] = *(p + 2)))
301: return(0);
302: if (1 != sscanf(hex, "%x", &c))
303: return(0);
304: if ('\0' == c)
305: return(0);
306:
1.63 schwarze 307: *q = (char)c;
308: p += 2;
1.1 kristaps 309: } else
1.63 schwarze 310: *q = '+' == *p ? ' ' : *p;
1.1 kristaps 311: }
312:
1.63 schwarze 313: *q = '\0';
1.1 kristaps 314: return(1);
315: }
316:
1.6 kristaps 317: static void
318: resp_begin_http(int code, const char *msg)
319: {
320:
321: if (200 != code)
1.62 schwarze 322: printf("Status: %d %s\r\n", code, msg);
1.6 kristaps 323:
1.62 schwarze 324: printf("Content-Type: text/html; charset=utf-8\r\n"
325: "Cache-Control: no-cache\r\n"
326: "Pragma: no-cache\r\n"
327: "\r\n");
1.6 kristaps 328:
329: fflush(stdout);
330: }
331:
332: static void
333: resp_begin_html(int code, const char *msg)
334: {
335:
336: resp_begin_http(code, msg);
337:
1.29 kristaps 338: printf("<!DOCTYPE HTML PUBLIC "
339: " \"-//W3C//DTD HTML 4.01//EN\""
340: " \"http://www.w3.org/TR/html4/strict.dtd\">\n"
341: "<HTML>\n"
342: "<HEAD>\n"
343: "<META HTTP-EQUIV=\"Content-Type\""
344: " CONTENT=\"text/html; charset=utf-8\">\n"
1.32 kristaps 345: "<LINK REL=\"stylesheet\" HREF=\"%s/man-cgi.css\""
346: " TYPE=\"text/css\" media=\"all\">\n"
347: "<LINK REL=\"stylesheet\" HREF=\"%s/man.css\""
1.29 kristaps 348: " TYPE=\"text/css\" media=\"all\">\n"
1.67 schwarze 349: "<TITLE>%s</TITLE>\n"
1.29 kristaps 350: "</HEAD>\n"
351: "<BODY>\n"
1.58 schwarze 352: "<!-- Begin page content. //-->\n",
1.67 schwarze 353: CSS_DIR, CSS_DIR, CUSTOMIZE_TITLE);
1.6 kristaps 354: }
355:
356: static void
357: resp_end_html(void)
358: {
359:
1.20 kristaps 360: puts("</BODY>\n"
361: "</HTML>");
1.6 kristaps 362: }
363:
364: static void
365: resp_searchform(const struct req *req)
366: {
1.27 kristaps 367: int i;
1.13 kristaps 368:
1.67 schwarze 369: puts(CUSTOMIZE_BEGIN);
1.6 kristaps 370: puts("<!-- Begin search form. //-->");
1.32 kristaps 371: printf("<DIV ID=\"mancgi\">\n"
1.66 schwarze 372: "<FORM ACTION=\"%s\" METHOD=\"get\">\n"
1.29 kristaps 373: "<FIELDSET>\n"
1.68 schwarze 374: "<LEGEND>Manual Page Search Parameters</LEGEND>\n",
1.58 schwarze 375: scriptname);
1.68 schwarze 376:
377: /* Write query input box. */
378:
379: printf( "<TABLE><TR><TD>\n"
380: "<INPUT TYPE=\"text\" NAME=\"query\" VALUE=\"");
381: if (NULL != req->q.expr)
382: html_print(req->q.expr);
383: puts("\" SIZE=\"40\">");
384:
385: /* Write submission and reset buttons. */
386:
387: printf( "<INPUT TYPE=\"submit\" VALUE=\"Submit\">\n"
388: "<INPUT TYPE=\"reset\" VALUE=\"Reset\">\n");
389:
390: /* Write show radio button */
391:
392: printf( "</TD><TD>\n"
393: "<INPUT TYPE=\"radio\" ");
1.65 schwarze 394: if (req->q.equal)
1.68 schwarze 395: printf("CHECKED ");
396: printf( "NAME=\"apropos\" ID=\"show\" VALUE=\"0\">\n"
397: "<LABEL FOR=\"show\">Show named manual page</LABEL>\n");
398:
399: /* Write section selector. */
400:
401: printf( "</TD></TR><TR><TD>\n"
402: "<SELECT NAME=\"sec\">");
403: for (i = 0; i < sec_MAX; i++) {
404: printf("<OPTION VALUE=\"%s\"", sec_numbers[i]);
405: if (NULL != req->q.sec &&
406: 0 == strcmp(sec_numbers[i], req->q.sec))
407: printf(" SELECTED");
408: printf(">%s</OPTION>\n", sec_names[i]);
409: }
410: puts("</SELECT>");
411:
412: /* Write architecture selector. */
413:
414: puts("<SELECT NAME=\"arch\">");
415: for (i = 0; i < arch_MAX; i++) {
416: printf("<OPTION VALUE=\"%s\"", arch_names[i]);
417: if (NULL != req->q.arch &&
418: 0 == strcmp(arch_names[i], req->q.arch))
419: printf(" SELECTED");
420: printf(">%s</OPTION>\n", arch_names[i]);
421: }
422: puts("</SELECT>");
423:
424: /* Write manpath selector. */
425:
1.27 kristaps 426: if (req->psz > 1) {
1.68 schwarze 427: puts("<SELECT NAME=\"manpath\">");
1.27 kristaps 428: for (i = 0; i < (int)req->psz; i++) {
1.52 schwarze 429: printf("<OPTION ");
1.58 schwarze 430: if (NULL == req->q.manpath ? 0 == i :
431: 0 == strcmp(req->q.manpath, req->p[i]))
1.68 schwarze 432: printf("SELECTED ");
1.52 schwarze 433: printf("VALUE=\"");
434: html_print(req->p[i]);
1.27 kristaps 435: printf("\">");
1.52 schwarze 436: html_print(req->p[i]);
1.27 kristaps 437: puts("</OPTION>");
438: }
439: puts("</SELECT>");
440: }
1.68 schwarze 441:
442: /* Write search radio button */
443:
444: printf( "</TD><TD>\n"
445: "<INPUT TYPE=\"radio\" ");
446: if (0 == req->q.equal)
447: printf("CHECKED ");
448: printf( "NAME=\"apropos\" ID=\"search\" VALUE=\"1\">\n"
449: "<LABEL FOR=\"search\">Search with apropos query</LABEL>\n");
450:
451: puts("</TD></TR></TABLE>\n"
1.12 kristaps 452: "</FIELDSET>\n"
1.32 kristaps 453: "</FORM>\n"
454: "</DIV>");
1.20 kristaps 455: puts("<!-- End search form. //-->");
1.6 kristaps 456: }
457:
458: static void
459: resp_index(const struct req *req)
460: {
461:
462: resp_begin_html(200, NULL);
463: resp_searchform(req);
1.64 schwarze 464: printf("<P>\n"
465: "This web interface is documented in the "
1.69 ! schwarze 466: "<A HREF=\"%s/mandoc/man8/man.cgi.8\">man.cgi</A> "
! 467: "manual, and the "
! 468: "<A HREF=\"%s/mandoc/man1/apropos.1\">apropos</A> "
! 469: "manual explains the query syntax.\n"
1.64 schwarze 470: "</P>\n",
471: scriptname, scriptname);
1.6 kristaps 472: resp_end_html();
473: }
474:
475: static void
1.59 schwarze 476: resp_noresult(const struct req *req, const char *msg)
477: {
478: resp_begin_html(200, NULL);
479: resp_searchform(req);
480: puts("<P>");
481: puts(msg);
482: puts("</P>");
483: resp_end_html();
484: }
485:
486: static void
487: resp_error_badrequest(const char *msg)
1.9 kristaps 488: {
489:
1.59 schwarze 490: resp_begin_html(400, "Bad Request");
491: puts("<H1>Bad Request</H1>\n"
492: "<P>\n");
493: puts(msg);
494: printf("Try again from the\n"
495: "<A HREF=\"%s\">main page</A>.\n"
1.58 schwarze 496: "</P>", scriptname);
1.9 kristaps 497: resp_end_html();
498: }
499:
500: static void
1.59 schwarze 501: resp_error_internal(void)
1.7 kristaps 502: {
503: resp_begin_html(500, "Internal Server Error");
1.58 schwarze 504: puts("<P>Internal Server Error</P>");
1.7 kristaps 505: resp_end_html();
506: }
507:
508: static void
1.52 schwarze 509: resp_search(const struct req *req, struct manpage *r, size_t sz)
1.1 kristaps 510: {
1.52 schwarze 511: size_t i;
1.19 kristaps 512:
1.52 schwarze 513: if (1 == sz) {
1.6 kristaps 514: /*
515: * If we have just one result, then jump there now
516: * without any delay.
517: */
1.62 schwarze 518: printf("Status: 303 See Other\r\n");
1.66 schwarze 519: printf("Location: http://%s%s/%s/%s?",
1.58 schwarze 520: httphost, scriptname, req->q.manpath, r[0].file);
1.36 kristaps 521: http_printquery(req);
1.62 schwarze 522: printf("\r\n"
523: "Content-Type: text/html; charset=utf-8\r\n"
524: "\r\n");
1.6 kristaps 525: return;
526: }
527:
1.59 schwarze 528: qsort(r, sz, sizeof(struct manpage), cmp);
529:
1.12 kristaps 530: resp_begin_html(200, NULL);
1.19 kristaps 531: resp_searchform(req);
1.33 kristaps 532: puts("<DIV CLASS=\"results\">");
533: puts("<TABLE>");
1.1 kristaps 534:
1.41 kristaps 535: for (i = 0; i < sz; i++) {
1.20 kristaps 536: printf("<TR>\n"
537: "<TD CLASS=\"title\">\n"
1.66 schwarze 538: "<A HREF=\"%s/%s/%s?",
1.58 schwarze 539: scriptname, req->q.manpath, r[i].file);
1.36 kristaps 540: html_printquery(req);
541: printf("\">");
1.52 schwarze 542: html_print(r[i].names);
543: printf("</A>\n"
1.20 kristaps 544: "</TD>\n"
545: "<TD CLASS=\"desc\">");
1.52 schwarze 546: html_print(r[i].output);
1.20 kristaps 547: puts("</TD>\n"
548: "</TR>");
1.1 kristaps 549: }
1.16 kristaps 550:
1.33 kristaps 551: puts("</TABLE>\n"
552: "</DIV>");
1.6 kristaps 553: resp_end_html();
554: }
555:
1.1 kristaps 556: static void
1.32 kristaps 557: catman(const struct req *req, const char *file)
1.9 kristaps 558: {
1.10 kristaps 559: FILE *f;
560: size_t len;
561: int i;
562: char *p;
563: int italic, bold;
1.9 kristaps 564:
1.10 kristaps 565: if (NULL == (f = fopen(file, "r"))) {
1.59 schwarze 566: resp_error_badrequest(
567: "You specified an invalid manual file.");
1.9 kristaps 568: return;
569: }
570:
1.32 kristaps 571: resp_begin_html(200, NULL);
572: resp_searchform(req);
573: puts("<DIV CLASS=\"catman\">\n"
574: "<PRE>");
1.10 kristaps 575:
576: while (NULL != (p = fgetln(f, &len))) {
577: bold = italic = 0;
578: for (i = 0; i < (int)len - 1; i++) {
579: /*
580: * This means that the catpage is out of state.
581: * Ignore it and keep going (although the
582: * catpage is bogus).
583: */
584:
585: if ('\b' == p[i] || '\n' == p[i])
586: continue;
587:
588: /*
589: * Print a regular character.
590: * Close out any bold/italic scopes.
591: * If we're in back-space mode, make sure we'll
592: * have something to enter when we backspace.
593: */
594:
595: if ('\b' != p[i + 1]) {
596: if (italic)
597: printf("</I>");
598: if (bold)
599: printf("</B>");
600: italic = bold = 0;
601: html_putchar(p[i]);
602: continue;
603: } else if (i + 2 >= (int)len)
604: continue;
605:
606: /* Italic mode. */
607:
608: if ('_' == p[i]) {
609: if (bold)
610: printf("</B>");
611: if ( ! italic)
612: printf("<I>");
613: bold = 0;
614: italic = 1;
615: i += 2;
616: html_putchar(p[i]);
617: continue;
618: }
619:
620: /*
621: * Handle funny behaviour troff-isms.
622: * These grok'd from the original man2html.c.
623: */
624:
625: if (('+' == p[i] && 'o' == p[i + 2]) ||
626: ('o' == p[i] && '+' == p[i + 2]) ||
627: ('|' == p[i] && '=' == p[i + 2]) ||
628: ('=' == p[i] && '|' == p[i + 2]) ||
629: ('*' == p[i] && '=' == p[i + 2]) ||
630: ('=' == p[i] && '*' == p[i + 2]) ||
631: ('*' == p[i] && '|' == p[i + 2]) ||
632: ('|' == p[i] && '*' == p[i + 2])) {
633: if (italic)
634: printf("</I>");
635: if (bold)
636: printf("</B>");
637: italic = bold = 0;
638: putchar('*');
639: i += 2;
640: continue;
641: } else if (('|' == p[i] && '-' == p[i + 2]) ||
642: ('-' == p[i] && '|' == p[i + 1]) ||
643: ('+' == p[i] && '-' == p[i + 1]) ||
644: ('-' == p[i] && '+' == p[i + 1]) ||
645: ('+' == p[i] && '|' == p[i + 1]) ||
646: ('|' == p[i] && '+' == p[i + 1])) {
647: if (italic)
648: printf("</I>");
649: if (bold)
650: printf("</B>");
651: italic = bold = 0;
652: putchar('+');
653: i += 2;
654: continue;
655: }
656:
657: /* Bold mode. */
658:
659: if (italic)
660: printf("</I>");
661: if ( ! bold)
662: printf("<B>");
663: bold = 1;
664: italic = 0;
665: i += 2;
666: html_putchar(p[i]);
667: }
668:
669: /*
670: * Clean up the last character.
671: * We can get to a newline; don't print that.
672: */
1.9 kristaps 673:
1.10 kristaps 674: if (italic)
675: printf("</I>");
676: if (bold)
677: printf("</B>");
1.9 kristaps 678:
1.10 kristaps 679: if (i == (int)len - 1 && '\n' != p[i])
680: html_putchar(p[i]);
1.9 kristaps 681:
1.10 kristaps 682: putchar('\n');
683: }
684:
685: puts("</PRE>\n"
1.32 kristaps 686: "</DIV>\n"
1.10 kristaps 687: "</BODY>\n"
688: "</HTML>");
689:
690: fclose(f);
1.9 kristaps 691: }
692:
693: static void
1.32 kristaps 694: format(const struct req *req, const char *file)
1.7 kristaps 695: {
1.8 kristaps 696: struct mparse *mp;
697: int fd;
698: struct mdoc *mdoc;
699: struct man *man;
700: void *vp;
701: enum mandoclevel rc;
1.45 schwarze 702: char opts[PATH_MAX + 128];
1.7 kristaps 703:
1.8 kristaps 704: if (-1 == (fd = open(file, O_RDONLY, 0))) {
1.59 schwarze 705: resp_error_badrequest(
706: "You specified an invalid manual file.");
1.7 kristaps 707: return;
708: }
709:
1.56 schwarze 710: mp = mparse_alloc(MPARSE_SO, MANDOCLEVEL_FATAL, NULL,
1.58 schwarze 711: req->q.manpath);
1.8 kristaps 712: rc = mparse_readfd(mp, fd, file);
713: close(fd);
1.7 kristaps 714:
1.8 kristaps 715: if (rc >= MANDOCLEVEL_FATAL) {
1.59 schwarze 716: fprintf(stderr, "fatal mandoc error: %s/%s\n",
717: req->q.manpath, file);
718: resp_error_internal();
1.7 kristaps 719: return;
720: }
721:
1.52 schwarze 722: snprintf(opts, sizeof(opts),
1.66 schwarze 723: "fragment,man=%s?query=%%N&sec=%%S",
1.58 schwarze 724: scriptname);
1.10 kristaps 725:
1.49 schwarze 726: mparse_result(mp, &mdoc, &man, NULL);
1.32 kristaps 727: if (NULL == man && NULL == mdoc) {
1.59 schwarze 728: fprintf(stderr, "fatal mandoc error: %s/%s\n",
729: req->q.manpath, file);
730: resp_error_internal();
1.32 kristaps 731: mparse_free(mp);
732: return;
733: }
734:
735: resp_begin_html(200, NULL);
736: resp_searchform(req);
737:
1.10 kristaps 738: vp = html_alloc(opts);
1.7 kristaps 739:
1.32 kristaps 740: if (NULL != mdoc)
1.8 kristaps 741: html_mdoc(vp, mdoc);
1.32 kristaps 742: else
1.8 kristaps 743: html_man(vp, man);
1.32 kristaps 744:
745: puts("</BODY>\n"
746: "</HTML>");
1.7 kristaps 747:
1.8 kristaps 748: html_free(vp);
749: mparse_free(mp);
1.7 kristaps 750: }
751:
752: static void
1.66 schwarze 753: pg_show(const struct req *req, const char *path)
1.1 kristaps 754: {
1.6 kristaps 755: char *sub;
1.25 kristaps 756:
757: if (NULL == path || NULL == (sub = strchr(path, '/'))) {
1.59 schwarze 758: resp_error_badrequest(
759: "You did not specify a page to show.");
1.25 kristaps 760: return;
761: }
762: *sub++ = '\0';
1.6 kristaps 763:
1.24 kristaps 764: /*
1.58 schwarze 765: * Begin by chdir()ing into the manpath.
1.24 kristaps 766: * This way we can pick up the database files, which are
767: * relative to the manpath root.
768: */
769:
1.52 schwarze 770: if (-1 == chdir(path)) {
1.59 schwarze 771: resp_error_badrequest(
772: "You specified an invalid manpath.");
1.24 kristaps 773: return;
774: }
775:
1.52 schwarze 776: if ('c' == *sub)
777: catman(req, sub);
778: else
779: format(req, sub);
1.6 kristaps 780: }
781:
782: static void
1.66 schwarze 783: pg_search(const struct req *req)
1.6 kristaps 784: {
1.52 schwarze 785: struct mansearch search;
786: struct manpaths paths;
787: struct manpage *res;
788: char **cp;
789: const char *ep, *start;
790: size_t ressz;
791: int i, sz;
1.6 kristaps 792:
793: /*
1.24 kristaps 794: * Begin by chdir()ing into the root of the manpath.
795: * This way we can pick up the database files, which are
796: * relative to the manpath root.
797: */
798:
1.58 schwarze 799: if (-1 == (chdir(req->q.manpath))) {
1.59 schwarze 800: resp_error_badrequest(
801: "You specified an invalid manpath.");
1.24 kristaps 802: return;
803: }
804:
1.52 schwarze 805: search.arch = req->q.arch;
806: search.sec = req->q.sec;
1.65 schwarze 807: search.deftype = req->q.equal ? TYPE_Nm : (TYPE_Nm | TYPE_Nd);
808: search.flags = req->q.equal ? MANSEARCH_MAN : 0;
1.52 schwarze 809:
810: paths.sz = 1;
811: paths.paths = mandoc_malloc(sizeof(char *));
812: paths.paths[0] = mandoc_strdup(".");
1.24 kristaps 813:
814: /*
815: * Poor man's tokenisation: just break apart by spaces.
1.6 kristaps 816: * Yes, this is half-ass. But it works for now.
817: */
818:
1.52 schwarze 819: ep = req->q.expr;
1.6 kristaps 820: while (ep && isspace((unsigned char)*ep))
821: ep++;
822:
1.52 schwarze 823: sz = 0;
824: cp = NULL;
1.6 kristaps 825: while (ep && '\0' != *ep) {
1.51 schwarze 826: cp = mandoc_reallocarray(cp, sz + 1, sizeof(char *));
1.6 kristaps 827: start = ep;
828: while ('\0' != *ep && ! isspace((unsigned char)*ep))
829: ep++;
830: cp[sz] = mandoc_malloc((ep - start) + 1);
831: memcpy(cp[sz], start, ep - start);
832: cp[sz++][ep - start] = '\0';
833: while (isspace((unsigned char)*ep))
834: ep++;
835: }
836:
1.59 schwarze 837: if (0 == mansearch(&search, &paths, sz, cp, "Nd", &res, &ressz))
838: resp_noresult(req, "You entered an invalid query.");
839: else if (0 == ressz)
840: resp_noresult(req, "No results found.");
841: else
1.52 schwarze 842: resp_search(req, res, ressz);
1.6 kristaps 843:
844: for (i = 0; i < sz; i++)
845: free(cp[i]);
1.52 schwarze 846: free(cp);
847:
848: for (i = 0; i < (int)ressz; i++) {
849: free(res[i].file);
850: free(res[i].names);
851: free(res[i].output);
852: }
853: free(res);
1.6 kristaps 854:
1.52 schwarze 855: free(paths.paths[0]);
856: free(paths.paths);
1.1 kristaps 857: }
858:
859: int
860: main(void)
861: {
1.66 schwarze 862: struct req req;
863: const char *path;
864: char *querystring;
1.1 kristaps 865: int i;
1.6 kristaps 866:
1.24 kristaps 867: /* Scan our run-time environment. */
1.6 kristaps 868:
1.58 schwarze 869: if (NULL == (scriptname = getenv("SCRIPT_NAME")))
870: scriptname = "";
1.6 kristaps 871:
1.58 schwarze 872: if (NULL == (httphost = getenv("HTTP_HOST")))
873: httphost = "localhost";
1.24 kristaps 874:
875: /*
1.67 schwarze 876: * First we change directory into the MAN_DIR so that
1.24 kristaps 877: * subsequent scanning for manpath directories is rooted
878: * relative to the same position.
879: */
880:
1.67 schwarze 881: if (-1 == chdir(MAN_DIR)) {
1.58 schwarze 882: fprintf(stderr, "MAN_DIR: %s: %s\n",
1.67 schwarze 883: MAN_DIR, strerror(errno));
1.59 schwarze 884: resp_error_internal();
1.24 kristaps 885: return(EXIT_FAILURE);
886: }
887:
888: memset(&req, 0, sizeof(struct req));
1.54 schwarze 889: pathgen(&req);
1.1 kristaps 890:
1.24 kristaps 891: /* Next parse out the query string. */
1.1 kristaps 892:
1.58 schwarze 893: if (NULL != (querystring = getenv("QUERY_STRING")))
894: http_parse(&req, querystring);
1.1 kristaps 895:
1.66 schwarze 896: /* Dispatch to the three different pages. */
1.1 kristaps 897:
1.66 schwarze 898: path = getenv("PATH_INFO");
899: if (NULL == path)
900: path = "";
901: else if ('/' == *path)
902: path++;
903:
904: if ('\0' != *path)
905: pg_show(&req, path);
906: else if (NULL != req.q.expr)
907: pg_search(&req);
908: else
909: resp_index(&req);
1.1 kristaps 910:
1.52 schwarze 911: for (i = 0; i < (int)req.psz; i++)
912: free(req.p[i]);
1.24 kristaps 913: free(req.p);
1.1 kristaps 914: return(EXIT_SUCCESS);
915: }
1.15 kristaps 916:
917: static int
918: cmp(const void *p1, const void *p2)
919: {
920:
1.52 schwarze 921: return(strcasecmp(((const struct manpage *)p1)->names,
922: ((const struct manpage *)p2)->names));
1.24 kristaps 923: }
924:
925: /*
926: * Scan for indexable paths.
927: */
928: static void
1.54 schwarze 929: pathgen(struct req *req)
1.24 kristaps 930: {
1.54 schwarze 931: FILE *fp;
932: char *dp;
933: size_t dpsz;
934:
935: if (NULL == (fp = fopen("manpath.conf", "r")))
936: return;
1.24 kristaps 937:
1.54 schwarze 938: while (NULL != (dp = fgetln(fp, &dpsz))) {
1.55 schwarze 939: if ('\n' == dp[dpsz - 1])
940: dpsz--;
1.54 schwarze 941: req->p = mandoc_realloc(req->p,
942: (req->psz + 1) * sizeof(char *));
943: req->p[req->psz++] = mandoc_strndup(dp, dpsz);
1.24 kristaps 944: }
945: }
CVSweb