/[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.1.1.1 - (show annotations) (download) (as text) (vendor branch)
Mon Jul 26 21:11:06 2010 UTC (8 years, 9 months ago) by simple
Branch: eggheads
CVS Tags: v1
Changes since 1.1: +0 -0 lines
File MIME type: text/x-chdr
Imported Eggdrop 1.6.20

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.86 2010/06/29 15:52:24 thommey 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, "%d day%s ago", days, (days == 1) ? "" : "s");
469 return;
470 }
471 egg_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, "in %d day%s", days, (days == 1) ? "" : "s");
483 return;
484 }
485 egg_strftime(out, 9, "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 char s[81];
494 int hrs, mins;
495
496 if (now - then > 86400) {
497 int days = (now - then) / 86400;
498
499 sprintf(out, "for %d day%s", days, (days == 1) ? "" : "s");
500 return;
501 }
502 strcpy(out, "for ");
503 now -= then;
504 hrs = (int) (now / 3600);
505 mins = (int) ((now - (hrs * 3600)) / 60);
506 sprintf(s, "%02d:%02d", hrs, mins);
507 strcat(out, s);
508 }
509
510
511 /*
512 * Logging functions
513 */
514
515 /* Log something
516 * putlog(level,channel_name,format,...);
517 */
518 void putlog EGG_VARARGS_DEF(int, arg1)
519 {
520 static int inhere = 0;
521 int i, type, tsl = 0;
522 char *format, *chname, s[LOGLINELEN], s1[256], *out, ct[81], *s2, stamp[34];
523 va_list va;
524 time_t now2 = time(NULL);
525 struct tm *t = localtime(&now2);
526
527 type = EGG_VARARGS_START(int, arg1, va);
528 chname = va_arg(va, char *);
529 format = va_arg(va, char *);
530
531 /* Create the timestamp */
532 t = localtime(&now2);
533 if (shtime) {
534 egg_strftime(stamp, sizeof(stamp) - 2, log_ts, t);
535 strcat(stamp, " ");
536 tsl = strlen(stamp);
537 }
538 else
539 *stamp = '\0';
540
541 /* Format log entry at offset 'tsl,' then i can prepend the timestamp */
542 out = s + tsl;
543 /* No need to check if out should be null-terminated here,
544 * just do it! <cybah>
545 */
546 egg_vsnprintf(out, LOGLINEMAX - tsl, format, va);
547 out[LOGLINEMAX - tsl] = 0;
548 if (keep_all_logs) {
549 if (!logfile_suffix[0])
550 egg_strftime(ct, 12, ".%d%b%Y", t);
551 else {
552 egg_strftime(ct, 80, logfile_suffix, t);
553 ct[80] = 0;
554 s2 = ct;
555 /* replace spaces by underscores */
556 while (s2[0]) {
557 if (s2[0] == ' ')
558 s2[0] = '_';
559 s2++;
560 }
561 }
562 }
563 /* Make sure the bind list is initialized and we're not looping here */
564 if (!inhere && H_log) {
565 inhere = 1;
566 check_tcl_log(type, chname, out);
567 inhere = 0;
568 }
569 /* Place the timestamp in the string to be printed */
570 if (out[0] && shtime) {
571 strncpy(s, stamp, tsl);
572 out = s;
573 }
574 strcat(out, "\n");
575 if (!use_stderr) {
576 for (i = 0; i < max_logs; i++) {
577 if ((logs[i].filename != NULL) && (logs[i].mask & type) &&
578 ((chname[0] == '*') || (logs[i].chname[0] == '*') ||
579 (!rfc_casecmp(chname, logs[i].chname)))) {
580 if (logs[i].f == NULL) {
581 /* Open this logfile */
582 if (keep_all_logs) {
583 egg_snprintf(s1, 256, "%s%s", logs[i].filename, ct);
584 logs[i].f = fopen(s1, "a+");
585 } else
586 logs[i].f = fopen(logs[i].filename, "a+");
587 }
588 if (logs[i].f != NULL) {
589 /* Check if this is the same as the last line added to
590 * the log. <cybah>
591 */
592 if (!egg_strcasecmp(out + tsl, logs[i].szlast))
593 /* It is a repeat, so increment repeats */
594 logs[i].repeats++;
595 else {
596 /* Not a repeat, check if there were any repeat
597 * lines previously...
598 */
599 if (logs[i].repeats > 0) {
600 /* Yep.. so display 'last message repeated x times'
601 * then reset repeats. We want the current time here,
602 * so put that in the file first.
603 */
604 fprintf(logs[i].f, stamp);
605 fprintf(logs[i].f, MISC_LOGREPEAT, logs[i].repeats);
606 logs[i].repeats = 0;
607 /* No need to reset logs[i].szlast here
608 * because we update it later on...
609 */
610 }
611 fputs(out, logs[i].f);
612 strncpyz(logs[i].szlast, out + tsl, LOGLINEMAX);
613 }
614 }
615 }
616 }
617 }
618 for (i = 0; i < dcc_total; i++) {
619 if ((dcc[i].type == &DCC_CHAT) && (dcc[i].u.chat->con_flags & type)) {
620 if ((chname[0] == '*') || (dcc[i].u.chat->con_chan[0] == '*') ||
621 !rfc_casecmp(chname, dcc[i].u.chat->con_chan)) {
622 dprintf(i, "%s", out);
623 }
624 }
625 }
626 if (!backgrd && !con_chan && !term_z)
627 dprintf(DP_STDOUT, "%s", out);
628 else if ((type & LOG_MISC) && use_stderr) {
629 if (shtime)
630 out += tsl;
631 dprintf(DP_STDERR, "%s", s);
632 }
633 va_end(va);
634 }
635
636 /* Called as soon as the logfile suffix changes. All logs are closed
637 * and the new suffix is stored in `logfile_suffix'.
638 */
639 void logsuffix_change(char *s)
640 {
641 int i;
642 char *s2 = logfile_suffix;
643
644 /* If the suffix didn't really change, ignore. It's probably a rehash. */
645 if (s && s2 && !strcmp(s, s2))
646 return;
647
648 debug0("Logfile suffix changed. Closing all open logs.");
649 strcpy(logfile_suffix, s);
650 while (s2[0]) {
651 if (s2[0] == ' ')
652 s2[0] = '_';
653 s2++;
654 }
655 for (i = 0; i < max_logs; i++) {
656 if (logs[i].f) {
657 fflush(logs[i].f);
658 fclose(logs[i].f);
659 logs[i].f = NULL;
660 }
661 }
662 }
663
664 void check_logsize()
665 {
666 struct stat ss;
667 int i;
668
669 /* int x=1; */
670 char buf[1024]; /* Should be plenty */
671
672 if (!keep_all_logs && max_logsize > 0) {
673 for (i = 0; i < max_logs; i++) {
674 if (logs[i].filename) {
675 if (stat(logs[i].filename, &ss) != 0) {
676 break;
677 }
678 if ((ss.st_size >> 10) > max_logsize) {
679 if (logs[i].f) {
680 /* write to the log before closing it huh.. */
681 putlog(LOG_MISC, "*", MISC_CLOGS, logs[i].filename, ss.st_size);
682 fflush(logs[i].f);
683 fclose(logs[i].f);
684 logs[i].f = NULL;
685 }
686
687 egg_snprintf(buf, sizeof buf, "%s.yesterday", logs[i].filename);
688 buf[1023] = 0;
689 unlink(buf);
690 movefile(logs[i].filename, buf);
691 }
692 }
693 }
694 }
695 }
696
697 /* Flush the logfiles to disk
698 */
699 void flushlogs()
700 {
701 int i;
702
703 /* Logs may not be initialised yet. */
704 if (!logs)
705 return;
706
707 /* Now also checks to see if there's a repeat message and
708 * displays the 'last message repeated...' stuff too <cybah>
709 */
710 for (i = 0; i < max_logs; i++) {
711 if (logs[i].f != NULL) {
712 if ((logs[i].repeats > 0) && quick_logs) {
713 /* Repeat.. if quicklogs used then display 'last message
714 * repeated x times' and reset repeats.
715 */
716 char stamp[33];
717
718 egg_strftime(stamp, sizeof(stamp) - 1, log_ts, localtime(&now));
719 fprintf(logs[i].f, "%s ", stamp);
720 fprintf(logs[i].f, MISC_LOGREPEAT, logs[i].repeats);
721 /* Reset repeats */
722 logs[i].repeats = 0;
723 }
724 fflush(logs[i].f);
725 }
726 }
727 }
728
729
730 /*
731 * String substitution functions
732 */
733
734 static int cols = 0;
735 static int colsofar = 0;
736 static int blind = 0;
737 static int subwidth = 70;
738 static char *colstr = NULL;
739
740
741 /* Add string to colstr
742 */
743 static void subst_addcol(char *s, char *newcol)
744 {
745 char *p, *q;
746 int i, colwidth;
747
748 if ((newcol[0]) && (newcol[0] != '\377'))
749 colsofar++;
750 colstr = nrealloc(colstr, strlen(colstr) + strlen(newcol) +
751 (colstr[0] ? 2 : 1));
752 if ((newcol[0]) && (newcol[0] != '\377')) {
753 if (colstr[0])
754 strcat(colstr, "\377");
755 strcat(colstr, newcol);
756 }
757 if ((colsofar == cols) || ((newcol[0] == '\377') && (colstr[0]))) {
758 colsofar = 0;
759 strcpy(s, " ");
760 colwidth = (subwidth - 5) / cols;
761 q = colstr;
762 p = strchr(colstr, '\377');
763 while (p != NULL) {
764 *p = 0;
765 strcat(s, q);
766 for (i = strlen(q); i < colwidth; i++)
767 strcat(s, " ");
768 q = p + 1;
769 p = strchr(q, '\377');
770 }
771 strcat(s, q);
772 nfree(colstr);
773 colstr = nmalloc(1);
774 colstr[0] = 0;
775 }
776 }
777
778 /* Substitute %x codes in help files
779 *
780 * %B = bot nickname
781 * %V = version
782 * %C = list of channels i monitor
783 * %E = eggdrop banner
784 * %A = admin line
785 * %n = network name
786 * %T = current time ("14:15")
787 * %N = user's nickname
788 * %U = display system name if possible
789 * %{+xy} require flags to read this section
790 * %{-} turn of required flag matching only
791 * %{center} center this line
792 * %{cols=N} start of columnated section (indented)
793 * %{help=TOPIC} start a section for a particular command
794 * %{end} end of section
795 */
796 #define HELP_BUF_LEN 256
797 #define HELP_BOLD 1
798 #define HELP_REV 2
799 #define HELP_UNDER 4
800 #define HELP_FLASH 8
801
802 void help_subst(char *s, char *nick, struct flag_record *flags,
803 int isdcc, char *topic)
804 {
805 char xx[HELP_BUF_LEN + 1], sub[161], *current, *q, chr, *writeidx,
806 *readidx, *towrite;
807 struct chanset_t *chan;
808 int i, j, center = 0;
809 static int help_flags;
810
811 #ifdef HAVE_UNAME
812 struct utsname uname_info;
813 #endif
814
815 if (s == NULL) {
816 /* Used to reset substitutions */
817 blind = 0;
818 cols = 0;
819 subwidth = 70;
820 if (colstr != NULL) {
821 nfree(colstr);
822 colstr = NULL;
823 }
824 help_flags = isdcc;
825 return;
826 }
827 strncpyz(xx, s, sizeof xx);
828 readidx = xx;
829 writeidx = s;
830 current = strchr(readidx, '%');
831 while (current) {
832 /* Are we about to copy a chuck to the end of the buffer?
833 * if so return
834 */
835 if ((writeidx + (current - readidx)) >= (s + HELP_BUF_LEN)) {
836 strncpy(writeidx, readidx, (s + HELP_BUF_LEN) - writeidx);
837 s[HELP_BUF_LEN] = 0;
838 return;
839 }
840 chr = *(current + 1);
841 *current = 0;
842 if (!blind)
843 writeidx += my_strcpy(writeidx, readidx);
844 towrite = NULL;
845 switch (chr) {
846 case 'b':
847 if (glob_hilite(*flags)) {
848 if (help_flags & HELP_IRC) {
849 towrite = "\002";
850 } else if (help_flags & HELP_BOLD) {
851 help_flags &= ~HELP_BOLD;
852 towrite = "\033[0m";
853 } else {
854 help_flags |= HELP_BOLD;
855 towrite = "\033[1m";
856 }
857 }
858 break;
859 case 'v':
860 if (glob_hilite(*flags)) {
861 if (help_flags & HELP_IRC) {
862 towrite = "\026";
863 } else if (help_flags & HELP_REV) {
864 help_flags &= ~HELP_REV;
865 towrite = "\033[0m";
866 } else {
867 help_flags |= HELP_REV;
868 towrite = "\033[7m";
869 }
870 }
871 break;
872 case '_':
873 if (glob_hilite(*flags)) {
874 if (help_flags & HELP_IRC) {
875 towrite = "\037";
876 } else if (help_flags & HELP_UNDER) {
877 help_flags &= ~HELP_UNDER;
878 towrite = "\033[0m";
879 } else {
880 help_flags |= HELP_UNDER;
881 towrite = "\033[4m";
882 }
883 }
884 break;
885 case 'f':
886 if (glob_hilite(*flags)) {
887 if (help_flags & HELP_FLASH) {
888 if (help_flags & HELP_IRC)
889 towrite = "\002\037";
890 else
891 towrite = "\033[0m";
892 help_flags &= ~HELP_FLASH;
893 } else {
894 help_flags |= HELP_FLASH;
895 if (help_flags & HELP_IRC)
896 towrite = "\037\002";
897 else
898 towrite = "\033[5m";
899 }
900 }
901 break;
902 case 'U':
903 #ifdef HAVE_UNAME
904 if (uname(&uname_info) >= 0) {
905 egg_snprintf(sub, sizeof sub, "%s %s", uname_info.sysname,
906 uname_info.release);
907 towrite = sub;
908 } else
909 #endif
910 towrite = "*UNKNOWN*";
911 break;
912 case 'B':
913 towrite = (isdcc ? botnetnick : botname);
914 break;
915 case 'V':
916 towrite = ver;
917 break;
918 case 'E':
919 towrite = version;
920 break;
921 case 'A':
922 towrite = admin;
923 break;
924 case 'n':
925 towrite = network;
926 break;
927 case 'T':
928 egg_strftime(sub, 6, "%H:%M", localtime(&now));
929 towrite = sub;
930 break;
931 case 'N':
932 towrite = strchr(nick, ':');
933 if (towrite)
934 towrite++;
935 else
936 towrite = nick;
937 break;
938 case 'C':
939 if (!blind)
940 for (chan = chanset; chan; chan = chan->next) {
941 if ((strlen(chan->dname) + writeidx + 2) >= (s + HELP_BUF_LEN)) {
942 strncpy(writeidx, chan->dname, (s + HELP_BUF_LEN) - writeidx);
943 s[HELP_BUF_LEN] = 0;
944 return;
945 }
946 writeidx += my_strcpy(writeidx, chan->dname);
947 if (chan->next) {
948 *writeidx++ = ',';
949 *writeidx++ = ' ';
950 }
951 }
952 break;
953 case '{':
954 q = current;
955 current++;
956 while ((*current != '}') && (*current))
957 current++;
958 if (*current) {
959 *current = 0;
960 current--;
961 q += 2;
962 /* Now q is the string and p is where the rest of the fcn expects */
963 if (!strncmp(q, "help=", 5)) {
964 if (topic && egg_strcasecmp(q + 5, topic))
965 blind |= 2;
966 else
967 blind &= ~2;
968 } else if (!(blind & 2)) {
969 if (q[0] == '+') {
970 struct flag_record fr = { FR_GLOBAL | FR_CHAN, 0, 0, 0, 0, 0 };
971
972 break_down_flags(q + 1, &fr, NULL);
973
974 /* We used to check flagrec_ok(), but we can use flagrec_eq()
975 * instead because lower flags are automatically added now.
976 */
977 if (!flagrec_eq(&fr, flags))
978 blind |= 1;
979 else
980 blind &= ~1;
981 } else if (q[0] == '-')
982 blind &= ~1;
983 else if (!egg_strcasecmp(q, "end")) {
984 blind &= ~1;
985 subwidth = 70;
986 if (cols) {
987 sub[0] = 0;
988 subst_addcol(sub, "\377");
989 nfree(colstr);
990 colstr = NULL;
991 cols = 0;
992 towrite = sub;
993 }
994 } else if (!egg_strcasecmp(q, "center"))
995 center = 1;
996 else if (!strncmp(q, "cols=", 5)) {
997 char *r;
998
999 cols = atoi(q + 5);
1000 colsofar = 0;
1001 colstr = nmalloc(1);
1002 colstr[0] = 0;
1003 r = strchr(q + 5, '/');
1004 if (r != NULL)
1005 subwidth = atoi(r + 1);
1006 }
1007 }
1008 } else
1009 current = q; /* no } so ignore */
1010 break;
1011 default:
1012 if (!blind) {
1013 *writeidx++ = chr;
1014 if (writeidx >= (s + HELP_BUF_LEN)) {
1015 *writeidx = 0;
1016 return;
1017 }
1018 }
1019 }
1020 if (towrite && !blind) {
1021 if ((writeidx + strlen(towrite)) >= (s + HELP_BUF_LEN)) {
1022 strncpy(writeidx, towrite, (s + HELP_BUF_LEN) - writeidx);
1023 s[HELP_BUF_LEN] = 0;
1024 return;
1025 }
1026 writeidx += my_strcpy(writeidx, towrite);
1027 }
1028 if (chr) {
1029 readidx = current + 2;
1030 current = strchr(readidx, '%');
1031 } else {
1032 readidx = current + 1;
1033 current = NULL;
1034 }
1035 }
1036 if (!blind) {
1037 i = strlen(readidx);
1038 if (i && ((writeidx + i) >= (s + HELP_BUF_LEN))) {
1039 strncpy(writeidx, readidx, (s + HELP_BUF_LEN) - writeidx);
1040 s[HELP_BUF_LEN] = 0;
1041 return;
1042 }
1043 strcpy(writeidx, readidx);
1044 } else
1045 *writeidx = 0;
1046 if (center) {
1047 strcpy(xx, s);
1048 i = 35 - (strlen(xx) / 2);
1049 if (i > 0) {
1050 s[0] = 0;
1051 for (j = 0; j < i; j++)
1052 s[j] = ' ';
1053 strcpy(s + i, xx);
1054 }
1055 }
1056 if (cols) {
1057 strcpy(xx, s);
1058 s[0] = 0;
1059 subst_addcol(s, xx);
1060 }
1061 }
1062
1063 static void scan_help_file(struct help_ref *current, char *filename, int type)
1064 {
1065 FILE *f;
1066 char s[HELP_BUF_LEN + 1], *p, *q;
1067 struct help_list_t *list;
1068
1069 if (is_file(filename) && (f = fopen(filename, "r"))) {
1070 while (!feof(f)) {
1071 fgets(s, HELP_BUF_LEN, f);
1072 if (!feof(f)) {
1073 p = s;
1074 while ((q = strstr(p, "%{help="))) {
1075 q += 7;
1076 if ((p = strchr(q, '}'))) {
1077 *p = 0;
1078 list = nmalloc(sizeof *list);
1079
1080 list->name = nmalloc(p - q + 1);
1081 strcpy(list->name, q);
1082 list->next = current->first;
1083 list->type = type;
1084 current->first = list;
1085 p++;
1086 } else
1087 p = "";
1088 }
1089 }
1090 }
1091 fclose(f);
1092 }
1093 }
1094
1095 void add_help_reference(char *file)
1096 {
1097 char s[1024];
1098 struct help_ref *current;
1099
1100 for (current = help_list; current; current = current->next)
1101 if (!strcmp(current->name, file))
1102 return; /* Already exists, can't re-add :P */
1103 current = nmalloc(sizeof *current);
1104
1105 current->name = nmalloc(strlen(file) + 1);
1106 strcpy(current->name, file);
1107 current->next = help_list;
1108 current->first = NULL;
1109 help_list = current;
1110 egg_snprintf(s, sizeof s, "%smsg/%s", helpdir, file);
1111 scan_help_file(current, s, 0);
1112 egg_snprintf(s, sizeof s, "%s%s", helpdir, file);
1113 scan_help_file(current, s, 1);
1114 egg_snprintf(s, sizeof s, "%sset/%s", helpdir, file);
1115 scan_help_file(current, s, 2);
1116 }
1117
1118 void rem_help_reference(char *file)
1119 {
1120 struct help_ref *current, *last = NULL;
1121 struct help_list_t *item;
1122
1123 for (current = help_list; current; last = current, current = current->next)
1124 if (!strcmp(current->name, file)) {
1125 while ((item = current->first)) {
1126 current->first = item->next;
1127 nfree(item->name);
1128 nfree(item);
1129 }
1130 nfree(current->name);
1131 if (last)
1132 last->next = current->next;
1133 else
1134 help_list = current->next;
1135 nfree(current);
1136 return;
1137 }
1138 }
1139
1140 void reload_help_data(void)
1141 {
1142 struct help_ref *current = help_list, *next;
1143 struct help_list_t *item;
1144
1145 help_list = NULL;
1146 while (current) {
1147 while ((item = current->first)) {
1148 current->first = item->next;
1149 nfree(item->name);
1150 nfree(item);
1151 }
1152 add_help_reference(current->name);
1153 nfree(current->name);
1154 next = current->next;
1155 nfree(current);
1156 current = next;
1157 }
1158 }
1159
1160 void debug_help(int idx)
1161 {
1162 struct help_ref *current;
1163 struct help_list_t *item;
1164
1165 for (current = help_list; current; current = current->next) {
1166 dprintf(idx, "HELP FILE(S): %s\n", current->name);
1167 for (item = current->first; item; item = item->next) {
1168 dprintf(idx, " %s (%s)\n", item->name, (item->type == 0) ? "msg/" :
1169 (item->type == 1) ? "" : "set/");
1170 }
1171 }
1172 }
1173
1174 FILE *resolve_help(int dcc, char *file)
1175 {
1176
1177 char s[1024];
1178 FILE *f;
1179 struct help_ref *current;
1180 struct help_list_t *item;
1181
1182 /* Somewhere here goes the eventual substituation */
1183 if (!(dcc & HELP_TEXT)) {
1184 for (current = help_list; current; current = current->next)
1185 for (item = current->first; item; item = item->next)
1186 if (!strcmp(item->name, file)) {
1187 if (!item->type && !dcc) {
1188 egg_snprintf(s, sizeof s, "%smsg/%s", helpdir, current->name);
1189 if ((f = fopen(s, "r")))
1190 return f;
1191 } else if (dcc && item->type) {
1192 if (item->type == 1)
1193 egg_snprintf(s, sizeof s, "%s%s", helpdir, current->name);
1194 else
1195 egg_snprintf(s, sizeof s, "%sset/%s", helpdir, current->name);
1196 if ((f = fopen(s, "r")))
1197 return f;
1198 }
1199 }
1200 /* No match was found, so we better return NULL */
1201 return NULL;
1202 }
1203 /* Since we're not dealing with help files, we should just prepend the filename with textdir */
1204 simple_sprintf(s, "%s%s", textdir, file);
1205 if (is_file(s))
1206 return fopen(s, "r");
1207 else
1208 return NULL;
1209 }
1210
1211 void showhelp(char *who, char *file, struct flag_record *flags, int fl)
1212 {
1213 int lines = 0;
1214 char s[HELP_BUF_LEN + 1];
1215 FILE *f = resolve_help(fl, file);
1216
1217 if (f) {
1218 help_subst(NULL, NULL, 0, HELP_IRC, NULL); /* Clear flags */
1219 while (!feof(f)) {
1220 fgets(s, HELP_BUF_LEN, f);
1221 if (!feof(f)) {
1222 if (s[strlen(s) - 1] == '\n')
1223 s[strlen(s) - 1] = 0;
1224 if (!s[0])
1225 strcpy(s, " ");
1226 help_subst(s, who, flags, 0, file);
1227 if ((s[0]) && (strlen(s) > 1)) {
1228 dprintf(DP_HELP, "NOTICE %s :%s\n", who, s);
1229 lines++;
1230 }
1231 }
1232 }
1233 fclose(f);
1234 }
1235 if (!lines && !(fl & HELP_TEXT))
1236 dprintf(DP_HELP, "NOTICE %s :%s\n", who, IRC_NOHELP2);
1237 }
1238
1239 static int display_tellhelp(int idx, char *file, FILE *f,
1240 struct flag_record *flags)
1241 {
1242 char s[HELP_BUF_LEN + 1];
1243 int lines = 0;
1244
1245 if (f) {
1246 help_subst(NULL, NULL, 0,
1247 (dcc[idx].status & STAT_TELNET) ? 0 : HELP_IRC, NULL);
1248 while (!feof(f)) {
1249 fgets(s, HELP_BUF_LEN, f);
1250 if (!feof(f)) {
1251 if (s[strlen(s) - 1] == '\n')
1252 s[strlen(s) - 1] = 0;
1253 if (!s[0])
1254 strcpy(s, " ");
1255 help_subst(s, dcc[idx].nick, flags, 1, file);
1256 if (s[0]) {
1257 dprintf(idx, "%s\n", s);
1258 lines++;
1259 }
1260 }
1261 }
1262 fclose(f);
1263 }
1264 return lines;
1265 }
1266
1267 void tellhelp(int idx, char *file, struct flag_record *flags, int fl)
1268 {
1269 int lines = 0;
1270 FILE *f = resolve_help(HELP_DCC | fl, file);
1271
1272 if (f)
1273 lines = display_tellhelp(idx, file, f, flags);
1274 if (!lines && !(fl & HELP_TEXT))
1275 dprintf(idx, "%s\n", IRC_NOHELP2);
1276 }
1277
1278 /* Same as tellallhelp, just using wild_match instead of strcmp
1279 */
1280 void tellwildhelp(int idx, char *match, struct flag_record *flags)
1281 {
1282 struct help_ref *current;
1283 struct help_list_t *item;
1284 FILE *f;
1285 char s[1024];
1286
1287 s[0] = '\0';
1288 for (current = help_list; current; current = current->next)
1289 for (item = current->first; item; item = item->next)
1290 if (wild_match(match, item->name) && item->type) {
1291 if (item->type == 1)
1292 egg_snprintf(s, sizeof s, "%s%s", helpdir, current->name);
1293 else
1294 egg_snprintf(s, sizeof s, "%sset/%s", helpdir, current->name);
1295 if ((f = fopen(s, "r")))
1296 display_tellhelp(idx, item->name, f, flags);
1297 }
1298 if (!s[0])
1299 dprintf(idx, "%s\n", IRC_NOHELP2);
1300 }
1301
1302 /* Same as tellwildhelp, just using strcmp instead of wild_match
1303 */
1304 void tellallhelp(int idx, char *match, struct flag_record *flags)
1305 {
1306 struct help_ref *current;
1307 struct help_list_t *item;
1308 FILE *f;
1309 char s[1024];
1310
1311 s[0] = '\0';
1312 for (current = help_list; current; current = current->next)
1313 for (item = current->first; item; item = item->next)
1314 if (!strcmp(match, item->name) && item->type) {
1315
1316 if (item->type == 1)
1317 egg_snprintf(s, sizeof s, "%s%s", helpdir, current->name);
1318 else
1319 egg_snprintf(s, sizeof s, "%sset/%s", helpdir, current->name);
1320 if ((f = fopen(s, "r")))
1321 display_tellhelp(idx, item->name, f, flags);
1322 }
1323 if (!s[0])
1324 dprintf(idx, "%s\n", IRC_NOHELP2);
1325 }
1326
1327 /* Substitute vars in a lang text to dcc chatter
1328 */
1329 void sub_lang(int idx, char *text)
1330 {
1331 char s[1024];
1332 struct flag_record fr = { FR_GLOBAL | FR_CHAN, 0, 0, 0, 0, 0 };
1333
1334 get_user_flagrec(dcc[idx].user, &fr, dcc[idx].u.chat->con_chan);
1335 help_subst(NULL, NULL, 0,
1336 (dcc[idx].status & STAT_TELNET) ? 0 : HELP_IRC, NULL);
1337 strncpyz(s, text, sizeof s);
1338 if (s[strlen(s) - 1] == '\n')
1339 s[strlen(s) - 1] = 0;
1340 if (!s[0])
1341 strcpy(s, " ");
1342 help_subst(s, dcc[idx].nick, &fr, 1, botnetnick);
1343 if (s[0])
1344 dprintf(idx, "%s\n", s);
1345 }
1346
1347 /* This will return a pointer to the first character after the @ in the
1348 * string given it. Possibly it's time to think about a regexp library
1349 * for eggdrop...
1350 */
1351 char *extracthostname(char *hostmask)
1352 {
1353 char *p = strrchr(hostmask, '@');
1354
1355 return p ? p + 1 : "";
1356 }
1357
1358 /* Show motd to dcc chatter
1359 */
1360 void show_motd(int idx)
1361 {
1362 FILE *vv;
1363 char s[1024];
1364 struct flag_record fr = { FR_GLOBAL | FR_CHAN, 0, 0, 0, 0, 0 };
1365
1366 if (!is_file(motdfile))
1367 return;
1368
1369 vv = fopen(motdfile, "r");
1370 if (!vv)
1371 return;
1372
1373 get_user_flagrec(dcc[idx].user, &fr, dcc[idx].u.chat->con_chan);
1374 dprintf(idx, "\n");
1375 /* reset the help_subst variables to their defaults */
1376 help_subst(NULL, NULL, 0,
1377 (dcc[idx].status & STAT_TELNET) ? 0 : HELP_IRC, NULL);
1378 while (!feof(vv)) {
1379 fgets(s, 120, vv);
1380 if (!feof(vv)) {
1381 if (s[strlen(s) - 1] == '\n')
1382 s[strlen(s) - 1] = 0;
1383 if (!s[0])
1384 strcpy(s, " ");
1385 help_subst(s, dcc[idx].nick, &fr, 1, botnetnick);
1386 if (s[0])
1387 dprintf(idx, "%s\n", s);
1388 }
1389 }
1390 fclose(vv);
1391 dprintf(idx, "\n");
1392 }
1393
1394 /* Show banner to telnet user
1395 */
1396 void show_banner(int idx)
1397 {
1398 FILE *vv;
1399 char s[1024];
1400 struct flag_record fr = { FR_GLOBAL | FR_CHAN, 0, 0, 0, 0, 0 };
1401
1402 if (!is_file(bannerfile))
1403 return;
1404
1405 vv = fopen(bannerfile, "r");
1406 if (!vv)
1407 return;
1408
1409 get_user_flagrec(dcc[idx].user, &fr, dcc[idx].u.chat->con_chan);
1410 /* reset the help_subst variables to their defaults */
1411 help_subst(NULL, NULL, 0, 0, NULL);
1412 while (!feof(vv)) {
1413 fgets(s, 120, vv);
1414 if (!feof(vv)) {
1415 if (!s[0])
1416 strcpy(s, " \n");
1417 help_subst(s, dcc[idx].nick, &fr, 0, botnetnick);
1418 dprintf(idx, "%s", s);
1419 }
1420 }
1421 fclose(vv);
1422 }
1423
1424 /* Create a string with random letters and digits
1425 */
1426 void make_rand_str(char *s, int len)
1427 {
1428 int j;
1429
1430 for (j = 0; j < len; j++) {
1431 if (!randint(3))
1432 s[j] = '0' + randint(10);
1433 else
1434 s[j] = 'a' + randint(26);
1435 }
1436 s[len] = 0;
1437 }
1438
1439 /* Convert an octal string into a decimal integer value. If the string
1440 * is empty or contains non-octal characters, -1 is returned.
1441 */
1442 int oatoi(const char *octal)
1443 {
1444 register int i;
1445
1446 if (!*octal)
1447 return -1;
1448 for (i = 0; ((*octal >= '0') && (*octal <= '7')); octal++)
1449 i = (i * 8) + (*octal - '0');
1450 if (*octal)
1451 return -1;
1452 return i;
1453 }
1454
1455 /* Return an allocated buffer which contains a copy of the string
1456 * 'str', with all 'div' characters escaped by 'mask'. 'mask'
1457 * characters are escaped too.
1458 *
1459 * Remember to free the returned memory block.
1460 */
1461 char *str_escape(const char *str, const char div, const char mask)
1462 {
1463 const int len = strlen(str);
1464 int buflen = (2 * len), blen = 0;
1465 char *buf = nmalloc(buflen + 1), *b = buf;
1466 const char *s;
1467
1468 if (!buf)
1469 return NULL;
1470 for (s = str; *s; s++) {
1471 /* Resize buffer. */
1472 if ((buflen - blen) <= 3) {
1473 buflen = (buflen * 2);
1474 buf = nrealloc(buf, buflen + 1);
1475 if (!buf)
1476 return NULL;
1477 b = buf + blen;
1478 }
1479
1480 if (*s == div || *s == mask) {
1481 sprintf(b, "%c%02x", mask, *s);
1482 b += 3;
1483 blen += 3;
1484 } else {
1485 *(b++) = *s;
1486 blen++;
1487 }
1488 }
1489 *b = 0;
1490 return buf;
1491 }
1492
1493 /* Search for a certain character 'div' in the string 'str', while
1494 * ignoring escaped characters prefixed with 'mask'.
1495 *
1496 * The string
1497 *
1498 * "\\3a\\5c i am funny \\3a):further text\\5c):oink"
1499 *
1500 * as str, '\\' as mask and ':' as div would change the str buffer
1501 * to
1502 *
1503 * ":\\ i am funny :)"
1504 *
1505 * and return a pointer to "further text\\5c):oink".
1506 *
1507 * NOTE: If you look carefully, you'll notice that strchr_unescape()
1508 * behaves differently than strchr().
1509 */
1510 char *strchr_unescape(char *str, const char div, register const char esc_char)
1511 {
1512 char buf[3];
1513 register char *s, *p;
1514
1515 buf[2] = 0;
1516 for (s = p = str; *s; s++, p++) {
1517 if (*s == esc_char) { /* Found escape character. */
1518 /* Convert code to character. */
1519 buf[0] = s[1], buf[1] = s[2];
1520 *p = (unsigned char) strtol(buf, NULL, 16);
1521 s += 2;
1522 } else if (*s == div) {
1523 *p = *s = 0;
1524 return (s + 1); /* Found searched for character. */
1525 } else
1526 *p = *s;
1527 }
1528 *p = 0;
1529 return NULL;
1530 }
1531
1532 /* Is every character in a string a digit? */
1533 int str_isdigit(const char *str)
1534 {
1535 if (!*str)
1536 return 0;
1537
1538 for (; *str; ++str) {
1539 if (!egg_isdigit(*str))
1540 return 0;
1541 }
1542 return 1;
1543 }
1544
1545 /* As strchr_unescape(), but converts the complete string, without
1546 * searching for a specific delimiter character.
1547 */
1548 void str_unescape(char *str, register const char esc_char)
1549 {
1550 (void) strchr_unescape(str, 0, esc_char);
1551 }
1552
1553 /* Kills the bot. s1 is the reason shown to other bots,
1554 * s2 the reason shown on the partyline. (Sup 25Jul2001)
1555 */
1556 void kill_bot(char *s1, char *s2)
1557 {
1558 call_hook(HOOK_DIE);
1559 chatout("*** %s\n", s1);
1560 botnet_send_chat(-1, botnetnick, s1);
1561 botnet_send_bye();
1562 write_userfile(-1);
1563 fatal(s2, 0);
1564 }

webmaster@eggheads.org
ViewVC Help
Powered by ViewVC 1.1.23