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