[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.11

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