/* tools.c - miscellaneous utility functions
* Copyright 2000-2004 srvx Development Team
*
- * This file is part of srvx.
+ * This file is part of x3.
*
- * srvx is free software; you can redistribute it and/or modify
+ * x3 is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 52,53,54,55,56,57,58,59,60,61, 0, 0, 0, 0, 0, 0,
+ 52,53,54,55,56,57,58,59,60,61, 0, 0, 0, 0, 0, 0,
0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,
15,16,17,18,19,20,21,22,23,24,25,62, 0,63, 0, 0,
0,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,
41,42,43,44,45,46,47,48,49,50,51, 0, 0, 0, 0, 0
};
+static const unsigned char ctype[256] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0,
+ 0,10,11,12,13,14,15, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0,10,11,12,13,14,15, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+};
+
unsigned long int
base64toint(const char* s, int count)
{
return buf;
}
+unsigned int
+irc_ntop(char *output, unsigned int out_size, const irc_in_addr_t *addr)
+{
+ static const char hexdigits[] = "0123456789abcdef";
+ unsigned int pos;
+
+ assert(output);
+ assert(addr);
+
+ if (irc_in_addr_is_ipv4(*addr)) {
+ unsigned int ip4;
+
+ ip4 = (ntohs(addr->in6[6]) << 16) | ntohs(addr->in6[7]);
+ pos = snprintf(output, out_size, "%u.%u.%u.%u", (ip4 >> 24), (ip4 >> 16) & 255, (ip4 >> 8) & 255, ip4 & 255);
+ } else {
+ unsigned int part, max_start, max_zeros, curr_zeros, ii;
+
+ /* Find longest run of zeros. */
+ for (max_start = max_zeros = curr_zeros = ii = 0; ii < 8; ++ii) {
+ if (!addr->in6[ii])
+ curr_zeros++;
+ else if (curr_zeros > max_zeros) {
+ max_start = ii - curr_zeros;
+ max_zeros = curr_zeros;
+ curr_zeros = 0;
+ }
+ }
+ if (curr_zeros > max_zeros) {
+ max_start = ii - curr_zeros;
+ max_zeros = curr_zeros;
+ }
+
+ /* Print out address. */
+#define APPEND(CH) do { if (pos < out_size) output[pos] = (CH); pos++; } while (0)
+ for (pos = 0, ii = 0; ii < 8; ++ii) {
+ if ((max_zeros > 0) && (ii == max_start)) {
+ if (ii == 0)
+ APPEND(':');
+ APPEND(':');
+ ii += max_zeros - 1;
+ continue;
+ }
+ part = ntohs(addr->in6[ii]);
+ if (part >= 0x1000)
+ APPEND(hexdigits[part >> 12]);
+ if (part >= 0x100)
+ APPEND(hexdigits[(part >> 8) & 15]);
+ if (part >= 0x10)
+ APPEND(hexdigits[(part >> 4) & 15]);
+ APPEND(hexdigits[part & 15]);
+ if (ii < 7)
+ APPEND(':');
+ }
+#undef APPEND
+ output[pos < out_size ? pos : out_size - 1] = '\0';
+ }
+
+ return pos;
+}
+
+unsigned int
+irc_ntop_mask(char *output, unsigned int out_size, const irc_in_addr_t *addr, unsigned char bits)
+{
+ char base_addr[IRC_NTOP_MAX_SIZE];
+ int len;
+
+ if (bits >= 128)
+ return irc_ntop(output, out_size, addr);
+ if (!irc_ntop(base_addr, sizeof(base_addr), addr))
+ return 0;
+ len = snprintf(output, out_size, "%s/%d", base_addr, bits);
+ if ((unsigned int)len >= out_size)
+ return 0;
+ return len;
+}
+
+static unsigned int
+irc_pton_ip4(const char *input, unsigned char *pbits, uint32_t *output)
+{
+ unsigned int dots = 0, pos = 0, part = 0, ip = 0, bits = 32;
+
+ /* Intentionally no support for bizarre IPv4 formats (plain
+ * integers, octal or hex components) -- only vanilla dotted
+ * decimal quads, optionally with trailing /nn.
+ */
+ if (input[0] == '.')
+ return 0;
+ while (1) switch (input[pos]) {
+ default:
+ if (dots < 3)
+ return 0;
+ out:
+ ip |= part << (24 - 8 * dots++);
+ *output = htonl(ip);
+ if (pbits)
+ *pbits = bits;
+ return pos;
+ case '.':
+ if (input[++pos] == '.')
+ return 0;
+ ip |= part << (24 - 8 * dots++);
+ part = 0;
+ if (input[pos] == '*') {
+ while (input[++pos] == '*') ;
+ if (input[pos] != '\0')
+ return 0;
+ if (pbits)
+ *pbits = dots * 8;
+ *output = htonl(ip);
+ return pos;
+ }
+ break;
+ case '/':
+ if (!pbits || !isdigit(input[pos + 1]))
+ return 0;
+ for (bits = 0; isdigit(input[++pos]); )
+ bits = bits * 10 + input[pos] - '0';
+ if (bits > 32)
+ return 0;
+ goto out;
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ part = part * 10 + input[pos++] - '0';
+ if (part > 255)
+ return 0;
+ break;
+ }
+}
+
+unsigned int
+irc_pton(irc_in_addr_t *addr, unsigned char *bits, const char *input)
+{
+ const char *part_start = NULL;
+ char *colon;
+ char *dot;
+ unsigned int part = 0, pos = 0, ii = 0, cpos = 8;
+
+ assert(input);
+ memset(addr, 0, sizeof(*addr));
+ colon = strchr(input, ':');
+ dot = strchr(input, '.');
+
+ if (colon && (!dot || (dot > colon))) {
+ /* Parse IPv6, possibly like ::127.0.0.1.
+ * This is pretty straightforward; the only trick is borrowed
+ * from Paul Vixie (BIND): when it sees a "::" continue as if
+ * it were a single ":", but note where it happened, and fill
+ * with zeros afterwards.
+ */
+ if (input[pos] == ':') {
+ if ((input[pos+1] != ':') || (input[pos+2] == ':'))
+ return 0;
+ cpos = 0;
+ pos += 2;
+ part_start = input + pos;
+ }
+ while (ii < 8) switch (input[pos]) {
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
+ case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
+ part = (part << 4) | (ctype[(unsigned char)input[pos++]] & 15);
+ if (part > 0xffff)
+ return 0;
+ break;
+ case ':':
+ part_start = input + ++pos;
+ if (input[pos] == '.')
+ return 0;
+ addr->in6[ii++] = htons(part);
+ part = 0;
+ if (input[pos] == ':') {
+ if (cpos < 8)
+ return 0;
+ cpos = ii;
+ }
+ break;
+ case '.': {
+ uint32_t ip4;
+ unsigned int len;
+ len = irc_pton_ip4(part_start, bits, &ip4);
+ if (!len || (ii > 6))
+ return 0;
+ memcpy(addr->in6 + ii, &ip4, sizeof(ip4));
+ if (bits)
+ *bits += 96;
+ ii += 2;
+ pos = part_start + len - input;
+ goto finish;
+ }
+ case '/':
+ if (!bits || !isdigit(input[pos + 1]))
+ return 0;
+ addr->in6[ii++] = htons(part);
+ for (part = 0; isdigit(input[++pos]); )
+ part = part * 10 + input[pos] - '0';
+ if (part > 128)
+ return 0;
+ *bits = part;
+ goto finish;
+ case '*':
+ while (input[++pos] == '*') ;
+ if (input[pos] != '\0' || cpos < 8)
+ return 0;
+ if (bits)
+ *bits = ii * 16;
+ return pos;
+ default:
+ addr->in6[ii++] = htons(part);
+ if (cpos == 8 && ii < 8)
+ return 0;
+ if (bits)
+ *bits = 128;
+ goto finish;
+ }
+ finish:
+ /* Shift stuff after "::" up and fill middle with zeros. */
+ if (cpos < 8) {
+ unsigned int jj;
+ for (jj = 0; jj < ii - cpos; jj++)
+ addr->in6[7 - jj] = addr->in6[ii - jj - 1];
+ for (jj = 0; jj < 8 - ii; jj++)
+ addr->in6[cpos + jj] = 0;
+ }
+ } else if (dot) {
+ unsigned int ip4;
+ pos = irc_pton_ip4(input, bits, &ip4);
+ if (pos) {
+ addr->in6[5] = htons(65535);
+ addr->in6[6] = htons(ntohl(ip4) >> 16);
+ addr->in6[7] = htons(ntohl(ip4) & 65535);
+ if (bits)
+ *bits += 96;
+ }
+ } else if (input[0] == '*') {
+ while (input[++pos] == '*') ;
+ if (input[pos] != '\0')
+ return 0;
+ if (bits)
+ *bits = 0;
+ }
+ return pos;
+}
+
+const char *irc_ntoa(const irc_in_addr_t *addr)
+{
+ static char ntoa[IRC_NTOP_MAX_SIZE];
+ irc_ntop(ntoa, sizeof(ntoa), addr);
+ return ntoa;
+}
+
+unsigned int
+irc_check_mask(const irc_in_addr_t *check, const irc_in_addr_t *mask, unsigned char bits)
+{
+ unsigned int ii;
+
+ for (ii = 0; (ii < 8) && (bits > 16); bits -= 16, ++ii)
+ if (check->in6[ii] != mask->in6[ii])
+ return 0;
+ if (ii < 8 && bits > 0
+ && (ntohs(check->in6[ii] ^ mask->in6[ii]) >> (16 - bits)))
+ return 0;
+ return 1;
+}
+
static char irc_tolower[256];
#undef tolower
#define tolower(X) irc_tolower[(unsigned char)(X)]
int
match_ircglob(const char *text, const char *glob)
{
- unsigned int star_p, q_cnt;
- while (1) {
- switch (*glob) {
- case 0:
- return !*text;
- case '\\':
- glob++;
- /* intentionally not tolower(...) so people can force
- * capitalization, or we can overload \ in the future */
- if (*text++ != *glob++)
- return 0;
- break;
- case '*':
- case '?':
- star_p = q_cnt = 0;
- do {
- if (*glob == '*')
- star_p = 1;
- else if (*glob == '?')
- q_cnt++;
- else
- break;
- glob++;
- } while (1);
- while (q_cnt) {
- if (!*text++)
- return 0;
- q_cnt--;
- }
- if (star_p) {
- /* if this is the last glob character, it will match any text */
- if (!*glob)
- return 1;
- /* Thanks to the loop above, we know that the next
- * character is a normal character. So just look for
- * the right character.
- */
- for (; *text; text++) {
- if ((tolower(*text) == tolower(*glob))
- && match_ircglob(text+1, glob+1)) {
- return 1;
- }
- }
- return 0;
- }
- /* if !star_p, fall through to normal character case,
- * first checking to see if ?s carried us to the end */
- if (!*glob && !*text)
+ const char *m = glob, *n = text;
+ const char *m_tmp = glob, *n_tmp = text;
+ int star_p;
+
+ for (;;) switch (*m) {
+ case '\0':
+ if (!*n)
+ return 1;
+ backtrack:
+ if (m_tmp == glob)
+ return 0;
+ m = m_tmp;
+ n = ++n_tmp;
+ break;
+ case '\\':
+ m++;
+ /* allow escaping to force capitalization */
+ if (*m++ != *n++)
+ goto backtrack;
+ break;
+ case '*': case '?':
+ for (star_p = 0; ; m++) {
+ if (*m == '*')
+ star_p = 1;
+ else if (*m == '?') {
+ if (!*n++)
+ goto backtrack;
+ } else break;
+ }
+ if (star_p) {
+ if (!*m)
return 1;
- default:
- if (!*text)
- return 0;
- while (*text && *glob && *glob != '*' && *glob != '?' && *glob != '\\') {
- if (tolower(*text++) != tolower(*glob++))
+ else if (*m == '\\') {
+ m_tmp = ++m;
+ if (!*m)
return 0;
- }
- }
+ for (n_tmp = n; *n && *n != *m; n++) ;
+ } else {
+ m_tmp = m;
+ for (n_tmp = n; *n && tolower(*n) != tolower(*m); n++) ;
+ }
+ }
+ /* and fall through */
+ default:
+ if (!*n)
+ return *m == '\0';
+ if (tolower(*m) != tolower(*n))
+ goto backtrack;
+ m++;
+ n++;
+ break;
}
}
extern const char *hidden_host_suffix;
-int user_matches_glob(struct userNode *user, const char *orig_glob, int include_nick)
+/* Prevent *@* *@** *@*a* type masks, while allowing anything else. This is the best way iv found to detect
+ * a global matching mask; if it matches this string, it'll match almost anything :) */
+int is_overmask(char *mask)
{
- /* A new glob function, the old one had many false positives.
- * look at IsSetHost(user) etc and include_nick, build a nick!user@host string
- * from user and save it in a variable. Compare with match_ircglob() and
- * return the results. Match any of the possible user@host combos (ugh) -Rubin
- */
- char *matchstr_user;
- char *matchstr_host;
- char *matchstr_full = NULL;
-
- if(IsSetHost(user)) /* S: line sethosts */
- {
- /* Grab host and user from sethost instead of real host */
- char *buff;
- buff = alloca(strlen(user->sethost) + 1);
- strcpy(buff, user->sethost);
- matchstr_user = mysep(&buff, "@");
- matchstr_host = mysep(&buff, "@");
-
- matchstr_full = alloca(strlen(user->nick) + strlen(matchstr_user) + strlen(matchstr_host) + 4);
- if(include_nick)
- sprintf(matchstr_full, "%s!%s@%s", user->nick, matchstr_user, matchstr_host);
- else
- sprintf(matchstr_full, "%s@%s", matchstr_user, matchstr_host);
- if(match_ircglob(matchstr_full, orig_glob))
- return(1);
- }
- else if(IsFakeHost(user)) /* Fakehost */
- {
- matchstr_full = alloca(strlen(user->nick) + strlen(user->ident) + strlen(user->fakehost) + 4);
- if(include_nick)
- sprintf(matchstr_full, "%s!%s@%s", user->nick, user->ident, user->fakehost);
- else
- sprintf(matchstr_full, "%s@%s", user->ident, user->fakehost);
- if(match_ircglob(matchstr_full, orig_glob))
- return(1);
- }
- else if(hidden_host_suffix && user->handle_info) /* name.users.network.org host */
- {
- matchstr_host = alloca(strlen(user->handle_info->handle) + strlen(hidden_host_suffix) + 2);
- sprintf(matchstr_host, "%s.%s", user->handle_info->handle, hidden_host_suffix);
- matchstr_user = user->ident;
-
- matchstr_full = alloca(strlen(user->nick) + strlen(user->ident) + strlen(matchstr_host) + 4);
- if(include_nick)
- sprintf(matchstr_full, "%s!%s@%s", user->nick, user->ident, matchstr_host);
- else
- sprintf(matchstr_full, "%s@%s", user->ident, matchstr_host);
- if(match_ircglob(matchstr_full, orig_glob))
- return(1);
- }
-
- /* Check normal hostname */
- matchstr_full = alloca(strlen(user->nick) + strlen(user->ident) + strlen(user->hostname) + 4);
- if(include_nick)
- sprintf(matchstr_full, "%s!%s@%s", user->nick, user->ident, user->hostname);
- else
- sprintf(matchstr_full, "%s@%s", user->ident, user->hostname);
- if(match_ircglob(matchstr_full, orig_glob))
- return(1);
-
- /* Check IP hostname (could skip this if same as above?)*/
- matchstr_host = inet_ntoa(user->ip);
- matchstr_full = alloca(strlen(user->nick) + strlen(user->ident) + strlen(matchstr_host) + 4);
- if(include_nick)
- sprintf(matchstr_full, "%s!%s@%s", user->nick, user->ident, matchstr_host);
- else
- sprintf(matchstr_full, "%s@%s", user->ident, matchstr_host);
- if(match_ircglob(matchstr_full, orig_glob))
- return(1);
-
- return(0); /* Didnt match anything */
+ return(match_ircglob("abcdefghijklmnopqrstuv!frcmbghilnrtoasde@apdic.yfa.dsfsdaffsdasfdasfd.abcdefghijklmnopqrstuvwxyz.asdfasfdfsdsfdasfda.ydfbe", mask));
}
int
-user_matches_glob_broken(struct userNode *user, const char *orig_glob, int include_nick)
+user_matches_glob(struct userNode *user, const char *orig_glob, int flags)
{
char *glob, *marker;
- char *setident = NULL, *sethostname = NULL; // sethost - reed/apples
/* Make a writable copy of the glob */
glob = alloca(strlen(orig_glob)+1);
strcpy(glob, orig_glob);
/* Check the nick, if it's present */
- if (include_nick) {
+ if (flags & MATCH_USENICK) {
if (!(marker = strchr(glob, '!'))) {
- log_module(MAIN_LOG, LOG_ERROR, "user_matches_glob(\"%s\", \"%s\", %d) called, and glob doesn't include a '!'", user->nick, orig_glob, include_nick);
+ log_module(MAIN_LOG, LOG_ERROR, "user_matches_glob(\"%s\", \"%s\", %d) called, and glob doesn't include a '!'", user->nick, orig_glob, flags);
return 0;
}
*marker = 0;
}
/* Check the ident */
if (!(marker = strchr(glob, '@'))) {
- log_module(MAIN_LOG, LOG_ERROR, "user_matches_glob(\"%s\", \"%s\", %d) called, and glob doesn't include an '@'", user->nick, orig_glob, include_nick);
+ log_module(MAIN_LOG, LOG_ERROR, "user_matches_glob(\"%s\", \"%s\", %d) called, and glob doesn't include an '@'", user->nick, orig_glob, flags);
return 0;
}
*marker = 0;
-
- // sethost - reed/apples
- if (IsSetHost(user)) {
- setident = alloca(strcspn(user->sethost, "@")+2);
- safestrncpy(setident, user->sethost, strcspn(user->sethost, "@")+1);
- sethostname = strchr(user->sethost, '@') + 1;
- }
-
- if (!match_ircglob(user->ident, glob) && (IsSetHost(user) && !match_ircglob(setident, glob)))
- return 0;
+ if (!match_ircglob(user->ident, glob))
+ return 0;
glob = marker + 1;
- /* Now check the host part */
- if (isdigit(*glob) && !glob[strspn(glob, "0123456789./*?")]) {
- /* Looks like an IP-based mask */
- return match_ircglob(inet_ntoa(user->ip), glob);
- } else {
- /* The host part of the mask isn't IP-based */
- if (IsSetHost(user) && match_ircglob(sethostname, glob))
- return 1;
- if (IsFakeHost(user) && match_ircglob(user->fakehost, glob))
+ /* Check for a fakehost match. */
+ if (IsFakeHost(user) && match_ircglob(user->fakehost, glob))
+ return 1;
+
+ /* Check for a sethost (S:lines) */
+ if (IsSetHost(user) && match_ircglob(user->sethost, glob))
+ return 1;
+
+ /* Check for an account match. */
+ if (hidden_host_suffix && user->handle_info) {
+ char hidden_host[HOSTLEN+1];
+ snprintf(hidden_host, sizeof(hidden_host), "%s.%s", user->handle_info->handle, hidden_host_suffix);
+ if (match_ircglob(hidden_host, glob))
return 1;
- if (hidden_host_suffix && user->handle_info) {
- char hidden_host[HOSTLEN+1];
- snprintf(hidden_host, sizeof(hidden_host), "%s.%s", user->handle_info->handle, hidden_host_suffix);
- if (match_ircglob(hidden_host, glob))
- return 1;
- }
- return match_ircglob(user->hostname, glob);
}
+ /* If only matching the visible hostnames, bail early. */
+ if ((flags & MATCH_VISIBLE) && IsHiddenHost(user)
+ && (IsFakeHost(user) || (hidden_host_suffix && user->handle_info)))
+ return 0;
+ /* If it might be an IP glob, test that. */
+ if (!glob[strspn(glob, "0123456789./*?")]
+ && match_ircglob(irc_ntoa(&user->ip), glob))
+ return 1;
+ /* None of the above; could only be a hostname match. */
+ return match_ircglob(user->hostname, glob);
}
int
return 0;
if (!*text)
return 0;
- while (*text && (isalnum((char)*text) || strchr(".-?*", *text)))
+ while (*text && (isalnum((char)*text) || strchr(".-?*:", *text)))
+ text++;
+ return !*text;
+}
+
+int
+is_shun(const char *text)
+{
+ if (*text == '@')
+ return 0;
+ text += strcspn(text, "@!% \t\r\n");
+ if (*text++ != '@')
+ return 0;
+ if (!*text)
+ return 0;
+ while (*text && (isalnum((char)*text) || strchr(".-?*:", *text)))
text++;
return !*text;
}
*ident = start;
start = ++text;
- while (*text && (isalnum((char)*text) || strchr(".-?*", *text)))
+ while (*text && (isalnum((char)*text) || strchr(".-?*:", *text)))
text++;
if (host)
*host = start;
{
switch (type) {
case 'y': return 365*24*60*60;
- case 'M': return 31*24*60*60;
+ case 'M': return 30*24*60*60;
case 'w': return 7*24*60*60;
case 'd': return 24*60*60;
case 'h': return 60*60;
}
}
+/* This function is not entirely accurate as it does not take into account leap units
+ * or varying months. TODO: use proper dateadd functions to calculate real seconds
+ * from now for the units (eg 1M should be give us seconds till todays date next month)
+ */
unsigned long
ParseInterval(const char *interval)
{
return accum + partial;
}
-int
-parse_ipmask(const char *str, struct in_addr *addr, unsigned long *mask)
-{
- int accum, pos;
- unsigned long t_a, t_m;
-
- t_a = t_m = pos = 0;
- if (addr)
- addr->s_addr = htonl(t_a);
- if (mask)
- *mask = t_m;
- while (*str) {
- if (!isdigit(*str))
- return 0;
- accum = 0;
- do {
- accum = (accum * 10) + *str++ - '0';
- } while (isdigit(*str));
- if (accum > 255)
- return 0;
- t_a = (t_a << 8) | accum;
- t_m = (t_m << 8) | 255;
- pos += 8;
- if (*str == '.') {
- str++;
- while (*str == '*') {
- str++;
- if (*str == '.') {
- t_a <<= 8;
- t_m <<= 8;
- pos += 8;
- str++;
- } else if (*str == 0) {
- t_a <<= 32 - pos;
- t_m <<= 32 - pos;
- pos = 32;
- goto out;
- } else
- return 0;
- }
- } else if (*str == '/') {
- int start = pos;
- accum = 0;
- do {
- accum = (accum * 10) + *str++ - '0';
- } while (isdigit(*str));
- while (pos < start+accum && pos < 32) {
- t_a = (t_a << 1) | 0;
- t_m = (t_m << 1) | 1;
- pos++;
- }
- if (pos != start+accum)
- return 0;
- } else if (*str == 0)
- break;
- else
- return 0;
- }
-out:
- if (pos != 32)
- return 0;
- if (addr)
- addr->s_addr = htonl(t_a);
- if (mask)
- *mask = t_m;
- return 1;
-}
-
char *
unsplit_string(char *set[], unsigned int max, char *dest)
{
if (words++ == 1) {
msg = language_find_message(lang, "MSG_AND");
- pos += sprintf(output + pos, " %s ", msg);
+ pos += sprintf(output + pos, "%s ", msg);
}
if (count == 1)
msg = language_find_message(lang, unit[type].msg_single);
else
msg = language_find_message(lang, unit[type].msg_plural);
- pos += sprintf(output + pos, "%d %s", count, msg);
+ pos += sprintf(output + pos, "%d%s", count, msg);
}
output[pos] = 0;
* multiple dilimiters, which can really
* offset tokens and cause huge corruption
* so this function will use strsep but
- * act like strtok in that sence.
+ * act like strtok in that sense.
*/
char *mysep(char **sepstr, char *delim)
{
return(buf);
}
+char* x3_strtok(char **save, char *str, char *fs)
+{
+ char *pos = *save; /* keep last position across calls */
+ char *tmp;
+
+ if (str)
+ pos = str; /* new string scan */
+
+ while (pos && *pos && strchr(fs, *pos) != NULL)
+ pos++; /* skip leading separators */
+
+ if (!pos || !*pos)
+ return (pos = *save = NULL); /* string contains only sep's */
+
+ tmp = pos; /* now, keep position of the token */
+
+ while (*pos && strchr(fs, *pos) == NULL)
+ pos++; /* skip content of the token */
+
+ if (*pos)
+ *pos++ = '\0'; /* remove first sep after the token */
+ else
+ pos = NULL; /* end of string */
+
+ *save = pos;
+ return (tmp);
+}
+
+int valid_email(const char *email)
+{
+ unsigned int i;
+ for (i=0;i<strlen(email);i++)
+ {
+ if(!isalnum(email[i]) &&
+ email[i] != '.' &&
+ email[i] != '@' &&
+ email[i] != '-' &&
+ email[i] != '+' &&
+ email[i] != '_' )
+ return false;
+ }
+ if(strchr(email, '@') == NULL)
+ return false;
+ return true;
+}
+