Annotation of embedaddon/mpd/src/chat.c, revision 1.1.1.1

1.1       misho       1: 
                      2: /*
                      3:  * chat.c
                      4:  *
                      5:  * Written by Archie Cobbs <archie@freebsd.org>
                      6:  * Copyright (c) 1995-1999 Whistle Communications, Inc. All rights reserved.
                      7:  * See ``COPYRIGHT.whistle''
                      8:  */
                      9: 
                     10: #include "ppp.h"
                     11: #include "chat.h"
                     12: #include "util.h"
                     13: #include <regex.h>
                     14: 
                     15: /*
                     16:  * DEFINITIONS
                     17:  */
                     18: 
                     19: /* Bounds */
                     20: 
                     21:   #define CHAT_MAX_ARGS                10
                     22:   #define CHAT_MAX_VARNAME     100
                     23: 
                     24: /* Keywords */
                     25: 
                     26:   #define IF                   "if"
                     27:   #define SET                  "set"
                     28:   #define MATCH                        "match"
                     29:   #define REGEX                        "regex"
                     30:   #define TIMER                        "timer"
                     31:   #define CANCEL               "cancel"
                     32:   #define PRINT                        "print"
                     33:   #define CALL                 "call"
                     34:   #define LOG                  "log"
                     35:   #define RETURN               "return"
                     36:   #define GOTO                 "goto"
                     37:   #define SUCCESS              "success"
                     38:   #define FAILURE              "failure"
                     39:   #define WAIT                 "wait"
                     40:   #define NOTHING              "nop"
                     41:   #define CHAT_KEYWORD_ALL     "all"
                     42: 
                     43: /* Special timer used when no label given */
                     44: 
                     45:   #define DEFAULT_LABEL                ""
                     46:   #define DEFAULT_SET          ""
                     47: 
                     48: /* States */
                     49: 
                     50:   enum
                     51:   {
                     52:     CHAT_IDLE = 0,
                     53:     CHAT_RUNNING,
                     54:     CHAT_WAIT
                     55:   };
                     56: 
                     57: /* Commands */
                     58: 
                     59:   enum
                     60:   {
                     61:     CMD_IF,
                     62:     CMD_SET,
                     63:     CMD_MATCH,
                     64:     CMD_REGEX,
                     65:     CMD_TIMER,
                     66:     CMD_CANCEL,
                     67:     CMD_PRINT,
                     68:     CMD_CALL,
                     69:     CMD_LOG,
                     70:     CMD_RETURN,
                     71:     CMD_GOTO,
                     72:     CMD_SUCCESS,
                     73:     CMD_FAILURE,
                     74:     CMD_WAIT,
                     75:     CMD_NOTHING
                     76:   };
                     77: 
                     78: /* Structures */
                     79: 
                     80:   struct chatcmd
                     81:   {
                     82:     const char *name;
                     83:     short      id;
                     84:     short      min;
                     85:     short      max;
                     86:   };
                     87: 
                     88:   struct chatvar
                     89:   {
                     90:     char               *name;
                     91:     char               *value;
                     92:     struct chatvar     *next;
                     93:   };
                     94:   typedef struct chatvar       *ChatVar;
                     95: 
                     96:   struct chatmatch
                     97:   {
                     98:     char               *set;
                     99:     char               *label;
                    100:     u_char             exact:1;        /* true if this is an exact match */
                    101:     union {
                    102:       struct cm_exact {
                    103:        char    *pat;                   /* exact string to match */
                    104:        u_short *fail;                  /* failure function */
                    105:        u_int   matched;                /* number of chars matched so far */
                    106:       }                exact;
                    107:       regex_t  regex;                  /* regular expression to match */
                    108:     }                  u;
                    109:     int                        frameDepth;     /* number of frames to pop */
                    110:     struct chatmatch   *next;
                    111:   };
                    112:   typedef struct chatmatch     *ChatMatch;
                    113: 
                    114:   struct chattimer
                    115:   {
                    116:     char               *set;
                    117:     char               *label;
                    118:     EventRef           event;
                    119:     int                        frameDepth;     /* number of frames to pop */
                    120:     struct chatinfo    *c;
                    121:     struct chattimer   *next;
                    122:   };
                    123:   typedef struct chattimer     *ChatTimer;
                    124: 
                    125:   struct chatframe
                    126:   {
                    127:     fpos_t             posn;
                    128:     int                        lineNum;
                    129:     struct chatframe   *up;
                    130:   };
                    131:   typedef struct chatframe     *ChatFrame;
                    132: 
                    133:   struct chatinfo
                    134:   {
                    135:     void               *arg;           /* opaque client context */
                    136:     FILE               *fp;            /* chat script file */
                    137:     int                        lineNum;        /* current script line number */
                    138:     int                        state;          /* run/wait/idle state */
                    139:     int                        fd;             /* device to talk to */
                    140:     EventRef           rdEvent;        /* readable event */
                    141:     EventRef           wrEvent;        /* writable event */
                    142:     char               *out;           /* output queue */
                    143:     int                        outLen;         /* output queue length */
                    144:     struct chattimer   *timers;        /* pending timers */
                    145:     struct chatmatch   *matches;       /* pending matches */
                    146:     struct chatframe   *stack;         /* call/return stack */
                    147:     struct chatvar     *globals;       /* global variables */
                    148:     struct chatvar     *temps;         /* temporary variables */
                    149:     char               *lastLog;       /* last message logged */
                    150:     char               *scriptName;    /* name of script we're running */
                    151:     char               lineBuf[CHAT_MAX_LINE];         /* line buffer */
                    152:     char               readBuf[CHAT_READBUF_SIZE];     /* read buffer */
                    153:     int                        readBufLen;
                    154:     chatbaudfunc_t     setBaudrate;
                    155:     chatresultfunc_t   result;
                    156:   };
                    157: 
                    158: /*
                    159:  * INTERNAL VARIABLES
                    160:  */
                    161: 
                    162:   static const struct chatcmd  gCmds[] =
                    163:   {
                    164:     { IF,      CMD_IF,         4, 0 },
                    165:     { SET,     CMD_SET,        3, 3 },
                    166:     { MATCH,   CMD_MATCH,      2, 4 },
                    167:     { REGEX,   CMD_REGEX,      2, 4 },
                    168:     { TIMER,   CMD_TIMER,      2, 4 },
                    169:     { CANCEL,  CMD_CANCEL,     2, 0 },
                    170:     { PRINT,   CMD_PRINT,      2, 2 },
                    171:     { CALL,    CMD_CALL,       2, 2 },
                    172:     { LOG,     CMD_LOG,        2, 2 },
                    173:     { RETURN,  CMD_RETURN,     1, 1 },
                    174:     { GOTO,    CMD_GOTO,       2, 2 },
                    175:     { SUCCESS, CMD_SUCCESS,    1, 1 },
                    176:     { FAILURE, CMD_FAILURE,    1, 1 },
                    177:     { WAIT,    CMD_WAIT,       1, 2 },
                    178:     { NOTHING, CMD_NOTHING,    1, 1 },
                    179:   };
                    180: 
                    181:   #define CHAT_NUM_COMMANDS    (sizeof(gCmds) / sizeof(*gCmds))
                    182: 
                    183: /*
                    184:  * INTERNAL FUNCTIONS
                    185:  */
                    186: 
                    187:   static void  ChatAddTimer(ChatInfo c, const char *set,
                    188:                        u_int secs, const char *label);
                    189:   static void  ChatAddMatch(ChatInfo c, int exact,
                    190:                        const char *set, char *pat, const char *label);
                    191:   static void  ChatCancel(ChatInfo c, const char *set);
                    192:   static int   ChatGoto(ChatInfo c, const char *label);
                    193:   static void  ChatCall(ChatInfo c, const char *label);
                    194:   static void  ChatLog(ChatInfo c, int code, const char *string);
                    195:   static void  ChatPrint(ChatInfo c, const char *string);
                    196:   static void  ChatReturn(ChatInfo c, int seek);
                    197:   static void  ChatRun(ChatInfo c);
                    198:   static void  ChatStop(ChatInfo c);
                    199:   static void  ChatSuccess(ChatInfo c);
                    200:   static void  ChatFailure(ChatInfo c);
                    201:   static void  ChatIf(ChatInfo c, int ac, char *av[]);
                    202: 
                    203:   static void  ChatRead(int type, void *cookie);
                    204:   static void  ChatWrite(int type, void *cookie);
                    205:   static void  ChatTimeout(int type, void *cookie);
                    206:   static int   ChatGetCmd(ChatInfo c, const char *token, int n_args);
                    207:   static void  ChatDoCmd(ChatInfo c, int ac, char *av[]);
                    208:   static void  ChatDumpReadBuf(ChatInfo c);
                    209: 
                    210:   static int   ChatVarSet(ChatInfo c, const char *name,
                    211:                  const char *val, int exp, int pre);
                    212:   static ChatVar ChatVarGet(ChatInfo c, const char *name);
                    213:   static ChatVar ChatVarFind(ChatVar *head, char *name);
                    214:   static int   ChatVarExtract(const char *string,
                    215:                  char *buf, int max, int strict);
                    216: 
                    217:   static void  ChatFreeMatch(ChatInfo c, ChatMatch match);
                    218:   static void  ChatFreeTimer(ChatInfo c, ChatTimer timer);
                    219:   static int   ChatMatchChar(struct cm_exact *ex, char ch);
                    220:   static int   ChatMatchRegex(ChatInfo c, regex_t *reg, const char *input);
                    221:   static void  ChatSetMatchVars(ChatInfo c, int exact, const char *input, ...);
                    222:   static void  ChatComputeFailure(ChatInfo c, struct cm_exact *ex);
                    223: 
                    224:   static int   ChatDecodeTime(ChatInfo c, char *string, u_int *secsp);
                    225:   static int   ChatSetBaudrate(ChatInfo c, const char *new);
                    226:   static char  *ChatExpandString(ChatInfo c, const char *string);
                    227: 
                    228:   static char  *ChatReadLine(ChatInfo c);
                    229:   static int   ChatParseLine(ChatInfo c, char *line, char *av[], int max);
                    230:   static int   ChatSeekToLabel(ChatInfo c, const char *label);
                    231:   static void  ChatDumpBuf(ChatInfo c, const char *buf,
                    232:                  int len, const char *fmt, ...);
                    233: 
                    234: /*
                    235:  * ChatInit()
                    236:  */
                    237: 
                    238: ChatInfo
                    239: ChatInit(void *arg, chatbaudfunc_t setBaudrate)
                    240: {
                    241:   ChatInfo     c;
                    242: 
                    243:   c = (ChatInfo) Malloc(MB_CHAT, sizeof(*c));
                    244:   c->arg = arg;
                    245:   c->setBaudrate = setBaudrate;
                    246:   return(c);
                    247: }
                    248: 
                    249: /*
                    250:  * ChatActive()
                    251:  */
                    252: 
                    253: int
                    254: ChatActive(ChatInfo c)
                    255: {
                    256:   return(c->state != CHAT_IDLE);
                    257: }
                    258: 
                    259: /*
                    260:  * ChatPresetVar()
                    261:  */
                    262: 
                    263: void
                    264: ChatPresetVar(ChatInfo c, const char *name, const char *value)
                    265: {
                    266:   Link const l = (Link) c->arg;
                    267: 
                    268:   if (ChatVarSet(c, name, value, 1, 1) < 0)
                    269:   {
                    270:     Log(LG_ERR, ("[%s] CHAT: chat: invalid variable \"%s\"", l->name, name));
                    271:     return;
                    272:   }
                    273: }
                    274: 
                    275: /*
                    276:  * ChatGetVar()
                    277:  *
                    278:  * Caller must free the returned value
                    279:  */
                    280: 
                    281: char *
                    282: ChatGetVar(ChatInfo c, const char *var)
                    283: {
                    284:   ChatVar      const cv = ChatVarGet(c, var);
                    285: 
                    286:   return cv ? Mstrdup(MB_CHAT, cv->value) : NULL;
                    287: }
                    288: 
                    289: /*
                    290:  * ChatStart()
                    291:  *
                    292:  * Start executing chat script at label, or at beginning of file
                    293:  * if no label specified. The "fd" must be set to non-blocking I/O.
                    294:  */
                    295: 
                    296: void
                    297: ChatStart(ChatInfo c, int fd, FILE *scriptfp,
                    298:        const char *label, chatresultfunc_t result)
                    299: {
                    300:   ChatVar      baud;
                    301:   const char   *labelName;
                    302:   Link const l = (Link) c->arg;
                    303: 
                    304: /* Sanity */
                    305: 
                    306:   assert(c->fp == NULL);
                    307:   assert(c->state == CHAT_IDLE);
                    308: 
                    309:   if (label)
                    310:     Log(LG_CHAT2, ("[%s] CHAT: running script at label \"%s\"", l->name, label));
                    311:   else
                    312:     Log(LG_CHAT2, ("[%s] CHAT: running script from beginning", l->name));
                    313: 
                    314:   c->result = result;
                    315:   c->fd = fd;
                    316:   c->fp = scriptfp;
                    317:   c->lineNum = 0;
                    318: 
                    319: /* Save script name */
                    320: 
                    321:   assert(!c->scriptName);
                    322:   labelName = label ? label : "<default>";
                    323:   c->scriptName = Mstrdup(MB_CHAT, labelName);
                    324: 
                    325: /* Jump to label, if any */
                    326: 
                    327:   if (label && ChatGoto(c, label) < 0)
                    328:     return;
                    329: 
                    330: /* Make sure serial port baudrate and $Baudrate variable agree */
                    331: 
                    332:   if ((baud = ChatVarGet(c, CHAT_VAR_BAUDRATE)))
                    333:     ChatSetBaudrate(c, baud->value);
                    334: 
                    335: /* Start running script */
                    336: 
                    337:   ChatRun(c);
                    338: }
                    339: 
                    340: /*
                    341:  * ChatAbort()
                    342:  */
                    343: 
                    344: void
                    345: ChatAbort(ChatInfo c)
                    346: {
                    347:   Link const l = (Link) c->arg;
                    348: 
                    349:   if (c->state != CHAT_IDLE)
                    350:     Log(LG_CHAT2, ("[%s] CHAT: script halted", l->name));
                    351:   ChatStop(c);
                    352: }
                    353: 
                    354: /*
                    355:  * ChatRead()
                    356:  */
                    357: 
                    358: static void
                    359: ChatRead(int type, void *cookie)
                    360: {
                    361:   ChatInfo     const c = (ChatInfo) cookie;
                    362:   ChatMatch    match;
                    363:   int          nread, lineBufLen;
                    364:   char         ch;
                    365:   Link         const l = (Link) c->arg;
                    366: 
                    367: /* Sanity */
                    368: 
                    369:   assert(c->state == CHAT_WAIT);
                    370: 
                    371: /* Process one byte at a time */
                    372: 
                    373:   for (lineBufLen = strlen(c->lineBuf); 1; )
                    374:   {
                    375: 
                    376:   /* Input next character */
                    377: 
                    378:     if ((nread = read(c->fd, &ch, 1)) < 0)
                    379:     {
                    380:       if (errno == EAGAIN)
                    381:        break;
                    382:       Perror("[%s] CHAT: read", l->name);
                    383:       goto die;
                    384:     }
                    385:     else if (nread == 0)
                    386:     {
                    387:       Log(LG_CHAT, ("[%s] CHAT: detected EOF from device", l->name));
                    388: die:
                    389:       ChatFailure(c);
                    390:       return;
                    391:     }
                    392: 
                    393:   /* Add to "bytes read" buffer for later debugging display */
                    394: 
                    395:     if (c->readBufLen == sizeof(c->readBuf) || ch == '\n')
                    396:       ChatDumpReadBuf(c);
                    397:     c->readBuf[c->readBufLen++] = ch;
                    398: 
                    399:   /* Add to current line buffer */
                    400: 
                    401:     if (lineBufLen < sizeof(c->lineBuf) - 1) {
                    402:       c->lineBuf[lineBufLen++] = ch;
                    403:     } else {
                    404:       Log(LG_CHAT, ("[%s] CHAT: warning: line buffer overflow", l->name));
                    405:     }
                    406: 
                    407:   /* Try to match a match pattern */
                    408: 
                    409:     for (match = c->matches; match; match = match->next) {
                    410:       if (match->exact) {
                    411:        if (ChatMatchChar(&match->u.exact, ch)) {
                    412:          ChatSetMatchVars(c, 1, match->u.exact.pat);
                    413:          break;
                    414:        }
                    415:       } else {
                    416:        const int       nmatch = match->u.regex.re_nsub + 1;
                    417:        regmatch_t      *pmatch;
                    418:        int             r, flags = (REG_STARTEND | REG_NOTEOL);
                    419: 
                    420:       /* Check for end of line */
                    421: 
                    422:        pmatch = Malloc(MB_CHAT, nmatch * sizeof(*pmatch));
                    423:        pmatch[0].rm_so = 0;
                    424:        pmatch[0].rm_eo = lineBufLen;
                    425:        if (pmatch[0].rm_eo > 0 && c->lineBuf[pmatch[0].rm_eo - 1] == '\n') {
                    426:          pmatch[0].rm_eo--;
                    427:          flags &= ~REG_NOTEOL;         /* this is a complete line */
                    428:          if (pmatch[0].rm_eo > 0 && c->lineBuf[pmatch[0].rm_eo - 1] == '\r')
                    429:            pmatch[0].rm_eo--;          /* elide the CR byte too */
                    430:        }
                    431: 
                    432:       /* Do comparison */
                    433: 
                    434:        switch ((r = regexec(&match->u.regex,
                    435:                c->lineBuf, nmatch, pmatch, flags))) {
                    436:          default:
                    437:            Log(LG_ERR, ("[%s] CHAT: regexec() returned %d?", l->name, r));
                    438:            /* fall through */
                    439:          case REG_NOMATCH:
                    440:            Freee(pmatch);
                    441:            continue;
                    442:          case 0:
                    443:            ChatSetMatchVars(c, 0, c->lineBuf, nmatch, pmatch);
                    444:            Freee(pmatch);
                    445:            break;
                    446:        }
                    447:        break;
                    448:       }
                    449:     }
                    450: 
                    451:     /* Reset line buffer after a newline */
                    452:     if (ch == '\n') {
                    453:       memset(&c->lineBuf, 0, sizeof(c->lineBuf));
                    454:       lineBufLen = 0;
                    455:     }
                    456: 
                    457:     /* Log match, pop the stack, and jump to target label */
                    458:     if (match) {
                    459:       char     label[CHAT_MAX_LABEL];
                    460:       int      numPop;
                    461: 
                    462:       ChatDumpReadBuf(c);
                    463:       Log(LG_CHAT2, ("[%s] CHAT: matched set \"%s\", goto label \"%s\"",
                    464:          l->name, match->set, match->label));
                    465:       numPop = match->frameDepth;
                    466:       strlcpy(label, match->label, sizeof(label));
                    467:       ChatCancel(c, match->set);
                    468:       while (numPop-- > 0)
                    469:        ChatReturn(c, 0);
                    470:       if (ChatGoto(c, label) >= 0)
                    471:        ChatRun(c);
                    472:       return;
                    473:     }
                    474:   }
                    475: 
                    476: /* Reregister input event */
                    477: 
                    478:   EventRegister(&c->rdEvent, EVENT_READ, c->fd, 0, ChatRead, c);
                    479: }
                    480: 
                    481: /*
                    482:  * ChatTimeout()
                    483:  *
                    484:  * Handle one of our timers timing out
                    485:  */
                    486: 
                    487: static void
                    488: ChatTimeout(int type, void *cookie)
                    489: {
                    490:   ChatTimer    const timer = (ChatTimer) cookie;
                    491:   ChatInfo     const c = timer->c;
                    492:   ChatTimer    *tp;
                    493:   char         label[CHAT_MAX_LABEL];
                    494:   int          numPop;
                    495:   Link         const l = (Link) c->arg;
                    496: 
                    497: /* Sanity */
                    498: 
                    499:   assert(c->state == CHAT_WAIT);
                    500: 
                    501: /* Locate timer in list */
                    502: 
                    503:   for (tp = &c->timers; *tp != timer; tp = &(*tp)->next);
                    504:   assert(*tp);
                    505:   Log(LG_CHAT2, ("[%s] CHAT: timer in set \"%s\" expired", l->name, timer->set));
                    506: 
                    507: /* Cancel set */
                    508: 
                    509:   strlcpy(label, timer->label, sizeof(label));
                    510:   numPop = timer->frameDepth;
                    511:   ChatCancel(c, timer->set);
                    512: 
                    513: /* Pop the stack as needed */
                    514: 
                    515:   while (numPop-- > 0)
                    516:     ChatReturn(c, 0);
                    517: 
                    518: /* Jump to target label */
                    519: 
                    520:   if (ChatGoto(c, label) >= 0)
                    521:     ChatRun(c);
                    522: }
                    523: 
                    524: /*
                    525:  * ChatRun()
                    526:  *
                    527:  * Run chat script
                    528:  */
                    529: 
                    530: static void
                    531: ChatRun(ChatInfo c)
                    532: {
                    533:   char *line;
                    534:   Link const l = (Link) c->arg;
                    535: 
                    536: /* Sanity */
                    537: 
                    538:   assert(c->state != CHAT_RUNNING);
                    539: 
                    540: /* Cancel default set before running */
                    541: 
                    542:   ChatCancel(c, DEFAULT_SET);
                    543: 
                    544: /* Execute commands while running */
                    545: 
                    546:   for (c->state = CHAT_RUNNING;
                    547:     c->state == CHAT_RUNNING && (line = ChatReadLine(c)) != NULL; )
                    548:   {
                    549:     int                ac;
                    550:     char       *av[CHAT_MAX_ARGS];
                    551: 
                    552:   /* Skip labels */
                    553: 
                    554:     if (!isspace(*line))
                    555:     {
                    556:       Freee(line);
                    557:       continue;
                    558:     }
                    559: 
                    560:   /* Parse out line */
                    561: 
                    562:     ac = ChatParseLine(c, line, av, CHAT_MAX_ARGS);
                    563:     Freee(line);
                    564: 
                    565:   /* Do command */
                    566: 
                    567:     ChatDoCmd(c, ac, av);
                    568:     while (ac > 0)
                    569:       Freee(av[--ac]);
                    570:   }
                    571: 
                    572: /* What state are we in? */
                    573: 
                    574:   switch (c->state)
                    575:   {
                    576:     case CHAT_RUNNING:
                    577:       Log(LG_ERR, ("[%s] CHAT: EOF while reading script", l->name));
                    578:       ChatFailure(c);
                    579:       break;
                    580:     case CHAT_WAIT:
                    581:       if (!EventIsRegistered(&c->rdEvent))
                    582:        EventRegister(&c->rdEvent, EVENT_READ, c->fd, 0, ChatRead, c);
                    583:       break;
                    584:     case CHAT_IDLE:
                    585:       break;
                    586:   }
                    587: 
                    588: /* Sanity */
                    589: 
                    590:   assert(c->state != CHAT_RUNNING);
                    591: }
                    592: 
                    593: /*
                    594:  * ChatDoCmd()
                    595:  */
                    596: 
                    597: static void
                    598: ChatDoCmd(ChatInfo c, int ac, char *av[])
                    599: {
                    600:   char buf[200];
                    601:   u_int        secs;
                    602:   int  j, k;
                    603:   Link const l = (Link) c->arg;
                    604: 
                    605: /* Show command */
                    606: 
                    607:   for (buf[0] = k = 0; k < ac; k++) {
                    608:     for (j = 0; av[k][j] && isgraph(av[k][j]); j++);
                    609:     snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
                    610:       !*av[k] || av[k][j] ? " \"%s\"" : " %s", av[k]);
                    611:   }
                    612:   Log(LG_CHAT2, ("[%s] CHAT: line %d:%s", l->name, c->lineNum, buf));
                    613: 
                    614: /* Execute command */
                    615: 
                    616:   if (ac == 0)
                    617:     return;
                    618:   switch (ChatGetCmd(c, av[0], ac))
                    619:   {
                    620:     case CMD_IF:
                    621:       ChatIf(c, ac, av);
                    622:       break;
                    623: 
                    624:     case CMD_SET:
                    625:       if (ChatVarSet(c, av[1], av[2], 1, 0) < 0)
                    626:       {
                    627:        Log(LG_ERR, ("[%s] CHAT: line %d: %s: invalid variable \"%s\"",
                    628:          l->name, c->lineNum, SET, av[1]));
                    629:        ChatFailure(c);
                    630:       }
                    631:       break;
                    632: 
                    633:     case CMD_MATCH:
                    634:       switch (ac)
                    635:       {
                    636:        case 4:
                    637:          ChatAddMatch(c, 1, av[1], av[2], av[3]);
                    638:          break;
                    639:        case 3:
                    640:          ChatAddMatch(c, 1, DEFAULT_SET, av[1], av[2]);
                    641:          break;
                    642:        case 2:
                    643:          ChatAddMatch(c, 1, DEFAULT_SET, av[1], DEFAULT_LABEL);
                    644:          break;
                    645:        default:
                    646:          assert(0);
                    647:       }
                    648:       break;
                    649: 
                    650:     case CMD_REGEX:
                    651:       switch (ac)
                    652:       {
                    653:        case 4:
                    654:          ChatAddMatch(c, 0, av[1], av[2], av[3]);
                    655:          break;
                    656:        case 3:
                    657:          ChatAddMatch(c, 0, DEFAULT_SET, av[1], av[2]);
                    658:          break;
                    659:        case 2:
                    660:          ChatAddMatch(c, 0, DEFAULT_SET, av[1], DEFAULT_LABEL);
                    661:          break;
                    662:        default:
                    663:          assert(0);
                    664:       }
                    665:       break;
                    666: 
                    667:     case CMD_TIMER:
                    668:       switch (ac)
                    669:       {
                    670:        case 4:
                    671:          if (!ChatDecodeTime(c, av[2], &secs))
                    672:            ChatFailure(c);
                    673:          else if (secs != 0)
                    674:            ChatAddTimer(c, av[1], secs, av[3]);
                    675:          break;
                    676:        case 3:
                    677:          if (!ChatDecodeTime(c, av[1], &secs))
                    678:            ChatFailure(c);
                    679:          else if (secs != 0)
                    680:            ChatAddTimer(c, DEFAULT_SET, secs, av[2]);
                    681:          break;
                    682:        case 2:
                    683:          if (!ChatDecodeTime(c, av[1], &secs))
                    684:            ChatFailure(c);
                    685:          else if (secs != 0)
                    686:            ChatAddTimer(c, DEFAULT_SET, secs, DEFAULT_LABEL);
                    687:          break;
                    688:        default:
                    689:          assert(0);
                    690:       }
                    691:       break;
                    692: 
                    693:     case CMD_WAIT:
                    694:       if (ac == 2)
                    695:       {
                    696:        if (!ChatDecodeTime(c, av[1], &secs))
                    697:          ChatFailure(c);
                    698:        else if (secs > 0)
                    699:        {
                    700:          ChatAddTimer(c, DEFAULT_SET, secs, DEFAULT_LABEL);
                    701:          c->state = CHAT_WAIT;
                    702:        }
                    703:        else
                    704:          ChatCancel(c, DEFAULT_SET);  /* Simulate immediate expiration */
                    705:       }
                    706:       else
                    707:       {
                    708:        if (!c->matches && !c->timers)
                    709:        {
                    710:          Log(LG_ERR, ("[%s] CHAT: line %d: %s with no events pending",
                    711:            l->name, c->lineNum, WAIT));
                    712:          ChatFailure(c);
                    713:        }
                    714:        else
                    715:          c->state = CHAT_WAIT;
                    716:       }
                    717:       break;
                    718: 
                    719:     case CMD_CANCEL:
                    720:       for (k = 1; k < ac; k++)
                    721:        ChatCancel(c, av[k]);
                    722:       break;
                    723: 
                    724:     case CMD_PRINT:
                    725:       ChatPrint(c, av[1]);
                    726:       break;
                    727: 
                    728:     case CMD_CALL:
                    729:       ChatCall(c, av[1]);
                    730:       break;
                    731: 
                    732:     case CMD_NOTHING:
                    733:       break;
                    734: 
                    735:     case CMD_LOG:
                    736:       ChatLog(c, 0, av[1]);
                    737:       break;
                    738: 
                    739:     case CMD_RETURN:
                    740:       ChatReturn(c, 1);
                    741:       break;
                    742: 
                    743:     case CMD_GOTO:
                    744:       ChatGoto(c, av[1]);
                    745:       break;
                    746: 
                    747:     case CMD_SUCCESS:
                    748:       ChatSuccess(c);
                    749:       break;
                    750: 
                    751:     case -1:
                    752:     case CMD_FAILURE:
                    753:       ChatFailure(c);
                    754:       break;
                    755: 
                    756:     default:
                    757:       assert(0);
                    758:   }
                    759: }
                    760: 
                    761: /*
                    762:  * ChatIf()
                    763:  */
                    764: 
                    765: static void
                    766: ChatIf(ChatInfo c, int ac, char *av[])
                    767: {
                    768:   char *const arg1 = ChatExpandString(c, av[1]);
                    769:   char *const arg2 = ChatExpandString(c, av[3]);
                    770:   int  proceed = 0, invert = 0;
                    771:   Link const l = (Link) c->arg;
                    772: 
                    773: /* Check operator */
                    774: 
                    775:   if (!strcmp(av[2], "==") || !strcmp(av[2], "!=")) {
                    776:     proceed = !strcmp(arg1, arg2);
                    777:     invert = (*av[2] == '!');
                    778:   } else if (!strncmp(av[2], MATCH, strlen(MATCH))
                    779:        || !strncmp(av[2], "!" MATCH, strlen("!" MATCH))) {
                    780:     regex_t    regex;
                    781:     char       errbuf[100];
                    782:     int                errcode;
                    783: 
                    784:     if ((errcode = regcomp(&regex, arg2, REG_EXTENDED)) != 0) {
                    785:       regerror(errcode, &regex, errbuf, sizeof(errbuf));
                    786:       Log(LG_ERR, ("[%s] CHAT: line %d: invalid regular expression \"%s\": %s",
                    787:         l->name, c->lineNum, arg2, errbuf));
                    788:     } else {
                    789:       proceed = ChatMatchRegex(c, &regex, arg1);
                    790:       regfree(&regex);
                    791:       invert = (*av[2] == '!');
                    792:     }
                    793:   } else {
                    794:     Log(LG_ERR, ("[%s] CHAT: line %d: invalid operator \"%s\"",
                    795:       l->name, c->lineNum, av[2]));
                    796:   }
                    797:   Freee(arg1);
                    798:   Freee(arg2);
                    799: 
                    800: /* Do command */
                    801: 
                    802:   if (proceed ^ invert)
                    803:     ChatDoCmd(c, ac - 4, av + 4);
                    804: }
                    805: 
                    806: /*
                    807:  * ChatSuccess()
                    808:  */
                    809: 
                    810: static void
                    811: ChatSuccess(ChatInfo c)
                    812: {
                    813:   ChatStop(c);
                    814:   (*c->result)(c->arg, 1, NULL);
                    815: }
                    816: 
                    817: /*
                    818:  * ChatFailure()
                    819:  */
                    820: 
                    821: static void
                    822: ChatFailure(ChatInfo c)
                    823: {
                    824:   char *reason;
                    825: 
                    826:   reason = c->lastLog;
                    827:   c->lastLog = NULL;
                    828:   ChatStop(c);
                    829:   (*c->result)(c->arg, 0, reason);
                    830:   Freee(reason);
                    831: }
                    832: 
                    833: /*
                    834:  * ChatStop()
                    835:  */
                    836: 
                    837: static void
                    838: ChatStop(ChatInfo c)
                    839: {
                    840:   ChatVar      var, next;
                    841: 
                    842: /* Free temporary variables */
                    843: 
                    844:   for (var = c->temps; var; var = next)
                    845:   {
                    846:     next = var->next;
                    847:     Freee(var->name);
                    848:     Freee(var->value);
                    849:     Freee(var);
                    850:   }
                    851:   c->temps = NULL;
                    852: 
                    853: /* Close script file */
                    854: 
                    855:   if (c->fp)
                    856:   {
                    857:     fclose(c->fp);
                    858:     c->fp = NULL;
                    859:   }
                    860: 
                    861: /* Forget active script name */
                    862: 
                    863:   Freee(c->scriptName);
                    864:   c->scriptName = NULL;
                    865: 
                    866: /* Cancel all sets */
                    867: 
                    868:   ChatCancel(c, CHAT_KEYWORD_ALL);
                    869: 
                    870: /* Cancel all input and output */
                    871: 
                    872:   EventUnRegister(&c->rdEvent);
                    873:   EventUnRegister(&c->wrEvent);
                    874:   if (c->out != NULL) {
                    875:     Freee(c->out);
                    876:     c->out = NULL;
                    877:     c->outLen = 0;
                    878:   }
                    879: 
                    880: /* Pop the stack */
                    881: 
                    882:   while (c->stack)
                    883:     ChatReturn(c, 0);
                    884: 
                    885: /* Empty read buffer and last log message buffer */
                    886: 
                    887:   c->readBufLen = 0;
                    888:   if (c->lastLog)
                    889:   {
                    890:     Freee(c->lastLog);
                    891:     c->lastLog = NULL;
                    892:   }
                    893: 
                    894: /* Done */
                    895: 
                    896:   c->state = CHAT_IDLE;
                    897: }
                    898: 
                    899: /*
                    900:  * ChatAddMatch()
                    901:  */
                    902: 
                    903: static void
                    904: ChatAddMatch(ChatInfo c, int exact, const char *set,
                    905:        char *pat, const char *label)
                    906: {
                    907:   ChatMatch    match, *mp;
                    908:   Link         const l = (Link) c->arg;
                    909: 
                    910:   /* Expand pattern */
                    911:   pat = ChatExpandString(c, pat);
                    912: 
                    913:   /* Create new match */
                    914:   match = (ChatMatch) Malloc(MB_CHAT, sizeof(*match));
                    915:   match->exact = !!exact;
                    916:   if (exact) {
                    917:     match->u.exact.pat = pat;
                    918:     match->u.exact.matched = 0;
                    919:     ChatComputeFailure(c, &match->u.exact);
                    920:   } else {
                    921:     int                errcode;
                    922:     char       errbuf[100];
                    923: 
                    924:     /* Convert pattern into compiled regular expression */
                    925:     errcode = regcomp(&match->u.regex, pat, REG_EXTENDED);
                    926:     Freee(pat);
                    927: 
                    928:     /* Check for error */
                    929:     if (errcode != 0) {
                    930:       regerror(errcode, &match->u.regex, errbuf, sizeof(errbuf));
                    931:       Log(LG_ERR, ("[%s] CHAT: line %d: invalid regular expression \"%s\": %s",
                    932:         l->name, c->lineNum, pat, errbuf));
                    933:       ChatFailure(c);
                    934:       Freee(match);
                    935:       return;
                    936:     }
                    937:   }
                    938:   match->set = ChatExpandString(c, set);
                    939:   match->label = ChatExpandString(c, label);
                    940: 
                    941: /* Add at the tail of the list, so if there's a tie, first match defined wins */
                    942: 
                    943:   for (mp = &c->matches; *mp; mp = &(*mp)->next);
                    944:   *mp = match;
                    945:   match->next = NULL;
                    946: }
                    947: 
                    948: /*
                    949:  * ChatAddTimer()
                    950:  */
                    951: 
                    952: static void
                    953: ChatAddTimer(ChatInfo c, const char *set, u_int secs, const char *label)
                    954: {
                    955:   ChatTimer    timer;
                    956: 
                    957: /* Add new timer */
                    958: 
                    959:   timer = (ChatTimer) Malloc(MB_CHAT, sizeof(*timer));
                    960:   timer->c = c;
                    961:   timer->set = ChatExpandString(c, set);
                    962:   timer->label = ChatExpandString(c, label);
                    963:   timer->next = c->timers;
                    964:   c->timers = timer;
                    965: 
                    966: /* Start it */
                    967: 
                    968:   EventRegister(&timer->event, EVENT_TIMEOUT,
                    969:     secs * 1000, 0, ChatTimeout, timer);
                    970: }
                    971: 
                    972: /*
                    973:  * ChatCancel()
                    974:  */
                    975: 
                    976: static void
                    977: ChatCancel(ChatInfo c, const char *set0)
                    978: {
                    979:   int          all;
                    980:   char         *set;
                    981:   ChatMatch    match, *mp;
                    982:   ChatTimer    timer, *tp;
                    983: 
                    984: /* Expand set name; check for special "all" keyword */
                    985: 
                    986:   set = ChatExpandString(c, set0);
                    987:   all = !strcasecmp(set, CHAT_KEYWORD_ALL);
                    988: 
                    989: /* Nuke matches */
                    990: 
                    991:   for (mp = &c->matches; (match = *mp) != NULL; ) {
                    992:     if (all || !strcmp(match->set, set)) {
                    993:       *mp = match->next;
                    994:       ChatFreeMatch(c, match);
                    995:     } else
                    996:       mp = &match->next;
                    997:   }
                    998: 
                    999: /* Nuke timers */
                   1000: 
                   1001:   for (tp = &c->timers; (timer = *tp) != NULL; ) {
                   1002:     if (all || !strcmp(timer->set, set)) {
                   1003:       *tp = timer->next;
                   1004:       ChatFreeTimer(c, timer);
                   1005:     } else
                   1006:       tp = &timer->next;
                   1007:   }
                   1008: 
                   1009: /* Done */
                   1010: 
                   1011:   Freee(set);
                   1012: }
                   1013: 
                   1014: /*
                   1015:  * ChatGoto()
                   1016:  */
                   1017: 
                   1018: static int
                   1019: ChatGoto(ChatInfo c, const char *label0)
                   1020: {
                   1021:   const int    lineNum = c->lineNum;
                   1022:   char         *label;
                   1023:   int          rtn;
                   1024:   Link         const l = (Link) c->arg;
                   1025: 
                   1026: /* Expand label */
                   1027: 
                   1028:   label = ChatExpandString(c, label0);
                   1029: 
                   1030: /* Default label means "here" */
                   1031: 
                   1032:   if (!strcmp(label, DEFAULT_LABEL))
                   1033:   {
                   1034:     Freee(label);
                   1035:     return(0);
                   1036:   }
                   1037: 
                   1038: /* Search script file */
                   1039: 
                   1040:   if ((rtn = ChatSeekToLabel(c, label)) < 0)
                   1041:   {
                   1042:     Log(LG_ERR, ("[%s] CHAT: line %d: label \"%s\" not found",
                   1043:       l->name, lineNum, label));
                   1044:     ChatFailure(c);
                   1045:   }
                   1046:   Freee(label);
                   1047:   return(rtn);
                   1048: }
                   1049: 
                   1050: /*
                   1051:  * ChatCall()
                   1052:  */
                   1053: 
                   1054: static void
                   1055: ChatCall(ChatInfo c, const char *label0)
                   1056: {
                   1057:   ChatFrame    frame;
                   1058:   ChatMatch    match;
                   1059:   ChatTimer    timer;
                   1060:   char         *label;
                   1061:   Link         const l = (Link) c->arg;
                   1062: 
                   1063: /* Expand label */
                   1064: 
                   1065:   label = ChatExpandString(c, label0);
                   1066: 
                   1067: /* Adjust stack */
                   1068: 
                   1069:   frame = (ChatFrame) Malloc(MB_CHAT, sizeof(*frame));
                   1070:   fgetpos(c->fp, &frame->posn);
                   1071:   frame->lineNum = c->lineNum;
                   1072:   frame->up = c->stack;
                   1073:   c->stack = frame;
                   1074: 
                   1075: /* Find label */
                   1076: 
                   1077:   if (ChatSeekToLabel(c, label) < 0)
                   1078:   {
                   1079:     Log(LG_ERR, ("[%s] CHAT: line %d: %s: label \"%s\" not found",
                   1080:       l->name, frame->lineNum, CALL, label));
                   1081:     ChatFailure(c);
                   1082:   }
                   1083:   Freee(label);
                   1084: 
                   1085: /* Increment call depth for timer and match events */
                   1086: 
                   1087:   for (match = c->matches; match; match = match->next)
                   1088:     match->frameDepth++;
                   1089:   for (timer = c->timers; timer; timer = timer->next)
                   1090:     timer->frameDepth++;
                   1091: }
                   1092: 
                   1093: /*
                   1094:  * ChatReturn()
                   1095:  */
                   1096: 
                   1097: static void
                   1098: ChatReturn(ChatInfo c, int seek)
                   1099: {
                   1100:   ChatFrame    frame;
                   1101:   ChatMatch    *mp, match;
                   1102:   ChatTimer    *tp, timer;
                   1103:   Link         const l = (Link) c->arg;
                   1104: 
                   1105:   if (c->stack == NULL)
                   1106:   {
                   1107:     Log(LG_ERR, ("[%s] CHAT: line %d: %s without corresponding %s",
                   1108:       l->name, c->lineNum, RETURN, CALL));
                   1109:     ChatFailure(c);
                   1110:     return;
                   1111:   }
                   1112: 
                   1113: /* Pop frame */
                   1114: 
                   1115:   frame = c->stack;
                   1116:   if (seek) {
                   1117:     fsetpos(c->fp, &frame->posn);
                   1118:     c->lineNum = frame->lineNum;
                   1119:   }
                   1120:   c->stack = frame->up;
                   1121:   Freee(frame);
                   1122: 
                   1123: /* Decrement call depth for timer and match events */
                   1124: 
                   1125:   for (mp = &c->matches; (match = *mp) != NULL; ) {
                   1126:     if (--match->frameDepth < 0) {
                   1127:       *mp = match->next;
                   1128:       ChatFreeMatch(c, match);
                   1129:     } else
                   1130:       mp = &match->next;
                   1131:   }
                   1132:   for (tp = &c->timers; (timer = *tp) != NULL; ) {
                   1133:     if (--timer->frameDepth < 0) {
                   1134:       *tp = timer->next;
                   1135:       ChatFreeTimer(c, timer);
                   1136:     } else
                   1137:       tp = &timer->next;
                   1138:   }
                   1139: }
                   1140: 
                   1141: /*
                   1142:  * ChatLog()
                   1143:  */
                   1144: 
                   1145: static void
                   1146: ChatLog(ChatInfo c, int code, const char *string)
                   1147: {
                   1148:   char *exp_string;
                   1149:   Link const l = (Link) c->arg;
                   1150: 
                   1151:   exp_string = ChatExpandString(c, string);
                   1152:   Log(LG_CHAT, ("[%s] CHAT: %s", l->name, exp_string));
                   1153:   if (c->lastLog)
                   1154:     Freee(c->lastLog);
                   1155:   c->lastLog = exp_string;
                   1156: }
                   1157: 
                   1158: /*
                   1159:  * ChatPrint()
                   1160:  */
                   1161: 
                   1162: static void
                   1163: ChatPrint(ChatInfo c, const char *string)
                   1164: {
                   1165:   char *exp_string, *buf;
                   1166:   int  exp_len;
                   1167: 
                   1168: /* Add variable-expanded string to output queue */
                   1169: 
                   1170:   exp_len = strlen(exp_string = ChatExpandString(c, string));
                   1171:   buf = Malloc(MB_CHAT, c->outLen + exp_len);
                   1172:   if (c->out != NULL) {
                   1173:     memcpy(buf, c->out, c->outLen);
                   1174:     Freee(c->out);
                   1175:   } else
                   1176:     assert(c->outLen == 0);
                   1177:   memcpy(buf + c->outLen, exp_string, exp_len);
                   1178:   c->out = buf;
                   1179:   c->outLen += exp_len;
                   1180: 
                   1181: /* Debugging dump */
                   1182: 
                   1183:   ChatDumpBuf(c, exp_string, exp_len, "sending");
                   1184:   Freee(exp_string);
                   1185: 
                   1186: /* Simulate a writable event to get things going */
                   1187: 
                   1188:   ChatWrite(EVENT_WRITE, c);
                   1189: }
                   1190: 
                   1191: /*
                   1192:  * ChatWrite()
                   1193:  */
                   1194: 
                   1195: static void
                   1196: ChatWrite(int type, void *cookie)
                   1197: {
                   1198:   ChatInfo     const c = (ChatInfo) cookie;
                   1199:   int          nw;
                   1200:   Link         const l = (Link) c->arg;
                   1201: 
                   1202: /* Write as much as we can */
                   1203: 
                   1204:   assert(c->out != NULL && c->outLen > 0);
                   1205:   if ((nw = write(c->fd, c->out, c->outLen)) < 0) {
                   1206:     if (errno == EAGAIN)
                   1207:       return;
                   1208:     Perror("[%s] CHAT: write", l->name);
                   1209:     ChatFailure(c);
                   1210:     return;
                   1211:   }
                   1212: 
                   1213: /* Update output queue */
                   1214: 
                   1215:   c->outLen -= nw;
                   1216:   if (c->outLen <= 0) {
                   1217:     Freee(c->out);
                   1218:     c->out = NULL;
                   1219:     c->outLen = 0;
                   1220:   } else {
                   1221:     memmove(c->out, c->out + nw, c->outLen);
                   1222:     EventRegister(&c->wrEvent, EVENT_WRITE, c->fd, 0, ChatWrite, c);
                   1223:   }
                   1224: }
                   1225: 
                   1226: /*
                   1227:  * ChatSetBaudrate()
                   1228:  */
                   1229: 
                   1230: static int
                   1231: ChatSetBaudrate(ChatInfo c, const char *new)
                   1232: {
                   1233:   char *endptr;
                   1234:   int  rate = strtoul(new, &endptr, 0);
                   1235:   Link const l = (Link) c->arg;
                   1236: 
                   1237:   if (!*new || *endptr || (*c->setBaudrate)(c->arg, rate) < 0)
                   1238:   {
                   1239:     Log(LG_CHAT, ("[%s] CHAT: line %d: invalid baudrate \"%s\"",
                   1240:        l->name, c->lineNum, new));
                   1241:     ChatFailure(c);
                   1242:     return(-1);
                   1243:   }
                   1244:   return(0);
                   1245: }
                   1246: 
                   1247: /*
                   1248:  * ChatVarSet()
                   1249:  */
                   1250: 
                   1251: static int
                   1252: ChatVarSet(ChatInfo c, const char *rname,
                   1253:        const char *value, int expand, int pre)
                   1254: {
                   1255:   ChatVar      var, *head;
                   1256:   char         *new, name[CHAT_MAX_VARNAME];
                   1257: 
                   1258: /* Check & extract variable name */
                   1259: 
                   1260:   assert(rname && value);
                   1261:   if (!*rname || ChatVarExtract(rname + 1, name, sizeof(name), 1) < 0)
                   1262:     return(-1);
                   1263:   head = isupper(*name) ? &c->globals : &c->temps;
                   1264: 
                   1265: /* Create new value */
                   1266: 
                   1267:   if (expand)
                   1268:     new = ChatExpandString(c, value);
                   1269:   else
                   1270:   {
                   1271:     new = Mstrdup(MB_CHAT, value);
                   1272:   }
                   1273: 
                   1274: /* Check for special variable names */
                   1275: 
                   1276:   if (!strcmp(rname, CHAT_VAR_BAUDRATE))
                   1277:   {
                   1278:     if (!pre && ChatSetBaudrate(c, new) < 0)
                   1279:     {
                   1280:       Freee(new);
                   1281:       return(0);
                   1282:     }
                   1283:   }
                   1284: 
                   1285: /* Find variable if it exists, otherwise create and add to list */
                   1286: 
                   1287:   if ((var = ChatVarFind(head, name)))
                   1288:   {
                   1289:     char       *ovalue;
                   1290: 
                   1291:   /* Replace value; free it after expanding (might be used in expansion) */
                   1292: 
                   1293:     ovalue = var->value;
                   1294:     var->value = new;
                   1295:     Freee(ovalue);
                   1296:   }
                   1297:   else
                   1298:   {
                   1299: 
                   1300:   /* Create new struct and add to list */
                   1301: 
                   1302:     var = Malloc(MB_CHAT, sizeof(*var));
                   1303:     var->name = Mstrdup(MB_CHAT, name);
                   1304:     var->value = new;
                   1305:     var->next = *head;
                   1306:     *head = var;
                   1307:   }
                   1308:   return(0);
                   1309: }
                   1310: 
                   1311: /*
                   1312:  * ChatVarGet()
                   1313:  */
                   1314: 
                   1315: static ChatVar
                   1316: ChatVarGet(ChatInfo c, const char *rname)
                   1317: {
                   1318:   char name[CHAT_MAX_VARNAME];
                   1319: 
                   1320: /* Check & extract variable name */
                   1321: 
                   1322:   if (!*rname || ChatVarExtract(rname + 1, name, sizeof(name), 1) < 0)
                   1323:     return(NULL);
                   1324:   return(ChatVarFind(isupper(*name) ? &c->globals : &c->temps, name));
                   1325: }
                   1326: 
                   1327: /*
                   1328:  * ChatVarFind()
                   1329:  */
                   1330: 
                   1331: static ChatVar
                   1332: ChatVarFind(ChatVar *head, char *name)
                   1333: {
                   1334:   ChatVar      var, *vp;
                   1335: 
                   1336:   assert(head && name);
                   1337:   for (vp = head;
                   1338:     (var = *vp) && strcmp(var->name, name);
                   1339:     vp = &var->next);
                   1340:   if (var)
                   1341:   {
                   1342:     *vp = var->next;           /* caching */
                   1343:     var->next = *head;
                   1344:     *head = var;
                   1345:   }
                   1346:   return(var);
                   1347: }
                   1348: 
                   1349: /*
                   1350:  * ChatVarExtract()
                   1351:  */
                   1352: 
                   1353: static int
                   1354: ChatVarExtract(const char *string, char *buf, int max, int strict)
                   1355: {
                   1356:   int  k, len, brace;
                   1357: 
                   1358: /* Get variable name, optionally surrounded by braces */
                   1359: 
                   1360:   k = brace = (*string == '{');
                   1361:   if (!isalpha(string[k]))
                   1362:     return(-1);
                   1363:   for (len = 0, k = brace;
                   1364:       (isalnum(string[k]) || string[k] == '_') && len < max - 1;
                   1365:       k++)
                   1366:     buf[len++] = string[k];
                   1367:   buf[len] = 0;
                   1368:   if (len == 0 || (brace && string[k] != '}') || (strict && string[k + brace]))
                   1369:     return(-1);
                   1370:   return(len + brace * 2);
                   1371: }
                   1372: 
                   1373: /*
                   1374:  * ChatFreeMatch()
                   1375:  */
                   1376: 
                   1377: static void
                   1378: ChatFreeMatch(ChatInfo c, ChatMatch match)
                   1379: {
                   1380:   Freee(match->set);
                   1381:   Freee(match->label);
                   1382:   if (match->exact) {
                   1383:     Freee(match->u.exact.pat);
                   1384:     Freee(match->u.exact.fail);
                   1385:   } else {
                   1386:     regfree(&match->u.regex);
                   1387:   }
                   1388:   Freee(match);
                   1389: }
                   1390: 
                   1391: /*
                   1392:  * ChatFreeTimer()
                   1393:  */
                   1394: 
                   1395: static void
                   1396: ChatFreeTimer(ChatInfo c, ChatTimer timer)
                   1397: {
                   1398:   EventUnRegister(&timer->event);
                   1399:   Freee(timer->set);
                   1400:   Freee(timer->label);
                   1401:   Freee(timer);
                   1402: }
                   1403: 
                   1404: /*
                   1405:  * ChatDumpReadBuf()
                   1406:  *
                   1407:  * Display accumulated input bytes for debugging purposes. Reset buffer.
                   1408:  */
                   1409: 
                   1410: static void
                   1411: ChatDumpReadBuf(ChatInfo c)
                   1412: {
                   1413:   if (c->readBufLen)
                   1414:   {
                   1415:     ChatDumpBuf(c, c->readBuf, c->readBufLen, "read");
                   1416:     c->readBufLen = 0;
                   1417:   }
                   1418: }
                   1419: 
                   1420: /*
                   1421:  * ChatGetCmd()
                   1422:  */
                   1423: 
                   1424: static int
                   1425: ChatGetCmd(ChatInfo c, const char *token, int n_args)
                   1426: {
                   1427:   int  k;
                   1428:   Link const l = (Link) c->arg;
                   1429: 
                   1430:   for (k = 0; k < CHAT_NUM_COMMANDS; k++)
                   1431:     if (!strcasecmp(gCmds[k].name, token))
                   1432:     {
                   1433:       if ((gCmds[k].min && n_args < gCmds[k].min)
                   1434:        || (gCmds[k].max && n_args > gCmds[k].max))
                   1435:       {
                   1436:        Log(LG_ERR, ("[%s] CHAT: line %d: %s: bad argument count",
                   1437:          l->name, c->lineNum, token));
                   1438:        return(-1);
                   1439:       }
                   1440:       else
                   1441:        return(gCmds[k].id);
                   1442:     }
                   1443:   Log(LG_ERR, ("[%s] CHAT: line %d: unknown command \"%s\"",
                   1444:     l->name, c->lineNum, token));
                   1445:   return(-1);
                   1446: }
                   1447: 
                   1448: /*
                   1449:  * ChatExpandString()
                   1450:  *
                   1451:  * Expand variables in string. Return result in malloc'd buffer.
                   1452:  */
                   1453: 
                   1454: static char *
                   1455: ChatExpandString(ChatInfo c, const char *string)
                   1456: {
                   1457:   ChatVar      var;
                   1458:   int          k, j, new_len, nlen, doit = 0;
                   1459:   char         *new = NULL, nbuf[CHAT_MAX_VARNAME];
                   1460: 
                   1461: /* Compute expanded length and then rescan and expand */
                   1462: 
                   1463:   new_len = 0;
                   1464: rescan:
                   1465:   for (j = k = 0; string[k]; k++)
                   1466:     switch (string[k])
                   1467:     {
                   1468:       case CHAT_VAR_PREFIX:
                   1469:        *nbuf = string[k];
                   1470:        if (string[k + 1] == CHAT_VAR_PREFIX)   /* $$ -> $ */
                   1471:        {
                   1472:          k++;
                   1473:          goto normal;
                   1474:        }
                   1475:        if ((nlen = ChatVarExtract(string + k + 1,
                   1476:            nbuf + 1, sizeof(nbuf) - 1, 0)) < 0)
                   1477:          goto normal;
                   1478:        k += nlen;
                   1479:        if ((var = ChatVarGet(c, nbuf)))
                   1480:        {
                   1481:          if (doit)
                   1482:          {
                   1483:            memcpy(new + j, var->value, strlen(var->value));
                   1484:            j += strlen(var->value);
                   1485:          }
                   1486:          else
                   1487:            new_len += strlen(var->value);
                   1488:        }
                   1489:        break;
                   1490:       default: normal:
                   1491:        if (doit)
                   1492:          new[j++] = string[k];
                   1493:        else
                   1494:          new_len++;
                   1495:        break;
                   1496:     }
                   1497: 
                   1498: /* Allocate and rescan */
                   1499: 
                   1500:   if (!doit)
                   1501:   {
                   1502:     new = Malloc(MB_CHAT, new_len + 1);
                   1503:     doit = 1;
                   1504:     goto rescan;
                   1505:   }
                   1506: 
                   1507: /* Done */
                   1508: 
                   1509:   new[j] = 0;
                   1510:   assert(j == new_len);
                   1511:   return(new);
                   1512: }
                   1513: 
                   1514: /*
                   1515:  * ChatSetMatchVars()
                   1516:  *
                   1517:  * Set the various special match string variables after a successful
                   1518:  * match (of either type, exact or regular expression).
                   1519:  */
                   1520: 
                   1521: static void
                   1522: ChatSetMatchVars(ChatInfo c, int exact, const char *input, ...)
                   1523: {
                   1524:   const int    preflen = strlen(CHAT_VAR_MATCHED);
                   1525:   ChatVar      var, *vp;
                   1526:   va_list      args;
                   1527: 
                   1528:   /* Unset old match variables */
                   1529:   for (vp = &c->temps; (var = *vp) != NULL; ) {
                   1530:     char       *eptr;
                   1531: 
                   1532:     if (strncmp(var->name, CHAT_VAR_MATCHED, preflen)) {
                   1533:       vp = &var->next;
                   1534:       continue;
                   1535:     }
                   1536:     (void) strtoul(var->name + preflen, &eptr, 10);
                   1537:     if (*eptr) {
                   1538:       vp = &var->next;
                   1539:       continue;
                   1540:     }
                   1541:     *vp = var->next;
                   1542:     Freee(var->name);
                   1543:     Freee(var->value);
                   1544:     Freee(var);
                   1545:   }
                   1546: 
                   1547:   /* Set new match variables */
                   1548:   va_start(args, input);
                   1549:   if (exact) {
                   1550:     ChatVarSet(c, CHAT_VAR_MATCHED, input, 0, 0);
                   1551:   } else {
                   1552:     const int  nmatch = va_arg(args, int);
                   1553:     regmatch_t *const pmatch = va_arg(args, regmatch_t *);
                   1554:     char       *const value = Malloc(MB_CHAT, strlen(input) + 1);
                   1555:     int                k;
                   1556: 
                   1557:     for (k = 0; k < nmatch; k++) {
                   1558:       const int        len = pmatch[k].rm_eo - pmatch[k].rm_so;
                   1559:       char     name[sizeof(CHAT_VAR_MATCHED) + 16];
                   1560: 
                   1561:       memcpy(value, input + pmatch[k].rm_so, len);
                   1562:       value[len] = 0;
                   1563:       snprintf(name, sizeof(name), "%s%d", CHAT_VAR_MATCHED, k);
                   1564:       if (k == 0)
                   1565:        ChatVarSet(c, CHAT_VAR_MATCHED, value, 0, 0);
                   1566:       ChatVarSet(c, name, value, 0, 0);
                   1567:     }
                   1568:     Freee(value);
                   1569:   }
                   1570:   va_end(args);
                   1571: }
                   1572: 
                   1573: /*
                   1574:  * ChatMatchChar()
                   1575:  *
                   1576:  * Update "ex" given that "ch" is the next character.
                   1577:  * Returns 1 if target has become fully matched.
                   1578:  */
                   1579: 
                   1580: static int
                   1581: ChatMatchChar(struct cm_exact *ex, char ch)
                   1582: {
                   1583:   const int    len = strlen(ex->pat);
                   1584: 
                   1585:   /* Account for zero length pattern string -- match on next input char */
                   1586:   if (len == 0)
                   1587:     return 1;
                   1588: 
                   1589:   /* Update longest-matched-prefix-length based on next char */
                   1590:   assert(ex->matched < len);
                   1591:   while (1) {
                   1592:     if (ch == ex->pat[ex->matched])
                   1593:       return ++ex->matched == len;
                   1594:     if (ex->matched == 0)
                   1595:       return 0;
                   1596:     ex->matched = ex->fail[ex->matched];
                   1597:   }
                   1598: }
                   1599: 
                   1600: /*
                   1601:  * ChatMatchRegex()
                   1602:  *
                   1603:  * See if a line matches a regular expression. If so, return 1
                   1604:  * and set the corresponding match variables.
                   1605:  */
                   1606: 
                   1607: static int
                   1608: ChatMatchRegex(ChatInfo c, regex_t *reg, const char *input)
                   1609: {
                   1610:   const int    nmatch = reg->re_nsub + 1;
                   1611:   regmatch_t   *pmatch = Malloc(MB_CHAT, nmatch * sizeof(*pmatch));
                   1612:   int          rtn, match;
                   1613:   Link         const l = (Link) c->arg;
                   1614: 
                   1615:   switch ((rtn = regexec(reg, input, nmatch, pmatch, 0))) {
                   1616:     default:
                   1617:       Log(LG_ERR, ("[%s] CHAT: regexec() returned %d?", l->name, rtn));
                   1618:       /* fall through */
                   1619:     case REG_NOMATCH:
                   1620:       match = 0;
                   1621:       break;
                   1622:     case 0:
                   1623:       ChatSetMatchVars(c, 0, input, nmatch, pmatch);
                   1624:       match = 1;
                   1625:       break;
                   1626:   }
                   1627:   Freee(pmatch);
                   1628:   return match;
                   1629: }
                   1630: 
                   1631: /*
                   1632:  * ChatComputeFailure()
                   1633:  *
                   1634:  * Compute the failure function for the exact match pattern string.
                   1635:  * That is, for each character position i=0..n-1, compute fail(i) =
                   1636:  * 0 if i==0, else fail(i) = the greatest j < i such that characters
                   1637:  * 0..j match characters (i - j)..(i - 1).
                   1638:  *
                   1639:  * There are linear time ways to compute this, but we're lazy.
                   1640:  */
                   1641: 
                   1642: static void
                   1643: ChatComputeFailure(ChatInfo c, struct cm_exact *ex)
                   1644: {
                   1645:   const int    len = strlen(ex->pat);
                   1646:   int          i, j, k;
                   1647: 
                   1648:   ex->fail = (u_short *) Malloc(MB_CHAT, len * sizeof(*ex->fail));
                   1649:   for (i = 1; i < len; i++) {
                   1650:     for (j = i - 1; j > 0; j--) {
                   1651:       for (k = 0; k < j && ex->pat[k] == ex->pat[i - j + k]; k++);
                   1652:       if (k == j) {
                   1653:        ex->fail[i] = j;
                   1654:        break;
                   1655:       }
                   1656:     }
                   1657:   }
                   1658: }
                   1659: 
                   1660: /*
                   1661:  * ChatDecodeTime()
                   1662:  */
                   1663: 
                   1664: static int
                   1665: ChatDecodeTime(ChatInfo c, char *string, u_int *secsp)
                   1666: {
                   1667:   u_long       secs;
                   1668:   char         *secstr, *mark;
                   1669:   Link         const l = (Link) c->arg;
                   1670: 
                   1671:   secstr = ChatExpandString(c, string);
                   1672:   secs = strtoul(secstr, &mark, 0);
                   1673:   Freee(secstr);
                   1674:   if (mark == secstr) {
                   1675:     Log(LG_ERR, ("[%s] CHAT: line %d: illegal value \"%s\"",
                   1676:       l->name, c->lineNum, string));
                   1677:     return(0);
                   1678:   }
                   1679:   *secsp = (u_int) secs;
                   1680:   return(1);
                   1681: }
                   1682: 
                   1683: /*
                   1684:  * ChatReadLine()
                   1685:  */
                   1686: 
                   1687: static char *
                   1688: ChatReadLine(ChatInfo c)
                   1689: {
                   1690:   return ReadFullLine(c->fp, &c->lineNum, NULL, 0);
                   1691: }
                   1692: 
                   1693: /*
                   1694:  * ChatParseLine()
                   1695:  */
                   1696: 
                   1697: static int
                   1698: ChatParseLine(ChatInfo c, char *line, char *av[], int max)
                   1699: {
                   1700:   return ParseLine(line, av, max, 1);
                   1701: }
                   1702: 
                   1703: /*
                   1704:  * ChatSeekToLabel()
                   1705:  */
                   1706: 
                   1707: static int
                   1708: ChatSeekToLabel(ChatInfo c, const char *label)
                   1709: {
                   1710:   return SeekToLabel(c->fp, label, &c->lineNum, NULL);
                   1711: }
                   1712: 
                   1713: /*
                   1714:  * ChatDumpBuf()
                   1715:  */
                   1716: 
                   1717: #define DUMP_BYTES_PER_LINE   16
                   1718: 
                   1719: static void
                   1720: ChatDumpBuf(ChatInfo c, const char *buf, int len, const char *fmt, ...)
                   1721: {
                   1722:   va_list      args;
                   1723:   char         cbuf[128];
                   1724:   int          k, pos;
                   1725:   Link         const l = (Link) c->arg;
                   1726: 
                   1727:   /* Do label */
                   1728:   va_start(args, fmt);
                   1729:   vsnprintf(cbuf, sizeof(cbuf), fmt, args);
                   1730:   va_end(args);
                   1731:   Log(LG_CHAT2, ("[%s] CHAT: %s", l->name, cbuf));
                   1732: 
                   1733:   /* Do bytes */
                   1734:   for (pos = 0; pos < len; pos += DUMP_BYTES_PER_LINE) {
                   1735:     *cbuf = '\0';
                   1736:     for (k = 0; k < DUMP_BYTES_PER_LINE; k++) {
                   1737:        if (pos + k < len) {
                   1738:            snprintf(cbuf + strlen(cbuf), sizeof(cbuf) - strlen(cbuf),
                   1739:                " %02x", buf[pos + k]);
                   1740:        } else {
                   1741:            snprintf(cbuf + strlen(cbuf), sizeof(cbuf) - strlen(cbuf),
                   1742:                "   ");
                   1743:        }
                   1744:     }
                   1745:     snprintf(cbuf + strlen(cbuf), sizeof(cbuf) - strlen(cbuf), "  ");
                   1746:     for (k = 0; k < DUMP_BYTES_PER_LINE; k++) {
                   1747:        if (pos + k < len) {
                   1748:            snprintf(cbuf + strlen(cbuf), sizeof(cbuf) - strlen(cbuf),
                   1749:                "%c", isprint(buf[pos + k]) ? buf[pos + k] : '.');
                   1750:        } else {
                   1751:            snprintf(cbuf + strlen(cbuf), sizeof(cbuf) - strlen(cbuf),
                   1752:                " ");
                   1753:        }
                   1754:     }
                   1755:     Log(LG_CHAT2, ("[%s] CHAT: %s", l->name, cbuf));
                   1756:   }
                   1757: }

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