ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/vendor/ircservices-5.1.24/actions.c
Revision: 3389
Committed: Fri Apr 25 14:12:15 2014 UTC (9 years, 10 months ago) by michael
Content type: text/x-csrc
File size: 24354 byte(s)
Log Message:
- Imported ircservices-5.1.24

File Contents

# Content
1 /* Various routines to perform simple actions.
2 *
3 * IRC Services is copyright (c) 1996-2009 Andrew Church.
4 * E-mail: <achurch@achurch.org>
5 * Parts written by Andrew Kempe and others.
6 * This program is free but copyrighted software; see the file GPL.txt for
7 * details.
8 */
9
10 #include "services.h"
11 #include "language.h"
12 #include "modules.h"
13 #include "timeout.h"
14
15 /*************************************************************************/
16
17 static int cb_clear_channel = -1;
18 static int cb_set_topic = -1;
19
20 /* Sender to be used with clear_channel() (empty string: use server name) */
21 static char clear_channel_sender[NICKMAX] = {0};
22
23 /*************************************************************************/
24 /*************************************************************************/
25
26 int actions_init(int ac, char **av)
27 {
28 cb_clear_channel = register_callback("clear channel");
29 cb_set_topic = register_callback("set topic");
30 if (cb_clear_channel < 0 || cb_set_topic < 0) {
31 log("actions_init: register_callback() failed\n");
32 return 0;
33 }
34 return 1;
35 }
36
37 /*************************************************************************/
38
39 void actions_cleanup(void)
40 {
41 unregister_callback(cb_set_topic);
42 unregister_callback(cb_clear_channel);
43 }
44
45 /*************************************************************************/
46 /*************************************************************************/
47
48 /* Note a bad password attempt for the given user from the given service.
49 * If they've used up their limit, toss them off. `service' is used only
50 * for the sender of the bad-password and warning messages; these messages
51 * are not sent if `service' is NULL. `what' describes what the password
52 * was for, and is used in the kill message if the user is killed. The
53 * function's return value is 1 if the user was warned, 2 if the user was
54 * killed, and 0 otherwise.
55 */
56
57 int bad_password(const char *service, User *u, const char *what)
58 {
59 time_t now = time(NULL);
60
61 if (service)
62 notice_lang(service, u, PASSWORD_INCORRECT);
63
64 if (!BadPassLimit)
65 return 0;
66
67 if (BadPassTimeout > 0 && u->bad_pw_time > 0
68 && now >= u->bad_pw_time + BadPassTimeout)
69 u->bad_pw_count = 0;
70 u->bad_pw_count++;
71 u->bad_pw_time = now;
72 if (u->bad_pw_count >= BadPassLimit) {
73 char buf[BUFSIZE];
74 snprintf(buf, sizeof(buf), "Too many invalid passwords (%s)", what);
75 kill_user(NULL, u->nick, buf);
76 return 2;
77 } else if (u->bad_pw_count == BadPassLimit-1) {
78 if (service)
79 notice_lang(service, u, PASSWORD_WARNING);
80 return 1;
81 }
82 return 0;
83 }
84
85 /*************************************************************************/
86
87 /* Clear modes/users from a channel. The "what" parameter is one or more
88 * of the CLEAR_* constants defined in services.h. "param" is:
89 * - for CLEAR_USERS, a kick message (const char *)
90 * - for CLEAR_UMODES, a bitmask of modes to clear (int32)
91 * - for CLEAR_BANS and CLEAR_EXCEPTS, a User * to match against, or
92 * NULL for all bans/exceptions
93 * Note that CLEAR_EXCEPTS must be handled via callback for protocols which
94 * support it.
95 */
96
97 static void clear_modes(const char *sender, Channel *chan);
98 static void clear_bans(const char *sender, Channel *chan, User *u);
99 static void clear_umodes(const char *sender, Channel *chan, int32 modes);
100 static void clear_users(const char *sender, Channel *chan, const char *reason);
101
102 void clear_channel(Channel *chan, int what, const void *param)
103 {
104 const char *sender =
105 *clear_channel_sender ? clear_channel_sender : ServerName;
106
107 if (call_callback_4(cb_clear_channel, sender, chan, what, param) > 0) {
108 set_cmode(NULL, chan);
109 return;
110 }
111
112 if (what & CLEAR_USERS) {
113 clear_users(sender, chan, (const char *)param);
114 /* Once we kick all the users, nothing else will matter */
115 return;
116 }
117
118 if (what & CLEAR_MODES)
119 clear_modes(sender, chan);
120 if (what & CLEAR_BANS)
121 clear_bans(sender, chan, (User *)param);
122 if (what & CLEAR_UMODES)
123 clear_umodes(sender, chan, (int32)(long)param);
124 set_cmode(NULL, chan); /* Flush modes out */
125 }
126
127 static void clear_modes(const char *sender, Channel *chan)
128 {
129 char buf[BUFSIZE];
130 snprintf(buf, sizeof(buf), "-%s",
131 mode_flags_to_string(chan->mode & ~chanmode_reg, MODE_CHANNEL));
132 set_cmode(sender, chan, buf, chan->key);
133 }
134
135 static void clear_bans(const char *sender, Channel *chan, User *u)
136 {
137 int i, count;
138 char **bans;
139
140 if (!chan->bans_count)
141 return;
142
143 /* Save original ban info */
144 count = chan->bans_count;
145 bans = smalloc(sizeof(char *) * count);
146 memcpy(bans, chan->bans, sizeof(char *) * count);
147
148 for (i = 0; i < count; i++) {
149 if (!u || match_usermask(bans[i], u))
150 set_cmode(sender, chan, "-b", bans[i]);
151 }
152 free(bans);
153 }
154
155 static void clear_umodes(const char *sender, Channel *chan, int32 modes)
156 {
157 struct c_userlist *cu;
158
159 LIST_FOREACH (cu, chan->users) {
160 int32 to_clear = cu->mode & modes; /* modes we need to clear */
161 int32 flag = 1; /* mode we're clearing now */
162 while (to_clear) {
163 if (flag == MODE_INVALID) {
164 log("BUG: hit invalid flag in clear_umodes!"
165 " modes to clear = %08X, user modes = %08X",
166 to_clear, cu->mode);
167 break;
168 }
169 if (to_clear & flag) {
170 char buf[3] = "-x";
171 buf[1] = mode_flag_to_char(flag, MODE_CHANUSER);
172 set_cmode(sender, chan, buf, cu->user->nick);
173 to_clear &= ~flag;
174 }
175 flag <<= 1;
176 }
177 cu->mode &= ~modes;
178 }
179 }
180
181 static void clear_users(const char *sender, Channel *chan, const char *reason)
182 {
183 char *av[3];
184 struct c_userlist *cu, *next;
185
186 /* Prevent anyone from coming back in. The ban will disappear
187 * once everyone's gone. */
188 set_cmode(sender, chan, "+b", "*!*@*");
189 set_cmode(NULL, chan); /* Flush modes out */
190
191 av[0] = chan->name;
192 av[2] = (char *)reason;
193 LIST_FOREACH_SAFE (cu, chan->users, next) {
194 av[1] = cu->user->nick;
195 send_channel_cmd(sender, "KICK %s %s :%s",
196 av[0], av[1], av[2]);
197 do_kick(sender, 3, av);
198 }
199 }
200
201 /*************************************************************************/
202
203 /* Set the nickname to be used to send commands in clear_channel() calls.
204 * If NULL, the server name is used; if PTR_INVALID, the name is not
205 * changed. Returns the old value of the sender, or the empty string if no
206 * nickname was set, in a static buffer.
207 */
208
209 const char *set_clear_channel_sender(const char *newsender)
210 {
211 static char oldsender[NICKMAX];
212 strbcpy(oldsender, clear_channel_sender);
213 if (newsender != PTR_INVALID) {
214 if (newsender) {
215 strbcpy(clear_channel_sender, newsender);
216 } else {
217 *clear_channel_sender = 0;
218 }
219 }
220 return oldsender;
221 }
222
223 /*************************************************************************/
224
225 /* Remove a user from the IRC network. `source' is the nick which should
226 * generate the kill, or NULL for a server-generated kill.
227 */
228
229 void kill_user(const char *source, const char *user, const char *reason)
230 {
231 char *av[2];
232 char buf[BUFSIZE];
233
234 if (!user || !*user)
235 return;
236 if (!source || !*source)
237 source = ServerName;
238 if (!reason)
239 reason = "";
240 snprintf(buf, sizeof(buf), "%s (%s)", source, reason);
241 av[0] = (char *)user;
242 av[1] = buf;
243 send_cmd(source, "KILL %s :%s", user, av[1]);
244 do_kill(source, 2, av);
245 }
246
247 /*************************************************************************/
248
249 /* Set the topic on a channel. `setter' must not be NULL. `source' is the
250 * nick to use to send the TOPIC message; if NULL, the server name is used.
251 */
252
253 void set_topic(const char *source, Channel *c, const char *topic,
254 const char *setter, time_t t)
255 {
256 if (!source)
257 source = ServerName;
258 call_callback_5(cb_set_topic, source, c, topic, setter, t);
259 free(c->topic);
260 if (topic && *topic)
261 c->topic = sstrdup(topic);
262 else
263 c->topic = NULL;
264 strbcpy(c->topic_setter, setter);
265 if (call_callback_5(cb_set_topic, source, c, NULL, NULL, t) > 0)
266 return;
267 }
268
269 /*************************************************************************/
270 /*************************************************************************/
271
272 /* set_cmode(): Set modes for a channel and send those modes to remote
273 * servers. Using this routine eliminates the necessity to modify the
274 * internal Channel structure and send the command out separately, and also
275 * allows the modes for a channel to be collected up over several calls and
276 * sent out in a single command, decreasing network traffic (and scroll).
277 * This function should be called as either:
278 * set_cmode(sender, channel, modes, param1, param2...)
279 * to send one or more channel modes, or
280 * set_cmode(NULL, channel)
281 * to flush buffered modes for a channel (if `channel' is NULL, flushes
282 * buffered modes for all channels).
283 *
284 * NOTE: When setting modes with parameters, all parameters MUST be
285 * strings. Numeric parameters must be converted to strings (with
286 * snprintf() or the like) before being passed.
287 */
288
289 #define MAXMODES 6 /* Maximum number of mode parameters */
290
291 #define MAXPARAMSLEN \
292 (510- NICKMAX - 6 - CHANMAX -(3+31+2*MAXMODES)-MAXMODES)
293 /* |:sender| | MODE | |#channel| | -...+...| { param}*
294 * Note that "-...+..." can contain at most 31 (binary) plus 2*MAXMODES
295 * (MAXMODES modes with parameters plus +/-) characters. */
296
297 static struct modedata {
298 time_t used;
299 Channel *channel;
300 char sender[NICKMAX];
301 int32 binmodes_on;
302 int32 binmodes_off;
303 char opmodes[MAXMODES*2+1];
304 char params[MAXMODES][MAXPARAMSLEN+1];
305 int nopmodes, nparams, paramslen;
306 Timeout *timeout; /* For timely flushing */
307 } modedata[MERGE_CHANMODES_MAX];
308
309 static void possibly_remove_mode(struct modedata *md, char mode,
310 const char *user);
311 static void add_mode_with_params(struct modedata *md, char mode, int is_add,
312 int params, const char *parambuf, int len);
313 static void flush_cmode(struct modedata *md);
314 static void flush_cmode_callback(Timeout *t);
315
316 /*************************************************************************/
317
318 void set_cmode(const char *sender, Channel *channel, ...)
319 {
320 va_list args;
321 const char *modes, *modes_orig;
322 struct modedata *md;
323 int which = -1, add;
324 int i;
325 char c;
326
327
328 /* If `sender' is NULL, flush out pending modes for the channel (for
329 * all channels if `channel' is also NULL) and return. */
330 if (!sender) {
331 for (i = 0; i < MERGE_CHANMODES_MAX; i++) {
332 if (modedata[i].used && (!channel || modedata[i].channel==channel))
333 flush_cmode(&modedata[i]);
334 }
335 return;
336 }
337
338 /* Get the mode string from the argument list; save the original value
339 * for error messages. */
340 va_start(args, channel);
341 modes = modes_orig = va_arg(args, const char *);
342
343 /* See if we already have pending modes for the channel; if so, reuse
344 * that entry (if the entry is for a different sender, flush out the
345 * pending modes first). */
346 for (i = 0; i < MERGE_CHANMODES_MAX; i++) {
347 if (modedata[i].used != 0 && modedata[i].channel == channel) {
348 if (irc_stricmp(modedata[i].sender, sender) != 0)
349 flush_cmode(&modedata[i]);
350 which = i;
351 break;
352 }
353 }
354 /* If there are no pending modes for the channel, look for an empty
355 * slot in the array. */
356 if (which < 0) {
357 for (i = 0; i < MERGE_CHANMODES_MAX; i++) {
358 if (modedata[i].used == 0) {
359 which = i;
360 break;
361 }
362 }
363 }
364 /* If no slots are free, we'll have to purge one. Find the oldest,
365 * send its modes out, then clear and reuse it. */
366 if (which < 0) {
367 int oldest = 0;
368 time_t oldest_time = modedata[0].used;
369 for (i = 1; i < MERGE_CHANMODES_MAX; i++) {
370 if (modedata[i].used < oldest_time) {
371 oldest_time = modedata[i].used;
372 oldest = i;
373 }
374 }
375 flush_cmode(&modedata[oldest]);
376 which = oldest;
377 }
378
379 /* Save a pointer to the entry, then set up sender and channel. */
380 md = &modedata[which];
381 strbcpy(md->sender, sender);
382 md->channel = channel;
383
384 /* Loop through and process all modes in the mode string. */
385 add = -2; /* -2 means we haven't warned about a missing leading +/- yet */
386 while ((c = *modes++) != 0) {
387 int32 flag;
388 int params, is_chanuser;
389
390 log_debug(2, "set_cmode(%s,%s): char=%c(%02X)",
391 sender, channel->name, c<0x20||c>0x7E ? '.' : c, c);
392
393 /* + and - are handled specially. */
394 if (c == '+') {
395 add = 1;
396 continue;
397 } else if (c == '-') {
398 add = 0;
399 continue;
400 }
401 /* If we see any other character without first seeing a + or -,
402 * note a bug in the logfile and move along. */
403 if (add < 0) {
404 if (add == -2) {
405 log("set_cmode(): BUG: mode string `%s' needs leading +/-",
406 modes_orig);
407 add = -1;
408 }
409 continue;
410 }
411
412 /* Find the flag value and parameter count for the character. */
413 is_chanuser = 0;
414 flag = mode_char_to_flag(c, MODE_CHANNEL);
415 params = mode_char_to_params(c, MODE_CHANNEL);
416 if (!flag) {
417 is_chanuser = 1;
418 flag = mode_char_to_flag(c, MODE_CHANUSER);
419 params = mode_char_to_params(c, MODE_CHANUSER);
420 if (!flag) {
421 log("set_cmode: bad mode '%c'", c);
422 continue;
423 }
424 }
425 params = (params >> (add*8)) & 0xFF;
426
427 if (params) { /* Mode with parameters */
428 char parambuf[BUFSIZE]; /* for putting the parameters in */
429 int len = 0;
430
431 if (params > MAXMODES) {
432 /* Sanity check */
433 fatal("set_cmode(): too many parameters (%d) for mode `%c'\n",
434 params, c);
435 }
436 /* Merge all the parameters into a single string (with no
437 * leading whitespace) */
438 for (i = 0; i < params; i++) {
439 const char *s = va_arg(args, const char *);
440 log_debug(2, "set_cmode(%s,%s): param=%s",
441 sender, channel->name, s);
442 len += snprintf(parambuf+len, sizeof(parambuf)-len,
443 "%s%s", len ? " " : "", s);
444 }
445 if (flag != MODE_INVALID) {
446 /* If it's a binary mode, see if we've set this mode before.
447 * If so (and if the nick is the same for channel user
448 * modes), remove it; the new one will be appended
449 * afterwards. Note that this assumes that setting each
450 * mode is independent, i.e. that -a+ba 2 1 has the same
451 * effect as +ba 2 1 by itself when +a is set. This doesn't
452 * work for +k/-k, so we let multiple such modes remain. */
453 if (c != 'k') {
454 possibly_remove_mode(md, c, is_chanuser ? parambuf : NULL);
455 }
456 }
457 add_mode_with_params(md, c, add, params, parambuf, len);
458 } else { /* Binary mode */
459 /* Note that `flag' should already be set to this value, since
460 * all channel user modes take parameters and thus will never
461 * get here, but just in case... */
462 flag = mode_char_to_flag(c, MODE_CHANNEL);
463 if (add) {
464 md->binmodes_on |= flag;
465 md->binmodes_off &= ~flag;
466 } else {
467 md->binmodes_off |= flag;
468 md->binmodes_on &= ~flag;
469 }
470 }
471 }
472 va_end(args);
473 md->used = time(NULL);
474
475 if (MergeChannelModes) {
476 if (!md->timeout) {
477 md->timeout = add_timeout_ms(MergeChannelModes,
478 flush_cmode_callback, 0);
479 md->timeout->data = md;
480 }
481 }
482 }
483
484 /*************************************************************************/
485
486 /* Remove the most recent occurrence of mode `mode' from the mode list if
487 * there is one, provided either `user' is NULL or the parameter associated
488 * with the previous mode is equal (according to irc_stricmp()) to the
489 * string pointed to by `user'.
490 */
491
492 static void possibly_remove_mode(struct modedata *md, char mode,
493 const char *user)
494 {
495 int i;
496 char *s;
497
498 log_debug(2, "possibly_remove_mode %c from %.*s%s%s",
499 mode, md->nopmodes*2, md->opmodes,
500 user ? " for user " : "", user ? user : "");
501 for (i = md->nopmodes-1; i >= 0; i--) {
502 if (md->opmodes[i*2+1] == mode) {
503 /* We've already set this mode once */
504 if (user) {
505 /* Only remove the old mode if the nick matches */
506 if (irc_stricmp(md->params[i], user) != 0)
507 continue;
508 }
509 /* Remove the mode */
510 log_debug(2, " removing mode %d/%d", i, md->nopmodes);
511 md->nopmodes--;
512 s = md->opmodes + (i*2);
513 memmove(s, s+2, strlen(s+2)+1);
514 /* Count parameters for this mode and decrement total by count */
515 md->nparams--;
516 s = md->params[i]-1;
517 while ((s = strchr(s+1, ' ')) != NULL)
518 md->nparams--;
519 /* Move parameter pointers */
520 if (i < md->nopmodes) {
521 memmove(md->params+i, md->params+i+1,
522 sizeof(md->params[0])*(md->nopmodes-i));
523 }
524 /* Clear tail slot */
525 memset(md->params+md->nopmodes, 0, sizeof(md->params[0]));
526 }
527 }
528 }
529
530 /*************************************************************************/
531
532 /* Add a single mode with parameters to the given mode data structure.
533 * `params' is the number of parameters, `parambuf' is the space-separated
534 * parameter list, and `len' is strlen(parambuf).
535 */
536
537 static void add_mode_with_params(struct modedata *md, char mode, int is_add,
538 int params, const char *parambuf, int len)
539 {
540 char *s;
541
542 if (len < 0) {
543 log("add_mode_with_params(): BUG: parameter length < 0 (%d)", len);
544 len = 0;
545 }
546 log_debug(2, "add_mode_with_params: current=%.*s mode=%c add=%d"
547 " params=%d[%.*s]", md->nopmodes*2, md->opmodes, mode, is_add,
548 params, len, parambuf);
549
550 /* Check for overflow of parameter count or length */
551 if (md->nparams+params > MAXMODES
552 || md->paramslen+1+len > MAXPARAMSLEN
553 ) {
554 /* Doesn't fit, so flush modes out first */
555 struct modedata mdtmp = *md;
556 log_debug(2, "add_mode_with_params: ...flushing first");
557 flush_cmode(md);
558 memcpy(md->sender, mdtmp.sender, sizeof(md->sender));
559 md->channel = mdtmp.channel;
560 md->used = time(NULL);
561 }
562 s = md->opmodes + 2*md->nopmodes;
563 *s++ = is_add ? '+' : '-';
564 *s++ = mode;
565 if (len > sizeof(md->params[0])-1) {
566 log("set_cmode(): Parameter string for mode %c%c is too long,"
567 " truncating to %d characters",
568 is_add ? '+' : '-', mode, sizeof(md->params[0])-1);
569 len = sizeof(md->params[0])-1;
570 }
571 if (len > 0)
572 memcpy(md->params[md->nopmodes], parambuf, len);
573 md->params[md->nopmodes][len] = 0;
574 md->nopmodes++;
575 md->nparams += params;
576 if (md->paramslen)
577 md->paramslen++;
578 md->paramslen += len;
579 /* If the parameters for this mode alone exceed MAXPARAMSLEN,
580 * we'll now have a string longer than MAXPARAMSLEN in
581 * md->params; not much we can do about it, though, and it'll
582 * get flushed next time around anyway. */
583 }
584
585 /*************************************************************************/
586
587 /* Flush out pending mode changes for the given mode data structure. If
588 * `clear' is nonzero, clear the entry, else leave it alone.
589 */
590
591 static void flush_cmode(struct modedata *md)
592 {
593 char buf[BUFSIZE], *s;
594 char *argv[MAXMODES+2];
595 int len = 0, i;
596 char lastc = 0;
597
598 /* Clear timeout for this entry if one is set */
599 if (md->timeout) {
600 del_timeout(md->timeout);
601 md->timeout = NULL;
602 }
603
604 if (!md->channel) {
605 /* This entry is unused, just return */
606 goto done;
607 }
608 if (!md->binmodes_on && !md->binmodes_off && !*md->opmodes) {
609 /* No actual modes here */
610 goto done;
611 }
612
613 if (debug >= 2) {
614 char onbuf[512];
615 strbcpy(onbuf, mode_flags_to_string(md->binmodes_on,MODE_CHANNEL));
616 log_debug(2, "flush_cmode(%s): bin_on=%s bin_off=%s opmodes=%d(%.*s)",
617 md->channel->name, onbuf,
618 mode_flags_to_string(md->binmodes_off, MODE_CHANNEL),
619 md->nopmodes, md->nopmodes*2, md->opmodes);
620 }
621
622 /* Note that - must come before + because some servers (Unreal, others?)
623 * ignore +s if followed by -p. */
624 if (md->binmodes_off) {
625 len += snprintf(buf+len, sizeof(buf)-len, "-%s",
626 mode_flags_to_string(md->binmodes_off, MODE_CHANNEL));
627 lastc = '-';
628 }
629 if (md->binmodes_on) {
630 len += snprintf(buf+len, sizeof(buf)-len, "+%s",
631 mode_flags_to_string(md->binmodes_on, MODE_CHANNEL));
632 lastc = '+';
633 }
634 s = md->opmodes;
635 while (*s) {
636 if (*s == lastc) {
637 /* +/- matches last mode change */
638 s++;
639 } else {
640 if (len < sizeof(buf)-1)
641 buf[len++] = *s;
642 else
643 fatal("BUG: buf too small in flush_cmode() (1)");
644 lastc = *s++;
645 }
646 if (len < sizeof(buf)-1) {
647 buf[len++] = *s;
648 buf[len] = 0;
649 } else {
650 fatal("BUG: buf too small in flush_cmode() (2)");
651 }
652 s++;
653 }
654 for (i = 0; i < md->nopmodes; i++) {
655 if (*md->params[i])
656 len += snprintf(buf+len, sizeof(buf)-len, " %s", md->params[i]);
657 }
658
659 /* Actually send the command */
660 send_cmode_cmd(md->sender, md->channel->name, "%s", buf);
661
662 /* Split buffer back up into individual parameters for do_cmode().
663 * This is inefficient, but taking the faster route of setting modes
664 * when they are sent to set_cmode() runs the risk of temporary desyncs.
665 * (Example: SomeNick enters #channel -> autoop, but delayed -> SomeNick
666 * does /cs op SomeNick -> ChanServ says "SomeNick is already opped" ->
667 * SomeNick goes "Huh?")
668 */
669 argv[0] = md->channel->name;
670 s = buf;
671 for (i = 0; i <= md->nparams; i++) {
672 argv[i+1] = s;
673 s = strchr(s, ' ');
674 if (!s) {
675 md->nparams = i;
676 break;
677 }
678 *s++ = 0;
679 }
680 /* Clear md->channel so a recursive set_cmode() doesn't find this entry
681 * and try to use/flush it */
682 md->channel = NULL;
683 /* Adjust our idea of the channel modes */
684 do_cmode(md->sender, md->nparams+2, argv);
685
686 done:
687 /* Clear entry and return */
688 memset(md, 0, sizeof(*md));
689 }
690
691 /*************************************************************************/
692
693 /* Timeout called to flush mode changes for a channel after
694 * `MergeChannelModes' seconds of inactivity.
695 */
696
697 static void flush_cmode_callback(Timeout *t)
698 {
699 flush_cmode((struct modedata *)t->data);
700 }
701
702 /*************************************************************************/
703
704 /*
705 * Local variables:
706 * c-file-style: "stroustrup"
707 * c-file-offsets: ((case-label . *) (statement-case-intro . *))
708 * indent-tabs-mode: nil
709 * End:
710 *
711 * vim: expandtab shiftwidth=4:
712 */