[BACK]Return to oxserv.c CVS log [TXT][DIR] Up to [local] / OpenXM / src / ox_ntl

Annotation of OpenXM/src/ox_ntl/oxserv.c, Revision 1.2

1.2     ! iwane       1: /* $OpenXM: OpenXM/src/ox_ntl/oxserv.c,v 1.1 2003/11/03 03:11:21 iwane Exp $ */
1.1       iwane       2:
                      3: #include <stdio.h>
                      4: #include <stdlib.h>
                      5: #include <string.h>
                      6: #include <errno.h>
1.2     ! iwane       7: #include <signal.h>
1.1       iwane       8:
                      9: #include "oxserv.h"
1.2     ! iwane      10: #include "oxstack.h"
1.1       iwane      11:
                     12:
                     13: #define DPRINTF(x)     printf x; fflush(stdout)
                     14:
                     15: #if 0
                     16: /*===========================================================================*
                     17:  * for DEBUG
                     18:  *===========================================================================*/
                     19: #include <stdarg.h>
                     20: void
                     21: dprintf(const char *fmt, ...)
                     22: {
                     23:        FILE *fp;
                     24:        va_list ap;
                     25:        va_start(ap, fmt);
                     26:
                     27:        fp = fopen("error.txt", "a");
                     28:
                     29:        vfprintf(fp, fmt, ap);
                     30:
                     31:        fflush(fp);
                     32:        fclose(fp);
                     33:
                     34:        va_end(ap);
                     35: }
                     36: #endif
                     37:
                     38: /*===========================================================================*
1.2     ! iwane      39:  * MACRO
1.1       iwane      40:  *===========================================================================*/
1.2     ! iwane      41: #define MOXSERV_GET_CMO_TAG(m) ((G_getCmoTag == NULL) ? m->tag : G_getCmoTag(m))
1.1       iwane      42:
                     43:
1.2     ! iwane      44: #define oxserv_delete_cmo(c)         \
        !            45: do {                                 \
        !            46:         if (G_DeleteCmo != NULL)     \
        !            47:                 G_DeleteCmo(c);      \
        !            48:         else                         \
        !            49:                 c = NULL;            \
        !            50: } while (0)
        !            51:
1.1       iwane      52:
                     53:
                     54: /*===========================================================================*
1.2     ! iwane      55:  * Global Variables.
1.1       iwane      56:  *===========================================================================*/
1.2     ! iwane      57: /* ox */
        !            58: static OXFILE *G_oxfilep = NULL;
        !            59: static cmo_mathcap *G_oxserv_mathcap = NULL;
1.1       iwane      60:
1.2     ! iwane      61: /* User Function */
        !            62: static void (*G_userExecuteFunction)(const char *, cmo **, int) = NULL;
        !            63: static void (*G_userExecuteStringParser)(const char *) = NULL;
1.1       iwane      64:
1.2     ! iwane      65: static cmo *(*G_convertCmo)(cmo *) = NULL; /* convert user object ==> cmo */
1.1       iwane      66:
1.2     ! iwane      67: static void (*G_DeleteCmo)(cmo *) = NULL;
1.1       iwane      68:
1.2     ! iwane      69: static int  (*G_getCmoTag)(cmo *) = NULL;
1.1       iwane      70:
                     71: /*===========================================================================*
                     72:  * OX_SERVER
                     73:  *===========================================================================*/
                     74:
                     75: /*****************************************************************************
1.2     ! iwane      76:  * -- SM_popCMO --
        !            77:  * pop an object from the stack, convert it into a serialized from according
        !            78:  * to the standard CMO encoding scheme, and send it to the stream
        !            79:  *
1.1       iwane      80:  * PARAM : fd : OXFILE
                     81:  * RETURN: NONE
                     82:  *****************************************************************************/
                     83: static void
                     84: oxserv_sm_popCMO(OXFILE *fd)
                     85: {
1.2     ! iwane      86:        cmo *m = oxstack_pop();
1.1       iwane      87:        if (m == NULL) {
                     88:                m = new_cmo_null();
                     89:        } else if (G_convertCmo) {
                     90:                m = G_convertCmo(m);
                     91:        }
                     92:
                     93:        send_ox_cmo(fd, m);
                     94:
1.2     ! iwane      95:        oxserv_delete_cmo(m);
1.1       iwane      96: }
                     97:
                     98: /*****************************************************************************
1.2     ! iwane      99:  * -- SM_popString --
1.1       iwane     100:  * pop an cmo from stack, convert it into a string according to the
                    101:  * output format of the local system, and send the string.
1.2     ! iwane     102:  *
1.1       iwane     103:  * PARAM : fd : OXFILE
                    104:  * RETURN: NONE
                    105:  *****************************************************************************/
                    106: static void
                    107: oxserv_sm_popString(OXFILE *fd)
                    108: {
                    109:        char *str;
1.2     ! iwane     110:        cmo *m = oxstack_pop();
1.1       iwane     111:        cmo_string *m_str;
                    112:
                    113:        if (m == NULL)
                    114:                m = new_cmo_null();
                    115:
                    116:        str = new_string_set_cmo(m);
                    117:
                    118:        m_str = new_cmo_string(str);
                    119:
                    120:        send_ox_cmo(fd, (cmo *)m_str);
                    121:
1.2     ! iwane     122:        oxserv_delete_cmo(m);
        !           123:        oxserv_delete_cmo((cmo *)m_str);
1.1       iwane     124:
1.2     ! iwane     125:        /* free(str); */
1.1       iwane     126: }
                    127:
                    128: /*****************************************************************************
1.2     ! iwane     129:  * -- SM_pops --
1.1       iwane     130:  * pop n and to discard element from the stack
1.2     ! iwane     131:  *
        !           132:  * PARAM : NONE
1.1       iwane     133:  * RETURN: NONE
                    134:  *****************************************************************************/
                    135: static void
                    136: oxserv_sm_pops()
                    137: {
                    138:        cmo_int32 *c;
1.2     ! iwane     139:        cmo *m;
        !           140:        int n;
        !           141:        int i;
        !           142:
        !           143:        c = (cmo_int32 *)oxstack_pop(); /* suppose */
1.1       iwane     144:
1.2     ! iwane     145:        n = oxstack_get_stack_pointer();
        !           146:        if (c->i < n)
        !           147:                n = c->i;
1.1       iwane     148:
1.2     ! iwane     149:        for (i = 0; i < n; i++) {
        !           150:                m = oxstack_pop();
        !           151:                oxserv_delete_cmo(m);
        !           152:        }
        !           153:
        !           154:        oxserv_delete_cmo((cmo *)c);
1.1       iwane     155:
                    156: }
                    157:
                    158: /*****************************************************************************
1.2     ! iwane     159:  * -- SM_getsp --
        !           160:  * push the current stack pointer onto the stack.
1.1       iwane     161:  *
                    162:  * PARAM : fd : OXFILE
                    163:  * RETURN: NONE
                    164:  *****************************************************************************/
                    165: static void
                    166: oxserv_sm_getsp()
                    167: {
1.2     ! iwane     168:        cmo_int32 *m = new_cmo_int32(oxstack_get_stack_pointer());
        !           169:        oxstack_push((cmo *)m);
1.1       iwane     170: }
                    171:
                    172: /*****************************************************************************
                    173:  *
                    174:  * PARAM : ver    :
                    175:  *       : vstr   :
                    176:  *       : sysname:
                    177:  *       : cmos   :
                    178:  *       : sms    :
                    179:  * RETURN: NONE
                    180:  * SEE   : oxserv_set();
                    181:  *****************************************************************************/
                    182: static void
                    183: oxserv_mathcap_init(int ver, char *vstr, char *sysname, int *cmos, int *sms)
                    184: {
                    185:        int i;
                    186:
                    187:        int local_sms[] = {
                    188:                SM_popCMO,
                    189:                SM_mathcap,
                    190:                SM_setMathCap,
                    191:                SM_pops,
                    192:                SM_getsp,
                    193:                SM_popString,
                    194:                SM_pushCMOtag,
                    195:                0,
                    196:                SM_executeFunction,
                    197:                SM_dupErrors,
                    198:                SM_executeStringByLocalParser,
                    199:                SM_executeStringByLocalParserInBatchMode,
                    200:                SM_shutdown,
                    201:                0,
                    202:        };
                    203:
                    204:        /* depend on ox_toolkit */
                    205:        int local_cmos[] = {
                    206:                CMO_ERROR2,
                    207:                CMO_NULL,
                    208:                CMO_INT32,
                    209:                CMO_STRING,
                    210:                CMO_MATHCAP,
                    211:                CMO_LIST,
                    212:                CMO_MONOMIAL32,
                    213:                CMO_ZZ,
                    214:                CMO_ZERO,
                    215:                CMO_RECURSIVE_POLYNOMIAL,
                    216:                CMO_DISTRIBUTED_POLYNOMIAL,
                    217:                CMO_POLYNOMIAL_IN_ONE_VARIABLE,
                    218:                CMO_DMS_GENERIC,
                    219:                CMO_RING_BY_NAME,
                    220:                CMO_INDETERMINATE,
                    221:                CMO_TREE,
                    222:                CMO_LAMBDA,
                    223:                0,
                    224:                CMO_QQ,
                    225:                CMO_ATTRIBUTE_LIST,
                    226:                CMO_DMS,
                    227:                CMO_DMS_OF_N_VARIABLES,
                    228:                CMO_LIST_R,
                    229:                CMO_INT32COEFF,
                    230:                CMO_RATIONAL,
                    231:                CMO_DATUM,
                    232:                0,
                    233:        };
                    234:
                    235:        if (sms == NULL) {
                    236:                sms = local_sms;
                    237:
                    238:                for (i = 0; sms[i] != 0; i++)
                    239:                        ;
                    240:                if (G_userExecuteFunction != NULL)
                    241:                        sms[i++] = SM_executeFunction;
                    242:
                    243:                if (G_userExecuteStringParser != NULL) {
                    244:                        sms[i++] = SM_executeStringByLocalParser;
                    245:                        sms[i++] = SM_executeStringByLocalParserInBatchMode;
                    246:                }
                    247:
                    248:                sms[i] = 0;
                    249:        }
                    250:        if (cmos == NULL)
                    251:                cmos = local_cmos;
                    252:
                    253:        mathcap_init(ver, vstr, sysname, cmos, sms);
                    254:
1.2     ! iwane     255:        oxserv_delete_cmo((cmo *)G_oxserv_mathcap);
1.1       iwane     256:
                    257:        G_oxserv_mathcap = mathcap_get(new_mathcap());
                    258: }
                    259:
1.2     ! iwane     260: /*****************************************************************************
        !           261:  * -- SM_mathcap --
        !           262:  * push the mathcap of the server.
        !           263:  *
        !           264:  * PARAM : NONE
        !           265:  * RETURN: NONE
        !           266:  *****************************************************************************/
1.1       iwane     267: static void
                    268: oxserv_sm_mathcap()
                    269: {
                    270:        if (G_oxserv_mathcap == NULL) {
                    271:                oxserv_mathcap_init(0, "", "oxserv", NULL, NULL);
                    272:        }
                    273:
1.2     ! iwane     274:        oxstack_push((cmo *)G_oxserv_mathcap);
1.1       iwane     275: }
                    276:
1.2     ! iwane     277: /*****************************************************************************
        !           278:  * -- SM_executeStringByLocalParserInBatchMode --
        !           279:  * peek a character string s, parse it by the local parser of the stack machine,
        !           280:  * and interpret by the local interpreter.
        !           281:  *
        !           282:  * PARAM : NONE
        !           283:  * RETURN: NONE
        !           284:  *****************************************************************************/
1.1       iwane     285: static void
                    286: oxserv_sm_executeStringByLocalParserInBatchMode(void)
                    287: {
1.2     ! iwane     288:        cmo_string *str = (cmo_string *)oxstack_peek();
        !           289:        G_userExecuteStringParser(str->s);
1.1       iwane     290: }
                    291:
1.2     ! iwane     292: /*****************************************************************************
        !           293:  * -- SM_executeStringByLocalParser --
        !           294:  * pop a character string s, parse it by the local parser of the stack machine,
        !           295:  * and interpret by the local interpreter.
        !           296:  *
        !           297:  * PARAM : NONE
        !           298:  * RETURN: NONE
        !           299:  *****************************************************************************/
1.1       iwane     300: static void
                    301: oxserv_sm_executeStringByLocalParser(void)
                    302: {
1.2     ! iwane     303:        cmo_string *str = (cmo_string *)oxstack_pop();
        !           304:        G_userExecuteStringParser(str->s);
1.1       iwane     305: }
                    306:
                    307:
                    308:
1.2     ! iwane     309: /*****************************************************************************
        !           310:  * -- SM_executeFunction --
        !           311:  * pop s as a function name, pop n as the number of arguments and to execute a
        !           312:  * local function s with n arguments poped from the stack.
        !           313:  *
        !           314:  * PARAM : NONE
        !           315:  * RETURN: NONE
        !           316:  *****************************************************************************/
1.1       iwane     317: static void
                    318: oxserv_sm_executeFunction(void)
                    319: {
                    320:        int i;
1.2     ! iwane     321:        cmo_string *name = (cmo_string *)oxstack_pop();
        !           322:        cmo_int32 *cnt = (cmo_int32 *)oxstack_pop();
1.1       iwane     323:        cmo **arg = (cmo **)malloc(cnt->i * sizeof(cmo *));
                    324:
                    325:        for (i = 0; i < cnt->i; i++) {
1.2     ! iwane     326:                arg[i] = oxstack_pop();
1.1       iwane     327:        }
                    328:
                    329:        /* user function */
1.2     ! iwane     330:        G_userExecuteFunction(name->s, arg, cnt->i);
1.1       iwane     331:
                    332:
                    333:        for (i = 0; i < cnt->i; i++) {
1.2     ! iwane     334:                oxserv_delete_cmo(arg[i]);
1.1       iwane     335:        }
                    336:
1.2     ! iwane     337:        oxserv_delete_cmo((cmo *)name);
        !           338:        oxserv_delete_cmo((cmo *)cnt);
1.1       iwane     339:
                    340:        free(arg);
                    341: }
                    342:
1.2     ! iwane     343: /*****************************************************************************
        !           344:  * -- SM_pushCMOtag --
        !           345:  * push the CMO tag of the top object on the stack.
        !           346:  *
        !           347:  * PARAM : NONE
        !           348:  * RETURN: NONE
        !           349:  *****************************************************************************/
1.1       iwane     350: static void
                    351: oxserv_sm_pushCMOtag()
                    352: {
1.2     ! iwane     353:        cmo *c = oxstack_peek();
        !           354:        cmo_int32 *tag = new_cmo_int32(MOXSERV_GET_CMO_TAG(c));
        !           355:        oxstack_push((cmo *)tag);
1.1       iwane     356: }
                    357:
                    358:
                    359: static void
                    360: oxserv_sm_dupErrors()
                    361: {
                    362:        cmo_list *list;
                    363:        cmo *c;
                    364:        int i;
                    365:
                    366:        list = new_cmo_list();
                    367:
1.2     ! iwane     368:        for (i = 0; i < oxstack_get_stack_pointer(); i++) {
        !           369:                c = oxstack_get(i);
1.1       iwane     370:                if (c->tag == CMO_ERROR2) {
                    371:                        list_append(list, c);
                    372:                }
                    373:        }
                    374:
1.2     ! iwane     375:        oxstack_push((cmo *)list);
1.1       iwane     376: }
                    377:
1.2     ! iwane     378: static void
        !           379: oxserv_sm_control_reset_connection(int sig)
        !           380: {
        !           381:        int tag;
        !           382:        OXFILE *fd = G_oxfilep;
        !           383:
        !           384:        DPRINTF(("reset -- start\n"));
        !           385:        send_ox_tag(fd, OX_SYNC_BALL);
        !           386:
        !           387:        oxstack_init_stack();
        !           388:
        !           389:        for (;;) {
        !           390:                tag = receive_ox_tag(fd);
        !           391:                DPRINTF(("[%d=0x%x]", tag, tag));
        !           392:                if (tag == OX_SYNC_BALL)
        !           393:                        break;
        !           394:                if (tag == OX_DATA)
        !           395:                        receive_cmo(fd);
        !           396:        }
        !           397:        DPRINTF(("-- end.\n"));
        !           398: }
1.1       iwane     399:
                    400: static int
                    401: oxserv_receive_and_execute_sm_command(OXFILE *fd)
                    402: {
                    403:        int code = receive_int32(fd);
                    404:
                    405:        switch (code) {
                    406:        case SM_popCMO:
                    407:                oxserv_sm_popCMO(fd);
                    408:                break;
                    409:        case SM_executeStringByLocalParser:
                    410:                if (G_userExecuteStringParser)
                    411:                        oxserv_sm_executeStringByLocalParser();
                    412:                break;
                    413:        case SM_executeStringByLocalParserInBatchMode:
                    414:                if (G_userExecuteStringParser)
                    415:                        oxserv_sm_executeStringByLocalParserInBatchMode();
                    416:                break;
                    417:        case SM_pops:
                    418:                oxserv_sm_pops();
                    419:                break;
                    420:        case SM_popString:
                    421:                oxserv_sm_popString(fd);
                    422:                break;
                    423:        case SM_getsp:
                    424:                oxserv_sm_getsp();
                    425:                break;
                    426:        case SM_mathcap:
                    427:                oxserv_sm_mathcap();
                    428:                break;
                    429:        case SM_setMathCap:
                    430:                /* dont support */
1.2     ! iwane     431:                oxstack_pop();
1.1       iwane     432:                break;
                    433:        case SM_executeFunction:
                    434:                if (G_userExecuteFunction)
                    435:                        oxserv_sm_executeFunction();
                    436:                break;
                    437:        case SM_pushCMOtag:
                    438:                oxserv_sm_pushCMOtag();
                    439:                break;
                    440:        case SM_dupErrors:
                    441:                oxserv_sm_dupErrors();
                    442:                break;
1.2     ! iwane     443:        case SM_control_reset_connection:
        !           444:        case SM_control_reset_connection_server:
1.1       iwane     445:        default:
                    446:                break;
                    447:        }
                    448:        return (OXSERV_SUCCESS);
                    449: }
                    450:
                    451:
1.2     ! iwane     452:
1.1       iwane     453: /*****************************************************************************
                    454:  * reveice ox_data
1.2     ! iwane     455:  *
1.1       iwane     456:  * PARAM : fd : OXFILE
                    457:  * RETURN: NONE
                    458:  *****************************************************************************/
                    459: int
                    460: oxserv_receive(OXFILE *fd)
                    461: {
                    462:        int tag;
                    463:        cmo *c;
                    464:        int ret = OXSERV_SUCCESS;
                    465:
                    466:        tag = receive_ox_tag(fd);
                    467:
                    468:        switch (tag) {
                    469:        case OX_DATA:
                    470:                c = receive_cmo(fd);
1.2     ! iwane     471:                DPRINTF(("[CMO:%d]", c->tag));
        !           472:                oxstack_push(c);
1.1       iwane     473:                break;
                    474:
                    475:        case OX_COMMAND:
1.2     ! iwane     476:                DPRINTF(("[SM:%d=0x%x]", tag, tag));
1.1       iwane     477:                ret = oxserv_receive_and_execute_sm_command(fd);
                    478:                break;
                    479:
                    480:        default:
                    481:                DPRINTF(("receive unknown ox_tag: %d=0x%x\n", tag, tag));
                    482:                return (OXSERV_FAILURE);
                    483:        }
                    484:
                    485:        return (ret);
                    486: }
                    487:
                    488:
                    489: /*****************************************************************************
                    490:  * initialize oxserver
                    491:  *
                    492:  * PARAM : see oxserv_mathcap_init()
1.2     ! iwane     493:  * RETURN: success : OXSERV_SUCCESS
        !           494:  *       : failure : OXSERV_FAILURE
1.1       iwane     495:  * SEE   : oxserv_mathcap_init()
                    496:  *       : oxserv_set();
                    497:  *****************************************************************************/
                    498: int
                    499: oxserv_init(OXFILE *oxfp, int ver, char *vstr, char *sysname, int *cmos, int *sms)
                    500: {
                    501:        int ret;
                    502:
1.2     ! iwane     503:        ret = oxstack_init_stack();
1.1       iwane     504:        if (ret != OXSERV_SUCCESS)
                    505:                return (ret);
                    506:
1.2     ! iwane     507:        G_oxfilep = oxfp;
        !           508:
1.1       iwane     509:        oxserv_mathcap_init(ver, vstr, sysname, cmos, sms);
                    510:
1.2     ! iwane     511:        signal(SIGUSR1, oxserv_sm_control_reset_connection);
        !           512:
1.1       iwane     513:        oxf_determine_byteorder_server(oxfp);
                    514:
                    515:        return (OXSERV_SUCCESS);
                    516: }
                    517:
                    518:
                    519: /*****************************************************************************
                    520:  * set oxserver
1.2     ! iwane     521:  *
1.1       iwane     522:  * PARAM : mode : mode
                    523:  *              :
                    524:  *       : ptr  :
                    525:  *       : rsv  : reserve space.
1.2     ! iwane     526:  * RETURN: success : OXSERV_SUCCESS
        !           527:  *       : failure : OXSERV_FAILURE
1.1       iwane     528:  * SEE   :
                    529:  *****************************************************************************/
                    530: int
                    531: oxserv_set(int mode, void *ptr, void *rsv)
                    532: {
                    533:        switch (mode) {
                    534:        case OXSERV_SET_EXECUTE_FUNCTION:
1.2     ! iwane     535:                G_userExecuteFunction = (void (*)(const char *, cmo **, int))ptr;
1.1       iwane     536:                break;
                    537:        case OXSERV_SET_EXECUTE_STRING_PARSER:
1.2     ! iwane     538:                G_userExecuteStringParser = (void (*)(const char *))ptr;
1.1       iwane     539:                break;
                    540:        case OXSERV_SET_CONVERT_CMO:
                    541:                G_convertCmo = (cmo *(*)(cmo *))ptr;
                    542:                break;
1.2     ! iwane     543:        case OXSERV_SET_DELETE_CMO:
        !           544:                G_DeleteCmo = (void (*)(cmo *))ptr;
        !           545:                break;
        !           546:        case OXSERV_SET_GET_CMOTAG:
        !           547:                G_getCmoTag = (int (*)(cmo *))ptr;
        !           548:                break;
1.1       iwane     549:        default:
                    550:                return (OXSERV_FAILURE);
                    551:        }
                    552:
                    553:
                    554:        return (OXSERV_SUCCESS);
                    555: }
                    556:
                    557:
                    558: /*****************************************************************************
                    559:  * destroy
1.2     ! iwane     560:  *
        !           561:  * PARAM : NONE
1.1       iwane     562:  * RETURN: NONE
                    563:  *****************************************************************************/
                    564: void
                    565: oxserv_dest()
                    566: {
1.2     ! iwane     567:        oxstack_dest();
1.1       iwane     568: }
                    569:
                    570:
1.2     ! iwane     571: #if __OXSERV_DEBUG
        !           572: /*===========================================================================*
        !           573:  * DEBUG
        !           574:  *===========================================================================*/
        !           575:
1.1       iwane     576:
                    577: cmo *
                    578: oxserv_executeFunction(const char *func, cmo **arg, int argc)
                    579: {
                    580:        int i;
                    581:
                    582:        printf("%s()\n", func);
                    583:
                    584:        for (i = 0; i < argc; i++) {
                    585:                printf("\t%2d: %s\n", i, new_string_set_cmo(arg[i]));
                    586:        }
                    587:
                    588:        return ((cmo *)new_cmo_int32(0));
                    589: }
                    590:
1.2     ! iwane     591: /*****************************************************************************
        !           592:  * main
        !           593:  *
        !           594:  * PARAM : NONE
        !           595:  * RETURN: NONE
        !           596:  *****************************************************************************/
1.1       iwane     597: int
                    598: main(int argc, char *argv[])
                    599: {
                    600:        int fd = 3;
                    601:        int i;
                    602:        int ret;
                    603:
                    604:        OXFILE *oxfp = oxf_open(fd);
                    605:
                    606:        ox_stderr_init(stderr);
                    607:
1.2     ! iwane     608:        oxserv_init(oxfp, 0, "$Date: 2003/11/02 16:39:16 $", "oxserv", NULL, NULL);
1.1       iwane     609:
                    610:        DPRINTF(("main - start\n"));
                    611:        for (i = 0;; i++) {
                    612:                DPRINTF(("@"));
                    613:                ret = oxserv_receive(oxfp);
                    614:                if (ret != OXSERV_SUCCESS)
                    615:                        break;
                    616:        }
                    617:
1.2     ! iwane     618:        oxf_close(oxfp);
        !           619:        oxserv_delete_cmo((cmo *)G_oxserv_mathcap);
1.1       iwane     620:
                    621:        return (0);
                    622: }
                    623:
                    624: #endif
1.2     ! iwane     625:
        !           626:

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>