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