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