[BACK]Return to cgi.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / mandoc

Annotation of src/usr.bin/mandoc/cgi.c, Revision 1.1

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