ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/ermyth/src/services.C
(Generate patch)

Comparing ermyth/src/services.C (file contents):
Revision 1.4 by pippijn, Sat Jul 21 15:51:23 2007 UTC vs.
Revision 1.5 by pippijn, Tue Aug 28 17:08:12 2007 UTC

1/* 1/*
2 * services.C: Routines commonly used by various services. 2 * services.C: Routines commonly used by various services.
3 * Rights to this code are documented in doc/pod/license.pod. 3 * Rights to this code are documented in doc/pod/license.pod.
4 * 4 *
5 * Copyright © 2005-2007 Atheme Project (http://www.atheme.org) 5 * Copyright © 2005-2007 Atheme Project (http://www.atheme.org)
6 */ 6 */
7 7
8static char const rcsid[] = "$Id: services.C,v 1.4 2007/07/21 15:51:23 pippijn Exp $"; 8static char const rcsid[] = "$Id: services.C,v 1.5 2007/08/28 17:08:12 pippijn Exp $";
9
10#include <boost/foreach.hpp>
9 11
10#include "atheme.h" 12#include "atheme.h"
11#include <account/myuser.h> 13#include <account/myuser.h>
12#include <account/mynick.h> 14#include <account/mynick.h>
13#include <account/mychan.h> 15#include <account/mychan.h>
14#include "pmodule.h" 16#include "pmodule.h"
15 17
16extern dictionary_tree_t *services;
17int authservice_loaded = 0; 18unsigned authservice_loaded = 0;
18int use_myuser_access = 0; 19int use_myuser_access = 0;
19int use_svsignore = 0; 20int use_svsignore = 0;
20 21
21#define MAX_BUF 256 22#define MAX_BUF 256
22 23
42 if (cb != NULL) 43 if (cb != NULL)
43 return 0; 44 return 0;
44 45
45 chanban_add (c, mask, 'b'); 46 chanban_add (c, mask, 'b');
46 47
47 mode_sts (sender->nick, c, modemask); 48 phandler->mode_sts (sender->nick, c, modemask);
48 return 1; 49 return 1;
49} 50}
50 51
51/* returns number of modes removed -- jilles */ 52/* returns number of modes removed -- jilles */
52int 53int
60 if (type == 0) 61 if (type == 0)
61 return 0; 62 return 0;
62 if (source == NULL || chan == NULL || target == NULL) 63 if (source == NULL || chan == NULL || target == NULL)
63 return 0; 64 return 0;
64 65
65 for (n = next_matching_ban (chan, target, type, chan->bans.head); n != NULL; n = next_matching_ban (chan, target, type, tn)) 66 for (n = phandler->next_matching_ban (chan, target, type, chan->bans.head); n != NULL; n = phandler->next_matching_ban (chan, target, type, tn))
66 { 67 {
67 tn = n->next; 68 tn = n->next;
68 cb = static_cast<chanban_t *> (n->data); 69 cb = static_cast<chanban_t *> (n->data);
69 70
70 snprintf (change, sizeof change, "-%c %s", cb->type, cb->mask); 71 snprintf (change, sizeof change, "-%c %s", cb->type, cb->mask);
71 mode_sts (source->nick, chan, change); 72 phandler->mode_sts (source->nick, chan, change);
72 chanban_delete (cb); 73 chanban_delete (cb);
73 count++; 74 count++;
74 } 75 }
75 return count; 76 return count;
76} 77}
82 return remove_banlike (source, chan, ircd->except_mchar, target); 83 return remove_banlike (source, chan, ircd->except_mchar, target);
83} 84}
84 85
85/* join a channel, creating it if necessary */ 86/* join a channel, creating it if necessary */
86void 87void
87join (char *chan, char *nick) 88join (char const * const chan, char const * const nick)
88{ 89{
89 channel_t *c; 90 channel_t *c;
90 user_t *u; 91 user_t *u;
91 chanuser_t *cu; 92 chanuser_t *cu;
92 bool isnew = false; 93 bool isnew = false;
124 else if ((cu = chanuser_find (c, u))) 125 else if ((cu = chanuser_find (c, u)))
125 { 126 {
126 slog (LG_DEBUG, "join(): i'm already in `%s'", c->name); 127 slog (LG_DEBUG, "join(): i'm already in `%s'", c->name);
127 return; 128 return;
128 } 129 }
129 join_sts (c, u, isnew, channel_modes (c, true)); 130 phandler->join_sts (c, u, isnew, channel_modes (c, true));
130 cu = chanuser_add (c, CLIENT_NAME (u)); 131 cu = chanuser_add (c, CLIENT_NAME (u));
131 cu->modes |= CMODE_OP; 132 cu->modes |= CMODE_OP;
132 if (isnew) 133 if (isnew)
133 { 134 {
134 hook_call_event ("channel_add", c); 135 c->callback.add (c);
135 if (config_options.chan != NULL && !irccasecmp (config_options.chan, c->name)) 136 if (config_options.chan != NULL && !irccasecmp (config_options.chan, c->name))
136 joinall (config_options.chan); 137 joinall (config_options.chan);
137 } 138 }
138} 139}
139 140
147 if (!u || !c) 148 if (!u || !c)
148 return; 149 return;
149 if (!chanuser_find (c, u)) 150 if (!chanuser_find (c, u))
150 return; 151 return;
151 if (me.connected) 152 if (me.connected)
152 part_sts (c, u); 153 phandler->part_sts (c, u);
153 chanuser_delete (c, u); 154 chanuser_delete (c, u);
154} 155}
155 156
156void 157void
157services_init (void) 158services_init (void)
158{ 159{
159 service_t *svs; 160 service_t *svs;
160 dictionary_iteration_state_t state;
161 161
162 DICTIONARY_FOREACH (svs, service_t, &state, services) 162 foreach (service_pair &sp, services)
163 { 163 {
164 svs = sp.second;
164 if (ircd->uses_uid && svs->me->uid[0] == '\0') 165 if (ircd->uses_uid && svs->me->uid[0] == '\0')
165 user_changeuid (svs->me, svs->uid); 166 user_changeuid (svs->me, svs->uid);
166 else if (!ircd->uses_uid && svs->me->uid[0] != '\0') 167 else if (!ircd->uses_uid && svs->me->uid[0] != '\0')
167 user_changeuid (svs->me, NULL); 168 user_changeuid (svs->me, NULL);
168 introduce_nick (svs->me); 169 phandler->introduce_nick (svs->me);
169 } 170 }
170} 171}
171 172
172void 173void
173joinall (char *name) 174joinall (char *name)
174{ 175{
175 service_t *svs;
176 dictionary_iteration_state_t state;
177
178 if (name == NULL) 176 if (name == NULL)
179 return; 177 return;
180 178
181 DICTIONARY_FOREACH (svs, service_t, &state, services) 179 foreach (service_pair &sp, services)
182 {
183 join (name, svs->name); 180 join (name, sp.second->name);
184 }
185} 181}
186 182
187void 183void
188partall (char *name) 184partall (char *name)
189{ 185{
190 dictionary_iteration_state_t state;
191 service_t *svs; 186 service_t *svs;
192 mychan_t *mc; 187 mychan_t *mc;
193 188
194 if (name == NULL) 189 if (name == NULL)
195 return; 190 return;
196 mc = mychan_find (name); 191 mc = mychan_find (name);
197 DICTIONARY_FOREACH (svs, service_t, &state, services) 192 foreach (service_pair &sp, services)
198 { 193 {
194 svs = sp.second;
199 if (svs == chansvs.me && mc != NULL && config_options.join_chans) 195 if (svs == chansvs.me && mc != NULL && config_options.join_chans)
200 continue; 196 continue;
201 /* Do not cache this channel_find(), the 197 /* Do not cache this channel_find(), the
202 * channel may disappear under our feet 198 * channel may disappear under our feet
203 * -- jilles */ 199 * -- jilles */
204 if (chanuser_find (channel_find (name), svs->me)) 200 if (chanuser_find (channel_find (name), svs->me))
205 part (name, svs->name); 201 part (name, svs->name);
206 } 202 }
207} 203}
208 204
209/* reintroduce a service e.g. after it's been killed -- jilles */ 205/* reintroduce a service e.g. after it's been killed -- jilles */
210void 206void
211reintroduce_user (user_t *u) 207reintroduce_user (user_t *u)
218 if (svs == NULL) 214 if (svs == NULL)
219 { 215 {
220 slog (LG_DEBUG, "tried to reintroduce_user non-service %s", u->nick); 216 slog (LG_DEBUG, "tried to reintroduce_user non-service %s", u->nick);
221 return; 217 return;
222 } 218 }
223 introduce_nick (u); 219 phandler->introduce_nick (u);
224 LIST_FOREACH (n, u->channels.head) 220 LIST_FOREACH (n, u->channels.head)
225 { 221 {
226 c = ((chanuser_t *) n->data)->chan; 222 c = ((chanuser_t *) n->data)->chan;
227 if (LIST_LENGTH (&c->members) > 1 || c->modes & ircd->perm_mode) 223 if (LIST_LENGTH (&c->members) > 1 || c->modes & ircd->perm_mode)
228 join_sts (c, u, 0, channel_modes (c, true)); 224 phandler->join_sts (c, u, 0, channel_modes (c, true));
229 else 225 else
230 { 226 {
231 /* channel will have been destroyed... */ 227 /* channel will have been destroyed... */
232 /* XXX resend the bans instead of destroying them? */ 228 /* XXX resend the bans instead of destroying them? */
233 chanban_clear (c); 229 chanban_clear (c);
234 join_sts (c, u, 1, channel_modes (c, true)); 230 phandler->join_sts (c, u, 1, channel_modes (c, true));
235 if (c->topic != NULL) 231 if (c->topic != NULL)
236 topic_sts (c, c->topic_setter, c->topicts, 0, c->topic); 232 phandler->topic_sts (c, c->topic_setter, c->topicts, 0, c->topic);
237 } 233 }
238 } 234 }
239} 235}
240 236
241void 237void
242verbose (mychan_t *mychan, char *fmt, ...) 238verbose (mychan_t *mychan, char const * const fmt, ...)
243{ 239{
244 va_list ap; 240 va_list ap;
245 char buf[BUFSIZE]; 241 char buf[BUFSIZE];
246 242
247 if (mychan->chan == NULL) 243 if (mychan->chan == NULL)
252 va_end (ap); 248 va_end (ap);
253 249
254 if ((MC_VERBOSE | MC_FORCEVERBOSE) & mychan->flags) 250 if ((MC_VERBOSE | MC_FORCEVERBOSE) & mychan->flags)
255 notice (chansvs.nick, mychan->name, "%s", buf); 251 notice (chansvs.nick, mychan->name, "%s", buf);
256 else if (MC_VERBOSE_OPS & mychan->flags) 252 else if (MC_VERBOSE_OPS & mychan->flags)
257 wallchops (chansvs.me->me, mychan->chan, buf); 253 phandler->wallchops (chansvs.me->me, mychan->chan, buf);
258} 254}
259 255
260void 256void
261snoop (char *fmt, ...) 257snoop (char const * const fmt, ...)
262{ 258{
263 va_list ap; 259 va_list ap;
264 char buf[BUFSIZE]; 260 char buf[BUFSIZE];
265 261
266 if (!config_options.chan) 262 if (!config_options.chan)
274 270
275 va_start (ap, fmt); 271 va_start (ap, fmt);
276 vsnprintf (buf, BUFSIZE, fmt, ap); 272 vsnprintf (buf, BUFSIZE, fmt, ap);
277 va_end (ap); 273 va_end (ap);
278 274
279 msg (opersvs.nick, config_options.chan, "%s", buf); 275 phandler->privmsg (opersvs.nick, config_options.chan, "%s", buf);
280} 276}
281 277
282/* protocol wrapper for nickchange/nick burst */ 278/* protocol wrapper for nickchange/nick burst */
283void 279void
284handle_nickchange (user_t *u) 280handle_nickchange (user_t *u)
285{ 281{
286 mynick_t *mn; 282 mynick_t *mn;
287 hook_nick_enforce_t hdata;
288 283
289 if (u == NULL) 284 if (u == NULL)
290 return; 285 return;
291 286
292 if (runflags & RF_LIVE && log_debug_enabled ()) 287 if (runflags & RF_LIVE && log_debug_enabled ())
331 mn->lastseen = NOW; 326 mn->lastseen = NOW;
332 return; 327 return;
333 } 328 }
334 329
335 notice (nicksvs.nick, u->nick, _("This nickname is registered. Please choose a different nickname, or identify via \2/%s%s identify <password>\2."), (ircd->uses_rcommand == false) ? "msg " : "", nicksvs.disp); 330 notice (nicksvs.nick, u->nick, _("This nickname is registered. Please choose a different nickname, or identify via \2/%s%s identify <password>\2."), (ircd->uses_rcommand == false) ? "msg " : "", nicksvs.disp);
336 hdata.u = u; 331 mn->callback.enforce (mn, u);
337 hdata.mn = mn;
338 hook_call_event ("nick_enforce", &hdata);
339} 332}
340 333
341/* User u is bursted as being logged in to login (if not NULL) or as 334/* User u is bursted as being logged in to login (if not NULL) or as
342 * being identified to their current nick (if login is NULL) 335 * being identified to their current nick (if login is NULL)
343 * Update the administration or log them out on ircd 336 * Update the administration or log them out on ircd
371 * if we have an authentication service, log them out */ 364 * if we have an authentication service, log them out */
372 slog (LG_DEBUG, "handle_burstlogin(): got nonexistent login %s for user %s", login, u->nick); 365 slog (LG_DEBUG, "handle_burstlogin(): got nonexistent login %s for user %s", login, u->nick);
373 if (authservice_loaded) 366 if (authservice_loaded)
374 { 367 {
375 notice (nicksvs.nick ? nicksvs.nick : me.name, u->nick, _("Account %s dropped, forcing logout"), login); 368 notice (nicksvs.nick ? nicksvs.nick : me.name, u->nick, _("Account %s dropped, forcing logout"), login);
376 ircd_on_logout (u->nick, login, NULL); 369 phandler->ircd_on_logout (u->nick, login, NULL);
377 } 370 }
378 return; 371 return;
379 } 372 }
380 if (u->myuser != NULL) /* already logged in, hmm */ 373 if (u->myuser != NULL) /* already logged in, hmm */
381 return; 374 return;
384 /* no splits for this account, this bursted login cannot 377 /* no splits for this account, this bursted login cannot
385 * be legit... 378 * be legit...
386 * if we have an authentication service, log them out */ 379 * if we have an authentication service, log them out */
387 slog (LG_INFO, "handle_burstlogin(): got illegit login %s for user %s", login, u->nick); 380 slog (LG_INFO, "handle_burstlogin(): got illegit login %s for user %s", login, u->nick);
388 notice (nicksvs.nick ? nicksvs.nick : me.name, u->nick, _("Login to account %s seems invalid, forcing logout"), login); 381 notice (nicksvs.nick ? nicksvs.nick : me.name, u->nick, _("Login to account %s seems invalid, forcing logout"), login);
389 ircd_on_logout (u->nick, login, NULL); 382 phandler->ircd_on_logout (u->nick, login, NULL);
390 return; 383 return;
391 } 384 }
392 u->myuser = mu; 385 u->myuser = mu;
393 mu->logins.insert (u); 386 mu->logins.insert (u);
394 slog (LG_DEBUG, "handle_burstlogin(): automatically identified %s as %s", u->nick, login); 387 slog (LG_DEBUG, "handle_burstlogin(): automatically identified %s as %s", u->nick, login);
395} 388}
396 389
397/* this could be done with more finesse, but hey! */ 390/* this could be done with more finesse, but hey! */
398void 391void
399notice (char *from, char *to, char const *fmt, ...) 392notice (char const * const from, char const * const to, char const * const message, va_list ap)
400{ 393{
401 va_list args;
402 char buf[BUFSIZE]; 394 char buf[BUFSIZE];
403 user_t *u; 395 user_t *u;
404 channel_t *c; 396 channel_t *c;
405 397
406 va_start (args, fmt);
407 vsnprintf (buf, BUFSIZE, fmt, args); 398 vsnprintf (buf, BUFSIZE, message, ap);
408 va_end (args);
409 399
410 if (config_options.use_privmsg) 400 if (config_options.use_privmsg)
411 msg (from, to, "%s", buf); 401 phandler->privmsg (from, to, "%s", buf);
412 else 402 else
413 { 403 {
414 if (*to == '#') 404 if (*to == '#')
415 { 405 {
416 c = channel_find (to); 406 c = channel_find (to);
417 if (c != NULL) 407 if (c != NULL)
418 notice_channel_sts (user_find_named (from), c, buf); 408 phandler->notice_channel_sts (user_find_named (from), c, buf);
419 } 409 }
420 else 410 else
421 { 411 {
422 u = user_find_named (to); 412 u = user_find_named (to);
423 if (u != NULL) 413 if (u != NULL)
424 notice_user_sts (user_find_named (from), u, buf); 414 phandler->notice_user_sts (user_find_named (from), u, buf);
425 } 415 }
426 } 416 }
427} 417}
428 418
429void 419void
420notice (char const * const from, char const * const to, char const * const message, ...)
421{
422 va_list args;
423 char buf[BUFSIZE];
424 user_t *u;
425 channel_t *c;
426
427 va_start (args, message);
428 vsnprintf (buf, BUFSIZE, message, args);
429 va_end (args);
430
431 if (config_options.use_privmsg)
432 phandler->privmsg (from, to, "%s", buf);
433 else
434 {
435 if (*to == '#')
436 {
437 c = channel_find (to);
438 if (c != NULL)
439 phandler->notice_channel_sts (user_find_named (from), c, buf);
440 }
441 else
442 {
443 u = user_find_named (to);
444 if (u != NULL)
445 phandler->notice_user_sts (user_find_named (from), u, buf);
446 }
447 }
448}
449
450void
430command_fail (sourceinfo_t *si, faultcode_t code, const char *fmt, ...) 451command_fail (sourceinfo_t *si, faultcode_t code, char const * const fmt, ...)
431{ 452{
432 va_list args; 453 va_list args;
433 char buf[BUFSIZE]; 454 char buf[BUFSIZE];
434 455
435 va_start (args, fmt); 456 va_start (args, fmt);
442 si->v->cmd_fail (si, code, buf); 463 si->v->cmd_fail (si, code, buf);
443 return; 464 return;
444 } 465 }
445 466
446 if (config_options.use_privmsg) 467 if (config_options.use_privmsg)
447 msg (si->service->name, si->su->nick, "%s", buf); 468 phandler->privmsg (si->service->name, si->su->nick, "%s", buf);
448 else 469 else
449 notice_user_sts (si->service->me, si->su, buf); 470 phandler->notice_user_sts (si->service->me, si->su, buf);
450} 471}
451 472
452void 473void
453command_success_nodata (sourceinfo_t *si, const char *fmt, ...) 474command_success_nodata (sourceinfo_t *si, char const * const fmt, ...)
454{ 475{
455 va_list args; 476 va_list args;
456 char buf[BUFSIZE]; 477 char buf[BUFSIZE];
457 478
458 va_start (args, fmt); 479 va_start (args, fmt);
465 si->v->cmd_success_nodata (si, buf); 486 si->v->cmd_success_nodata (si, buf);
466 return; 487 return;
467 } 488 }
468 489
469 if (config_options.use_privmsg) 490 if (config_options.use_privmsg)
470 msg (si->service->name, si->su->nick, "%s", buf); 491 phandler->privmsg (si->service->name, si->su->nick, "%s", buf);
471 else 492 else
472 notice_user_sts (si->service->me, si->su, buf); 493 phandler->notice_user_sts (si->service->me, si->su, buf);
473} 494}
474 495
475void 496void
476command_success_string (sourceinfo_t *si, const char *result, const char *fmt, ...) 497command_success_string (sourceinfo_t *si, char const * const result, char const * const fmt, ...)
477{ 498{
478 va_list args; 499 va_list args;
479 char buf[BUFSIZE]; 500 char buf[BUFSIZE];
480 501
481 va_start (args, fmt); 502 va_start (args, fmt);
488 si->v->cmd_success_string (si, result, buf); 509 si->v->cmd_success_string (si, result, buf);
489 return; 510 return;
490 } 511 }
491 512
492 if (config_options.use_privmsg) 513 if (config_options.use_privmsg)
493 msg (si->service->name, si->su->nick, "%s", buf); 514 phandler->privmsg (si->service->name, si->su->nick, "%s", buf);
494 else 515 else
495 notice_user_sts (si->service->me, si->su, buf); 516 phandler->notice_user_sts (si->service->me, si->su, buf);
496} 517}
497 518
498static void 519static void
499command_table_cb (const char *line, void *data) 520command_table_cb (char const * const line, void *data)
500{ 521{
501 command_success_nodata (static_cast<sourceinfo_t *> (data), "%s", line); 522 command_success_nodata (static_cast<sourceinfo_t *> (data), "%s", line);
502} 523}
503 524
504void 525void
505command_success_table (sourceinfo_t *si, table_t * table) 526command_success_table (sourceinfo_t *si, table_t * table)
506{ 527{
507 table_render (table, command_table_cb, si); 528 table_render (table, command_table_cb, si);
508} 529}
509 530
510const char * 531char const * const
511get_source_name (sourceinfo_t *si) 532get_source_name (sourceinfo_t *si)
512{ 533{
513 static char result[NICKLEN + NICKLEN + 10]; 534 static char result[NICKLEN + NICKLEN + 10];
514 535
515 if (si->su != NULL) 536 if (si->su != NULL)
526 snprintf (result, sizeof result, "<%s>%s", si->v->description, si->smu ? si->smu->name : ""); 547 snprintf (result, sizeof result, "<%s>%s", si->v->description, si->smu ? si->smu->name : "");
527 } 548 }
528 return result; 549 return result;
529} 550}
530 551
531const char * 552char const * const
532get_source_mask (sourceinfo_t *si) 553get_source_mask (sourceinfo_t *si)
533{ 554{
534 static char result[NICKLEN + USERLEN + HOSTLEN + 10]; 555 static char result[NICKLEN + USERLEN + HOSTLEN + 10];
535 556
536 if (si->su != NULL) 557 if (si->su != NULL)
544 snprintf (result, sizeof result, "<%s>%s", si->v->description, si->smu ? si->smu->name : ""); 565 snprintf (result, sizeof result, "<%s>%s", si->v->description, si->smu ? si->smu->name : "");
545 } 566 }
546 return result; 567 return result;
547} 568}
548 569
549const char * 570char const * const
550get_oper_name (sourceinfo_t *si) 571get_oper_name (sourceinfo_t *si)
551{ 572{
552 static char result[NICKLEN + USERLEN + HOSTLEN + NICKLEN + 10]; 573 static char result[NICKLEN + USERLEN + HOSTLEN + NICKLEN + 10];
553 574
554 if (si->su != NULL) 575 if (si->su != NULL)
568 } 589 }
569 return result; 590 return result;
570} 591}
571 592
572void 593void
573wallops (char *fmt, ...) 594wallops (char const * const fmt, ...)
574{ 595{
575 va_list args; 596 va_list args;
576 char buf[BUFSIZE]; 597 char buf[BUFSIZE];
577 598
578 if (config_options.silent) 599 if (config_options.silent)
581 va_start (args, fmt); 602 va_start (args, fmt);
582 vsnprintf (buf, BUFSIZE, fmt, args); 603 vsnprintf (buf, BUFSIZE, fmt, args);
583 va_end (args); 604 va_end (args);
584 605
585 if (me.me != NULL && me.connected) 606 if (me.me != NULL && me.connected)
586 wallops_sts (buf); 607 phandler->wallops_sts (buf);
587 else 608 else
588 slog (LG_ERROR, "wallops(): unable to send: %s", buf); 609 slog (LG_ERROR, "wallops(): unable to send: %s", buf);
589} 610}
590 611
591void 612void
592verbose_wallops (char *fmt, ...) 613verbose_wallops (char const * const fmt, ...)
593{ 614{
594 va_list args; 615 va_list args;
595 char buf[BUFSIZE]; 616 char buf[BUFSIZE];
596 617
597 if (config_options.silent || !config_options.verbose_wallops) 618 if (config_options.silent || !config_options.verbose_wallops)
600 va_start (args, fmt); 621 va_start (args, fmt);
601 vsnprintf (buf, BUFSIZE, fmt, args); 622 vsnprintf (buf, BUFSIZE, fmt, args);
602 va_end (args); 623 va_end (args);
603 624
604 if (me.me != NULL && me.connected) 625 if (me.me != NULL && me.connected)
605 wallops_sts (buf); 626 phandler->wallops_sts (buf);
606 else 627 else
607 slog (LG_ERROR, "verbose_wallops(): unable to send: %s", buf); 628 slog (LG_ERROR, "verbose_wallops(): unable to send: %s", buf);
608} 629}
609
610/* vim:cinoptions=>s,e0,n0,f0,{0,}0,^0,=s,ps,t0,c3,+s,(2s,us,)20,*30,gs,hs
611 * vim:ts=8
612 * vim:sw=8
613 * vim:noexpandtab
614 */

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines