[BACK]Return to cgi.c CVS log [TXT][DIR] Up to [cvsweb.bsd.lv] / mandoc

Annotation of mandoc/cgi.c, Revision 1.67

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

CVSweb