]> jfr.im git - irc/evilnet/x3.git/blame - src/tools.c
Merge pull request #39 from d7415/master
[irc/evilnet/x3.git] / src / tools.c
CommitLineData
d76ed9a9 1/* tools.c - miscellaneous utility functions
2 * Copyright 2000-2004 srvx Development Team
3 *
83ff05c3 4 * This file is part of x3.
d76ed9a9 5 *
d0f04f71 6 * x3 is free software; you can redistribute it and/or modify
d76ed9a9 7 * it under the terms of the GNU General Public License as published by
348683aa 8 * the Free Software Foundation; either version 3 of the License, or
d76ed9a9 9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with srvx; if not, write to the Free Software Foundation,
18 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
19 */
20
21#include "helpfile.h"
22#include "log.h"
23#include "nickserv.h"
24#include "recdb.h"
25
26#ifdef HAVE_NETDB_H
27#include <netdb.h>
28#endif
29#ifdef HAVE_SYS_SOCKET_H
30#include <sys/socket.h>
31#endif
32#ifdef HAVE_ARPA_INET_H
33#include <arpa/inet.h>
34#endif
35
36#define NUMNICKLOG 6
37#define NUMNICKBASE (1 << NUMNICKLOG)
38#define NUMNICKMASK (NUMNICKBASE - 1)
39
40/* Yes, P10's encoding here is almost-but-not-quite MIME Base64. Yay
41 * for gratuitous incompatibilities. */
42static const char convert2y[256] = {
43 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
44 'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',
45 'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
46 'w','x','y','z','0','1','2','3','4','5','6','7','8','9','[',']'
47};
48
49static const unsigned char convert2n[256] = {
50 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
51 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
52 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2f61d1d7 53 52,53,54,55,56,57,58,59,60,61, 0, 0, 0, 0, 0, 0,
d76ed9a9 54 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,
55 15,16,17,18,19,20,21,22,23,24,25,62, 0,63, 0, 0,
56 0,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,
57 41,42,43,44,45,46,47,48,49,50,51, 0, 0, 0, 0, 0
58};
59
2f61d1d7 60static const unsigned char ctype[256] = {
61 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
62 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
63 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
64 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0,
65 0,10,11,12,13,14,15, 0, 0, 0, 0, 0, 0, 0, 0, 0,
66 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
67 0,10,11,12,13,14,15, 0, 0, 0, 0, 0, 0, 0, 0, 0,
68};
69
d76ed9a9 70unsigned long int
71base64toint(const char* s, int count)
72{
73 unsigned int i = 0;
74 while (*s && count) {
75 i = (i << NUMNICKLOG) + convert2n[(unsigned char)*s++];
76 count--;
77 }
78 return i;
79}
80
81const char* inttobase64(char* buf, unsigned int v, unsigned int count)
82{
83 buf[count] = '\0';
84 while (count > 0) {
85 buf[--count] = convert2y[(unsigned char)(v & NUMNICKMASK)];
86 v >>= NUMNICKLOG;
87 }
88 return buf;
89}
90
2f61d1d7 91unsigned int
92irc_ntop(char *output, unsigned int out_size, const irc_in_addr_t *addr)
93{
94 static const char hexdigits[] = "0123456789abcdef";
95 unsigned int pos;
96
97 assert(output);
98 assert(addr);
99
100 if (irc_in_addr_is_ipv4(*addr)) {
101 unsigned int ip4;
102
103 ip4 = (ntohs(addr->in6[6]) << 16) | ntohs(addr->in6[7]);
104 pos = snprintf(output, out_size, "%u.%u.%u.%u", (ip4 >> 24), (ip4 >> 16) & 255, (ip4 >> 8) & 255, ip4 & 255);
105 } else {
106 unsigned int part, max_start, max_zeros, curr_zeros, ii;
107
108 /* Find longest run of zeros. */
109 for (max_start = max_zeros = curr_zeros = ii = 0; ii < 8; ++ii) {
110 if (!addr->in6[ii])
111 curr_zeros++;
112 else if (curr_zeros > max_zeros) {
113 max_start = ii - curr_zeros;
114 max_zeros = curr_zeros;
115 curr_zeros = 0;
116 }
117 }
118 if (curr_zeros > max_zeros) {
119 max_start = ii - curr_zeros;
120 max_zeros = curr_zeros;
121 }
122
123 /* Print out address. */
124#define APPEND(CH) do { if (pos < out_size) output[pos] = (CH); pos++; } while (0)
125 for (pos = 0, ii = 0; ii < 8; ++ii) {
126 if ((max_zeros > 0) && (ii == max_start)) {
127 if (ii == 0)
128 APPEND(':');
129 APPEND(':');
130 ii += max_zeros - 1;
131 continue;
132 }
133 part = ntohs(addr->in6[ii]);
134 if (part >= 0x1000)
135 APPEND(hexdigits[part >> 12]);
136 if (part >= 0x100)
137 APPEND(hexdigits[(part >> 8) & 15]);
138 if (part >= 0x10)
139 APPEND(hexdigits[(part >> 4) & 15]);
140 APPEND(hexdigits[part & 15]);
141 if (ii < 7)
142 APPEND(':');
143 }
144#undef APPEND
145 output[pos < out_size ? pos : out_size - 1] = '\0';
146 }
147
148 return pos;
149}
150
151unsigned int
152irc_ntop_mask(char *output, unsigned int out_size, const irc_in_addr_t *addr, unsigned char bits)
153{
154 char base_addr[IRC_NTOP_MAX_SIZE];
155 int len;
156
157 if (bits >= 128)
158 return irc_ntop(output, out_size, addr);
159 if (!irc_ntop(base_addr, sizeof(base_addr), addr))
160 return 0;
161 len = snprintf(output, out_size, "%s/%d", base_addr, bits);
162 if ((unsigned int)len >= out_size)
163 return 0;
164 return len;
165}
166
167static unsigned int
168irc_pton_ip4(const char *input, unsigned char *pbits, uint32_t *output)
169{
170 unsigned int dots = 0, pos = 0, part = 0, ip = 0, bits = 32;
171
172 /* Intentionally no support for bizarre IPv4 formats (plain
173 * integers, octal or hex components) -- only vanilla dotted
174 * decimal quads, optionally with trailing /nn.
175 */
176 if (input[0] == '.')
177 return 0;
178 while (1) switch (input[pos]) {
179 default:
180 if (dots < 3)
181 return 0;
182 out:
183 ip |= part << (24 - 8 * dots++);
184 *output = htonl(ip);
185 if (pbits)
186 *pbits = bits;
187 return pos;
188 case '.':
189 if (input[++pos] == '.')
190 return 0;
191 ip |= part << (24 - 8 * dots++);
192 part = 0;
193 if (input[pos] == '*') {
194 while (input[++pos] == '*') ;
195 if (input[pos] != '\0')
196 return 0;
197 if (pbits)
198 *pbits = dots * 8;
199 *output = htonl(ip);
200 return pos;
201 }
202 break;
203 case '/':
204 if (!pbits || !isdigit(input[pos + 1]))
205 return 0;
206 for (bits = 0; isdigit(input[++pos]); )
207 bits = bits * 10 + input[pos] - '0';
208 if (bits > 32)
209 return 0;
210 goto out;
211 case '0': case '1': case '2': case '3': case '4':
212 case '5': case '6': case '7': case '8': case '9':
213 part = part * 10 + input[pos++] - '0';
214 if (part > 255)
215 return 0;
216 break;
217 }
218}
219
220unsigned int
221irc_pton(irc_in_addr_t *addr, unsigned char *bits, const char *input)
222{
223 const char *part_start = NULL;
224 char *colon;
225 char *dot;
226 unsigned int part = 0, pos = 0, ii = 0, cpos = 8;
227
228 assert(input);
229 memset(addr, 0, sizeof(*addr));
230 colon = strchr(input, ':');
231 dot = strchr(input, '.');
232
233 if (colon && (!dot || (dot > colon))) {
234 /* Parse IPv6, possibly like ::127.0.0.1.
235 * This is pretty straightforward; the only trick is borrowed
236 * from Paul Vixie (BIND): when it sees a "::" continue as if
237 * it were a single ":", but note where it happened, and fill
238 * with zeros afterwards.
239 */
240 if (input[pos] == ':') {
241 if ((input[pos+1] != ':') || (input[pos+2] == ':'))
242 return 0;
243 cpos = 0;
244 pos += 2;
245 part_start = input + pos;
246 }
247 while (ii < 8) switch (input[pos]) {
248 case '0': case '1': case '2': case '3': case '4':
249 case '5': case '6': case '7': case '8': case '9':
250 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
251 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
252 part = (part << 4) | (ctype[(unsigned char)input[pos++]] & 15);
253 if (part > 0xffff)
254 return 0;
255 break;
256 case ':':
257 part_start = input + ++pos;
258 if (input[pos] == '.')
259 return 0;
260 addr->in6[ii++] = htons(part);
261 part = 0;
262 if (input[pos] == ':') {
263 if (cpos < 8)
264 return 0;
265 cpos = ii;
266 }
267 break;
268 case '.': {
269 uint32_t ip4;
270 unsigned int len;
271 len = irc_pton_ip4(part_start, bits, &ip4);
272 if (!len || (ii > 6))
273 return 0;
274 memcpy(addr->in6 + ii, &ip4, sizeof(ip4));
275 if (bits)
276 *bits += 96;
277 ii += 2;
278 pos = part_start + len - input;
279 goto finish;
280 }
281 case '/':
282 if (!bits || !isdigit(input[pos + 1]))
283 return 0;
284 addr->in6[ii++] = htons(part);
285 for (part = 0; isdigit(input[++pos]); )
286 part = part * 10 + input[pos] - '0';
287 if (part > 128)
288 return 0;
289 *bits = part;
290 goto finish;
291 case '*':
292 while (input[++pos] == '*') ;
293 if (input[pos] != '\0' || cpos < 8)
294 return 0;
295 if (bits)
296 *bits = ii * 16;
297 return pos;
298 default:
299 addr->in6[ii++] = htons(part);
300 if (cpos == 8 && ii < 8)
301 return 0;
302 if (bits)
303 *bits = 128;
304 goto finish;
305 }
306 finish:
307 /* Shift stuff after "::" up and fill middle with zeros. */
308 if (cpos < 8) {
309 unsigned int jj;
310 for (jj = 0; jj < ii - cpos; jj++)
311 addr->in6[7 - jj] = addr->in6[ii - jj - 1];
312 for (jj = 0; jj < 8 - ii; jj++)
313 addr->in6[cpos + jj] = 0;
314 }
315 } else if (dot) {
1136f709 316 uint32_t ip4;
2f61d1d7 317 pos = irc_pton_ip4(input, bits, &ip4);
318 if (pos) {
319 addr->in6[5] = htons(65535);
320 addr->in6[6] = htons(ntohl(ip4) >> 16);
321 addr->in6[7] = htons(ntohl(ip4) & 65535);
322 if (bits)
323 *bits += 96;
324 }
325 } else if (input[0] == '*') {
326 while (input[++pos] == '*') ;
327 if (input[pos] != '\0')
328 return 0;
329 if (bits)
330 *bits = 0;
331 }
332 return pos;
333}
334
335const char *irc_ntoa(const irc_in_addr_t *addr)
336{
337 static char ntoa[IRC_NTOP_MAX_SIZE];
338 irc_ntop(ntoa, sizeof(ntoa), addr);
339 return ntoa;
340}
341
342unsigned int
343irc_check_mask(const irc_in_addr_t *check, const irc_in_addr_t *mask, unsigned char bits)
344{
345 unsigned int ii;
346
347 for (ii = 0; (ii < 8) && (bits > 16); bits -= 16, ++ii)
348 if (check->in6[ii] != mask->in6[ii])
349 return 0;
350 if (ii < 8 && bits > 0
351 && (ntohs(check->in6[ii] ^ mask->in6[ii]) >> (16 - bits)))
352 return 0;
353 return 1;
354}
355
d76ed9a9 356static char irc_tolower[256];
357#undef tolower
358#define tolower(X) irc_tolower[(unsigned char)(X)]
359
d6b0769f 360void
361irc_strtolower(char *str) {
362 char *p;
363 for(p = str;*p;p++) {
364 *p = tolower(*p);
365 }
366}
367
d76ed9a9 368int
369irccasecmp(const char *stra, const char *strb) {
cf945df8
MB
370 if (!stra)
371 return -1;
372 if (!strb)
373 return 1;
d76ed9a9 374 while (*stra && (tolower(*stra) == tolower(*strb)))
375 stra++, strb++;
376 return tolower(*stra) - tolower(*strb);
377}
378
379int
380ircncasecmp(const char *stra, const char *strb, unsigned int len) {
381 len--;
382 while (*stra && (tolower(*stra) == tolower(*strb)) && len)
383 stra++, strb++, len--;
384 return tolower(*stra) - tolower(*strb);
385}
386
387const char *
388irccasestr(const char *haystack, const char *needle) {
389 unsigned int hay_len = strlen(haystack), needle_len = strlen(needle), pos;
390 if (hay_len < needle_len)
391 return NULL;
392 for (pos=0; pos<hay_len+1-needle_len; ++pos) {
393 if ((tolower(haystack[pos]) == tolower(*needle))
394 && !ircncasecmp(haystack+pos, needle, needle_len))
395 return haystack+pos;
396 }
397 return NULL;
398}
399
1136f709 400char *
401ircstrlower(char *str) {
402 size_t ii;
403 for (ii = 0; str[ii] != '\0'; ++ii)
404 str[ii] = tolower(str[ii]);
405 return str;
406}
407
d76ed9a9 408int
409split_line(char *line, int irc_colon, int argv_size, char *argv[])
410{
411 int argc = 0;
412 int n;
413 while (*line && (argc < argv_size)) {
414 while (*line == ' ')
415 *line++ = 0;
416 if (*line == ':' && irc_colon && argc > 0) {
417 /* the rest is a single parameter */
418 argv[argc++] = line + 1;
419 break;
420 }
421 if (!*line)
422 break;
423 argv[argc++] = line;
424 if (argc >= argv_size)
425 break;
426 while (*line != ' ' && *line)
427 line++;
428 }
429#ifdef NDEBUG
430 n = 0;
431#else
432 for (n=argc; n<argv_size; n++)
433 argv[n] = (char*)0xFEEDBEEF;
434#endif
435 return argc;
436}
437
438/* This is ircu's mmatch() function, from match.c. */
439int mmatch(const char *old_mask, const char *new_mask)
440{
441 register const char *m = old_mask;
442 register const char *n = new_mask;
443 const char *ma = m;
444 const char *na = n;
445 int wild = 0;
446 int mq = 0, nq = 0;
447
448 while (1)
449 {
450 if (*m == '*')
451 {
452 while (*m == '*')
453 m++;
454 wild = 1;
455 ma = m;
456 na = n;
457 }
458
459 if (!*m)
460 {
461 if (!*n)
462 return 0;
463 for (m--; (m > old_mask) && (*m == '?'); m--)
464 ;
465 if ((*m == '*') && (m > old_mask) && (m[-1] != '\\'))
466 return 0;
467 if (!wild)
468 return 1;
469 m = ma;
470
471 /* Added to `mmatch' : Because '\?' and '\*' now is one character: */
472 if ((*na == '\\') && ((na[1] == '*') || (na[1] == '?')))
473 ++na;
474
475 n = ++na;
476 }
477 else if (!*n)
478 {
479 while (*m == '*')
480 m++;
481 return (*m != 0);
482 }
483 if ((*m == '\\') && ((m[1] == '*') || (m[1] == '?')))
484 {
485 m++;
486 mq = 1;
487 }
488 else
489 mq = 0;
490
491 /* Added to `mmatch' : Because '\?' and '\*' now is one character: */
492 if ((*n == '\\') && ((n[1] == '*') || (n[1] == '?')))
493 {
494 n++;
495 nq = 1;
496 }
497 else
498 nq = 0;
499
500/*
501 * This `if' has been changed compared to match() to do the following:
502 * Match when:
503 * old (m) new (n) boolean expression
504 * * any (*m == '*' && !mq) ||
505 * ? any except '*' (*m == '?' && !mq && (*n != '*' || nq)) ||
506 * any except * or ? same as m (!((*m == '*' || *m == '?') && !mq) &&
507 * toLower(*m) == toLower(*n) &&
508 * !((mq && !nq) || (!mq && nq)))
509 *
510 * Here `any' also includes \* and \? !
511 *
512 * After reworking the boolean expressions, we get:
513 * (Optimized to use boolean shortcircuits, with most frequently occuring
514 * cases upfront (which took 2 hours!)).
515 */
516 if ((*m == '*' && !mq) ||
517 ((!mq || nq) && tolower(*m) == tolower(*n)) ||
518 (*m == '?' && !mq && (*n != '*' || nq)))
519 {
520 if (*m)
521 m++;
522 if (*n)
523 n++;
524 }
525 else
526 {
527 if (!wild)
528 return 1;
529 m = ma;
530
531 /* Added to `mmatch' : Because '\?' and '\*' now is one character: */
532 if ((*na == '\\') && ((na[1] == '*') || (na[1] == '?')))
533 ++na;
534
535 n = ++na;
536 }
537 }
538}
539
540int
541match_ircglob(const char *text, const char *glob)
542{
dee9951d 543 const char *m = glob, *n = text;
544 const char *m_tmp = glob, *n_tmp = text;
545 int star_p;
546
547 for (;;) switch (*m) {
548 case '\0':
549 if (!*n)
550 return 1;
551 backtrack:
552 if (m_tmp == glob)
553 return 0;
554 m = m_tmp;
555 n = ++n_tmp;
1136f709 556 if (!*n)
557 return 0;
dee9951d 558 break;
559 case '\\':
560 m++;
561 /* allow escaping to force capitalization */
562 if (*m++ != *n++)
2f61d1d7 563 goto backtrack;
dee9951d 564 break;
565 case '*': case '?':
566 for (star_p = 0; ; m++) {
567 if (*m == '*')
568 star_p = 1;
569 else if (*m == '?') {
570 if (!*n++)
571 goto backtrack;
572 } else break;
573 }
574 if (star_p) {
575 if (!*m)
d76ed9a9 576 return 1;
dee9951d 577 else if (*m == '\\') {
578 m_tmp = ++m;
579 if (!*m)
d76ed9a9 580 return 0;
dee9951d 581 for (n_tmp = n; *n && *n != *m; n++) ;
582 } else {
583 m_tmp = m;
584 for (n_tmp = n; *n && tolower(*n) != tolower(*m); n++) ;
585 }
586 }
587 /* and fall through */
588 default:
589 if (!*n)
590 return *m == '\0';
591 if (tolower(*m) != tolower(*n))
592 goto backtrack;
593 m++;
594 n++;
595 break;
d76ed9a9 596 }
597}
598
599extern const char *hidden_host_suffix;
600
d1a65675 601/* Prevent *@* *@** *@*a* type masks, while allowing anything else. This is the best way iv found to detect
602 * a global matching mask; if it matches this string, it'll match almost anything :) */
603int is_overmask(char *mask)
604{
605 return(match_ircglob("abcdefghijklmnopqrstuv!frcmbghilnrtoasde@apdic.yfa.dsfsdaffsdasfdasfd.abcdefghijklmnopqrstuvwxyz.asdfasfdfsdsfdasfda.ydfbe", mask));
606}
607
d76ed9a9 608int
277ad996 609user_matches_glob(struct userNode *user, const char *orig_glob, int flags, int shared)
d76ed9a9 610{
2e9062b9 611 char *tmpglob, *glob, *marker;
634d32a3 612 char exttype = 0;
80ca5a69 613 int extreverse = 0, is_extended = 0, match = 0, banned = 0;
614 unsigned int count, n;
615 struct modeNode *mn;
277ad996 616 struct chanNode *channel;
617 struct banNode *ban;
d76ed9a9 618
619 /* Make a writable copy of the glob */
620 glob = alloca(strlen(orig_glob)+1);
621 strcpy(glob, orig_glob);
634d32a3 622
623 /* Extended bans */
624 tmpglob = alloca(strlen(orig_glob)+1);
625 tmpglob = strdup(orig_glob);
626
627 if (*tmpglob == '~') {
628 tmpglob++; /* get rid of the ~ */
629
630 if (*tmpglob == '!') {
631 extreverse = 1;
632 tmpglob++; /* get rid of the ! */
633 }
634
635 exttype = *tmpglob;
636 tmpglob++; /* get rid of the type */
637
638 if (*tmpglob == ':') {
639 is_extended = 1;
640 tmpglob++; /* get rid of the : */
641 glob = strdup(tmpglob);
642 }
643 }
644
645 if (is_extended) {
646 log_module(MAIN_LOG, LOG_DEBUG, "Extended ban. T (%c) R (%d) M (%s)", exttype, extreverse, glob);
647 switch (exttype) {
648 case 'a':
649 if (user->handle_info) {
650 if (extreverse) {
651 if (0 != strcasecmp(glob, user->handle_info->handle))
652 return 1;
653 } else {
654 if (0 == strcasecmp(glob, user->handle_info->handle))
655 return 1;
656 }
657 } else {
658 if (extreverse)
659 return 1;
660 }
661 return match_ircglob(user->hostname, glob);
80ca5a69 662 case 'c':
663 if (!strstr(glob, "#"))
3b7fa78b 664 return -1;
80ca5a69 665
80ca5a69 666 if (extreverse) {
667 for (n=count=0; n<user->channels.used; n++) {
668 mn = user->channels.list[n];
669 match = 0;
670
2e9062b9 671 if (*glob == '#') {
672 if (0 == strcasecmp(glob, mn->channel->name))
80ca5a69 673 match = 1;
2e9062b9 674 } else {
675 if (0 == strcasecmp(glob+1, mn->channel->name)) {
676 if ((*glob == '@') && (mn->modes & MODE_CHANOP))
677 match = 1;
678 else if ((*glob == '%') && (mn->modes & MODE_HALFOP))
679 match = 1;
680 else if ((*glob == '+') && (mn->modes & MODE_VOICE))
681 match = 1;
682 }
80ca5a69 683 }
684
685 if (match == 0)
686 banned = 1;
687 else {
688 banned = 0;
689 break;
690 }
691 }
692 } else {
693 for (n=count=0; n<user->channels.used; n++) {
694 mn = user->channels.list[n];
695 match = 0;
696
2e9062b9 697 if (*glob == '#') {
698 if (0 == strcasecmp(glob, mn->channel->name))
80ca5a69 699 match = 1;
2e9062b9 700 } else {
701 if (0 == strcasecmp(glob+1, mn->channel->name)) {
702 if ((*glob == '@') && (mn->modes & MODE_CHANOP))
703 match = 1;
704 else if ((*glob == '%') && (mn->modes & MODE_HALFOP))
705 match = 1;
706 else if ((*glob == '+') && (mn->modes & MODE_VOICE))
707 match = 1;
708 }
80ca5a69 709 }
2e9062b9 710
80ca5a69 711 if (match == 1)
712 banned = 1;
713 }
714 }
715
716 if (banned)
717 return 1;
718 else
719 return match_ircglob(user->hostname, glob);
277ad996 720 case 'j':
721 if (shared == 0) {
95b9d0bc 722 if (*glob != '#')
723 return -1;
277ad996 724 if ((channel = GetChannel(glob))) {
725 for (n = 0; n < channel->banlist.used; n++) {
726 ban = channel->banlist.list[n];
727 if (user_matches_glob(user, ban->ban, flags, 1))
728 return 1;
729 }
730 }
731 }
732 return match_ircglob(user->hostname, glob);
80ca5a69 733 case 'n': /* this is handled ircd side */
734 return match_ircglob(user->hostname, glob);
277ad996 735 case 'q': /* this is handled ircd side */
736 return match_ircglob(user->hostname, glob);
80ca5a69 737 case 't': /* this is handled ircd side */
738 return match_ircglob(user->hostname, glob);
69517d70 739 case 'R': /* this is handled ircd side */
740 return match_ircglob(user->hostname, glob);
634d32a3 741 default:
3b7fa78b 742 return -1;
634d32a3 743 }
744 }
745
d76ed9a9 746 /* Check the nick, if it's present */
2f61d1d7 747 if (flags & MATCH_USENICK) {
d76ed9a9 748 if (!(marker = strchr(glob, '!'))) {
2f61d1d7 749 log_module(MAIN_LOG, LOG_ERROR, "user_matches_glob(\"%s\", \"%s\", %d) called, and glob doesn't include a '!'", user->nick, orig_glob, flags);
d76ed9a9 750 return 0;
751 }
752 *marker = 0;
753 if (!match_ircglob(user->nick, glob)) return 0;
754 glob = marker + 1;
755 }
756 /* Check the ident */
757 if (!(marker = strchr(glob, '@'))) {
2f61d1d7 758 log_module(MAIN_LOG, LOG_ERROR, "user_matches_glob(\"%s\", \"%s\", %d) called, and glob doesn't include an '@'", user->nick, orig_glob, flags);
d76ed9a9 759 return 0;
760 }
761 *marker = 0;
2f61d1d7 762 if (!match_ircglob(user->ident, glob))
763 return 0;
d76ed9a9 764 glob = marker + 1;
ec1a68c8 765 /* Check for a fakehost match. */
766 if (IsFakeHost(user) && match_ircglob(user->fakehost, glob))
efabf227 767 return 1;
2f61d1d7 768
769 /* Check for a sethost (S:lines) */
770 if (IsSetHost(user) && match_ircglob(user->sethost, glob))
efabf227 771 return 1;
2f61d1d7 772
ec1a68c8 773 /* Check for an account match. */
774 if (hidden_host_suffix && user->handle_info) {
775 char hidden_host[HOSTLEN+1];
776 snprintf(hidden_host, sizeof(hidden_host), "%s.%s", user->handle_info->handle, hidden_host_suffix);
777 if (match_ircglob(hidden_host, glob))
a32da4c7 778 return 1;
d76ed9a9 779 }
f16ad9e7 780
781 /* Match crypt hostname */
782 if (match_ircglob(user->crypthost, glob))
783 return 1;
784
785 /* Match crypt IP */
786 if (match_ircglob(user->cryptip, glob))
787 return 1;
788
2f61d1d7 789 /* If only matching the visible hostnames, bail early. */
790 if ((flags & MATCH_VISIBLE) && IsHiddenHost(user)
b6f98d33
MB
791 && (IsFakeHost(user) || (hidden_host_suffix && user->handle_info) ||
792 user->crypthost || user->cryptip))
2f61d1d7 793 return 0;
794 /* If it might be an IP glob, test that. */
795 if (!glob[strspn(glob, "0123456789./*?")]
796 && match_ircglob(irc_ntoa(&user->ip), glob))
797 return 1;
ec1a68c8 798 /* None of the above; could only be a hostname match. */
799 return match_ircglob(user->hostname, glob);
d76ed9a9 800}
801
802int
803is_ircmask(const char *text)
804{
8062bfc3 805 char *tmptext;
806
807 if (*text == '~') {
808 tmptext = alloca(strlen(text)+1);
809 tmptext = strdup(text);
810
811 tmptext++; /* get rid of the ~ */
812
813 if (*tmptext == '!')
814 tmptext++; /* get rid of the ! if it exists */
815
816 tmptext++; /* get rid of the ext ban type */
817
818 if (*tmptext == ':') {
819 tmptext++; /* get rid of the : */
820 while (*tmptext && !isspace((char)*tmptext))
821 tmptext++; /* get rid of the rest */
822 return !*tmptext;
823 }
824 }
825
d76ed9a9 826 while (*text && (isalnum((char)*text) || strchr("-_[]|\\`^{}?*", *text)))
827 text++;
828 if (*text++ != '!')
829 return 0;
830 while (*text && *text != '@' && !isspace((char)*text))
831 text++;
832 if (*text++ != '@')
833 return 0;
834 while (*text && !isspace((char)*text))
835 text++;
836 return !*text;
837}
838
839int
840is_gline(const char *text)
d914d1cb 841{
842 if (*text == '@')
843 return 0;
844 text += strcspn(text, "@!% \t\r\n");
845 if (*text++ != '@')
846 return 0;
847 if (!*text)
848 return 0;
849 while (*text && (isalnum((char)*text) || strchr(".-?*:", *text)))
850 text++;
851 return !*text;
852}
853
854int
855is_shun(const char *text)
d76ed9a9 856{
857 if (*text == '@')
858 return 0;
859 text += strcspn(text, "@!% \t\r\n");
860 if (*text++ != '@')
861 return 0;
862 if (!*text)
863 return 0;
ec1a68c8 864 while (*text && (isalnum((char)*text) || strchr(".-?*:", *text)))
d76ed9a9 865 text++;
866 return !*text;
867}
868
869int
870split_ircmask(char *text, char **nick, char **ident, char **host)
871{
872 char *start;
873
874 start = text;
875 while (isalnum((char)*text) || strchr("=[]\\`^{}?*", *text))
876 text++;
877 if (*text != '!' || ((text - start) > NICKLEN))
878 return 0;
879 *text = 0;
880 if (nick)
881 *nick = start;
882
883 start = ++text;
884 while (*text && *text != '@' && !isspace((char)*text))
885 text++;
886 if (*text != '@' || ((text - start) > USERLEN))
887 return 0;
888 *text = 0;
889 if (ident)
890 *ident = start;
891
892 start = ++text;
ec1a68c8 893 while (*text && (isalnum((char)*text) || strchr(".-?*:", *text)))
d76ed9a9 894 text++;
895 if (host)
896 *host = start;
897 return !*text && ((text - start) <= HOSTLEN) && nick && ident && host;
898}
899
900char *
901sanitize_ircmask(char *input)
902{
903 unsigned int length, flag;
904 char *mask, *start, *output;
905
906 /* Sanitize everything in place; input *must* be a valid
907 hostmask. */
908 output = input;
909 flag = 0;
910
911 /* The nick is truncated at the end. */
912 length = 0;
913 mask = input;
914 while(*input++ != '!')
915 {
916 length++;
917 }
918 if(length > NICKLEN)
919 {
920 mask += NICKLEN;
921 *mask++ = '!';
922
923 /* This flag is used to indicate following parts should
924 be shifted. */
925 flag = 1;
926 }
927 else
928 {
929 mask = input;
930 }
931
932 /* The ident and host must be truncated at the beginning and
933 replaced with a '*' to be compatible with ircu. */
934 length = 0;
935 start = input;
936 while(*input++ != '@')
937 {
938 length++;
939 }
940 if(length > USERLEN || flag)
941 {
942 if(length > USERLEN)
943 {
944 start = input - USERLEN;
945 *mask++ = '*';
946 }
947 while(*start != '@')
948 {
949 *mask++ = *start++;
950 }
951 *mask++ = '@';
952
953 flag = 1;
954 }
955 else
956 {
957 mask = input;
958 }
959
960 length = 0;
961 start = input;
962 while(*input++)
963 {
964 length++;
965 }
966 if(length > HOSTLEN || flag)
967 {
968 if(length > HOSTLEN)
969 {
970 start = input - HOSTLEN;
971 *mask++ = '*';
972 }
973 while(*start)
974 {
975 *mask++ = *start++;
976 }
977 *mask = '\0';
978 }
979
980 return output;
981}
982
983static long
984TypeLength(char type)
985{
986 switch (type) {
987 case 'y': return 365*24*60*60;
ca22ccd3 988 case 'M': return 30*24*60*60;
d76ed9a9 989 case 'w': return 7*24*60*60;
990 case 'd': return 24*60*60;
991 case 'h': return 60*60;
992 case 'm': return 60;
993 case 's': return 1;
994 default: return 0;
995 }
996}
997
ca22ccd3 998/* This function is not entirely accurate as it does not take into account leap units
999 * or varying months. TODO: use proper dateadd functions to calculate real seconds
1000 * from now for the units (eg 1M should be give us seconds till todays date next month)
1001 */
d76ed9a9 1002unsigned long
1003ParseInterval(const char *interval)
1004{
1005 unsigned long seconds = 0;
1006 int partial = 0;
1007 char c;
1008
1009 /* process the string, resetting the count if we find a unit character */
1010 while ((c = *interval++)) {
1136f709 1011 if (isdigit((int)c)) {
1012 partial = partial*10 + c - '0';
1013 } else if (strchr("yMwdhms", c)) {
1014 seconds += TypeLength(c) * partial;
1015 partial = 0;
1016 } else {
1017 return 0;
1018 }
d76ed9a9 1019 }
1020 /* assume the last chunk is seconds (the normal case) */
1021 return seconds + partial;
1022}
1023
1024static long
1025GetSizeMultiplier(char type)
1026{
1027 switch (type) {
1028 case 'G': case 'g': return 1024*1024*1024;
1029 case 'M': case 'm': return 1024*1024;
1030 case 'K': case 'k': return 1024;
1031 case 'B': case 'b': return 1;
1032 default: return 0;
1033 }
1034}
1035
1036unsigned long
1037ParseVolume(const char *volume)
1038{
1039 unsigned long accum = 0, partial = 0;
1040 char c;
1041 while ((c = *volume++)) {
1042 if (isdigit((int)c)) {
1043 partial = partial*10 + c - '0';
1044 } else {
1045 accum += GetSizeMultiplier(c) * partial;
1046 partial = 0;
1047 }
1048 }
1049 return accum + partial;
1050}
1051
d76ed9a9 1052char *
1053unsplit_string(char *set[], unsigned int max, char *dest)
1054{
1055 static char unsplit_buffer[MAXLEN*2];
1056 unsigned int ii, jj, pos;
1057
1058 if (!dest)
1059 dest = unsplit_buffer;
1060 for (ii=pos=0; ii<max; ii++) {
1061 for (jj=0; set[ii][jj]; jj++)
1062 dest[pos++] = set[ii][jj];
1063 dest[pos++] = ' ';
1064 }
1065 dest[--pos] = 0;
1066 return dest;
1067}
1068
1069char *
1070intervalString(char *output, time_t interval, struct handle_info *hi)
1071{
1072 static const struct {
1073 const char *msg_single;
1074 const char *msg_plural;
1075 long length;
1076 } unit[] = {
1077 { "MSG_YEAR", "MSG_YEARS", 365 * 24 * 60 * 60 },
1078 { "MSG_WEEK", "MSG_WEEKS", 7 * 24 * 60 * 60 },
1079 { "MSG_DAY", "MSG_DAYS", 24 * 60 * 60 },
1080 { "MSG_HOUR", "MSG_HOURS", 60 * 60 },
1081 { "MSG_MINUTE", "MSG_MINUTES", 60 },
1082 { "MSG_SECOND", "MSG_SECONDS", 1 }
1083 };
1084 struct language *lang;
1085 const char *msg;
1086 unsigned int type, words, pos, count;
1087
1088 lang = hi ? hi->language : lang_C;
1089 if(!interval)
1090 {
1091 msg = language_find_message(lang, "MSG_0_SECONDS");
1092 return strcpy(output, msg);
1093 }
1094
1095 for (type = 0, words = pos = 0;
1096 interval && (words < 2) && (type < ArrayLength(unit));
1097 type++) {
1098 if (interval < unit[type].length)
1099 continue;
1100 count = interval / unit[type].length;
1101 interval = interval % unit[type].length;
1102
1103 if (words++ == 1) {
1104 msg = language_find_message(lang, "MSG_AND");
de9510bc 1105 pos += sprintf(output + pos, "%s ", msg);
d76ed9a9 1106 }
1107 if (count == 1)
1108 msg = language_find_message(lang, unit[type].msg_single);
1109 else
1110 msg = language_find_message(lang, unit[type].msg_plural);
de9510bc 1111 pos += sprintf(output + pos, "%d%s", count, msg);
d76ed9a9 1112 }
1113
1114 output[pos] = 0;
1115 return output;
1116}
1117
1118int
1119getipbyname(const char *name, unsigned long *ip)
1120{
1121 struct hostent *he = gethostbyname(name);
1122 if (!he)
1123 return 0;
1124 if (he->h_addrtype != AF_INET)
1125 return 0;
1126 memcpy(ip, he->h_addr_list[0], sizeof(*ip));
1127 return 1;
1128}
1129
1130DEFINE_LIST(string_buffer, char)
1131
1132void
1133string_buffer_append_substring(struct string_buffer *buf, const char *tail, unsigned int len)
1134{
1135 while (buf->used + len >= buf->size) {
1136 if (!buf->size)
1137 buf->size = 16;
1138 else
1139 buf->size <<= 1;
1140 buf->list = realloc(buf->list, buf->size*sizeof(buf->list[0]));
1141 }
1142 memcpy(buf->list + buf->used, tail, len+1);
1143 buf->used += len;
1144}
1145
1146void
1147string_buffer_append_string(struct string_buffer *buf, const char *tail)
1148{
1149 string_buffer_append_substring(buf, tail, strlen(tail));
1150}
1151
1152void
1153string_buffer_append_vprintf(struct string_buffer *buf, const char *fmt, va_list args)
1154{
1155 va_list working;
1156 unsigned int len;
1157 int ret;
1158
1159 VA_COPY(working, args);
1160 len = strlen(fmt);
1161 if (!buf->list || ((buf->used + buf->size) < len)) {
1162 buf->size = buf->used + len;
1163 buf->list = realloc(buf->list, buf->size);
1164 }
1165 ret = vsnprintf(buf->list + buf->used, buf->size - buf->used, fmt, working);
1166 if (ret <= 0) {
1167 /* pre-C99 behavior; double buffer size until it is big enough */
1168 va_end(working);
1169 VA_COPY(working, args);
a32da4c7 1170 while ((ret = vsnprintf(buf->list + buf->used, buf->size - buf->used, fmt, working)) <= 0) {
d76ed9a9 1171 buf->size += len;
1172 buf->list = realloc(buf->list, buf->size);
1173 va_end(working);
1174 VA_COPY(working, args);
1175 }
1176 buf->used += ret;
1177 } else if (buf->used + ret < buf->size) {
1178 /* no need to increase allocation size */
1179 buf->used += ret;
1180 } else {
1181 /* now we know exactly how much space we need */
1182 if (buf->size <= buf->used + ret) {
1183 buf->size = buf->used + ret + 1;
1184 buf->list = realloc(buf->list, buf->size);
1185 }
1186 va_end(working);
1187 VA_COPY(working, args);
1188 buf->used += vsnprintf(buf->list + buf->used, buf->size, fmt, working);
1189 }
1190 va_end(working);
1191 va_end(args);
1192}
1193
1194void string_buffer_append_printf(struct string_buffer *buf, const char *fmt, ...)
1195{
1196 va_list args;
1197 va_start(args, fmt);
1198 string_buffer_append_vprintf(buf, fmt, args);
1199}
1200
1201void
1202string_buffer_replace(struct string_buffer *buf, unsigned int from, unsigned int len, const char *repl)
1203{
1204 unsigned int repl_len = strlen(repl);
1205 if (from > buf->used)
1206 return;
1207 if (len + from > buf->used)
1208 len = buf->used - from;
1209 buf->used = buf->used + repl_len - len;
1210 if (buf->size <= buf->used) {
1211 while (buf->used >= buf->size)
1212 buf->size <<= 1;
1213 buf->list = realloc(buf->list, buf->size*sizeof(buf->list[0]));
1214 }
1215 memmove(buf->list+from+repl_len, buf->list+from+len, strlen(buf->list+from+len));
1216 strcpy(buf->list+from, repl);
1217}
1218
1219struct string_list str_tab;
1220
1221const char *
1222strtab(unsigned int ii) {
1223 if (ii > 65536)
1224 return NULL;
1225 if (ii > str_tab.size) {
1226 unsigned int old_size = str_tab.size;
1227 while (ii >= str_tab.size)
1228 str_tab.size <<= 1;
1229 str_tab.list = realloc(str_tab.list, str_tab.size*sizeof(str_tab.list[0]));
1230 memset(str_tab.list+old_size, 0, (str_tab.size-old_size)*sizeof(str_tab.list[0]));
1231 }
1232 if (!str_tab.list[ii]) {
1233 str_tab.list[ii] = malloc(12);
1234 sprintf(str_tab.list[ii], "%u", ii);
1235 }
1236 return str_tab.list[ii];
1237}
1238
1239void
1240tools_init(void)
1241{
1242 unsigned int upr, lwr;
1243 for (lwr=0; lwr<256; ++lwr)
1244 tolower(lwr) = lwr;
1245 for (upr='A', lwr='a'; lwr <= 'z'; ++upr, ++lwr)
1246 tolower(upr) = lwr;
1247#ifdef WITH_PROTOCOL_P10
1248 for (upr='[', lwr='{'; lwr <= '~'; ++upr, ++lwr)
1249 tolower(upr) = lwr;
1250 for (upr=0xc0, lwr=0xe0; lwr <= 0xf6; ++upr, ++lwr)
1251 tolower(upr) = lwr;
1252 for (upr=0xd8, lwr=0xf8; lwr <= 0xfe; ++upr, ++lwr)
1253 tolower(upr) = lwr;
1254#endif
1255 str_tab.size = 1001;
1256 str_tab.list = calloc(str_tab.size, sizeof(str_tab.list[0]));
1257}
1258
1259void
1260tools_cleanup(void)
1261{
1262 unsigned int ii;
1263 for (ii=0; ii<str_tab.size; ++ii)
1264 free(str_tab.list[ii]);
1265 free(str_tab.list);
1266}
2aef5f4b 1267
1268/* mysep() is my answer to the strtok/strsep
1269 * issue. strsep is nice but doesn't skip
1270 * multiple dilimiters, which can really
1271 * offset tokens and cause huge corruption
1272 * so this function will use strsep but
31f23f13 1273 * act like strtok in that sense.
2aef5f4b 1274 */
1275char *mysep(char **sepstr, char *delim)
1276{
1277 static char *retstr;
1278
1279 if(!*sepstr || !**sepstr)
1280 return(NULL);
1281
1282 do
1283 {
1284 retstr = strsep(sepstr, delim);
1285 }while (retstr && !(*retstr));
1286
1287 return(retstr);
1288}
1289
c9bf23fe 1290/* Mallocing snprintf *
1291 *
1292 * If it overruns size, it will simply be safely truncated.
1293 */
1294char *
1295x3_msnprintf(const int size, const char *format, ...)
1296{
1297 va_list ap;
1298 char* buff = calloc(sizeof(char *), size+1);
1299
1300 va_start(ap, format);
1301 vsnprintf(buff, size, format, ap);
1302 va_end(ap);
1303 buff = realloc(buff, strlen(buff) + 1);
1304 return buff;
1305}
1306
eb5d6b73 1307char *time2str(time_t thetime)
1308{
1309 char *buf, *tmp;
1310
1311 buf = ctime(&thetime);
1312 tmp = (char *)strchr(buf, '\n');
1313 *tmp = '\0';
1314 return(buf);
1315}
1316
63665495 1317char* x3_strtok(char **save, char *str, char *fs)
1318{
1319 char *pos = *save; /* keep last position across calls */
1320 char *tmp;
1321
1322 if (str)
1323 pos = str; /* new string scan */
1324
1325 while (pos && *pos && strchr(fs, *pos) != NULL)
1326 pos++; /* skip leading separators */
1327
1328 if (!pos || !*pos)
1329 return (pos = *save = NULL); /* string contains only sep's */
1330
1331 tmp = pos; /* now, keep position of the token */
1332
1333 while (*pos && strchr(fs, *pos) == NULL)
1334 pos++; /* skip content of the token */
1335
1336 if (*pos)
1337 *pos++ = '\0'; /* remove first sep after the token */
1338 else
1339 pos = NULL; /* end of string */
1340
1341 *save = pos;
1342 return (tmp);
1343}
1344
4c26ef3e 1345int valid_email(const char *email)
1346{
1347 unsigned int i;
1348 for (i=0;i<strlen(email);i++)
1349 {
1350 if(!isalnum(email[i]) &&
1351 email[i] != '.' &&
1352 email[i] != '@' &&
1353 email[i] != '-' &&
1354 email[i] != '+' &&
1355 email[i] != '_' )
1356 return false;
1357 }
1358 if(strchr(email, '@') == NULL)
1359 return false;
1360 return true;
1361}
1362
668dc38e 1363/*
1364 * Create a string of form "foo!bar@fubar" given foo, bar and fubar
1365 * as the parameters. If NULL, they become "*".
1366 */
1367#define NUH_BUFSIZE (NICKLEN + USERLEN + HOSTLEN + 10)
1368static char *make_nick_user_host(char *namebuf, const char *nick,
1369 const char *name, const char *host)
1370{
1371 snprintf(namebuf, NUH_BUFSIZE, "%s!%s@%s", nick, name, host);
1372 return namebuf;
1373}
1374
1375/*
1376 * pretty_mask
1377 *
1378 * by Carlo Wood (Run), 05 Oct 1998.
1379 *
1380 * Canonify a mask.
1381 *
1382 * When the nick is longer then NICKLEN, it is cut off (its an error of course).
1383 * When the user name or host name are too long (USERLEN and HOSTLEN
1384 * respectively) then they are cut off at the start with a '*'.
1385 *
1386 * The following transformations are made:
1387 *
1388 * 1) xxx -> nick!*@*
1389 * 2) xxx.xxx -> *!*@host
1390 * 3) xxx!yyy -> nick!user@*
1391 * 4) xxx@yyy -> *!user@host
1392 * 5) xxx!yyy@zzz -> nick!user@host
1393 */
1394char *pretty_mask(char *mask)
1395{
1396 static char star[2] = { '*', 0 };
1397 static char retmask[NUH_BUFSIZE] = "";
1398 char *last_dot = NULL;
1399 char *ptr = NULL;
1400
1401 /* Case 1: default */
1402 char *nick = mask;
1403 char *user = star;
1404 char *host = star;
1405
1406 /* Do a _single_ pass through the characters of the mask: */
1407 for (ptr = mask; *ptr; ++ptr)
1408 {
1409 if (*ptr == '!')
1410 {
1411 /* Case 3 or 5: Found first '!' (without finding a '@' yet) */
1412 user = ++ptr;
1413 host = star;
1414 }
1415 else if (*ptr == '@')
1416 {
1417 /* Case 4: Found last '@' (without finding a '!' yet) */
1418 nick = star;
1419 user = mask;
1420 host = ++ptr;
1421 }
1422 else if (*ptr == '.')
1423 {
1424 /* Case 2: Found last '.' (without finding a '!' or '@' yet) */
1425 last_dot = ptr;
1426 continue;
1427 }
1428 else
1429 continue;
1430 for (; *ptr; ++ptr)
1431 {
1432 if (*ptr == '@')
1433 {
1434 /* Case 4 or 5: Found last '@' */
1435 host = ptr + 1;
1436 }
1437 }
1438 break;
1439 }
1440 if (user == star && last_dot)
1441 {
1442 /* Case 2: */
1443 nick = star;
1444 user = star;
1445 host = mask;
1446 }
1447 /* Check lengths */
1448 if (nick != star)
1449 {
1450 char *nick_end = (user != star) ? user - 1 : ptr;
1451 if (nick_end - nick > NICKLEN)
1452 nick[NICKLEN] = 0;
1453 *nick_end = 0;
1454 }
1455 if (user != star)
1456 {
1457 char *user_end = (host != star) ? host - 1 : ptr;
1458 if (user_end - user > USERLEN)
1459 {
1460 user = user_end - USERLEN;
1461 *user = '*';
1462 }
1463 *user_end = 0;
1464 }
1465 if (host != star && ptr - host > HOSTLEN)
1466 {
1467 host = ptr - HOSTLEN;
1468 *host = '*';
1469 }
1470 return make_nick_user_host(retmask, nick, user, host);
1471}
c9bf23fe 1472
7a278540 1473int str_is_number(const char *str)
c9bf23fe 1474{
1475 char *ptr;
1476 int ret = false;
7a278540 1477 for(ptr = (char *)str;*ptr;ptr++) {
c9bf23fe 1478 if((*ptr >= '0' && *ptr <= '9') || *ptr == '-')
1479 ret = true;
1480 else
1481 return false;
1482 }
1483 return ret;
1484}