Annotation of embedaddon/mpd/src/chat.c, revision 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>