Annotation of mandoc/cgi.c, Revision 1.75
1.75 ! schwarze 1: /* $Id: cgi.c,v 1.74 2014/07/18 14:46:25 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.72 schwarze 67: static void pg_error_badrequest(const char *);
68: static void pg_error_internal(void);
69: static void pg_index(const struct req *);
70: static void pg_noresult(const struct req *, const char *);
1.66 schwarze 71: static void pg_search(const struct req *);
1.72 schwarze 72: static void pg_searchres(const struct req *,
73: struct manpage *, size_t);
1.66 schwarze 74: static void pg_show(const struct req *, const char *);
1.6 kristaps 75: static void resp_begin_html(int, const char *);
76: static void resp_begin_http(int, const char *);
77: static void resp_end_html(void);
78: static void resp_searchform(const struct req *);
1.70 schwarze 79: static void resp_show(const struct req *, const char *);
1.6 kristaps 80:
1.58 schwarze 81: static const char *scriptname; /* CGI script name */
1.1 kristaps 82:
1.70 schwarze 83: static const int sec_prios[] = {1, 4, 5, 8, 6, 3, 7, 2, 9};
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;
1.73 schwarze 252: else if (0 == strcmp(key, "manpath")) {
253: #ifdef COMPAT_OLDURI
254: if (0 == strncmp(val, "OpenBSD ", 8)) {
255: val[7] = '-';
256: if ('C' == val[8])
257: val[8] = 'c';
258: }
259: #endif
1.58 schwarze 260: req->q.manpath = val;
1.73 schwarze 261: } else if (0 == strcmp(key, "apropos"))
1.65 schwarze 262: req->q.equal = !strcmp(val, "0");
1.73 schwarze 263: else if (0 == strcmp(key, "sec")) {
264: if (strcmp(val, "0"))
265: req->q.sec = val;
266: #ifdef COMPAT_OLDURI
267: } else if (0 == strcmp(key, "sektion")) {
1.65 schwarze 268: if (strcmp(val, "0"))
269: req->q.sec = val;
1.73 schwarze 270: #endif
1.65 schwarze 271: } else if (0 == strcmp(key, "arch")) {
272: if (strcmp(val, "default"))
273: req->q.arch = val;
274: }
1.24 kristaps 275: }
1.1 kristaps 276: }
277:
1.36 kristaps 278: static void
279: http_putchar(char c)
280: {
281:
282: if (isalnum((unsigned char)c)) {
283: putchar((unsigned char)c);
284: return;
285: } else if (' ' == c) {
286: putchar('+');
287: return;
288: }
289: printf("%%%.2x", c);
290: }
291:
1.1 kristaps 292: /*
1.6 kristaps 293: * HTTP-decode a string. The standard explanation is that this turns
294: * "%4e+foo" into "n foo" in the regular way. This is done in-place
295: * over the allocated string.
1.1 kristaps 296: */
297: static int
1.24 kristaps 298: http_decode(char *p)
1.1 kristaps 299: {
300: char hex[3];
1.63 schwarze 301: char *q;
1.1 kristaps 302: int c;
303:
304: hex[2] = '\0';
305:
1.63 schwarze 306: q = p;
307: for ( ; '\0' != *p; p++, q++) {
1.1 kristaps 308: if ('%' == *p) {
309: if ('\0' == (hex[0] = *(p + 1)))
310: return(0);
311: if ('\0' == (hex[1] = *(p + 2)))
312: return(0);
313: if (1 != sscanf(hex, "%x", &c))
314: return(0);
315: if ('\0' == c)
316: return(0);
317:
1.63 schwarze 318: *q = (char)c;
319: p += 2;
1.1 kristaps 320: } else
1.63 schwarze 321: *q = '+' == *p ? ' ' : *p;
1.1 kristaps 322: }
323:
1.63 schwarze 324: *q = '\0';
1.1 kristaps 325: return(1);
326: }
327:
1.6 kristaps 328: static void
329: resp_begin_http(int code, const char *msg)
330: {
331:
332: if (200 != code)
1.62 schwarze 333: printf("Status: %d %s\r\n", code, msg);
1.6 kristaps 334:
1.62 schwarze 335: printf("Content-Type: text/html; charset=utf-8\r\n"
336: "Cache-Control: no-cache\r\n"
337: "Pragma: no-cache\r\n"
338: "\r\n");
1.6 kristaps 339:
340: fflush(stdout);
341: }
342:
343: static void
344: resp_begin_html(int code, const char *msg)
345: {
346:
347: resp_begin_http(code, msg);
348:
1.29 kristaps 349: printf("<!DOCTYPE HTML PUBLIC "
350: " \"-//W3C//DTD HTML 4.01//EN\""
351: " \"http://www.w3.org/TR/html4/strict.dtd\">\n"
352: "<HTML>\n"
353: "<HEAD>\n"
354: "<META HTTP-EQUIV=\"Content-Type\""
355: " CONTENT=\"text/html; charset=utf-8\">\n"
1.32 kristaps 356: "<LINK REL=\"stylesheet\" HREF=\"%s/man-cgi.css\""
357: " TYPE=\"text/css\" media=\"all\">\n"
358: "<LINK REL=\"stylesheet\" HREF=\"%s/man.css\""
1.29 kristaps 359: " TYPE=\"text/css\" media=\"all\">\n"
1.67 schwarze 360: "<TITLE>%s</TITLE>\n"
1.29 kristaps 361: "</HEAD>\n"
362: "<BODY>\n"
1.58 schwarze 363: "<!-- Begin page content. //-->\n",
1.67 schwarze 364: CSS_DIR, CSS_DIR, CUSTOMIZE_TITLE);
1.6 kristaps 365: }
366:
367: static void
368: resp_end_html(void)
369: {
370:
1.20 kristaps 371: puts("</BODY>\n"
372: "</HTML>");
1.6 kristaps 373: }
374:
375: static void
376: resp_searchform(const struct req *req)
377: {
1.27 kristaps 378: int i;
1.13 kristaps 379:
1.67 schwarze 380: puts(CUSTOMIZE_BEGIN);
1.6 kristaps 381: puts("<!-- Begin search form. //-->");
1.32 kristaps 382: printf("<DIV ID=\"mancgi\">\n"
1.66 schwarze 383: "<FORM ACTION=\"%s\" METHOD=\"get\">\n"
1.29 kristaps 384: "<FIELDSET>\n"
1.68 schwarze 385: "<LEGEND>Manual Page Search Parameters</LEGEND>\n",
1.58 schwarze 386: scriptname);
1.68 schwarze 387:
388: /* Write query input box. */
389:
390: printf( "<TABLE><TR><TD>\n"
391: "<INPUT TYPE=\"text\" NAME=\"query\" VALUE=\"");
392: if (NULL != req->q.expr)
393: html_print(req->q.expr);
394: puts("\" SIZE=\"40\">");
395:
396: /* Write submission and reset buttons. */
397:
398: printf( "<INPUT TYPE=\"submit\" VALUE=\"Submit\">\n"
399: "<INPUT TYPE=\"reset\" VALUE=\"Reset\">\n");
400:
401: /* Write show radio button */
402:
403: printf( "</TD><TD>\n"
404: "<INPUT TYPE=\"radio\" ");
1.65 schwarze 405: if (req->q.equal)
1.68 schwarze 406: printf("CHECKED ");
407: printf( "NAME=\"apropos\" ID=\"show\" VALUE=\"0\">\n"
408: "<LABEL FOR=\"show\">Show named manual page</LABEL>\n");
409:
410: /* Write section selector. */
411:
412: printf( "</TD></TR><TR><TD>\n"
413: "<SELECT NAME=\"sec\">");
414: for (i = 0; i < sec_MAX; i++) {
415: printf("<OPTION VALUE=\"%s\"", sec_numbers[i]);
416: if (NULL != req->q.sec &&
417: 0 == strcmp(sec_numbers[i], req->q.sec))
418: printf(" SELECTED");
419: printf(">%s</OPTION>\n", sec_names[i]);
420: }
421: puts("</SELECT>");
422:
423: /* Write architecture selector. */
424:
425: puts("<SELECT NAME=\"arch\">");
426: for (i = 0; i < arch_MAX; i++) {
427: printf("<OPTION VALUE=\"%s\"", arch_names[i]);
428: if (NULL != req->q.arch &&
429: 0 == strcmp(arch_names[i], req->q.arch))
430: printf(" SELECTED");
431: printf(">%s</OPTION>\n", arch_names[i]);
432: }
433: puts("</SELECT>");
434:
435: /* Write manpath selector. */
436:
1.27 kristaps 437: if (req->psz > 1) {
1.68 schwarze 438: puts("<SELECT NAME=\"manpath\">");
1.27 kristaps 439: for (i = 0; i < (int)req->psz; i++) {
1.52 schwarze 440: printf("<OPTION ");
1.58 schwarze 441: if (NULL == req->q.manpath ? 0 == i :
442: 0 == strcmp(req->q.manpath, req->p[i]))
1.68 schwarze 443: printf("SELECTED ");
1.52 schwarze 444: printf("VALUE=\"");
445: html_print(req->p[i]);
1.27 kristaps 446: printf("\">");
1.52 schwarze 447: html_print(req->p[i]);
1.27 kristaps 448: puts("</OPTION>");
449: }
450: puts("</SELECT>");
451: }
1.68 schwarze 452:
453: /* Write search radio button */
454:
455: printf( "</TD><TD>\n"
456: "<INPUT TYPE=\"radio\" ");
457: if (0 == req->q.equal)
458: printf("CHECKED ");
459: printf( "NAME=\"apropos\" ID=\"search\" VALUE=\"1\">\n"
460: "<LABEL FOR=\"search\">Search with apropos query</LABEL>\n");
461:
462: puts("</TD></TR></TABLE>\n"
1.12 kristaps 463: "</FIELDSET>\n"
1.32 kristaps 464: "</FORM>\n"
465: "</DIV>");
1.20 kristaps 466: puts("<!-- End search form. //-->");
1.6 kristaps 467: }
468:
469: static void
1.72 schwarze 470: pg_index(const struct req *req)
1.6 kristaps 471: {
472:
473: resp_begin_html(200, NULL);
474: resp_searchform(req);
1.64 schwarze 475: printf("<P>\n"
476: "This web interface is documented in the "
1.69 schwarze 477: "<A HREF=\"%s/mandoc/man8/man.cgi.8\">man.cgi</A> "
478: "manual, and the "
479: "<A HREF=\"%s/mandoc/man1/apropos.1\">apropos</A> "
480: "manual explains the query syntax.\n"
1.64 schwarze 481: "</P>\n",
482: scriptname, scriptname);
1.6 kristaps 483: resp_end_html();
484: }
485:
486: static void
1.72 schwarze 487: pg_noresult(const struct req *req, const char *msg)
1.59 schwarze 488: {
489: resp_begin_html(200, NULL);
490: resp_searchform(req);
491: puts("<P>");
492: puts(msg);
493: puts("</P>");
494: resp_end_html();
495: }
496:
497: static void
1.72 schwarze 498: pg_error_badrequest(const char *msg)
1.9 kristaps 499: {
500:
1.59 schwarze 501: resp_begin_html(400, "Bad Request");
502: puts("<H1>Bad Request</H1>\n"
503: "<P>\n");
504: puts(msg);
505: printf("Try again from the\n"
506: "<A HREF=\"%s\">main page</A>.\n"
1.58 schwarze 507: "</P>", scriptname);
1.9 kristaps 508: resp_end_html();
509: }
510:
511: static void
1.72 schwarze 512: pg_error_internal(void)
1.7 kristaps 513: {
514: resp_begin_html(500, "Internal Server Error");
1.58 schwarze 515: puts("<P>Internal Server Error</P>");
1.7 kristaps 516: resp_end_html();
517: }
518:
519: static void
1.72 schwarze 520: pg_searchres(const struct req *req, struct manpage *r, size_t sz)
1.1 kristaps 521: {
1.70 schwarze 522: size_t i, iuse, isec;
523: int prio, priouse;
524: char sec;
1.19 kristaps 525:
1.52 schwarze 526: if (1 == sz) {
1.6 kristaps 527: /*
528: * If we have just one result, then jump there now
529: * without any delay.
530: */
1.62 schwarze 531: printf("Status: 303 See Other\r\n");
1.75 ! schwarze 532: printf("Location: %s/%s/%s?",
! 533: scriptname, req->q.manpath, r[0].file);
1.36 kristaps 534: http_printquery(req);
1.62 schwarze 535: printf("\r\n"
536: "Content-Type: text/html; charset=utf-8\r\n"
537: "\r\n");
1.6 kristaps 538: return;
539: }
540:
1.59 schwarze 541: qsort(r, sz, sizeof(struct manpage), cmp);
542:
1.12 kristaps 543: resp_begin_html(200, NULL);
1.19 kristaps 544: resp_searchform(req);
1.33 kristaps 545: puts("<DIV CLASS=\"results\">");
546: puts("<TABLE>");
1.1 kristaps 547:
1.41 kristaps 548: for (i = 0; i < sz; i++) {
1.20 kristaps 549: printf("<TR>\n"
550: "<TD CLASS=\"title\">\n"
1.66 schwarze 551: "<A HREF=\"%s/%s/%s?",
1.58 schwarze 552: scriptname, req->q.manpath, r[i].file);
1.36 kristaps 553: html_printquery(req);
554: printf("\">");
1.52 schwarze 555: html_print(r[i].names);
556: printf("</A>\n"
1.20 kristaps 557: "</TD>\n"
558: "<TD CLASS=\"desc\">");
1.52 schwarze 559: html_print(r[i].output);
1.20 kristaps 560: puts("</TD>\n"
561: "</TR>");
1.1 kristaps 562: }
1.16 kristaps 563:
1.33 kristaps 564: puts("</TABLE>\n"
565: "</DIV>");
1.70 schwarze 566:
567: /*
568: * In man(1) mode, show one of the pages
569: * even if more than one is found.
570: */
571:
572: if (req->q.equal) {
573: puts("<HR>");
574: iuse = 0;
575: priouse = 10;
576: for (i = 0; i < sz; i++) {
577: isec = strcspn(r[i].file, "123456789");
578: sec = r[i].file[isec];
579: if ('\0' == sec)
580: continue;
581: prio = sec_prios[sec - '1'];
582: if (prio >= priouse)
583: continue;
584: priouse = prio;
585: iuse = i;
586: }
587: resp_show(req, r[iuse].file);
588: }
589:
1.6 kristaps 590: resp_end_html();
591: }
592:
1.1 kristaps 593: static void
1.32 kristaps 594: catman(const struct req *req, const char *file)
1.9 kristaps 595: {
1.10 kristaps 596: FILE *f;
597: size_t len;
598: int i;
599: char *p;
600: int italic, bold;
1.9 kristaps 601:
1.10 kristaps 602: if (NULL == (f = fopen(file, "r"))) {
1.70 schwarze 603: puts("<P>You specified an invalid manual file.</P>");
1.9 kristaps 604: return;
605: }
606:
1.32 kristaps 607: puts("<DIV CLASS=\"catman\">\n"
608: "<PRE>");
1.10 kristaps 609:
610: while (NULL != (p = fgetln(f, &len))) {
611: bold = italic = 0;
612: for (i = 0; i < (int)len - 1; i++) {
613: /*
614: * This means that the catpage is out of state.
615: * Ignore it and keep going (although the
616: * catpage is bogus).
617: */
618:
619: if ('\b' == p[i] || '\n' == p[i])
620: continue;
621:
622: /*
623: * Print a regular character.
624: * Close out any bold/italic scopes.
625: * If we're in back-space mode, make sure we'll
626: * have something to enter when we backspace.
627: */
628:
629: if ('\b' != p[i + 1]) {
630: if (italic)
631: printf("</I>");
632: if (bold)
633: printf("</B>");
634: italic = bold = 0;
635: html_putchar(p[i]);
636: continue;
637: } else if (i + 2 >= (int)len)
638: continue;
639:
640: /* Italic mode. */
641:
642: if ('_' == p[i]) {
643: if (bold)
644: printf("</B>");
645: if ( ! italic)
646: printf("<I>");
647: bold = 0;
648: italic = 1;
649: i += 2;
650: html_putchar(p[i]);
651: continue;
652: }
653:
654: /*
655: * Handle funny behaviour troff-isms.
656: * These grok'd from the original man2html.c.
657: */
658:
659: if (('+' == p[i] && 'o' == p[i + 2]) ||
660: ('o' == p[i] && '+' == p[i + 2]) ||
661: ('|' == p[i] && '=' == p[i + 2]) ||
662: ('=' == p[i] && '|' == p[i + 2]) ||
663: ('*' == p[i] && '=' == p[i + 2]) ||
664: ('=' == p[i] && '*' == p[i + 2]) ||
665: ('*' == p[i] && '|' == p[i + 2]) ||
666: ('|' == p[i] && '*' == p[i + 2])) {
667: if (italic)
668: printf("</I>");
669: if (bold)
670: printf("</B>");
671: italic = bold = 0;
672: putchar('*');
673: i += 2;
674: continue;
675: } else if (('|' == p[i] && '-' == p[i + 2]) ||
676: ('-' == p[i] && '|' == p[i + 1]) ||
677: ('+' == p[i] && '-' == p[i + 1]) ||
678: ('-' == p[i] && '+' == p[i + 1]) ||
679: ('+' == p[i] && '|' == p[i + 1]) ||
680: ('|' == p[i] && '+' == p[i + 1])) {
681: if (italic)
682: printf("</I>");
683: if (bold)
684: printf("</B>");
685: italic = bold = 0;
686: putchar('+');
687: i += 2;
688: continue;
689: }
690:
691: /* Bold mode. */
692:
693: if (italic)
694: printf("</I>");
695: if ( ! bold)
696: printf("<B>");
697: bold = 1;
698: italic = 0;
699: i += 2;
700: html_putchar(p[i]);
701: }
702:
703: /*
704: * Clean up the last character.
705: * We can get to a newline; don't print that.
706: */
1.9 kristaps 707:
1.10 kristaps 708: if (italic)
709: printf("</I>");
710: if (bold)
711: printf("</B>");
1.9 kristaps 712:
1.10 kristaps 713: if (i == (int)len - 1 && '\n' != p[i])
714: html_putchar(p[i]);
1.9 kristaps 715:
1.10 kristaps 716: putchar('\n');
717: }
718:
719: puts("</PRE>\n"
1.70 schwarze 720: "</DIV>");
1.10 kristaps 721:
722: fclose(f);
1.9 kristaps 723: }
724:
725: static void
1.32 kristaps 726: format(const struct req *req, const char *file)
1.7 kristaps 727: {
1.8 kristaps 728: struct mparse *mp;
729: int fd;
730: struct mdoc *mdoc;
731: struct man *man;
732: void *vp;
733: enum mandoclevel rc;
1.45 schwarze 734: char opts[PATH_MAX + 128];
1.7 kristaps 735:
1.8 kristaps 736: if (-1 == (fd = open(file, O_RDONLY, 0))) {
1.70 schwarze 737: puts("<P>You specified an invalid manual file.</P>");
1.7 kristaps 738: return;
739: }
740:
1.56 schwarze 741: mp = mparse_alloc(MPARSE_SO, MANDOCLEVEL_FATAL, NULL,
1.58 schwarze 742: req->q.manpath);
1.8 kristaps 743: rc = mparse_readfd(mp, fd, file);
744: close(fd);
1.7 kristaps 745:
1.8 kristaps 746: if (rc >= MANDOCLEVEL_FATAL) {
1.59 schwarze 747: fprintf(stderr, "fatal mandoc error: %s/%s\n",
748: req->q.manpath, file);
1.72 schwarze 749: pg_error_internal();
1.7 kristaps 750: return;
751: }
752:
1.52 schwarze 753: snprintf(opts, sizeof(opts),
1.66 schwarze 754: "fragment,man=%s?query=%%N&sec=%%S",
1.58 schwarze 755: scriptname);
1.10 kristaps 756:
1.49 schwarze 757: mparse_result(mp, &mdoc, &man, NULL);
1.32 kristaps 758: if (NULL == man && NULL == mdoc) {
1.59 schwarze 759: fprintf(stderr, "fatal mandoc error: %s/%s\n",
760: req->q.manpath, file);
1.72 schwarze 761: pg_error_internal();
1.32 kristaps 762: mparse_free(mp);
763: return;
764: }
765:
1.10 kristaps 766: vp = html_alloc(opts);
1.7 kristaps 767:
1.32 kristaps 768: if (NULL != mdoc)
1.8 kristaps 769: html_mdoc(vp, mdoc);
1.32 kristaps 770: else
1.8 kristaps 771: html_man(vp, man);
1.32 kristaps 772:
1.8 kristaps 773: html_free(vp);
774: mparse_free(mp);
1.7 kristaps 775: }
776:
777: static void
1.70 schwarze 778: resp_show(const struct req *req, const char *file)
779: {
1.71 schwarze 780: if ('.' == file[0] || '/' == file[1])
781: file += 2;
1.70 schwarze 782:
783: if ('c' == *file)
784: catman(req, file);
785: else
786: format(req, file);
787: }
788:
789: static void
1.66 schwarze 790: pg_show(const struct req *req, const char *path)
1.1 kristaps 791: {
1.6 kristaps 792: char *sub;
1.25 kristaps 793:
794: if (NULL == path || NULL == (sub = strchr(path, '/'))) {
1.72 schwarze 795: pg_error_badrequest(
1.59 schwarze 796: "You did not specify a page to show.");
1.25 kristaps 797: return;
798: }
799: *sub++ = '\0';
1.6 kristaps 800:
1.24 kristaps 801: /*
1.58 schwarze 802: * Begin by chdir()ing into the manpath.
1.24 kristaps 803: * This way we can pick up the database files, which are
804: * relative to the manpath root.
805: */
806:
1.52 schwarze 807: if (-1 == chdir(path)) {
1.72 schwarze 808: pg_error_badrequest(
1.59 schwarze 809: "You specified an invalid manpath.");
1.24 kristaps 810: return;
811: }
812:
1.70 schwarze 813: resp_begin_html(200, NULL);
814: resp_searchform(req);
815: resp_show(req, sub);
816: resp_end_html();
1.6 kristaps 817: }
818:
819: static void
1.66 schwarze 820: pg_search(const struct req *req)
1.6 kristaps 821: {
1.52 schwarze 822: struct mansearch search;
823: struct manpaths paths;
824: struct manpage *res;
825: char **cp;
826: const char *ep, *start;
827: size_t ressz;
828: int i, sz;
1.6 kristaps 829:
830: /*
1.24 kristaps 831: * Begin by chdir()ing into the root of the manpath.
832: * This way we can pick up the database files, which are
833: * relative to the manpath root.
834: */
835:
1.58 schwarze 836: if (-1 == (chdir(req->q.manpath))) {
1.72 schwarze 837: pg_error_badrequest(
1.59 schwarze 838: "You specified an invalid manpath.");
1.24 kristaps 839: return;
840: }
841:
1.52 schwarze 842: search.arch = req->q.arch;
843: search.sec = req->q.sec;
1.65 schwarze 844: search.deftype = req->q.equal ? TYPE_Nm : (TYPE_Nm | TYPE_Nd);
845: search.flags = req->q.equal ? MANSEARCH_MAN : 0;
1.52 schwarze 846:
847: paths.sz = 1;
848: paths.paths = mandoc_malloc(sizeof(char *));
849: paths.paths[0] = mandoc_strdup(".");
1.24 kristaps 850:
851: /*
852: * Poor man's tokenisation: just break apart by spaces.
1.6 kristaps 853: * Yes, this is half-ass. But it works for now.
854: */
855:
1.52 schwarze 856: ep = req->q.expr;
1.6 kristaps 857: while (ep && isspace((unsigned char)*ep))
858: ep++;
859:
1.52 schwarze 860: sz = 0;
861: cp = NULL;
1.6 kristaps 862: while (ep && '\0' != *ep) {
1.51 schwarze 863: cp = mandoc_reallocarray(cp, sz + 1, sizeof(char *));
1.6 kristaps 864: start = ep;
865: while ('\0' != *ep && ! isspace((unsigned char)*ep))
866: ep++;
867: cp[sz] = mandoc_malloc((ep - start) + 1);
868: memcpy(cp[sz], start, ep - start);
869: cp[sz++][ep - start] = '\0';
870: while (isspace((unsigned char)*ep))
871: ep++;
872: }
873:
1.59 schwarze 874: if (0 == mansearch(&search, &paths, sz, cp, "Nd", &res, &ressz))
1.72 schwarze 875: pg_noresult(req, "You entered an invalid query.");
1.59 schwarze 876: else if (0 == ressz)
1.72 schwarze 877: pg_noresult(req, "No results found.");
1.59 schwarze 878: else
1.72 schwarze 879: pg_searchres(req, res, ressz);
1.6 kristaps 880:
881: for (i = 0; i < sz; i++)
882: free(cp[i]);
1.52 schwarze 883: free(cp);
884:
885: for (i = 0; i < (int)ressz; i++) {
886: free(res[i].file);
887: free(res[i].names);
888: free(res[i].output);
889: }
890: free(res);
1.6 kristaps 891:
1.52 schwarze 892: free(paths.paths[0]);
893: free(paths.paths);
1.1 kristaps 894: }
895:
896: int
897: main(void)
898: {
1.66 schwarze 899: struct req req;
900: const char *path;
901: char *querystring;
1.1 kristaps 902: int i;
1.6 kristaps 903:
1.24 kristaps 904: /* Scan our run-time environment. */
1.6 kristaps 905:
1.58 schwarze 906: if (NULL == (scriptname = getenv("SCRIPT_NAME")))
907: scriptname = "";
1.24 kristaps 908:
909: /*
1.67 schwarze 910: * First we change directory into the MAN_DIR so that
1.24 kristaps 911: * subsequent scanning for manpath directories is rooted
912: * relative to the same position.
913: */
914:
1.67 schwarze 915: if (-1 == chdir(MAN_DIR)) {
1.58 schwarze 916: fprintf(stderr, "MAN_DIR: %s: %s\n",
1.67 schwarze 917: MAN_DIR, strerror(errno));
1.72 schwarze 918: pg_error_internal();
1.24 kristaps 919: return(EXIT_FAILURE);
920: }
921:
922: memset(&req, 0, sizeof(struct req));
1.54 schwarze 923: pathgen(&req);
1.1 kristaps 924:
1.24 kristaps 925: /* Next parse out the query string. */
1.1 kristaps 926:
1.58 schwarze 927: if (NULL != (querystring = getenv("QUERY_STRING")))
928: http_parse(&req, querystring);
1.1 kristaps 929:
1.66 schwarze 930: /* Dispatch to the three different pages. */
1.1 kristaps 931:
1.66 schwarze 932: path = getenv("PATH_INFO");
933: if (NULL == path)
934: path = "";
935: else if ('/' == *path)
936: path++;
937:
938: if ('\0' != *path)
939: pg_show(&req, path);
940: else if (NULL != req.q.expr)
941: pg_search(&req);
942: else
1.72 schwarze 943: pg_index(&req);
1.1 kristaps 944:
1.52 schwarze 945: for (i = 0; i < (int)req.psz; i++)
946: free(req.p[i]);
1.24 kristaps 947: free(req.p);
1.1 kristaps 948: return(EXIT_SUCCESS);
949: }
1.15 kristaps 950:
951: static int
952: cmp(const void *p1, const void *p2)
953: {
954:
1.52 schwarze 955: return(strcasecmp(((const struct manpage *)p1)->names,
956: ((const struct manpage *)p2)->names));
1.24 kristaps 957: }
958:
959: /*
960: * Scan for indexable paths.
961: */
962: static void
1.54 schwarze 963: pathgen(struct req *req)
1.24 kristaps 964: {
1.54 schwarze 965: FILE *fp;
966: char *dp;
967: size_t dpsz;
968:
1.74 schwarze 969: if (NULL == (fp = fopen("manpath.conf", "r"))) {
970: fprintf(stderr, "%s/manpath.conf: %s\n",
971: MAN_DIR, strerror(errno));
972: pg_error_internal();
973: exit(EXIT_FAILURE);
974: }
1.24 kristaps 975:
1.54 schwarze 976: while (NULL != (dp = fgetln(fp, &dpsz))) {
1.55 schwarze 977: if ('\n' == dp[dpsz - 1])
978: dpsz--;
1.54 schwarze 979: req->p = mandoc_realloc(req->p,
980: (req->psz + 1) * sizeof(char *));
981: req->p[req->psz++] = mandoc_strndup(dp, dpsz);
1.74 schwarze 982: }
983:
984: if ( req->p == NULL ) {
985: fprintf(stderr, "%s/manpath.conf is empty\n", MAN_DIR);
986: pg_error_internal();
987: exit(EXIT_FAILURE);
1.24 kristaps 988: }
989: }
CVSweb