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(®ex, arg2, REG_EXTENDED)) != 0) {
! 785: regerror(errcode, ®ex, 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, ®ex, arg1);
! 790: regfree(®ex);
! 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>