/************************************************************************
* IRC - Internet Relay Chat, common/support.c
* Copyright (C) 1990, 1991 Armin Gruner
*
* This program 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 1, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef lint
static char rcsid[] = "@(#)$Id: support.c,v 1.8 2004/02/27 14:05:22 skold Exp $";
#endif
#include "os.h"
#ifndef CLIENT_COMPILE
# include "s_defines.h"
#else
# include "c_defines.h"
#endif
#define SUPPORT_C
#ifndef CLIENT_COMPILE
# include "s_externs.h"
#else
# include "c_externs.h"
#endif
#undef SUPPORT_C
char *mystrdup(s)
char *s;
{
/* Portable strdup(), contributed by mrg, thanks! -roy */
char *t;
t = (char *) MyMalloc(strlen(s) + 1);
if (t)
return ((char *)strcpy(t, s));
return NULL;
}
#if ! HAVE_STRTOKEN
/*
** strtoken.c -- walk through a string of tokens, using a set
** of separators
** argv 9/90
*/
char *strtoken(save, str, fs)
char **save;
char *str, *fs;
{
char *pos = *save; /* keep last position across calls */
Reg char *tmp;
if (str)
pos = str; /* new string scan */
while (pos && *pos && index(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 && index(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);
}
#endif /* HAVE_STRTOKEN */
#if ! HAVE_STRTOK
/*
** NOT encouraged to use!
*/
char *strtok(str, fs)
char *str, *fs;
{
static char *pos;
return strtoken(&pos, str, fs);
}
#endif /* HAVE_STRTOK */
#if ! HAVE_STRERROR
/*
** strerror - return an appropriate system error string to a given errno
**
** argv 11/90
*/
char *strerror(err_no)
int err_no;
{
static char buff[40];
char *errp;
errp = (err_no > sys_nerr ? (char *)NULL : sys_errlist[err_no]);
if (errp == (char *)NULL)
{
errp = buff;
SPRINTF(errp, "Unknown Error %d", err_no);
}
return errp;
}
#endif /* HAVE_STRERROR */
/**
** myctime()
** This is like standard ctime()-function, but it zaps away
** the newline from the end of that string. Also, it takes
** the time value as parameter, instead of pointer to it.
** Note that it is necessary to copy the string to alternate
** buffer (who knows how ctime() implements it, maybe it statically
** has newline there and never 'refreshes' it -- zapping that
** might break things in other places...)
**
**/
char *myctime(value)
time_t value;
{
static char buf[28];
Reg char *p;
(void)strcpy(buf, ctime(&value));
if ((p = (char *)index(buf, '\n')) != NULL)
*p = '\0';
return buf;
}
/*
** mybasename()
** removes path from a filename
*/
char *
mybasename(path)
char *path;
{
char *lastslash;
if (lastslash = rindex(path, '/'))
return lastslash + 1;
return path;
}
#ifdef INET6
/*
* inetntop: return the : notation of a given IPv6 internet number.
* or the dotted-decimal notation for IPv4
* make sure the compressed representation (rfc 1884) isn't used.
*/
char *inetntop(af, in, out, the_size)
int af;
const void *in;
char *out;
size_t the_size;
{
static char local_dummy[MYDUMMY_SIZE];
if (!inet_ntop(af, in, local_dummy, the_size))
{
/* good that every function calling this one
* checks the return value ... NOT */
return NULL;
}
/* quick and dirty hack to give ipv4 just ipv4 instead of
* ::ffff:ipv4 - Q */
if (af == AF_INET6 && IN6_IS_ADDR_V4MAPPED((struct in6_addr *)in))
{
char *p;
if (!(p = strstr(local_dummy, ":ffff:")) &&
!(p = strstr(local_dummy, ":FFFF:")))
{
return NULL; /* crash and burn */
}
strcpy(out, p + 6);
return out;
}
if (strstr(local_dummy, "::"))
{
char cnt = 0, *cp = local_dummy, *op = out;
while (*cp)
{
if (*cp == ':')
cnt += 1;
if (*cp++ == '.')
{
cnt += 1;
break;
}
}
cp = local_dummy;
while (*cp)
{
*op++ = *cp++;
if (*(cp-1) == ':' && *cp == ':')
{
if ((cp-1) == local_dummy)
{
op--;
*op++ = '0';
*op++ = ':';
}
*op++ = '0';
while (cnt++ < 7)
{
*op++ = ':';
*op++ = '0';
}
}
}
if (*(op-1)==':') *op++ = '0';
*op = '\0';
#ifndef CLIENT_COMPILE
Debug((DEBUG_DNS,"Expanding `%s' -> `%s'", local_dummy,
out));
#endif
}
else
bcopy(local_dummy, out, 64);
return out;
}
/* inetpton(af, src, dst)
**
** This is a wrapper for inet_pton(), so we can use ipv4 addresses with an
** af of AF_INET6, and that it gets converted to ipv4 mapped ipv6.
*/
int inetpton(int af, const char *src, void *dst)
{
int i;
/* an empty string should listen to all */
if (af == AF_INET6 && *src && !strchr(src, ':'))
{
i = inet_pton(AF_INET, src, dst);
/* ugly hack */
memcpy(dst + 12, dst, 4);
memset(dst, 0, 10);
memset(dst + 10, 0xff, 2);
return i;
}
return inet_pton(af, src, dst);
}
#endif
#if ! HAVE_INET_NTOA
/*
** inetntoa -- changed name to remove collision possibility and
** so behaviour is gaurunteed to take a pointer arg.
** -avalon 23/11/92
** inet_ntoa -- returned the dotted notation of a given
** internet number (some ULTRIX don't have this)
** argv 11/90).
** inet_ntoa -- its broken on some Ultrix/Dynix too. -avalon
*/
char *inetntoa(in)
char *in;
{
static char buf[16];
Reg u_char *s = (u_char *)in;
Reg int a,b,c,d;
a = (int)*s++;
b = (int)*s++;
c = (int)*s++;
d = (int)*s;
(void)sprintf(buf, "%d.%d.%d.%d", a,b,c,d );
return buf;
}
#endif
#if ! HAVE_INET_NETOF
/*
** inet_netof -- return the net portion of an internet number
** argv 11/90
*/
int inetnetof(in)
struct in_addr in;
{
register u_long i = ntohl(in.s_addr);
if (IN_CLASSA(i))
return (((i)&IN_CLASSA_NET) >> IN_CLASSA_NSHIFT);
else if (IN_CLASSB(i))
return (((i)&IN_CLASSB_NET) >> IN_CLASSB_NSHIFT);
else
return (((i)&IN_CLASSC_NET) >> IN_CLASSC_NSHIFT);
}
#endif
#if ! HAVE_INET_ADDR
# ifndef INADDR_NONE
# define INADDR_NONE 0xffffffff
# endif
/*
* Ascii internet address interpretation routine.
* The value returned is in network order.
*/
u_long
inetaddr(cp)
register const char *cp;
{
struct in_addr val;
if (inetaton(cp, &val))
return (val.s_addr);
return (INADDR_NONE);
}
#endif
#if ! HAVE_INET_ATON
/*
* Check whether "cp" is a valid ascii representation
* of an Internet address and convert to a binary address.
* Returns 1 if the address is valid, 0 if not.
* This replaces inet_addr, the return value from which
* cannot distinguish between failure and a local broadcast address.
*/
int
inetaton(cp, addr)
register const char *cp;
struct in_addr *addr;
{
register u_long val;
register int base, n;
register char c;
u_int parts[4];
register u_int *pp = parts;
c = *cp;
for (;;) {
/*
* Collect number up to ``.''.
* Values are specified as for C:
* 0x=hex, 0=octal, isdigit=decimal.
*/
if (!isdigit(c))
return (0);
val = 0; base = 10;
if (c == '0') {
c = *++cp;
if (c == 'x' || c == 'X')
base = 16, c = *++cp;
else
base = 8;
}
for (;;) {
if (isascii(c) && isdigit(c)) {
val = (val * base) + (c - '0');
c = *++cp;
} else if (base == 16 && isascii(c) && isxdigit(c)) {
val = (val << 4) |
(c + 10 - (islower(c) ? 'a' : 'A'));
c = *++cp;
} else
break;
}
if (c == '.') {
/*
* Internet format:
* a.b.c.d
* a.b.c (with c treated as 16 bits)
* a.b (with b treated as 24 bits)
*/
if (pp >= parts + 3)
return (0);
*pp++ = val;
c = *++cp;
} else
break;
}
/*
* Check for trailing characters.
*/
if (c != '\0' && (!isascii(c) || !isspace(c)))
return (0);
/*
* Concoct the address according to
* the number of parts specified.
*/
n = pp - parts + 1;
switch (n) {
case 0:
return (0); /* initial nondigit */
case 1: /* a -- 32 bits */
break;
case 2: /* a.b -- 8.24 bits */
if (val > 0xffffff)
return (0);
val |= parts[0] << 24;
break;
case 3: /* a.b.c -- 8.8.16 bits */
if (val > 0xffff)
return (0);
val |= (parts[0] << 24) | (parts[1] << 16);
break;
case 4: /* a.b.c.d -- 8.8.8.8 bits */
if (val > 0xff)
return (0);
val |= (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8);
break;
}
if (addr)
addr->s_addr = htonl(val);
return (1);
}
#endif
#if defined(DEBUGMODE) && !defined(CLIENT_COMPILE)
void dumpcore(msg, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11)
char *msg, *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8, *p9, *p10, *p11;
{
static time_t lastd = 0;
static int dumps = 0;
char corename[12];
time_t now;
int p;
now = time(NULL);
if (!lastd)
lastd = now;
else if (now - lastd < 60 && dumps > 2)
(void)s_die(0);
if (now - lastd > 60)
{
lastd = now;
dumps = 1;
}
else
dumps++;
p = getpid();
if (fork()>0) {
kill(p, 3);
kill(p, 9);
}
write_pidfile();
SPRINTF(corename, "core.%d", p);
(void)rename("core", corename);
Debug((DEBUG_FATAL, "Dumped core : core.%d", p));
sendto_flag(SCH_ERROR, "Dumped core : core.%d", p);
Debug((DEBUG_FATAL, msg, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10,p11));
sendto_flag(SCH_ERROR, msg, p1, p2, p3, p4, p5, p6, p7, p8,p9,p10,p11);
(void)s_die(0);
}
#endif
#if defined(DEBUGMODE) && !defined(CLIENT_COMPILE) && defined(DO_DEBUG_MALLOC)
static char *marray[100000];
static int mindex = 0;
#define SZ_EX (sizeof(char *) + sizeof(size_t) + 4)
#define SZ_CHST (sizeof(char *) + sizeof(size_t))
#define SZ_CH (sizeof(char *))
#define SZ_ST (sizeof(size_t))
char *MyMalloc(x)
size_t x;
{
register int i;
register char **s;
char *ret;
ret = (char *)malloc(x + (size_t)SZ_EX);
if (!ret)
{
# ifndef CLIENT_COMPILE
outofmemory();
# else
perror("malloc");
exit(-1);
# endif /* CLIENT_COMPILE */
}
bzero(ret, (int)x + SZ_EX);
bcopy((char *)&ret, ret, SZ_CH);
bcopy((char *)&x, ret + SZ_CH, SZ_ST);
bcopy("VAVA", ret + SZ_CHST + (int)x, 4);
Debug((DEBUG_MALLOC, "MyMalloc(%ld) = %#x", x, ret + SZ_CHST));
for(i = 0, s = marray; *s && i < mindex; i++, s++)
;
if (i < 100000)
{
*s = ret;
if (i == mindex)
mindex++;
}
return ret + SZ_CHST;
}
char *MyRealloc(x, y)
char *x;
size_t y;
{
register int l;
register char **s;
char *ret, *cp;
size_t i;
int k;
if (x != NULL)
{
x -= SZ_CHST;
bcopy(x, (char *)&cp, SZ_CH);
bcopy(x + SZ_CH, (char *)&i, SZ_ST);
bcopy(x + (int)i + SZ_CHST, (char *)&k, 4);
if (bcmp((char *)&k, "VAVA", 4) || (x != cp))
dumpcore("MyRealloc %#x %d %d %#x %#x", x, y, i, cp, k);
}
ret = (char *)realloc(x, y + (size_t)SZ_EX);
if (!ret)
{
# ifndef CLIENT_COMPILE
outofmemory();
# else
perror("realloc");
exit(-1);
# endif
}
bcopy((char *)&ret, ret, SZ_CH);
bcopy((char *)&y, ret + SZ_CH, SZ_ST);
bcopy("VAVA", ret + SZ_CHST + (int)y, 4);
Debug((DEBUG_NOTICE, "MyRealloc(%#x,%ld) = %#x", x, y, ret + SZ_CHST));
for(l = 0, s = marray; *s != x && l < mindex; l++, s++)
;
if (l < mindex)
*s = NULL;
else if (l == mindex)
Debug((DEBUG_MALLOC, "%#x !found", x));
for(l = 0, s = marray; *s && l < mindex; l++,s++)
;
if (l < 100000)
{
*s = ret;
if (l == mindex)
mindex++;
}
return ret + SZ_CHST;
}
void MyFree(x)
char *x;
{
size_t i;
char *j;
u_char k[4];
register int l;
register char **s;
if (!x)
return;
x -= SZ_CHST;
bcopy(x, (char *)&j, SZ_CH);
bcopy(x + SZ_CH, (char *)&i, SZ_ST);
bcopy(x + SZ_CHST + (int)i, (char *)k, 4);
if (bcmp((char *)k, "VAVA", 4) || (j != x))
dumpcore("MyFree %#x %ld %#x %#x", x, i, j,
(k[3]<<24) | (k[2]<<16) | (k[1]<<8) | k[0]);
Debug((DEBUG_MALLOC, "MyFree(%#x)",x + SZ_CHST));
#undef free
(void)free(x);
#define free(x) MyFree(x)
for (l = 0, s = marray; *s != x && l < mindex; l++, s++)
;
if (l < mindex)
*s = NULL;
else if (l == mindex)
Debug((DEBUG_MALLOC, "%#x !found", x));
}
#else
char *MyMalloc(x)
size_t x;
{
char *ret = (char *)malloc(x);
if (!ret)
{
# ifndef CLIENT_COMPILE
outofmemory();
# else
perror("malloc");
exit(-1);
# endif
}
return ret;
}
char *MyRealloc(x, y)
char *x;
size_t y;
{
char *ret = (char *)realloc(x, y);
if (!ret)
{
# ifndef CLIENT_COMPILE
outofmemory();
# else
perror("realloc");
exit(-1);
# endif
}
return ret;
}
#endif
/*
** read a string terminated by \r or \n in from a fd
**
** Created: Sat Dec 12 06:29:58 EST 1992 by avalon
** Changed: Thu Jul 3 13:38:58 MSD 2003 by erra
** Returns:
** 0 - EOF
** -1 - error on read
** >0 - number of bytes returned (<=num)
** After opening a fd, it is necessary to init dgets() by calling it as
** dgets(x,y,0);
** to mark the buffer as being empty.
*/
int dgets(fd, buf, num, head, tail)
int fd, num;
char *buf, **head, **tail;
{
register char *s, *t;
register int n, nr;
/*
** Sanity checks.
*/
if (!num)
{
*head = *tail = buf;
**head = '\0';
return 0;
}
if (*head == *tail)
**head = '\0';
dgetsagain:
if (*head > buf)
{
for (nr = *tail - *head, s = *head, t = buf; nr > 0; nr--)
*t++ = *s++;
*tail = t;
*head = buf;
}
/*
** check input buffer for EOL and if present return string.
*/
if (*head < *tail && ((s = index(*head, '\n')) ||
(s = index(*head, '\r'))) && s < *tail)
{
n = MIN(s - *head + 1, num); /* at least 1 byte */
dgetsreturnbuf:
*head += n;
if (*head == *tail)
*head = *tail = buf;
return n;
}
if (*tail - *head >= num) /* dgets buf is big enough */
{
n = num;
goto dgetsreturnbuf;
}
n = num - (*tail - buf) - 1;
nr = read(fd, *tail, n);
if (nr == -1)
{
*head = *tail = buf;
return -1;
}
if (!nr)
{
if (*tail > *head)
{
n = MIN(*tail - *head, num);
goto dgetsreturnbuf;
}
*head = *tail = buf;
return 0;
}
*tail += nr;
**tail = '\0';
for (t = *head; (s = index(t, '\n')); )
{
if ((s > *head) && (s > buf))
{
t = s-1;
for (nr = 0; *t == '\\'; nr++)
t--;
if (nr & 1)
{
t = s+1;
s--;
nr = *tail - t;
while (nr--)
*s++ = *t++;
*tail -= 2;
**tail = '\0';
}
else
s++;
}
else
s++;
t = s;
}
**tail = '\0';
goto dgetsagain;
}
#if ! USE_STDARG
/*
* By Mika
*/
int irc_sprintf(outp, formp,
i0, i1, i2, i3, i4, i5, i6, i7, i8, i9, i10, i11)
char *outp;
char *formp;
char *i0, *i1, *i2, *i3, *i4, *i5, *i6, *i7, *i8, *i9, *i10, *i11;
{
/* rp for Reading, wp for Writing, fp for the Format string */
/* we could hack this if we know the format of the stack */
char *inp[12];
Reg char *rp, *fp, *wp, **pp = inp;
Reg char f;
Reg long myi;
int i;
inp[0] = i0;
inp[1] = i1;
inp[2] = i2;
inp[3] = i3;
inp[4] = i4;
inp[5] = i5;
inp[6] = i6;
inp[7] = i7;
inp[8] = i8;
inp[9] = i9;
inp[10] = i10;
inp[11] = i11;
/*
* just scan the format string and puke out whatever is necessary
* along the way...
*/
for (i = 0, wp = outp, fp = formp; (f = *fp++); )
if (f != '%')
*wp++ = f;
else
switch (*fp++)
{
/* put the most common case at the top */
/* copy a string */
case 's':
for (rp = *pp++; (*wp++ = *rp++); )
;
--wp;
/* get the next parameter */
break;
/*
* reject range for params to this mean that the
* param must be within 100-999 and this +ve int
*/
case 'd':
case 'u':
myi = (long)*pp++;
if ((myi < 100) || (myi > 999))
{
(void)sprintf(outp, formp, i0, i1, i2,
i3, i4, i5, i6, i7, i8,
i9, i10, i11);
return -1;
}
*wp++ = (char)(myi / 100 + (int) '0');
myi %= 100;
*wp++ = (char)(myi / 10 + (int) '0');
myi %= 10;
*wp++ = (char)(myi + (int) '0');
break;
case 'c':
*wp++ = (char)(long)*pp++;
break;
case '%':
*wp++ = '%';
break;
default :
(void)sprintf(outp, formp, i0, i1, i2, i3, i4,
i5, i6, i7, i8, i9, i10, i11);
return -1;
}
*wp = '\0';
return wp - outp;
}
#endif
/*
* Make 'readable' version string.
*/
char *make_version()
{
int ve, re, mi, dv, pl;
char ver[15];
sscanf(PATCHLEVEL, "%2d%2d%2d%2d%2d", &ve, &re, &mi, &dv, &pl);
/* version & revision */
sprintf(ver, "%d.%d", ve, (mi == 99) ? re + 1 : re);
if (mi == 99) mi = -1;
/* minor revision */
sprintf(ver + strlen(ver), ".%d", dv ? mi+1 : mi);
if (dv) /* alpha/beta, note how visual patchlevel is raised above */
sprintf(ver + strlen(ver), "%c%d", DEVLEVEL, dv);
if (pl) /* patchlevel */
sprintf(ver + strlen(ver), "p%d", pl);
return mystrdup(ver);
}
#ifndef CLIENT_COMPILE
#ifdef SEND_ISUPPORT
/*
* Make ISUPPORT string
*/
char *make_isupport()
{
static char isupport[200];
char *p = isupport;
#ifdef SERVER_MAP
strcpy(isupport,"MAP ");
p +=4;
#endif
SPRINTF(p, "PREFIX=(ov)@+ MODES=%d CHANTYPES=#&!+ MAXCHANNELS=%d "
"NICKLEN=%d TOPICLEN=%d KICKLEN=%d NETWORK=%s "
"CHANMODES=beI,k,l,acimnpsrtz", MAXMODEPARAMS,
MAXCHANNELSPERUSER, NICKLEN, TOPICLEN, TOPICLEN,NETWORKNAME);
return isupport;
}
#endif
#endif
#ifndef HAVE_TRUNCATE
/* truncate: set a file to a specified length
* I don't know of any UNIX that doesn't have truncate, but CYGWIN32 beta18
* doesn't have it. -krys
* Replacement version from Dave Miller.
*/
int truncate(path, length)
const char *path;
off_t length;
{
int fd, res;
fd = open(path, O_WRONLY);
if (fd == -1)
return -1;
res = ftruncate(fd, length);
close(fd);
return res;
}
#endif /* HAVE_TRUNCATE */
#if SOLARIS_2_3
/*
* On Solaris 2.3 (SunOS 5.3) systems, gethostbyname() has a bug, it always
* returns null in h->aliases. Workaround: use the undocumented
* _switch_gethostbyname_r(...).
*/
#define HBUFSIZE 4096
struct hostent *solaris_gethostbyname(name)
const char *name;
{
static struct hostent hp;
static char buf[HBUFSIZE];
return _switch_gethostbyname_r(name, &hp, buf, sizeof(buf), &h_errno);
}
#endif /* SOLARIS_2_3 */
#if HAVE_MEMCMP && MEMCMP_BROKEN
/*
* Some OS may have a memcmp that is not 8-bit clean.
*
* Copyright (C) 1991, 1993, 1995 Free Software Foundation, Inc.
* Contributed by Torbjorn Granlund (tege@sics.se).
*
* NOTE: The canonical source of this part of the file is maintained with the
* GNU C Library. Bugs can be reported to bug-glibc@prep.ai.mit.edu.
*/
/* Type to use for aligned memory operations.
This should normally be the biggest type supported by a single load
and store. Must be an unsigned type. */
#define op_t unsigned long int
#define OPSIZ (sizeof(op_t))
/* Threshold value for when to enter the unrolled loops. */
#define OP_T_THRES 16
/* Type to use for unaligned operations. */
typedef unsigned char byte;
#if ! WORDS_BIGENDIAN
#define MERGE(w0, sh_1, w1, sh_2) (((w0) >> (sh_1)) | ((w1) << (sh_2)))
#else
#define MERGE(w0, sh_1, w1, sh_2) (((w0) << (sh_1)) | ((w1) >> (sh_2)))
#endif
#if WORDS_BIGENDIAN
#define CMP_LT_OR_GT(a, b) ((a) > (b) ? 1 : -1)
#else
#define CMP_LT_OR_GT(a, b) memcmp_bytes ((a), (b))
#endif
/* BE VERY CAREFUL IF YOU CHANGE THIS CODE! */
/* The strategy of this memcmp is:
1. Compare bytes until one of the block pointers is aligned.
2. Compare using memcmp_common_alignment or
memcmp_not_common_alignment, regarding the alignment of the other
block after the initial byte operations. The maximum number of
full words (of type op_t) are compared in this way.
3. Compare the few remaining bytes. */
#if ! WORDS_BIGENDIAN
/* memcmp_bytes -- Compare A and B bytewise in the byte order of the machine.
A and B are known to be different.
This is needed only on little-endian machines. */
#ifdef __GNUC__
__inline
#endif
static int
memcmp_bytes (a, b)
op_t a, b;
{
long int srcp1 = (long int) &a;
long int srcp2 = (long int) &b;
op_t a0, b0;
do
{
a0 = ((byte *) srcp1)[0];
b0 = ((byte *) srcp2)[0];
srcp1 += 1;
srcp2 += 1;
}
while (a0 == b0);
return a0 - b0;
}
#endif
/* memcmp_common_alignment -- Compare blocks at SRCP1 and SRCP2 with LEN `op_t'
objects (not LEN bytes!). Both SRCP1 and SRCP2 should be aligned for
memory operations on `op_t's. */
#ifdef __GNUC__
__inline
#endif
static int
memcmp_common_alignment (srcp1, srcp2, len)
long int srcp1;
long int srcp2;
size_t len;
{
op_t a0, a1;
op_t b0, b1;
switch (len % 4)
{
case 2:
a0 = ((op_t *) srcp1)[0];
b0 = ((op_t *) srcp2)[0];
srcp1 -= 2 * OPSIZ;
srcp2 -= 2 * OPSIZ;
len += 2;
goto do1;
case 3:
a1 = ((op_t *) srcp1)[0];
b1 = ((op_t *) srcp2)[0];
srcp1 -= OPSIZ;
srcp2 -= OPSIZ;
len += 1;
goto do2;
case 0:
if (OP_T_THRES <= 3 * OPSIZ && len == 0)
return 0;
a0 = ((op_t *) srcp1)[0];
b0 = ((op_t *) srcp2)[0];
goto do3;
case 1:
a1 = ((op_t *) srcp1)[0];
b1 = ((op_t *) srcp2)[0];
srcp1 += OPSIZ;
srcp2 += OPSIZ;
len -= 1;
if (OP_T_THRES <= 3 * OPSIZ && len == 0)
goto do0;
/* Fall through. */
}
do
{
a0 = ((op_t *) srcp1)[0];
b0 = ((op_t *) srcp2)[0];
if (a1 != b1)
return CMP_LT_OR_GT (a1, b1);
do3:
a1 = ((op_t *) srcp1)[1];
b1 = ((op_t *) srcp2)[1];
if (a0 != b0)
return CMP_LT_OR_GT (a0, b0);
do2:
a0 = ((op_t *) srcp1)[2];
b0 = ((op_t *) srcp2)[2];
if (a1 != b1)
return CMP_LT_OR_GT (a1, b1);
do1:
a1 = ((op_t *) srcp1)[3];
b1 = ((op_t *) srcp2)[3];
if (a0 != b0)
return CMP_LT_OR_GT (a0, b0);
srcp1 += 4 * OPSIZ;
srcp2 += 4 * OPSIZ;
len -= 4;
}
while (len != 0);
/* This is the right position for do0. Please don't move
it into the loop. */
do0:
if (a1 != b1)
return CMP_LT_OR_GT (a1, b1);
return 0;
}
/* memcmp_not_common_alignment -- Compare blocks at SRCP1 and SRCP2 with LEN
`op_t' objects (not LEN bytes!). SRCP2 should be aligned for memory
operations on `op_t', but SRCP1 *should be unaligned*. */
#ifdef __GNUC__
__inline
#endif
static int
memcmp_not_common_alignment (srcp1, srcp2, len)
long int srcp1;
long int srcp2;
size_t len;
{
op_t a0, a1, a2, a3;
op_t b0, b1, b2, b3;
op_t x;
int shl, shr;
/* Calculate how to shift a word read at the memory operation
aligned srcp1 to make it aligned for comparison. */
shl = 8 * (srcp1 % OPSIZ);
shr = 8 * OPSIZ - shl;
/* Make SRCP1 aligned by rounding it down to the beginning of the `op_t'
it points in the middle of. */
srcp1 &= -OPSIZ;
switch (len % 4)
{
case 2:
a1 = ((op_t *) srcp1)[0];
a2 = ((op_t *) srcp1)[1];
b2 = ((op_t *) srcp2)[0];
srcp1 -= 1 * OPSIZ;
srcp2 -= 2 * OPSIZ;
len += 2;
goto do1;
case 3:
a0 = ((op_t *) srcp1)[0];
a1 = ((op_t *) srcp1)[1];
b1 = ((op_t *) srcp2)[0];
srcp2 -= 1 * OPSIZ;
len += 1;
goto do2;
case 0:
if (OP_T_THRES <= 3 * OPSIZ && len == 0)
return 0;
a3 = ((op_t *) srcp1)[0];
a0 = ((op_t *) srcp1)[1];
b0 = ((op_t *) srcp2)[0];
srcp1 += 1 * OPSIZ;
goto do3;
case 1:
a2 = ((op_t *) srcp1)[0];
a3 = ((op_t *) srcp1)[1];
b3 = ((op_t *) srcp2)[0];
srcp1 += 2 * OPSIZ;
srcp2 += 1 * OPSIZ;
len -= 1;
if (OP_T_THRES <= 3 * OPSIZ && len == 0)
goto do0;
/* Fall through. */
}
do
{
a0 = ((op_t *) srcp1)[0];
b0 = ((op_t *) srcp2)[0];
x = MERGE(a2, shl, a3, shr);
if (x != b3)
return CMP_LT_OR_GT (x, b3);
do3:
a1 = ((op_t *) srcp1)[1];
b1 = ((op_t *) srcp2)[1];
x = MERGE(a3, shl, a0, shr);
if (x != b0)
return CMP_LT_OR_GT (x, b0);
do2:
a2 = ((op_t *) srcp1)[2];
b2 = ((op_t *) srcp2)[2];
x = MERGE(a0, shl, a1, shr);
if (x != b1)
return CMP_LT_OR_GT (x, b1);
do1:
a3 = ((op_t *) srcp1)[3];
b3 = ((op_t *) srcp2)[3];
x = MERGE(a1, shl, a2, shr);
if (x != b2)
return CMP_LT_OR_GT (x, b2);
srcp1 += 4 * OPSIZ;
srcp2 += 4 * OPSIZ;
len -= 4;
}
while (len != 0);
/* This is the right position for do0. Please don't move
it into the loop. */
do0:
x = MERGE(a2, shl, a3, shr);
if (x != b3)
return CMP_LT_OR_GT (x, b3);
return 0;
}
int
irc_memcmp (s1, s2, len)
const __ptr_t s1;
const __ptr_t s2;
size_t len;
{
op_t a0;
op_t b0;
long int srcp1 = (long int) s1;
long int srcp2 = (long int) s2;
op_t res;
if (len >= OP_T_THRES)
{
/* There are at least some bytes to compare. No need to test
for LEN == 0 in this alignment loop. */
while (srcp2 % OPSIZ != 0)
{
a0 = ((byte *) srcp1)[0];
b0 = ((byte *) srcp2)[0];
srcp1 += 1;
srcp2 += 1;
res = a0 - b0;
if (res != 0)
return res;
len -= 1;
}
/* SRCP2 is now aligned for memory operations on `op_t'.
SRCP1 alignment determines if we can do a simple,
aligned compare or need to shuffle bits. */
if (srcp1 % OPSIZ == 0)
res = memcmp_common_alignment (srcp1, srcp2, len / OPSIZ);
else
res = memcmp_not_common_alignment (srcp1, srcp2, len / OPSIZ);
if (res != 0)
return res;
/* Number of bytes remaining in the interval [0..OPSIZ-1]. */
srcp1 += len & -OPSIZ;
srcp2 += len & -OPSIZ;
len %= OPSIZ;
}
/* There are just a few bytes to compare. Use byte memory operations. */
while (len != 0)
{
a0 = ((byte *) srcp1)[0];
b0 = ((byte *) srcp2)[0];
srcp1 += 1;
srcp2 += 1;
res = a0 - b0;
if (res != 0)
return res;
len -= 1;
}
return 0;
}
#endif /* HAVE_MEMCMP && MEMCMP_BROKEN */
syntax highlighted by Code2HTML, v. 0.9.1