1 |
/* |
2 |
* chanprog.c -- handles: |
3 |
* rmspace() |
4 |
* timers, utimers |
5 |
* telling the current programmed settings |
6 |
* initializing a lot of stuff and loading the tcl scripts |
7 |
* |
8 |
* $Id: chanprog.c,v 1.29 2001/10/10 10:44:03 tothwolf Exp $ |
9 |
*/ |
10 |
/* |
11 |
* Copyright (C) 1997 Robey Pointer |
12 |
* Copyright (C) 1999, 2000, 2001 Eggheads Development Team |
13 |
* |
14 |
* This program is free software; you can redistribute it and/or |
15 |
* modify it under the terms of the GNU General Public License |
16 |
* as published by the Free Software Foundation; either version 2 |
17 |
* of the License, or (at your option) any later version. |
18 |
* |
19 |
* This program is distributed in the hope that it will be useful, |
20 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
21 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
22 |
* GNU General Public License for more details. |
23 |
* |
24 |
* You should have received a copy of the GNU General Public License |
25 |
* along with this program; if not, write to the Free Software |
26 |
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
27 |
*/ |
28 |
|
29 |
#include "main.h" |
30 |
#if HAVE_GETRUSAGE |
31 |
#include <sys/resource.h> |
32 |
#if HAVE_SYS_RUSAGE_H |
33 |
#include <sys/rusage.h> |
34 |
#endif |
35 |
#endif |
36 |
#ifdef HAVE_UNAME |
37 |
#include <sys/utsname.h> |
38 |
#endif |
39 |
#include "modules.h" |
40 |
|
41 |
extern struct userrec *userlist; |
42 |
extern log_t *logs; |
43 |
extern Tcl_Interp *interp; |
44 |
extern char ver[], botnetnick[], firewall[], |
45 |
motdfile[], userfile[], helpdir[], tempdir[], |
46 |
moddir[], notify_new[], owner[], configfile[]; |
47 |
extern time_t now, online_since; |
48 |
extern int backgrd, term_z, con_chan, cache_hit, cache_miss, |
49 |
firewallport, default_flags, max_logs, conmask, |
50 |
protect_readonly, make_userfile, noshare, |
51 |
ignore_time; |
52 |
|
53 |
tcl_timer_t *timer = NULL; /* Minutely timer */ |
54 |
tcl_timer_t *utimer = NULL; /* Secondly timer */ |
55 |
unsigned long timer_id = 1; /* Next timer of any sort will |
56 |
have this number */ |
57 |
struct chanset_t *chanset = NULL; /* Channel list */ |
58 |
char admin[121] = ""; /* Admin info */ |
59 |
char origbotname[NICKLEN + 1]; |
60 |
char botname[NICKLEN + 1]; /* Primary botname */ |
61 |
|
62 |
|
63 |
/* Remove space characters from beginning and end of string |
64 |
* (more efficent by Fred1) |
65 |
*/ |
66 |
void rmspace(char *s) |
67 |
{ |
68 |
#define whitespace(c) (((c) == 32) || ((c) == 9) || ((c) == 13) || ((c) == 10)) |
69 |
char *p; |
70 |
|
71 |
if (*s == '\0') |
72 |
return; |
73 |
|
74 |
/* Wipe end of string */ |
75 |
for (p = s + strlen(s) - 1; ((whitespace(*p)) && (p >= s)); p--); |
76 |
if (p != s + strlen(s) - 1) |
77 |
*(p + 1) = 0; |
78 |
for (p = s; ((whitespace(*p)) && (*p)); p++); |
79 |
if (p != s) |
80 |
strcpy(s, p); |
81 |
} |
82 |
|
83 |
/* Returns memberfields if the nick is in the member list. |
84 |
*/ |
85 |
memberlist *ismember(struct chanset_t *chan, char *nick) |
86 |
{ |
87 |
register memberlist *x; |
88 |
|
89 |
for (x = chan->channel.member; x && x->nick[0]; x = x->next) |
90 |
if (!irccmp(x->nick, nick)) |
91 |
return x; |
92 |
return NULL; |
93 |
} |
94 |
|
95 |
/* Find a chanset by channel name as the server knows it (ie !ABCDEchannel) |
96 |
*/ |
97 |
struct chanset_t *findchan(const char *name) |
98 |
{ |
99 |
register struct chanset_t *chan; |
100 |
|
101 |
for (chan = chanset; chan; chan = chan->next) |
102 |
if (!irccmp(chan->name, name)) |
103 |
return chan; |
104 |
return NULL; |
105 |
} |
106 |
|
107 |
/* Find a chanset by display name (ie !channel) |
108 |
*/ |
109 |
struct chanset_t *findchan_by_dname(const char *name) |
110 |
{ |
111 |
register struct chanset_t *chan; |
112 |
|
113 |
for (chan = chanset; chan; chan = chan->next) |
114 |
if (!irccmp(chan->dname, name)) |
115 |
return chan; |
116 |
return NULL; |
117 |
} |
118 |
|
119 |
/* |
120 |
* "caching" functions |
121 |
*/ |
122 |
|
123 |
/* Shortcut for get_user_by_host -- might have user record in one |
124 |
* of the channel caches. |
125 |
*/ |
126 |
struct userrec *check_chanlist(const char *host) |
127 |
{ |
128 |
char *nick, *uhost, buf[UHOSTLEN]; |
129 |
register memberlist *m; |
130 |
register struct chanset_t *chan; |
131 |
|
132 |
strncpyz(buf, host, sizeof buf); |
133 |
uhost = buf; |
134 |
nick = splitnick(&uhost); |
135 |
for (chan = chanset; chan; chan = chan->next) |
136 |
for (m = chan->channel.member; m && m->nick[0]; m = m->next) |
137 |
if (!irccmp(nick, m->nick) && !egg_strcasecmp(uhost, m->userhost)) |
138 |
return m->user; |
139 |
return NULL; |
140 |
} |
141 |
|
142 |
/* Shortcut for get_user_by_handle -- might have user record in channels |
143 |
*/ |
144 |
struct userrec *check_chanlist_hand(const char *hand) |
145 |
{ |
146 |
register struct chanset_t *chan; |
147 |
register memberlist *m; |
148 |
|
149 |
for (chan = chanset; chan; chan = chan->next) |
150 |
for (m = chan->channel.member; m && m->nick[0]; m = m->next) |
151 |
if (m->user && !egg_strcasecmp(m->user->handle, hand)) |
152 |
return m->user; |
153 |
return NULL; |
154 |
} |
155 |
|
156 |
/* Clear the user pointers in the chanlists. |
157 |
* |
158 |
* Necessary when a hostmask is added/removed, a user is added or a new |
159 |
* userfile is loaded. |
160 |
*/ |
161 |
void clear_chanlist(void) |
162 |
{ |
163 |
register memberlist *m; |
164 |
register struct chanset_t *chan; |
165 |
|
166 |
for (chan = chanset; chan; chan = chan->next) |
167 |
for (m = chan->channel.member; m && m->nick[0]; m = m->next) |
168 |
m->user = NULL; |
169 |
} |
170 |
|
171 |
/* Clear the user pointer of a specific nick in the chanlists. |
172 |
* |
173 |
* Necessary when a hostmask is added/removed, a nick changes, etc. |
174 |
* Does not completely invalidate the channel cache like clear_chanlist(). |
175 |
*/ |
176 |
void clear_chanlist_member(const char *nick) |
177 |
{ |
178 |
register memberlist *m; |
179 |
register struct chanset_t *chan; |
180 |
|
181 |
for (chan = chanset; chan; chan = chan->next) |
182 |
for (m = chan->channel.member; m && m->nick[0]; m = m->next) |
183 |
if (!irccmp(m->nick, nick)) { |
184 |
m->user = NULL; |
185 |
break; |
186 |
} |
187 |
} |
188 |
|
189 |
/* If this user@host is in a channel, set it (it was null) |
190 |
*/ |
191 |
void set_chanlist(const char *host, struct userrec *rec) |
192 |
{ |
193 |
char *nick, *uhost, buf[UHOSTLEN]; |
194 |
register memberlist *m; |
195 |
register struct chanset_t *chan; |
196 |
|
197 |
strncpyz(buf, host, sizeof buf); |
198 |
uhost = buf; |
199 |
nick = splitnick(&uhost); |
200 |
for (chan = chanset; chan; chan = chan->next) |
201 |
for (m = chan->channel.member; m && m->nick[0]; m = m->next) |
202 |
if (!irccmp(nick, m->nick) && !egg_strcasecmp(uhost, m->userhost)) |
203 |
m->user = rec; |
204 |
} |
205 |
|
206 |
/* Dump status info out to dcc |
207 |
*/ |
208 |
void tell_verbose_status(int idx) |
209 |
{ |
210 |
char s[256], s1[121], s2[81]; |
211 |
char *vers_t, *uni_t; |
212 |
int i; |
213 |
time_t now2, hr, min; |
214 |
#if HAVE_GETRUSAGE |
215 |
struct rusage ru; |
216 |
#else |
217 |
# if HAVE_CLOCK |
218 |
clock_t cl; |
219 |
# endif |
220 |
#endif |
221 |
#ifdef HAVE_UNAME |
222 |
struct utsname un; |
223 |
|
224 |
if (!uname(&un) < 0) { |
225 |
#endif |
226 |
vers_t = " "; |
227 |
uni_t = "*unknown*"; |
228 |
#ifdef HAVE_UNAME |
229 |
} else { |
230 |
vers_t = un.release; |
231 |
uni_t = un.sysname; |
232 |
} |
233 |
#endif |
234 |
|
235 |
i = count_users(userlist); |
236 |
dprintf(idx, "I am %s, running %s: %d user%s\n", |
237 |
botnetnick, ver, i, i == 1 ? "" : "s"); |
238 |
dprintf(idx, "Running on %s %s\n", uni_t, vers_t); |
239 |
if (admin[0]) |
240 |
dprintf(idx, "Admin: %s\n", admin); |
241 |
now2 = now - online_since; |
242 |
s[0] = 0; |
243 |
if (now2 > 86400) { |
244 |
/* days */ |
245 |
sprintf(s, "%d day", (int) (now2 / 86400)); |
246 |
if ((int) (now2 / 86400) >= 2) |
247 |
strcat(s, "s"); |
248 |
strcat(s, ", "); |
249 |
now2 -= (((int) (now2 / 86400)) * 86400); |
250 |
} |
251 |
hr = (time_t) ((int) now2 / 3600); |
252 |
now2 -= (hr * 3600); |
253 |
min = (time_t) ((int) now2 / 60); |
254 |
sprintf(&s[strlen(s)], "%02d:%02d", (int) hr, (int) min); |
255 |
s1[0] = 0; |
256 |
if (backgrd) |
257 |
strcpy(s1, _("background")); |
258 |
else { |
259 |
if (term_z) |
260 |
strcpy(s1, _("terminal mode")); |
261 |
else if (con_chan) |
262 |
strcpy(s1, _("status mode")); |
263 |
else |
264 |
strcpy(s1, _("log dump mode")); |
265 |
} |
266 |
#if HAVE_GETRUSAGE |
267 |
getrusage(RUSAGE_SELF, &ru); |
268 |
hr = (int) ((ru.ru_utime.tv_sec + ru.ru_stime.tv_sec) / 60); |
269 |
min = (int) ((ru.ru_utime.tv_sec + ru.ru_stime.tv_sec) - (hr * 60)); |
270 |
sprintf(s2, "CPU %02d:%02d", (int) hr, (int) min); /* Actally min/sec */ |
271 |
#else |
272 |
# if HAVE_CLOCK |
273 |
cl = (clock() / CLOCKS_PER_SEC); |
274 |
hr = (int) (cl / 60); |
275 |
min = (int) (cl - (hr * 60)); |
276 |
sprintf(s2, "CPU %02d:%02d", (int) hr, (int) min); /* Actually min/sec */ |
277 |
# else |
278 |
sprintf(s2, "CPU ???"); |
279 |
# endif |
280 |
#endif |
281 |
dprintf(idx, "%s %s (%s) %s %s %4.1f%%\n", _("Online for"), |
282 |
s, s1, s2, _("cache hit"), |
283 |
100.0 * ((float) cache_hit) / ((float) (cache_hit + cache_miss))); |
284 |
|
285 |
/* info library */ |
286 |
dprintf(idx, "%s %s\n", _("Tcl library:"), |
287 |
((interp) && (Tcl_Eval(interp, "info library") == TCL_OK)) ? |
288 |
interp->result : "*unknown*"); |
289 |
|
290 |
/* info tclversion/patchlevel */ |
291 |
dprintf(idx, "%s %s (%s %s)\n", _("Tcl version:"), |
292 |
((interp) && (Tcl_Eval(interp, "info patchlevel") == TCL_OK)) ? |
293 |
interp->result : (Tcl_Eval(interp, "info tclversion") == TCL_OK) ? |
294 |
interp->result : "*unknown*", _("header version"), |
295 |
TCL_PATCH_LEVEL ? TCL_PATCH_LEVEL : "*unknown*"); |
296 |
|
297 |
#if HAVE_TCL_THREADS |
298 |
dprintf(idx, "Tcl is threaded\n"); |
299 |
#endif |
300 |
|
301 |
} |
302 |
|
303 |
/* Show all internal state variables |
304 |
*/ |
305 |
void tell_settings(int idx) |
306 |
{ |
307 |
char s[1024]; |
308 |
int i; |
309 |
struct flag_record fr = {FR_GLOBAL, 0, 0, 0, 0, 0}; |
310 |
|
311 |
dprintf(idx, "Botnet Nickname: %s\n", botnetnick); |
312 |
if (firewall[0]) |
313 |
dprintf(idx, "Firewall: %s, port %d\n", firewall, firewallport); |
314 |
dprintf(idx, "Userfile: %s Motd: %s\n", userfile, motdfile); |
315 |
dprintf(idx, "Directories:\n"); |
316 |
dprintf(idx, " Help : %s\n", helpdir); |
317 |
dprintf(idx, " Temp : %s\n", tempdir); |
318 |
#ifndef STATIC |
319 |
dprintf(idx, " Modules : %s\n", moddir); |
320 |
#endif |
321 |
fr.global = default_flags; |
322 |
|
323 |
build_flags(s, &fr, NULL); |
324 |
dprintf(idx, "%s [%s], %s: %s\n", _("New users get flags"), s, |
325 |
_("notify"), notify_new); |
326 |
if (owner[0]) |
327 |
dprintf(idx, "%s: %s\n", _("Permanent owner(s)"), owner); |
328 |
for (i = 0; i < max_logs; i++) |
329 |
if (logs[i].filename != NULL) { |
330 |
dprintf(idx, "Logfile #%d: %s on %s (%s: %s)\n", i + 1, |
331 |
logs[i].filename, logs[i].chname, |
332 |
masktype(logs[i].mask), maskname(logs[i].mask)); |
333 |
} |
334 |
dprintf(idx, "Ignores last %d mins\n", ignore_time); |
335 |
} |
336 |
|
337 |
void reaffirm_owners() |
338 |
{ |
339 |
char *p, *q, s[121]; |
340 |
struct userrec *u; |
341 |
|
342 |
/* Make sure default owners are +n */ |
343 |
if (owner[0]) { |
344 |
q = owner; |
345 |
p = strchr(q, ','); |
346 |
while (p) { |
347 |
strncpyz(s, q, p - q); |
348 |
rmspace(s); |
349 |
u = get_user_by_handle(userlist, s); |
350 |
if (u) |
351 |
u->flags = sanity_check(u->flags | USER_OWNER); |
352 |
q = p + 1; |
353 |
p = strchr(q, ','); |
354 |
} |
355 |
strcpy(s, q); |
356 |
rmspace(s); |
357 |
u = get_user_by_handle(userlist, s); |
358 |
if (u) |
359 |
u->flags = sanity_check(u->flags | USER_OWNER); |
360 |
} |
361 |
} |
362 |
|
363 |
void chanprog() |
364 |
{ |
365 |
int i; |
366 |
|
367 |
admin[0] = 0; |
368 |
helpdir[0] = 0; |
369 |
tempdir[0] = 0; |
370 |
for (i = 0; i < max_logs; i++) |
371 |
logs[i].flags |= LF_EXPIRING; |
372 |
conmask = 0; |
373 |
/* Turn off read-only variables (make them write-able) for rehash */ |
374 |
protect_readonly = 0; |
375 |
/* Now read it */ |
376 |
if (!readtclprog(configfile)) |
377 |
fatal(_("CONFIG FILE NOT LOADED (NOT FOUND, OR ERROR)"), 0); |
378 |
for (i = 0; i < max_logs; i++) { |
379 |
if (logs[i].flags & LF_EXPIRING) { |
380 |
if (logs[i].filename != NULL) |
381 |
free_null(logs[i].filename); |
382 |
if (logs[i].chname != NULL) |
383 |
free_null(logs[i].chname); |
384 |
if (logs[i].f != NULL) { |
385 |
fclose(logs[i].f); |
386 |
logs[i].f = NULL; |
387 |
} |
388 |
logs[i].mask = 0; |
389 |
logs[i].flags = 0; |
390 |
} |
391 |
} |
392 |
/* We should be safe now */ |
393 |
call_hook(HOOK_REHASH); |
394 |
protect_readonly = 1; |
395 |
if (!userfile[0]) |
396 |
fatal(_("STARTING BOT IN USERFILE CREATION MODE.\n\ |
397 |
Telnet to the bot and enter 'NEW' as your nickname."), 0); |
398 |
if (!readuserfile(userfile, &userlist)) { |
399 |
if (!make_userfile) { |
400 |
char tmp[178]; |
401 |
|
402 |
egg_snprintf(tmp, sizeof tmp, _("USER FILE NOT FOUND! (try ./eggdrop -m %s to make one)\n"), configfile); |
403 |
fatal(tmp, 0); |
404 |
} |
405 |
printf(_("\n\nSTARTING BOT IN USERFILE CREATION MODE.\n\ |
406 |
Telnet to the bot and enter 'NEW' as your nickname.\n")); |
407 |
if (module_find("server", 0, 0)) |
408 |
printf(_("OR Go to IRC and: /msg %s hello\n"), origbotname); |
409 |
printf(_("This will make the bot recognize you as the master.\n\n")); |
410 |
} else if (make_userfile) { |
411 |
make_userfile = 0; |
412 |
printf(_("USERFILE ALREADY EXISTS (drop the -m)\n")); |
413 |
} |
414 |
if (helpdir[0]) |
415 |
if (helpdir[strlen(helpdir) - 1] != '/') |
416 |
strcat(helpdir, "/"); |
417 |
if (tempdir[0]) |
418 |
if (tempdir[strlen(tempdir) - 1] != '/') |
419 |
strcat(tempdir, "/"); |
420 |
if (!botnetnick[0]) { |
421 |
strncpyz(botnetnick, origbotname, HANDLEN + 1); |
422 |
} |
423 |
if (!botnetnick[0]) |
424 |
fatal("I don't have a botnet nick!!\n", 0); |
425 |
/* Test tempdir: it's vital */ |
426 |
{ |
427 |
FILE *f; |
428 |
char s[161], rands[8]; |
429 |
|
430 |
/* Possible file race condition solved by using a random string |
431 |
* and the process id in the filename. |
432 |
* FIXME: This race is only partitially fixed. We could still be |
433 |
* overwriting an existing file / following a malicious |
434 |
* link. |
435 |
*/ |
436 |
make_rand_str(rands, 7); /* create random string */ |
437 |
sprintf(s, "%s.test-%u-%s", tempdir, getpid(), rands); |
438 |
f = fopen(s, "w"); |
439 |
if (f == NULL) |
440 |
fatal(_("CANT WRITE TO TEMP DIR"), 0); |
441 |
fclose(f); |
442 |
unlink(s); |
443 |
} |
444 |
reaffirm_owners(); |
445 |
} |
446 |
|
447 |
/* Reload the user file from disk |
448 |
*/ |
449 |
void reload() |
450 |
{ |
451 |
FILE *f; |
452 |
|
453 |
f = fopen(userfile, "r"); |
454 |
if (f == NULL) { |
455 |
putlog(LOG_MISC, "*", _("Cant reload user file!")); |
456 |
return; |
457 |
} |
458 |
fclose(f); |
459 |
noshare = 1; |
460 |
clear_userlist(userlist); |
461 |
noshare = 0; |
462 |
userlist = NULL; |
463 |
if (!readuserfile(userfile, &userlist)) |
464 |
fatal(_("User file is missing!"), 0); |
465 |
reaffirm_owners(); |
466 |
call_hook(HOOK_READ_USERFILE); |
467 |
} |
468 |
|
469 |
void rehash() |
470 |
{ |
471 |
call_hook(HOOK_PRE_REHASH); |
472 |
noshare = 1; |
473 |
clear_userlist(userlist); |
474 |
noshare = 0; |
475 |
userlist = NULL; |
476 |
chanprog(); |
477 |
} |
478 |
|
479 |
/* |
480 |
* Brief venture into timers |
481 |
*/ |
482 |
|
483 |
/* Add a timer |
484 |
*/ |
485 |
unsigned long add_timer(tcl_timer_t **stack, int elapse, char *cmd, |
486 |
unsigned long prev_id) |
487 |
{ |
488 |
tcl_timer_t *old = (*stack); |
489 |
|
490 |
*stack = (tcl_timer_t *) malloc(sizeof(tcl_timer_t)); |
491 |
(*stack)->next = old; |
492 |
(*stack)->mins = elapse; |
493 |
malloc_strcpy((*stack)->cmd, cmd); |
494 |
|
495 |
/* If it's just being added back and already had an id, |
496 |
* don't create a new one. |
497 |
*/ |
498 |
if (prev_id > 0) |
499 |
(*stack)->id = prev_id; |
500 |
else |
501 |
(*stack)->id = timer_id++; |
502 |
return (*stack)->id; |
503 |
} |
504 |
|
505 |
/* Remove a timer, by id |
506 |
*/ |
507 |
int remove_timer(tcl_timer_t **stack, unsigned long id) |
508 |
{ |
509 |
tcl_timer_t *old; |
510 |
int ok = 0; |
511 |
|
512 |
while (*stack) { |
513 |
if ((*stack)->id == id) { |
514 |
ok++; |
515 |
old = *stack; |
516 |
*stack = ((*stack)->next); |
517 |
free(old->cmd); |
518 |
free(old); |
519 |
} else |
520 |
stack = &((*stack)->next); |
521 |
} |
522 |
return ok; |
523 |
} |
524 |
|
525 |
/* Check timers, execute the ones that have expired. |
526 |
*/ |
527 |
void do_check_timers(tcl_timer_t **stack) |
528 |
{ |
529 |
tcl_timer_t *mark = *stack, *old = NULL; |
530 |
char x[16]; |
531 |
|
532 |
/* New timers could be added by a Tcl script inside a current timer |
533 |
* so i'll just clear out the timer list completely, and add any |
534 |
* unexpired timers back on. |
535 |
*/ |
536 |
*stack = NULL; |
537 |
while (mark) { |
538 |
if (mark->mins > 0) |
539 |
mark->mins--; |
540 |
old = mark; |
541 |
mark = mark->next; |
542 |
if (!old->mins) { |
543 |
egg_snprintf(x, sizeof x, "timer%lu", old->id); |
544 |
do_tcl(x, old->cmd); |
545 |
free(old->cmd); |
546 |
free(old); |
547 |
} else { |
548 |
old->next = *stack; |
549 |
*stack = old; |
550 |
} |
551 |
} |
552 |
} |
553 |
|
554 |
/* Wipe all timers. |
555 |
*/ |
556 |
void wipe_timers(Tcl_Interp *irp, tcl_timer_t **stack) |
557 |
{ |
558 |
tcl_timer_t *mark = *stack, *old; |
559 |
|
560 |
while (mark) { |
561 |
old = mark; |
562 |
mark = mark->next; |
563 |
free(old->cmd); |
564 |
free(old); |
565 |
} |
566 |
*stack = NULL; |
567 |
} |
568 |
|
569 |
/* Return list of timers |
570 |
*/ |
571 |
void list_timers(Tcl_Interp *irp, tcl_timer_t *stack) |
572 |
{ |
573 |
tcl_timer_t *mark; |
574 |
char mins[10], id[16], *argv[3], *x; |
575 |
|
576 |
for (mark = stack; mark; mark = mark->next) { |
577 |
egg_snprintf(mins, sizeof mins, "%u", mark->mins); |
578 |
egg_snprintf(id, sizeof id, "timer%lu", mark->id); |
579 |
argv[0] = mins; |
580 |
argv[1] = mark->cmd; |
581 |
argv[2] = id; |
582 |
x = Tcl_Merge(3, argv); |
583 |
Tcl_AppendElement(irp, x); |
584 |
Tcl_Free((char *) x); |
585 |
} |
586 |
} |
587 |
|
588 |
/* Oddly enough, written by proton (Emech's coder) |
589 |
*/ |
590 |
int isowner(char *name) |
591 |
{ |
592 |
char *pa, *pb; |
593 |
char nl, pl; |
594 |
|
595 |
if (!owner || !*owner) |
596 |
return (0); |
597 |
if (!name || !*name) |
598 |
return (0); |
599 |
nl = strlen(name); |
600 |
pa = owner; |
601 |
pb = owner; |
602 |
while (1) { |
603 |
while (1) { |
604 |
if ((*pb == 0) || (*pb == ',') || (*pb == ' ')) |
605 |
break; |
606 |
pb++; |
607 |
} |
608 |
pl = (unsigned int) pb - (unsigned int) pa; |
609 |
if (pl == nl && !egg_strncasecmp(pa, name, nl)) |
610 |
return (1); |
611 |
while (1) { |
612 |
if ((*pb == 0) || ((*pb != ',') && (*pb != ' '))) |
613 |
break; |
614 |
pb++; |
615 |
} |
616 |
if (*pb == 0) |
617 |
return (0); |
618 |
pa = pb; |
619 |
} |
620 |
} |