File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / mpd / src / chat.c
Revision 1.1: download - view: text, annotated - select for diffs - revision graph
Tue Feb 21 23:32:47 2012 UTC (12 years, 4 months ago) by misho
CVS tags: MAIN, HEAD
Initial revision

    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>