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

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

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