/[cvs]/eggdrop1.8/src/misc.c
ViewVC logotype

Contents of /eggdrop1.8/src/misc.c

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.2.2.2 - (show annotations) (download) (as text)
Wed Jan 12 13:54:00 2011 UTC (8 years, 10 months ago) by pseudo
Branch: gettext
Changes since 1.2.2.1: +8 -8 lines
File MIME type: text/x-chdr
Removed memcpy(), memset() and strftime() compatibility replacements.

1 /*
2 * misc.c -- handles:
3 * split() maskhost() dumplots() daysago() days() daysdur()
4 * logging things
5 * queueing output for the bot (msg and help)
6 * resync buffers for sharebots
7 * help system
8 * motd display and %var substitution
9 *
10 * $Id: misc.c,v 1.2.2.1 2010/11/10 13:39:19 pseudo Exp $
11 */
12 /*
13 * Copyright (C) 1997 Robey Pointer
14 * Copyright (C) 1999 - 2010 Eggheads Development Team
15 *
16 * This program is free software; you can redistribute it and/or
17 * modify it under the terms of the GNU General Public License
18 * as published by the Free Software Foundation; either version 2
19 * of the License, or (at your option) any later version.
20 *
21 * This program is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 * GNU General Public License for more details.
25 *
26 * You should have received a copy of the GNU General Public License
27 * along with this program; if not, write to the Free Software
28 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
29 */
30
31 #include "main.h"
32 #include <sys/stat.h>
33 #include <unistd.h>
34 #include <fcntl.h>
35 #include <ctype.h>
36 #include "chan.h"
37 #include "tandem.h"
38 #include "modules.h"
39
40 #ifdef HAVE_UNAME
41 # include <sys/utsname.h>
42 #endif
43
44 #include "stat.h"
45
46 extern struct dcc_t *dcc;
47 extern struct chanset_t *chanset;
48
49 extern char helpdir[], version[], origbotname[], botname[], admin[], network[],
50 motdfile[], ver[], botnetnick[], bannerfile[], logfile_suffix[],
51 textdir[];
52 extern int backgrd, con_chan, term_z, use_stderr, dcc_total, keep_all_logs,
53 quick_logs, strict_host;
54
55 extern time_t now;
56 extern Tcl_Interp *interp;
57
58 char log_ts[32] = "[%H:%M:%S]"; /* Timestamp format for logfile entries */
59
60 int shtime = 1; /* Display the time with console output */
61 log_t *logs = 0; /* Logfiles */
62 int max_logs = 5; /* Current maximum log files */
63 int max_logsize = 0; /* Maximum logfile size, 0 for no limit */
64 int raw_log = 0; /* Disply output to server to LOG_SERVEROUT */
65
66 int conmask = LOG_MODES | LOG_CMDS | LOG_MISC; /* Console mask */
67
68 struct help_list_t {
69 struct help_list_t *next;
70 char *name;
71 int type;
72 };
73
74 static struct help_ref {
75 char *name;
76 struct help_list_t *first;
77 struct help_ref *next;
78 } *help_list = NULL;
79
80
81 /* Expected memory usage
82 */
83 int expmem_misc()
84 {
85 struct help_ref *current;
86 struct help_list_t *item;
87 int tot = 0;
88
89 for (current = help_list; current; current = current->next) {
90 tot += sizeof(struct help_ref) + strlen(current->name) + 1;
91
92 for (item = current->first; item; item = item->next)
93 tot += sizeof(struct help_list_t) + strlen(item->name) + 1;
94 }
95 return tot + (max_logs * sizeof(log_t));
96 }
97
98 void init_misc()
99 {
100 static int last = 0;
101
102 if (max_logs < 1)
103 max_logs = 1;
104 if (logs)
105 logs = nrealloc(logs, max_logs * sizeof(log_t));
106 else
107 logs = nmalloc(max_logs * sizeof(log_t));
108 for (; last < max_logs; last++) {
109 logs[last].filename = logs[last].chname = NULL;
110 logs[last].mask = 0;
111 logs[last].f = NULL;
112 /* Added by cybah */
113 logs[last].szlast[0] = 0;
114 logs[last].repeats = 0;
115 /* Added by rtc */
116 logs[last].flags = 0;
117 }
118 }
119
120
121 /*
122 * Misc functions
123 */
124
125 /* low-level stuff for other modules
126 */
127 int is_file(const char *s)
128 {
129 struct stat ss;
130 int i = stat(s, &ss);
131
132 if (i < 0)
133 return 0;
134 if ((ss.st_mode & S_IFREG) || (ss.st_mode & S_IFLNK))
135 return 1;
136 return 0;
137 }
138
139 /* This implementation wont overrun dst - 'max' is the max bytes that dst
140 * can be, including the null terminator. So if 'dst' is a 128 byte buffer,
141 * pass 128 as 'max'. The function will _always_ null-terminate 'dst'.
142 *
143 * Returns: The number of characters appended to 'dst'.
144 *
145 * Usage example:
146 *
147 * char buf[128];
148 * size_t bufsize = sizeof(buf);
149 *
150 * buf[0] = 0, bufsize--;
151 *
152 * while (blah && bufsize) {
153 * bufsize -= egg_strcatn(buf, <some-long-string>, sizeof(buf));
154 * }
155 *
156 * <Cybah>
157 */
158 int egg_strcatn(char *dst, const char *src, size_t max)
159 {
160 size_t tmpmax = 0;
161
162 /* find end of 'dst' */
163 while (*dst && max > 0) {
164 dst++;
165 max--;
166 }
167
168 /* Store 'max', so we can use it to workout how many characters were
169 * written later on.
170 */
171 tmpmax = max;
172
173 /* copy upto, but not including the null terminator */
174 while (*src && max > 1) {
175 *dst++ = *src++;
176 max--;
177 }
178
179 /* null-terminate the buffer */
180 *dst = 0;
181
182 /* Don't include the terminating null in our count, as it will cumulate
183 * in loops - causing a headache for the caller.
184 */
185 return tmpmax - max;
186 }
187
188 int my_strcpy(register char *a, register char *b)
189 {
190 register char *c = b;
191
192 while (*b)
193 *a++ = *b++;
194 *a = *b;
195 return b - c;
196 }
197
198 /* Split first word off of rest and put it in first
199 */
200 void splitc(char *first, char *rest, char divider)
201 {
202 char *p = strchr(rest, divider);
203
204 if (p == NULL) {
205 if (first != rest && first)
206 first[0] = 0;
207 return;
208 }
209 *p = 0;
210 if (first != NULL)
211 strcpy(first, rest);
212 if (first != rest)
213 /* In most circumstances, strcpy with src and dst being the same buffer
214 * can produce undefined results. We're safe here, as the src is
215 * guaranteed to be at least 2 bytes higher in memory than dest. <Cybah>
216 */
217 strcpy(rest, p + 1);
218 }
219
220 /* As above, but lets you specify the 'max' number of bytes (EXCLUDING the
221 * terminating null).
222 *
223 * Example of use:
224 *
225 * char buf[HANDLEN + 1];
226 *
227 * splitcn(buf, input, "@", HANDLEN);
228 *
229 * <Cybah>
230 */
231 void splitcn(char *first, char *rest, char divider, size_t max)
232 {
233 char *p = strchr(rest, divider);
234
235 if (p == NULL) {
236 if (first != rest && first)
237 first[0] = 0;
238 return;
239 }
240 *p = 0;
241 if (first != NULL)
242 strncpyz(first, rest, max);
243 if (first != rest)
244 /* In most circumstances, strcpy with src and dst being the same buffer
245 * can produce undefined results. We're safe here, as the src is
246 * guaranteed to be at least 2 bytes higher in memory than dest. <Cybah>
247 */
248 strcpy(rest, p + 1);
249 }
250
251 char *splitnick(char **blah)
252 {
253 char *p = strchr(*blah, '!'), *q = *blah;
254
255 if (p) {
256 *p = 0;
257 *blah = p + 1;
258 return q;
259 }
260 return "";
261 }
262
263 void remove_crlf(char **line)
264 {
265 char *p;
266
267 p = strchr(*line, '\n');
268 if (p != NULL)
269 *p = 0;
270 p = strchr(*line, '\r');
271 if (p != NULL)
272 *p = 0;
273 }
274
275 char *newsplit(char **rest)
276 {
277 register char *o, *r;
278
279 if (!rest)
280 return "";
281 o = *rest;
282 while (*o == ' ')
283 o++;
284 r = o;
285 while (*o && (*o != ' '))
286 o++;
287 if (*o)
288 *o++ = 0;
289 *rest = o;
290 return r;
291 }
292
293 /* maskhost(), modified to support custom mask types, as defined
294 * by mIRC.
295 * Does not require a proper hostmask in 's'. Accepts any strings,
296 * including empty ones and attempts to provide meaningful results.
297 *
298 * Strings containing no '@' character will be parsed as if the
299 * whole string is a host.
300 * Strings containing no '!' character will be interpreted as if
301 * there is no nick.
302 * '!' as a nick/user separator must precede any '@' characters.
303 * Otherwise it will be considered a part of the host.
304 * Supported types are listed in tcl-commands.doc in the maskhost
305 * command section. Type 3 resembles the older maskhost() most closely.
306 *
307 * Specific examples (with type=3):
308 *
309 * "nick!user@is.the.lamest.bg" -> *!*user@*.the.lamest.bg (ccTLD)
310 * "nick!user@is.the.lamest.com" -> *!*user@*.lamest.com (gTLD)
311 * "lamest.example" -> *!*@lamest.example
312 * "whatever@lamest.example" -> *!*whatever@lamest.example
313 * "com.example@user!nick" -> *!*com.example@user!nick
314 * "!" -> *!*@!
315 * "@" -> *!*@*
316 * "" -> *!*@*
317 * "abc!user@2001:db8:618:5c0:263:15:dead:babe"
318 * -> *!*user@2001:db8:618:5c0:263:15:dead:*
319 * "abc!user@0:0:0:0:0:ffff:1.2.3.4"
320 * -> *!*user@0:0:0:0:0:ffff:1.2.3.*
321 */
322 void maskaddr(const char *s, char *nw, int type)
323 {
324 int d = type % 5, num = 1;
325 register char *p, *u = 0, *h = 0, *ss;
326
327 /* Look for user and host.. */
328 ss = (char *)s;
329 u = strchr(s, '!');
330 if (u)
331 h = strchr(u, '@');
332 if (!h){
333 h = strchr(s, '@');
334 u = 0;
335 }
336
337 /* Print nick if required and available */
338 if (!u || (type % 10) < 5)
339 *nw++ = '*';
340 else {
341 strncpy(nw, s, u - s);
342 nw += u - s;
343 }
344 *nw++ = '!';
345
346 /* Write user if required and available */
347 u = (u ? u + 1 : ss);
348 if (!h || (d == 2) || (d == 4))
349 *nw++ = '*';
350 else {
351 if (d) {
352 *nw++ = '*';
353 if (strchr("~+-^=", *u))
354 u++; /* trim leading crap */
355 }
356 strncpy(nw, u, h - u);
357 nw += h - u;
358 }
359 *nw++ = '@';
360
361 /* The rest is for the host */
362 h = (h ? h + 1 : ss);
363 for (p = h; *p; p++) /* hostname? */
364 if ((*p > '9' || *p < '0') && *p != '.') {
365 num = 0;
366 break;
367 }
368 p = strrchr(h, ':'); /* IPv6? */
369 /* Mask out after the last colon/dot */
370 if (p && d > 2) {
371 if ((u = strrchr(p, '.')))
372 p = u;
373 strncpy(nw, h, ++p - h);
374 nw += p - h;
375 strcpy(nw, "*");
376 } else if (!p && !num && type >= 10) {
377 /* we have a hostname and type
378 requires us to replace numbers */
379 num = 0;
380 for (p = h; *p; p++) {
381 if (*p < '0' || *p > '9') {
382 *nw++ = *p;
383 num = 0;
384 } else {
385 if (type < 20)
386 *nw++ = '?';
387 else if (!num) {
388 *nw++ = '*';
389 num = 1; /* place only one '*'
390 per numeric sequence */
391 }
392 }
393 }
394 *nw = 0;
395 } else if (d > 2 && (p = strrchr(h, '.'))) {
396 if (num) { /* IPv4 */
397 strncpy(nw, h, p - h);
398 nw += p - h;
399 strcpy(nw, ".*");
400 return;
401 }
402 for (u = h, d = 0; (u = strchr(++u, '.')); d++);
403 if (d < 2) { /* types < 2 don't mask the host */
404 strcpy(nw, h);
405 return;
406 }
407 u = strchr(h, '.');
408 if (d > 3 || (d == 3 && strlen(p) > 3))
409 u = strchr(++u, '.'); /* ccTLD or not? Look above. */
410 sprintf(nw, "*%s", u);
411 } else if (!*h)
412 /* take care if the mask is empty or contains only '@' */
413 strcpy(nw, "*");
414 else
415 strcpy(nw, h);
416 }
417
418 /* Dump a potentially super-long string of text.
419 */
420 void dumplots(int idx, const char *prefix, const char *data)
421 {
422 const char *p = data, *q, *n;
423 const int max_data_len = 500 - strlen(prefix);
424
425 if (!*data) {
426 dprintf(idx, "%s\n", prefix);
427 return;
428 }
429 while (strlen(p) > max_data_len) {
430 q = p + max_data_len;
431 /* Search for embedded linefeed first */
432 n = strchr(p, '\n');
433 if (n && n < q) {
434 /* Great! dump that first line then start over */
435 dprintf(idx, "%s%.*s\n", prefix, n - p, p);
436 p = n + 1;
437 } else {
438 /* Search backwards for the last space */
439 while (*q != ' ' && q != p)
440 q--;
441 if (q == p)
442 q = p + max_data_len;
443 dprintf(idx, "%s%.*s\n", prefix, q - p, p);
444 p = q;
445 if (*q == ' ')
446 p++;
447 }
448 }
449 /* Last trailing bit: split by linefeeds if possible */
450 n = strchr(p, '\n');
451 while (n) {
452 dprintf(idx, "%s%.*s\n", prefix, n - p, p);
453 p = n + 1;
454 n = strchr(p, '\n');
455 }
456 if (*p)
457 dprintf(idx, "%s%s\n", prefix, p); /* Last trailing bit */
458 }
459
460 /* Convert an interval (in seconds) to one of:
461 * "19 days ago", "1 day ago", "18:12"
462 */
463 void daysago(time_t now, time_t then, char *out)
464 {
465 if (now - then > 86400) {
466 int days = (now - then) / 86400;
467
468 sprintf(out, P_("a day ago", "%d days ago", days), days);
469 return;
470 }
471 strftime(out, 6, "%H:%M", localtime(&then));
472 }
473
474 /* Convert an interval (in seconds) to one of:
475 * "in 19 days", "in 1 day", "at 18:12"
476 */
477 void days(time_t now, time_t then, char *out)
478 {
479 if (now - then > 86400) {
480 int days = (now - then) / 86400;
481
482 sprintf(out, P_("in a day", "in %d days", days), days);
483 return;
484 }
485 strftime(out, 20, _("at %H:%M"), localtime(&now));
486 }
487
488 /* Convert an interval (in seconds) to one of:
489 * "for 19 days", "for 1 day", "for 09:10"
490 */
491 void daysdur(time_t now, time_t then, char *out)
492 {
493 int hrs, mins;
494
495 if (now - then > 86400) {
496 int days = (now - then) / 86400;
497
498 sprintf(out, P_("for one day", "for %d days", days), days);
499 return;
500 }
501 now -= then;
502 hrs = (int) (now / 3600);
503 mins = (int) ((now - (hrs * 3600)) / 60);
504 sprintf(out, "for %02d:%02d", hrs, mins);
505 }
506
507
508 /*
509 * Logging functions
510 */
511
512 /* Log something
513 * putlog(level,channel_name,format,...);
514 */
515 void putlog EGG_VARARGS_DEF(int, arg1)
516 {
517 static int inhere = 0;
518 int i, type, tsl = 0;
519 char *format, *chname, s[LOGLINELEN], s1[256], *out, ct[81], *s2, stamp[34];
520 va_list va;
521 time_t now2 = time(NULL);
522 struct tm *t = localtime(&now2);
523
524 type = EGG_VARARGS_START(int, arg1, va);
525 chname = va_arg(va, char *);
526 format = va_arg(va, char *);
527
528 /* Create the timestamp */
529 t = localtime(&now2);
530 if (shtime) {
531 strftime(stamp, sizeof(stamp) - 2, log_ts, t);
532 strcat(stamp, " ");
533 tsl = strlen(stamp);
534 }
535 else
536 *stamp = '\0';
537
538 /* Format log entry at offset 'tsl,' then i can prepend the timestamp */
539 out = s + tsl;
540 /* No need to check if out should be null-terminated here,
541 * just do it! <cybah>
542 */
543 egg_vsnprintf(out, LOGLINEMAX - tsl, format, va);
544 out[LOGLINEMAX - tsl] = 0;
545 if (keep_all_logs) {
546 if (!logfile_suffix[0])
547 strftime(ct, 12, ".%d%b%Y", t);
548 else {
549 strftime(ct, 80, logfile_suffix, t);
550 ct[80] = 0;
551 s2 = ct;
552 /* replace spaces by underscores */
553 while (s2[0]) {
554 if (s2[0] == ' ')
555 s2[0] = '_';
556 s2++;
557 }
558 }
559 }
560 /* Make sure the bind list is initialized and we're not looping here */
561 if (!inhere && H_log) {
562 inhere = 1;
563 check_tcl_log(type, chname, out);
564 inhere = 0;
565 }
566 /* Place the timestamp in the string to be printed */
567 if (out[0] && shtime) {
568 strncpy(s, stamp, tsl);
569 out = s;
570 }
571 strcat(out, "\n");
572 if (!use_stderr) {
573 for (i = 0; i < max_logs; i++) {
574 if ((logs[i].filename != NULL) && (logs[i].mask & type) &&
575 ((chname[0] == '*') || (logs[i].chname[0] == '*') ||
576 (!rfc_casecmp(chname, logs[i].chname)))) {
577 if (logs[i].f == NULL) {
578 /* Open this logfile */
579 if (keep_all_logs) {
580 egg_snprintf(s1, 256, "%s%s", logs[i].filename, ct);
581 logs[i].f = fopen(s1, "a+");
582 } else
583 logs[i].f = fopen(logs[i].filename, "a+");
584 }
585 if (logs[i].f != NULL) {
586 /* Check if this is the same as the last line added to
587 * the log. <cybah>
588 */
589 if (!egg_strcasecmp(out + tsl, logs[i].szlast))
590 /* It is a repeat, so increment repeats */
591 logs[i].repeats++;
592 else {
593 /* Not a repeat, check if there were any repeat
594 * lines previously...
595 */
596 if (logs[i].repeats > 0) {
597 /* Yep.. so display 'last message repeated x times'
598 * then reset repeats. We want the current time here,
599 * so put that in the file first.
600 */
601 fprintf(logs[i].f, stamp);
602 fprintf(logs[i].f, _("Last message repeated %d time(s).\n"), logs[i].repeats);
603 logs[i].repeats = 0;
604 /* No need to reset logs[i].szlast here
605 * because we update it later on...
606 */
607 }
608 fputs(out, logs[i].f);
609 strncpyz(logs[i].szlast, out + tsl, LOGLINEMAX);
610 }
611 }
612 }
613 }
614 }
615 for (i = 0; i < dcc_total; i++) {
616 if ((dcc[i].type == &DCC_CHAT) && (dcc[i].u.chat->con_flags & type)) {
617 if ((chname[0] == '*') || (dcc[i].u.chat->con_chan[0] == '*') ||
618 !rfc_casecmp(chname, dcc[i].u.chat->con_chan)) {
619 dprintf(i, "%s", out);
620 }
621 }
622 }
623 if (!backgrd && !con_chan && !term_z)
624 dprintf(DP_STDOUT, "%s", out);
625 else if ((type & LOG_MISC) && use_stderr) {
626 if (shtime)
627 out += tsl;
628 dprintf(DP_STDERR, "%s", s);
629 }
630 va_end(va);
631 }
632
633 /* Called as soon as the logfile suffix changes. All logs are closed
634 * and the new suffix is stored in `logfile_suffix'.
635 */
636 void logsuffix_change(char *s)
637 {
638 int i;
639 char *s2 = logfile_suffix;
640
641 /* If the suffix didn't really change, ignore. It's probably a rehash. */
642 if (s && s2 && !strcmp(s, s2))
643 return;
644
645 debug0(_("Logfile suffix changed. Closing all open logs."));
646 strcpy(logfile_suffix, s);
647 while (s2[0]) {
648 if (s2[0] == ' ')
649 s2[0] = '_';
650 s2++;
651 }
652 for (i = 0; i < max_logs; i++) {
653 if (logs[i].f) {
654 fflush(logs[i].f);
655 fclose(logs[i].f);
656 logs[i].f = NULL;
657 }
658 }
659 }
660
661 void check_logsize()
662 {
663 struct stat ss;
664 int i;
665
666 /* int x=1; */
667 char buf[1024]; /* Should be plenty */
668
669 if (!keep_all_logs && max_logsize > 0) {
670 for (i = 0; i < max_logs; i++) {
671 if (logs[i].filename) {
672 if (stat(logs[i].filename, &ss) != 0) {
673 break;
674 }
675 if ((ss.st_size >> 10) > max_logsize) {
676 if (logs[i].f) {
677 /* write to the log before closing it huh.. */
678 putlog(LOG_MISC, "*", _("Cycling logfile %s over max-logsize "
679 "(%d)"), logs[i].filename, ss.st_size);
680 fflush(logs[i].f);
681 fclose(logs[i].f);
682 logs[i].f = NULL;
683 }
684
685 egg_snprintf(buf, sizeof buf, "%s.yesterday", logs[i].filename);
686 buf[1023] = 0;
687 unlink(buf);
688 movefile(logs[i].filename, buf);
689 }
690 }
691 }
692 }
693 }
694
695 /* Flush the logfiles to disk
696 */
697 void flushlogs()
698 {
699 int i;
700
701 /* Logs may not be initialised yet. */
702 if (!logs)
703 return;
704
705 /* Now also checks to see if there's a repeat message and
706 * displays the 'last message repeated...' stuff too <cybah>
707 */
708 for (i = 0; i < max_logs; i++) {
709 if (logs[i].f != NULL) {
710 if ((logs[i].repeats > 0) && quick_logs) {
711 /* Repeat.. if quicklogs used then display 'last message
712 * repeated x times' and reset repeats.
713 */
714 char stamp[33];
715
716 strftime(stamp, sizeof(stamp) - 1, log_ts, localtime(&now));
717 fprintf(logs[i].f, "%s ", stamp);
718 fprintf(logs[i].f, _("Last message repeated %d time(s).\n"),
719 logs[i].repeats);
720 /* Reset repeats */
721 logs[i].repeats = 0;
722 }
723 fflush(logs[i].f);
724 }
725 }
726 }
727
728
729 /*
730 * String substitution functions
731 */
732
733 static int cols = 0;
734 static int colsofar = 0;
735 static int blind = 0;
736 static int subwidth = 70;
737 static char *colstr = NULL;
738
739
740 /* Add string to colstr
741 */
742 static void subst_addcol(char *s, char *newcol)
743 {
744 char *p, *q;
745 int i, colwidth;
746
747 if ((newcol[0]) && (newcol[0] != '\377'))
748 colsofar++;
749 colstr = nrealloc(colstr, strlen(colstr) + strlen(newcol) +
750 (colstr[0] ? 2 : 1));
751 if ((newcol[0]) && (newcol[0] != '\377')) {
752 if (colstr[0])
753 strcat(colstr, "\377");
754 strcat(colstr, newcol);
755 }
756 if ((colsofar == cols) || ((newcol[0] == '\377') && (colstr[0]))) {
757 colsofar = 0;
758 strcpy(s, " ");
759 colwidth = (subwidth - 5) / cols;
760 q = colstr;
761 p = strchr(colstr, '\377');
762 while (p != NULL) {
763 *p = 0;
764 strcat(s, q);
765 for (i = strlen(q); i < colwidth; i++)
766 strcat(s, " ");
767 q = p + 1;
768 p = strchr(q, '\377');
769 }
770 strcat(s, q);
771 nfree(colstr);
772 colstr = nmalloc(1);
773 colstr[0] = 0;
774 }
775 }
776
777 /* Substitute %x codes in help files
778 *
779 * %B = bot nickname
780 * %V = version
781 * %C = list of channels i monitor
782 * %E = eggdrop banner
783 * %A = admin line
784 * %n = network name
785 * %T = current time ("14:15")
786 * %N = user's nickname
787 * %U = display system name if possible
788 * %{+xy} require flags to read this section
789 * %{-} turn of required flag matching only
790 * %{center} center this line
791 * %{cols=N} start of columnated section (indented)
792 * %{help=TOPIC} start a section for a particular command
793 * %{end} end of section
794 */
795 #define HELP_BUF_LEN 256
796 #define HELP_BOLD 1
797 #define HELP_REV 2
798 #define HELP_UNDER 4
799 #define HELP_FLASH 8
800
801 void help_subst(char *s, char *nick, struct flag_record *flags,
802 int isdcc, char *topic)
803 {
804 char xx[HELP_BUF_LEN + 1], sub[161], *current, *q, chr, *writeidx,
805 *readidx, *towrite;
806 struct chanset_t *chan;
807 int i, j, center = 0;
808 static int help_flags;
809
810 #ifdef HAVE_UNAME
811 struct utsname uname_info;
812 #endif
813
814 if (s == NULL) {
815 /* Used to reset substitutions */
816 blind = 0;
817 cols = 0;
818 subwidth = 70;
819 if (colstr != NULL) {
820 nfree(colstr);
821 colstr = NULL;
822 }
823 help_flags = isdcc;
824 return;
825 }
826 strncpyz(xx, s, sizeof xx);
827 readidx = xx;
828 writeidx = s;
829 current = strchr(readidx, '%');
830 while (current) {
831 /* Are we about to copy a chuck to the end of the buffer?
832 * if so return
833 */
834 if ((writeidx + (current - readidx)) >= (s + HELP_BUF_LEN)) {
835 strncpy(writeidx, readidx, (s + HELP_BUF_LEN) - writeidx);
836 s[HELP_BUF_LEN] = 0;
837 return;
838 }
839 chr = *(current + 1);
840 *current = 0;
841 if (!blind)
842 writeidx += my_strcpy(writeidx, readidx);
843 towrite = NULL;
844 switch (chr) {
845 case 'b':
846 if (glob_hilite(*flags)) {
847 if (help_flags & HELP_IRC) {
848 towrite = "\002";
849 } else if (help_flags & HELP_BOLD) {
850 help_flags &= ~HELP_BOLD;
851 towrite = "\033[0m";
852 } else {
853 help_flags |= HELP_BOLD;
854 towrite = "\033[1m";
855 }
856 }
857 break;
858 case 'v':
859 if (glob_hilite(*flags)) {
860 if (help_flags & HELP_IRC) {
861 towrite = "\026";
862 } else if (help_flags & HELP_REV) {
863 help_flags &= ~HELP_REV;
864 towrite = "\033[0m";
865 } else {
866 help_flags |= HELP_REV;
867 towrite = "\033[7m";
868 }
869 }
870 break;
871 case '_':
872 if (glob_hilite(*flags)) {
873 if (help_flags & HELP_IRC) {
874 towrite = "\037";
875 } else if (help_flags & HELP_UNDER) {
876 help_flags &= ~HELP_UNDER;
877 towrite = "\033[0m";
878 } else {
879 help_flags |= HELP_UNDER;
880 towrite = "\033[4m";
881 }
882 }
883 break;
884 case 'f':
885 if (glob_hilite(*flags)) {
886 if (help_flags & HELP_FLASH) {
887 if (help_flags & HELP_IRC)
888 towrite = "\002\037";
889 else
890 towrite = "\033[0m";
891 help_flags &= ~HELP_FLASH;
892 } else {
893 help_flags |= HELP_FLASH;
894 if (help_flags & HELP_IRC)
895 towrite = "\037\002";
896 else
897 towrite = "\033[5m";
898 }
899 }
900 break;
901 case 'U':
902 #ifdef HAVE_UNAME
903 if (uname(&uname_info) >= 0) {
904 egg_snprintf(sub, sizeof sub, "%s %s", uname_info.sysname,
905 uname_info.release);
906 towrite = sub;
907 } else
908 #endif
909 /* That's a substitution string for unknown text substitution variables
910 found in help files and motds */
911 towrite = _("*UNKNOWN*");
912 break;
913 case 'B':
914 towrite = (isdcc ? botnetnick : botname);
915 break;
916 case 'V':
917 towrite = ver;
918 break;
919 case 'E':
920 towrite = version;
921 break;
922 case 'A':
923 towrite = admin;
924 break;
925 case 'n':
926 towrite = network;
927 break;
928 case 'T':
929 strftime(sub, 6, "%H:%M", localtime(&now));
930 towrite = sub;
931 break;
932 case 'N':
933 towrite = strchr(nick, ':');
934 if (towrite)
935 towrite++;
936 else
937 towrite = nick;
938 break;
939 case 'C':
940 if (!blind)
941 for (chan = chanset; chan; chan = chan->next) {
942 if ((strlen(chan->dname) + writeidx + 2) >= (s + HELP_BUF_LEN)) {
943 strncpy(writeidx, chan->dname, (s + HELP_BUF_LEN) - writeidx);
944 s[HELP_BUF_LEN] = 0;
945 return;
946 }
947 writeidx += my_strcpy(writeidx, chan->dname);
948 if (chan->next) {
949 *writeidx++ = ',';
950 *writeidx++ = ' ';
951 }
952 }
953 break;
954 case '{':
955 q = current;
956 current++;
957 while ((*current != '}') && (*current))
958 current++;
959 if (*current) {
960 *current = 0;
961 current--;
962 q += 2;
963 /* Now q is the string and p is where the rest of the fcn expects */
964 if (!strncmp(q, "help=", 5)) {
965 if (topic && egg_strcasecmp(q + 5, topic))
966 blind |= 2;
967 else
968 blind &= ~2;
969 } else if (!(blind & 2)) {
970 if (q[0] == '+') {
971 struct flag_record fr = { FR_GLOBAL | FR_CHAN, 0, 0, 0, 0, 0 };
972
973 break_down_flags(q + 1, &fr, NULL);
974
975 /* We used to check flagrec_ok(), but we can use flagrec_eq()
976 * instead because lower flags are automatically added now.
977 */
978 if (!flagrec_eq(&fr, flags))
979 blind |= 1;
980 else
981 blind &= ~1;
982 } else if (q[0] == '-')
983 blind &= ~1;
984 else if (!egg_strcasecmp(q, "end")) {
985 blind &= ~1;
986 subwidth = 70;
987 if (cols) {
988 sub[0] = 0;
989 subst_addcol(sub, "\377");
990 nfree(colstr);
991 colstr = NULL;
992 cols = 0;
993 towrite = sub;
994 }
995 } else if (!egg_strcasecmp(q, "center"))
996 center = 1;
997 else if (!strncmp(q, "cols=", 5)) {
998 char *r;
999
1000 cols = atoi(q + 5);
1001 colsofar = 0;
1002 colstr = nmalloc(1);
1003 colstr[0] = 0;
1004 r = strchr(q + 5, '/');
1005 if (r != NULL)
1006 subwidth = atoi(r + 1);
1007 }
1008 }
1009 } else
1010 current = q; /* no } so ignore */
1011 break;
1012 default:
1013 if (!blind) {
1014 *writeidx++ = chr;
1015 if (writeidx >= (s + HELP_BUF_LEN)) {
1016 *writeidx = 0;
1017 return;
1018 }
1019 }
1020 }
1021 if (towrite && !blind) {
1022 if ((writeidx + strlen(towrite)) >= (s + HELP_BUF_LEN)) {
1023 strncpy(writeidx, towrite, (s + HELP_BUF_LEN) - writeidx);
1024 s[HELP_BUF_LEN] = 0;
1025 return;
1026 }
1027 writeidx += my_strcpy(writeidx, towrite);
1028 }
1029 if (chr) {
1030 readidx = current + 2;
1031 current = strchr(readidx, '%');
1032 } else {
1033 readidx = current + 1;
1034 current = NULL;
1035 }
1036 }
1037 if (!blind) {
1038 i = strlen(readidx);
1039 if (i && ((writeidx + i) >= (s + HELP_BUF_LEN))) {
1040 strncpy(writeidx, readidx, (s + HELP_BUF_LEN) - writeidx);
1041 s[HELP_BUF_LEN] = 0;
1042 return;
1043 }
1044 strcpy(writeidx, readidx);
1045 } else
1046 *writeidx = 0;
1047 if (center) {
1048 strcpy(xx, s);
1049 i = 35 - (strlen(xx) / 2);
1050 if (i > 0) {
1051 s[0] = 0;
1052 for (j = 0; j < i; j++)
1053 s[j] = ' ';
1054 strcpy(s + i, xx);
1055 }
1056 }
1057 if (cols) {
1058 strcpy(xx, s);
1059 s[0] = 0;
1060 subst_addcol(s, xx);
1061 }
1062 }
1063
1064 static void scan_help_file(struct help_ref *current, char *filename, int type)
1065 {
1066 FILE *f;
1067 char s[HELP_BUF_LEN + 1], *p, *q;
1068 struct help_list_t *list;
1069
1070 if (is_file(filename) && (f = fopen(filename, "r"))) {
1071 while (!feof(f)) {
1072 fgets(s, HELP_BUF_LEN, f);
1073 if (!feof(f)) {
1074 p = s;
1075 while ((q = strstr(p, "%{help="))) {
1076 q += 7;
1077 if ((p = strchr(q, '}'))) {
1078 *p = 0;
1079 list = nmalloc(sizeof *list);
1080
1081 list->name = nmalloc(p - q + 1);
1082 strcpy(list->name, q);
1083 list->next = current->first;
1084 list->type = type;
1085 current->first = list;
1086 p++;
1087 } else
1088 p = "";
1089 }
1090 }
1091 }
1092 fclose(f);
1093 }
1094 }
1095
1096 void add_help_reference(char *file)
1097 {
1098 char s[1024];
1099 struct help_ref *current;
1100
1101 for (current = help_list; current; current = current->next)
1102 if (!strcmp(current->name, file))
1103 return; /* Already exists, can't re-add :P */
1104 current = nmalloc(sizeof *current);
1105
1106 current->name = nmalloc(strlen(file) + 1);
1107 strcpy(current->name, file);
1108 current->next = help_list;
1109 current->first = NULL;
1110 help_list = current;
1111 egg_snprintf(s, sizeof s, "%smsg/%s", helpdir, file);
1112 scan_help_file(current, s, 0);
1113 egg_snprintf(s, sizeof s, "%s%s", helpdir, file);
1114 scan_help_file(current, s, 1);
1115 egg_snprintf(s, sizeof s, "%sset/%s", helpdir, file);
1116 scan_help_file(current, s, 2);
1117 }
1118
1119 void rem_help_reference(char *file)
1120 {
1121 struct help_ref *current, *last = NULL;
1122 struct help_list_t *item;
1123
1124 for (current = help_list; current; last = current, current = current->next)
1125 if (!strcmp(current->name, file)) {
1126 while ((item = current->first)) {
1127 current->first = item->next;
1128 nfree(item->name);
1129 nfree(item);
1130 }
1131 nfree(current->name);
1132 if (last)
1133 last->next = current->next;
1134 else
1135 help_list = current->next;
1136 nfree(current);
1137 return;
1138 }
1139 }
1140
1141 void reload_help_data(void)
1142 {
1143 struct help_ref *current = help_list, *next;
1144 struct help_list_t *item;
1145
1146 help_list = NULL;
1147 while (current) {
1148 while ((item = current->first)) {
1149 current->first = item->next;
1150 nfree(item->name);
1151 nfree(item);
1152 }
1153 add_help_reference(current->name);
1154 nfree(current->name);
1155 next = current->next;
1156 nfree(current);
1157 current = next;
1158 }
1159 }
1160
1161 void debug_help(int idx)
1162 {
1163 struct help_ref *current;
1164 struct help_list_t *item;
1165
1166 for (current = help_list; current; current = current->next) {
1167 dprintf(idx, _("HELP FILE(S): %s\n"), current->name);
1168 for (item = current->first; item; item = item->next) {
1169 dprintf(idx, " %s (%s)\n", item->name, (item->type == 0) ? "msg/" :
1170 (item->type == 1) ? "" : "set/");
1171 }
1172 }
1173 }
1174
1175 FILE *resolve_help(int dcc, char *file)
1176 {
1177
1178 char s[1024];
1179 FILE *f;
1180 struct help_ref *current;
1181 struct help_list_t *item;
1182
1183 /* Somewhere here goes the eventual substituation */
1184 if (!(dcc & HELP_TEXT)) {
1185 for (current = help_list; current; current = current->next)
1186 for (item = current->first; item; item = item->next)
1187 if (!strcmp(item->name, file)) {
1188 if (!item->type && !dcc) {
1189 egg_snprintf(s, sizeof s, "%smsg/%s", helpdir, current->name);
1190 if ((f = fopen(s, "r")))
1191 return f;
1192 } else if (dcc && item->type) {
1193 if (item->type == 1)
1194 egg_snprintf(s, sizeof s, "%s%s", helpdir, current->name);
1195 else
1196 egg_snprintf(s, sizeof s, "%sset/%s", helpdir, current->name);
1197 if ((f = fopen(s, "r")))
1198 return f;
1199 }
1200 }
1201 /* No match was found, so we better return NULL */
1202 return NULL;
1203 }
1204 /* Since we're not dealing with help files, we should just prepend the filename with textdir */
1205 simple_sprintf(s, "%s%s", textdir, file);
1206 if (is_file(s))
1207 return fopen(s, "r");
1208 else
1209 return NULL;
1210 }
1211
1212 void showhelp(char *who, char *file, struct flag_record *flags, int fl)
1213 {
1214 int lines = 0;
1215 char s[HELP_BUF_LEN + 1];
1216 FILE *f = resolve_help(fl, file);
1217
1218 if (f) {
1219 help_subst(NULL, NULL, 0, HELP_IRC, NULL); /* Clear flags */
1220 while (!feof(f)) {
1221 fgets(s, HELP_BUF_LEN, f);
1222 if (!feof(f)) {
1223 if (s[strlen(s) - 1] == '\n')
1224 s[strlen(s) - 1] = 0;
1225 if (!s[0])
1226 strcpy(s, " ");
1227 help_subst(s, who, flags, 0, file);
1228 if ((s[0]) && (strlen(s) > 1)) {
1229 dprintf(DP_HELP, "NOTICE %s :%s\n", who, s);
1230 lines++;
1231 }
1232 }
1233 }
1234 fclose(f);
1235 }
1236 if (!lines && !(fl & HELP_TEXT))
1237 dprintf(DP_HELP, "NOTICE %s :%s\n", who, _("No help available on that."));
1238 }
1239
1240 static int display_tellhelp(int idx, char *file, FILE *f,
1241 struct flag_record *flags)
1242 {
1243 char s[HELP_BUF_LEN + 1];
1244 int lines = 0;
1245
1246 if (f) {
1247 help_subst(NULL, NULL, 0,
1248 (dcc[idx].status & STAT_TELNET) ? 0 : HELP_IRC, NULL);
1249 while (!feof(f)) {
1250 fgets(s, HELP_BUF_LEN, f);
1251 if (!feof(f)) {
1252 if (s[strlen(s) - 1] == '\n')
1253 s[strlen(s) - 1] = 0;
1254 if (!s[0])
1255 strcpy(s, " ");
1256 help_subst(s, dcc[idx].nick, flags, 1, file);
1257 if (s[0]) {
1258 dprintf(idx, "%s\n", s);
1259 lines++;
1260 }
1261 }
1262 }
1263 fclose(f);
1264 }
1265 return lines;
1266 }
1267
1268 void tellhelp(int idx, char *file, struct flag_record *flags, int fl)
1269 {
1270 int lines = 0;
1271 FILE *f = resolve_help(HELP_DCC | fl, file);
1272
1273 if (f)
1274 lines = display_tellhelp(idx, file, f, flags);
1275 if (!lines && !(fl & HELP_TEXT))
1276 dprintf(idx, "%s\n", _("No help available on that."));
1277 }
1278
1279 /* Same as tellallhelp, just using wild_match instead of strcmp
1280 */
1281 void tellwildhelp(int idx, char *match, struct flag_record *flags)
1282 {
1283 struct help_ref *current;
1284 struct help_list_t *item;
1285 FILE *f;
1286 char s[1024];
1287
1288 s[0] = '\0';
1289 for (current = help_list; current; current = current->next)
1290 for (item = current->first; item; item = item->next)
1291 if (wild_match(match, item->name) && item->type) {
1292 if (item->type == 1)
1293 egg_snprintf(s, sizeof s, "%s%s", helpdir, current->name);
1294 else
1295 egg_snprintf(s, sizeof s, "%sset/%s", helpdir, current->name);
1296 if ((f = fopen(s, "r")))
1297 display_tellhelp(idx, item->name, f, flags);
1298 }
1299 if (!s[0])
1300 dprintf(idx, "%s\n", _("No help available on that."));
1301 }
1302
1303 /* Same as tellwildhelp, just using strcmp instead of wild_match
1304 */
1305 void tellallhelp(int idx, char *match, struct flag_record *flags)
1306 {
1307 struct help_ref *current;
1308 struct help_list_t *item;
1309 FILE *f;
1310 char s[1024];
1311
1312 s[0] = '\0';
1313 for (current = help_list; current; current = current->next)
1314 for (item = current->first; item; item = item->next)
1315 if (!strcmp(match, item->name) && item->type) {
1316
1317 if (item->type == 1)
1318 egg_snprintf(s, sizeof s, "%s%s", helpdir, current->name);
1319 else
1320 egg_snprintf(s, sizeof s, "%sset/%s", helpdir, current->name);
1321 if ((f = fopen(s, "r")))
1322 display_tellhelp(idx, item->name, f, flags);
1323 }
1324 if (!s[0])
1325 dprintf(idx, "%s\n", _("No help available on that."));
1326 }
1327
1328 /* Substitute vars in a lang text to dcc chatter
1329 */
1330 void sub_lang(int idx, char *text)
1331 {
1332 char s[1024];
1333 struct flag_record fr = { FR_GLOBAL | FR_CHAN, 0, 0, 0, 0, 0 };
1334
1335 get_user_flagrec(dcc[idx].user, &fr, dcc[idx].u.chat->con_chan);
1336 help_subst(NULL, NULL, 0,
1337 (dcc[idx].status & STAT_TELNET) ? 0 : HELP_IRC, NULL);
1338 strncpyz(s, text, sizeof s);
1339 if (s[strlen(s) - 1] == '\n')
1340 s[strlen(s) - 1] = 0;
1341 if (!s[0])
1342 strcpy(s, " ");
1343 help_subst(s, dcc[idx].nick, &fr, 1, botnetnick);
1344 if (s[0])
1345 dprintf(idx, "%s\n", s);
1346 }
1347
1348 /* This will return a pointer to the first character after the @ in the
1349 * string given it. Possibly it's time to think about a regexp library
1350 * for eggdrop...
1351 */
1352 char *extracthostname(char *hostmask)
1353 {
1354 char *p = strrchr(hostmask, '@');
1355
1356 return p ? p + 1 : "";
1357 }
1358
1359 /* Show motd to dcc chatter
1360 */
1361 void show_motd(int idx)
1362 {
1363 FILE *vv;
1364 char s[1024];
1365 struct flag_record fr = { FR_GLOBAL | FR_CHAN, 0, 0, 0, 0, 0 };
1366
1367 if (!is_file(motdfile))
1368 return;
1369
1370 vv = fopen(motdfile, "r");
1371 if (!vv)
1372 return;
1373
1374 get_user_flagrec(dcc[idx].user, &fr, dcc[idx].u.chat->con_chan);
1375 dprintf(idx, "\n");
1376 /* reset the help_subst variables to their defaults */
1377 help_subst(NULL, NULL, 0,
1378 (dcc[idx].status & STAT_TELNET) ? 0 : HELP_IRC, NULL);
1379 while (!feof(vv)) {
1380 fgets(s, 120, vv);
1381 if (!feof(vv)) {
1382 if (s[strlen(s) - 1] == '\n')
1383 s[strlen(s) - 1] = 0;
1384 if (!s[0])
1385 strcpy(s, " ");
1386 help_subst(s, dcc[idx].nick, &fr, 1, botnetnick);
1387 if (s[0])
1388 dprintf(idx, "%s\n", s);
1389 }
1390 }
1391 fclose(vv);
1392 dprintf(idx, "\n");
1393 }
1394
1395 /* Show banner to telnet user
1396 */
1397 void show_banner(int idx)
1398 {
1399 FILE *vv;
1400 char s[1024];
1401 struct flag_record fr = { FR_GLOBAL | FR_CHAN, 0, 0, 0, 0, 0 };
1402
1403 if (!is_file(bannerfile))
1404 return;
1405
1406 vv = fopen(bannerfile, "r");
1407 if (!vv)
1408 return;
1409
1410 get_user_flagrec(dcc[idx].user, &fr, dcc[idx].u.chat->con_chan);
1411 /* reset the help_subst variables to their defaults */
1412 help_subst(NULL, NULL, 0, 0, NULL);
1413 while (!feof(vv)) {
1414 fgets(s, 120, vv);
1415 if (!feof(vv)) {
1416 if (!s[0])
1417 strcpy(s, " \n");
1418 help_subst(s, dcc[idx].nick, &fr, 0, botnetnick);
1419 dprintf(idx, "%s", s);
1420 }
1421 }
1422 fclose(vv);
1423 }
1424
1425 /* Create a string with random letters and digits
1426 */
1427 void make_rand_str(char *s, int len)
1428 {
1429 int j;
1430
1431 for (j = 0; j < len; j++) {
1432 if (!randint(3))
1433 s[j] = '0' + randint(10);
1434 else
1435 s[j] = 'a' + randint(26);
1436 }
1437 s[len] = 0;
1438 }
1439
1440 /* Convert an octal string into a decimal integer value. If the string
1441 * is empty or contains non-octal characters, -1 is returned.
1442 */
1443 int oatoi(const char *octal)
1444 {
1445 register int i;
1446
1447 if (!*octal)
1448 return -1;
1449 for (i = 0; ((*octal >= '0') && (*octal <= '7')); octal++)
1450 i = (i * 8) + (*octal - '0');
1451 if (*octal)
1452 return -1;
1453 return i;
1454 }
1455
1456 /* Return an allocated buffer which contains a copy of the string
1457 * 'str', with all 'div' characters escaped by 'mask'. 'mask'
1458 * characters are escaped too.
1459 *
1460 * Remember to free the returned memory block.
1461 */
1462 char *str_escape(const char *str, const char div, const char mask)
1463 {
1464 const int len = strlen(str);
1465 int buflen = (2 * len), blen = 0;
1466 char *buf = nmalloc(buflen + 1), *b = buf;
1467 const char *s;
1468
1469 if (!buf)
1470 return NULL;
1471 for (s = str; *s; s++) {
1472 /* Resize buffer. */
1473 if ((buflen - blen) <= 3) {
1474 buflen = (buflen * 2);
1475 buf = nrealloc(buf, buflen + 1);
1476 if (!buf)
1477 return NULL;
1478 b = buf + blen;
1479 }
1480
1481 if (*s == div || *s == mask) {
1482 sprintf(b, "%c%02x", mask, *s);
1483 b += 3;
1484 blen += 3;
1485 } else {
1486 *(b++) = *s;
1487 blen++;
1488 }
1489 }
1490 *b = 0;
1491 return buf;
1492 }
1493
1494 /* Search for a certain character 'div' in the string 'str', while
1495 * ignoring escaped characters prefixed with 'mask'.
1496 *
1497 * The string
1498 *
1499 * "\\3a\\5c i am funny \\3a):further text\\5c):oink"
1500 *
1501 * as str, '\\' as mask and ':' as div would change the str buffer
1502 * to
1503 *
1504 * ":\\ i am funny :)"
1505 *
1506 * and return a pointer to "further text\\5c):oink".
1507 *
1508 * NOTE: If you look carefully, you'll notice that strchr_unescape()
1509 * behaves differently than strchr().
1510 */
1511 char *strchr_unescape(char *str, const char div, register const char esc_char)
1512 {
1513 char buf[3];
1514 register char *s, *p;
1515
1516 buf[2] = 0;
1517 for (s = p = str; *s; s++, p++) {
1518 if (*s == esc_char) { /* Found escape character. */
1519 /* Convert code to character. */
1520 buf[0] = s[1], buf[1] = s[2];
1521 *p = (unsigned char) strtol(buf, NULL, 16);
1522 s += 2;
1523 } else if (*s == div) {
1524 *p = *s = 0;
1525 return (s + 1); /* Found searched for character. */
1526 } else
1527 *p = *s;
1528 }
1529 *p = 0;
1530 return NULL;
1531 }
1532
1533 /* Is every character in a string a digit? */
1534 int str_isdigit(const char *str)
1535 {
1536 if (!*str)
1537 return 0;
1538
1539 for (; *str; ++str) {
1540 if (!egg_isdigit(*str))
1541 return 0;
1542 }
1543 return 1;
1544 }
1545
1546 /* As strchr_unescape(), but converts the complete string, without
1547 * searching for a specific delimiter character.
1548 */
1549 void str_unescape(char *str, register const char esc_char)
1550 {
1551 (void) strchr_unescape(str, 0, esc_char);
1552 }
1553
1554 /* Kills the bot. s1 is the reason shown to other bots,
1555 * s2 the reason shown on the partyline. (Sup 25Jul2001)
1556 */
1557 void kill_bot(char *s1, char *s2)
1558 {
1559 check_tcl_die(s2);
1560 call_hook(HOOK_DIE);
1561 chatout("*** %s\n", s1);
1562 botnet_send_chat(-1, botnetnick, s1);
1563 botnet_send_bye();
1564 write_userfile(-1);
1565 fatal(s2, 2);
1566 }

webmaster@eggheads.org
ViewVC Help
Powered by ViewVC 1.1.23