685 lines
13 KiB
C
685 lines
13 KiB
C
|
/* util.c
|
|||
|
Utilities for the UUCP package.
|
|||
|
|
|||
|
Copyright (C) 1991, 1992 Ian Lance Taylor
|
|||
|
|
|||
|
This file is part of the Taylor UUCP package.
|
|||
|
|
|||
|
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 2 of the
|
|||
|
License, 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.
|
|||
|
|
|||
|
The author of the program may be contacted at ian@airs.com or
|
|||
|
c/o AIRS, P.O. Box 520, Waltham, MA 02254.
|
|||
|
|
|||
|
$Log: util.c,v $
|
|||
|
Revision 1.1.1.1 1993/03/21 09:45:37 cgd
|
|||
|
initial import of 386bsd-0.1 sources
|
|||
|
|
|||
|
Revision 1.11 1992/03/12 19:56:10 ian
|
|||
|
Debugging based on types rather than number
|
|||
|
|
|||
|
Revision 1.10 1992/03/02 04:53:07 ian
|
|||
|
Marc Unangst: added HAVE_SCO_LOCKFILES configuration parameter
|
|||
|
|
|||
|
Revision 1.9 1992/02/23 03:26:51 ian
|
|||
|
Overhaul to use automatic configure shell script
|
|||
|
|
|||
|
Revision 1.8 1992/02/08 03:54:18 ian
|
|||
|
Include <string.h> only in <uucp.h>, added 1992 copyright
|
|||
|
|
|||
|
Revision 1.7 1992/01/19 18:29:05 ian
|
|||
|
Added HAVE_BSEARCH configuration parameter
|
|||
|
|
|||
|
Revision 1.6 1992/01/15 20:40:04 ian
|
|||
|
Mike Park: some systems don't have <limits.h>
|
|||
|
|
|||
|
Revision 1.5 1991/12/28 06:10:50 ian
|
|||
|
Added HAVE_STRCHR and HAVE_INDEX to conf.h
|
|||
|
|
|||
|
Revision 1.4 1991/12/28 03:49:23 ian
|
|||
|
Added HAVE_MEMFNS and HAVE_BFNS; changed uses of memset to bzero
|
|||
|
|
|||
|
Revision 1.3 1991/12/11 19:35:48 ian
|
|||
|
Mark Powell: put in my own version of strtol
|
|||
|
|
|||
|
Revision 1.2 1991/11/21 21:20:41 ian
|
|||
|
Brian Campbell: offer str{n}icmp as an alternative to str{n}casecmp
|
|||
|
|
|||
|
Revision 1.1 1991/09/10 19:40:31 ian
|
|||
|
Initial revision
|
|||
|
|
|||
|
*/
|
|||
|
|
|||
|
#include "uucp.h"
|
|||
|
|
|||
|
#if USE_RCS_ID
|
|||
|
char util_rcsid[] = "$Id: util.c,v 1.1.1.1 1993/03/21 09:45:37 cgd Exp $";
|
|||
|
#endif
|
|||
|
|
|||
|
#include <stdio.h>
|
|||
|
#include <ctype.h>
|
|||
|
#include <errno.h>
|
|||
|
|
|||
|
#if HAVE_LIMITS_H
|
|||
|
#include <limits.h>
|
|||
|
#endif
|
|||
|
|
|||
|
/* Allocate a block of memory without fail. */
|
|||
|
|
|||
|
pointer
|
|||
|
xmalloc (c)
|
|||
|
int c;
|
|||
|
{
|
|||
|
pointer pret;
|
|||
|
|
|||
|
pret = malloc (c);
|
|||
|
if (pret == NULL && c != 0)
|
|||
|
ulog (LOG_FATAL, "Out of memory");
|
|||
|
return pret;
|
|||
|
}
|
|||
|
|
|||
|
/* Realloc a block of memory without fail. Supposedly some versions of
|
|||
|
realloc can't handle a NULL first argument, so we check for that
|
|||
|
here. */
|
|||
|
|
|||
|
pointer
|
|||
|
xrealloc (p, c)
|
|||
|
pointer p;
|
|||
|
int c;
|
|||
|
{
|
|||
|
pointer pret;
|
|||
|
|
|||
|
if (p == NULL)
|
|||
|
return xmalloc (c);
|
|||
|
pret = realloc (p, c);
|
|||
|
if (pret == NULL && c != 0)
|
|||
|
ulog (LOG_FATAL, "Out of memory");
|
|||
|
return pret;
|
|||
|
}
|
|||
|
|
|||
|
/* Some versions of free (like the one in SCO Unix 3.2.2) don't handle
|
|||
|
null pointers correctly, so we go through our own routine. */
|
|||
|
|
|||
|
void
|
|||
|
xfree (p)
|
|||
|
pointer p;
|
|||
|
{
|
|||
|
if (p != NULL)
|
|||
|
free (p);
|
|||
|
}
|
|||
|
|
|||
|
/* Read a string of arbitrary length from a stdio file, returning an
|
|||
|
malloced memory area. */
|
|||
|
|
|||
|
#define CFGETSDEFAULT (63)
|
|||
|
|
|||
|
char *
|
|||
|
zfgets (e, fbackslash)
|
|||
|
FILE *e;
|
|||
|
boolean fbackslash;
|
|||
|
{
|
|||
|
char *zret, *z, *zend;
|
|||
|
int clen;
|
|||
|
int ichar;
|
|||
|
|
|||
|
clen = CFGETSDEFAULT;
|
|||
|
|
|||
|
/* Allocate one extra byte for the '\0'. */
|
|||
|
zret = (char *) xmalloc (clen + 1);
|
|||
|
|
|||
|
z = zret;
|
|||
|
zend = zret + clen;
|
|||
|
while ((ichar = getc (e)) != EOF)
|
|||
|
{
|
|||
|
if (z >= zend)
|
|||
|
{
|
|||
|
char *znew;
|
|||
|
|
|||
|
clen += CFGETSDEFAULT;
|
|||
|
|
|||
|
/* Allocate one extra byte for the '\0'. */
|
|||
|
znew = (char *) xrealloc ((pointer) zret, clen + 1);
|
|||
|
z = znew + (z - zret);
|
|||
|
zret = znew;
|
|||
|
zend = zret + clen;
|
|||
|
}
|
|||
|
|
|||
|
*z++ = (char) ichar;
|
|||
|
if (ichar == '\n')
|
|||
|
{
|
|||
|
if (! fbackslash || z - zret < 2 || z[-2] != '\\')
|
|||
|
break;
|
|||
|
z -= 2;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (z == zret)
|
|||
|
{
|
|||
|
xfree ((pointer) zret);
|
|||
|
return NULL;
|
|||
|
}
|
|||
|
|
|||
|
*z = '\0';
|
|||
|
|
|||
|
return zret;
|
|||
|
}
|
|||
|
|
|||
|
#if ! HAVE_STRDUP
|
|||
|
|
|||
|
/* Duplicate a string in memory. */
|
|||
|
|
|||
|
char *
|
|||
|
strdup (z)
|
|||
|
const char *z;
|
|||
|
{
|
|||
|
char *zret;
|
|||
|
|
|||
|
zret = malloc (strlen (z) + 1);
|
|||
|
if (zret != NULL)
|
|||
|
strcpy (zret, z);
|
|||
|
return zret;
|
|||
|
}
|
|||
|
|
|||
|
#endif /* ! HAVE_STRDUP */
|
|||
|
|
|||
|
/* Duplicate a string in memory with no errors. */
|
|||
|
|
|||
|
char *
|
|||
|
xstrdup (z)
|
|||
|
const char *z;
|
|||
|
{
|
|||
|
char *zret;
|
|||
|
|
|||
|
zret = strdup (z);
|
|||
|
if (zret == NULL)
|
|||
|
ulog (LOG_FATAL, "Out of memory");
|
|||
|
return zret;
|
|||
|
}
|
|||
|
|
|||
|
#if ! HAVE_STRSTR
|
|||
|
|
|||
|
/* Look for one string inside another. */
|
|||
|
|
|||
|
char *
|
|||
|
strstr (zhold, zwithin)
|
|||
|
const char *zhold;
|
|||
|
const char *zwithin;
|
|||
|
{
|
|||
|
register char b;
|
|||
|
register char bwithin;
|
|||
|
|
|||
|
if ((bwithin = *zwithin++) == '\0')
|
|||
|
return (char *) zhold;
|
|||
|
|
|||
|
while ((b = *zhold++) != '\0')
|
|||
|
{
|
|||
|
if (bwithin == b)
|
|||
|
{
|
|||
|
register const char *zout, *zin;
|
|||
|
|
|||
|
zout = zhold;
|
|||
|
zin = zwithin;
|
|||
|
do
|
|||
|
{
|
|||
|
if (*zin == '\0')
|
|||
|
return (char *) (zhold - 1);
|
|||
|
}
|
|||
|
while (*zout++ == *zin++);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return (char *) NULL;
|
|||
|
}
|
|||
|
|
|||
|
#endif /* ! HAVE_STRSTR */
|
|||
|
|
|||
|
#if ! HAVE_STRCASECMP && ! HAVE_STRICMP
|
|||
|
|
|||
|
/* Do a case insensitive string comparison. */
|
|||
|
|
|||
|
int
|
|||
|
strcasecmp (z1, z2)
|
|||
|
const char *z1;
|
|||
|
const char *z2;
|
|||
|
{
|
|||
|
char b1, b2;
|
|||
|
|
|||
|
while ((b1 = *z1++) != '\0')
|
|||
|
{
|
|||
|
b2 = *z2++;
|
|||
|
if (b2 == '\0')
|
|||
|
return 1;
|
|||
|
if (b1 != b2)
|
|||
|
{
|
|||
|
if (isupper (BUCHAR (b1)))
|
|||
|
b1 = tolower (BUCHAR (b1));
|
|||
|
if (isupper (BUCHAR (b2)))
|
|||
|
b2 = tolower (BUCHAR (b2));
|
|||
|
if (b1 != b2)
|
|||
|
return b1 - b2;
|
|||
|
}
|
|||
|
}
|
|||
|
if (*z2 == '\0')
|
|||
|
return 0;
|
|||
|
else
|
|||
|
return -1;
|
|||
|
}
|
|||
|
|
|||
|
int
|
|||
|
strncasecmp (z1, z2, c)
|
|||
|
const char *z1;
|
|||
|
const char *z2;
|
|||
|
int c;
|
|||
|
{
|
|||
|
char b1, b2;
|
|||
|
|
|||
|
if (c <= 0)
|
|||
|
return 0;
|
|||
|
while ((b1 = *z1++) != '\0')
|
|||
|
{
|
|||
|
b2 = *z2++;
|
|||
|
if (b2 == '\0')
|
|||
|
return 1;
|
|||
|
if (b1 != b2)
|
|||
|
{
|
|||
|
if (isupper (BUCHAR (b1)))
|
|||
|
b1 = tolower (BUCHAR (b1));
|
|||
|
if (isupper (BUCHAR (b2)))
|
|||
|
b2 = tolower (BUCHAR (b2));
|
|||
|
if (b1 != b2)
|
|||
|
return b1 - b2;
|
|||
|
}
|
|||
|
--c;
|
|||
|
if (c <= 0)
|
|||
|
return 0;
|
|||
|
}
|
|||
|
if (*z2 == '\0')
|
|||
|
return 0;
|
|||
|
else
|
|||
|
return -1;
|
|||
|
}
|
|||
|
|
|||
|
#endif /* ! HAVE_STRCASECMP && ! HAVE_STRICMP */
|
|||
|
|
|||
|
#if ! HAVE_MEMCHR
|
|||
|
|
|||
|
/* Find a single byte in a memory block. */
|
|||
|
|
|||
|
pointer
|
|||
|
memchr (parg, b, c)
|
|||
|
constpointer parg;
|
|||
|
int b;
|
|||
|
int c;
|
|||
|
{
|
|||
|
const char *p = (const char *) parg;
|
|||
|
|
|||
|
b = BUCHAR (b);
|
|||
|
while (c-- != 0)
|
|||
|
if (BUCHAR (*p++) == b)
|
|||
|
return (pointer) --p;
|
|||
|
return NULL;
|
|||
|
}
|
|||
|
|
|||
|
#endif /* ! HAVE_MEMCHR */
|
|||
|
|
|||
|
#if ! HAVE_MEMCMP && ! HAVE_BCMP
|
|||
|
|
|||
|
/* Compare two memory blocks. */
|
|||
|
|
|||
|
int
|
|||
|
memcmp (p1arg, p2arg, c)
|
|||
|
constpointer p1arg;
|
|||
|
constpointer p2arg;
|
|||
|
int c;
|
|||
|
{
|
|||
|
const char *p1 = (const char *) p1arg;
|
|||
|
const char *p2 = (const char *) p2arg;
|
|||
|
|
|||
|
while (c-- != 0)
|
|||
|
if (*p1++ != *p2++)
|
|||
|
return BUCHAR (*--p1) - BUCHAR (*--p2);
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
#endif /* ! HAVE_MEMCMP && ! HAVE_BCMP */
|
|||
|
|
|||
|
#if ! HAVE_MEMCPY && ! HAVE_BCOPY
|
|||
|
|
|||
|
/* Copy one memory block to another. */
|
|||
|
|
|||
|
pointer
|
|||
|
memcpy (ptoarg, pfromarg, c)
|
|||
|
pointer ptoarg;
|
|||
|
constpointer pfromarg;
|
|||
|
int c;
|
|||
|
{
|
|||
|
char *pto = (char *) ptoarg;
|
|||
|
const char *pfrom = (const char *) pfromarg;
|
|||
|
|
|||
|
while (c-- != 0)
|
|||
|
*pto++ = *pfrom++;
|
|||
|
return ptoarg;
|
|||
|
}
|
|||
|
|
|||
|
#endif /* ! HAVE_MEMCPY && ! HAVE_BCOPY */
|
|||
|
|
|||
|
#if ! HAVE_BZERO && ! HAVE_MEMSET
|
|||
|
|
|||
|
/* Zero out a block of memory. */
|
|||
|
|
|||
|
void
|
|||
|
bzero (parg, c)
|
|||
|
pointer parg;
|
|||
|
int c;
|
|||
|
{
|
|||
|
char *p = (char *) parg;
|
|||
|
|
|||
|
while (c-- != 0)
|
|||
|
*p++ = 0;
|
|||
|
}
|
|||
|
|
|||
|
#endif /* ! HAVE_BZERO && ! HAVE_MEMSET */
|
|||
|
|
|||
|
#if ! HAVE_MEMMOVE
|
|||
|
|
|||
|
/* Move a memory block safely despite overlap. This function is
|
|||
|
almost impossible to write in strictly conforming C, because it
|
|||
|
wants to compare pointers to different objects, but this
|
|||
|
implementation will suffice for all normal systems. I hope. */
|
|||
|
|
|||
|
pointer
|
|||
|
xmemmove (pto, pfrom, c)
|
|||
|
pointer pto;
|
|||
|
constpointer pfrom;
|
|||
|
int c;
|
|||
|
{
|
|||
|
char *zto = (char *) pto;
|
|||
|
const char *zfrom = (const char *) pfrom;
|
|||
|
|
|||
|
if (zto <= zfrom || zto >= zfrom + c)
|
|||
|
{
|
|||
|
while (c-- != 0)
|
|||
|
*zto++ = *zfrom++;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
zto += c;
|
|||
|
zfrom += c;
|
|||
|
while (c-- != 0)
|
|||
|
*--zto = *--zfrom;
|
|||
|
}
|
|||
|
|
|||
|
return pto;
|
|||
|
}
|
|||
|
|
|||
|
#endif /* ! HAVE_MEMMOVE */
|
|||
|
|
|||
|
#if ! HAVE_STRCHR && ! HAVE_INDEX
|
|||
|
|
|||
|
/* I doubt there are any systems for which this is true, but who
|
|||
|
knows? Provide my own version of strchr. */
|
|||
|
|
|||
|
/* Look for a character in a string. This is supposed to work for a
|
|||
|
null byte, although we never actually call it with one. */
|
|||
|
|
|||
|
char *
|
|||
|
strchr (z, b)
|
|||
|
const char *z;
|
|||
|
int b;
|
|||
|
{
|
|||
|
b = (char) b;
|
|||
|
while (*z != b)
|
|||
|
if (*z++ == '\0')
|
|||
|
return NULL;
|
|||
|
return (char *) z;
|
|||
|
}
|
|||
|
|
|||
|
#endif /* ! HAVE_STRCHR && ! HAVE_INDEX */
|
|||
|
|
|||
|
#if ! HAVE_STRRCHR && ! HAVE_RINDEX
|
|||
|
|
|||
|
/* Look for the last occurrence of a character in a string. This is
|
|||
|
supposed to work for a null byte, although we never actually call
|
|||
|
it with one. */
|
|||
|
|
|||
|
char *
|
|||
|
strrchr (z, b)
|
|||
|
const char *z;
|
|||
|
int b;
|
|||
|
{
|
|||
|
char *zret;
|
|||
|
|
|||
|
b = (char) b;
|
|||
|
zret = NULL;
|
|||
|
do
|
|||
|
{
|
|||
|
if (*z == b)
|
|||
|
zret = (char *) z;
|
|||
|
}
|
|||
|
while (*z++ != '\0');
|
|||
|
return zret;
|
|||
|
}
|
|||
|
|
|||
|
#endif /* ! HAVE_STRRCHR && ! HAVE_RINDEX */
|
|||
|
|
|||
|
#if ! HAVE_STRLWR
|
|||
|
|
|||
|
/* Convert a string to lower case. */
|
|||
|
|
|||
|
char *
|
|||
|
strlwr (zarg)
|
|||
|
char *zarg;
|
|||
|
{
|
|||
|
char *z = zarg;
|
|||
|
|
|||
|
while (*z != '\0')
|
|||
|
{
|
|||
|
if (isupper (*z))
|
|||
|
*z = tolower (*z);
|
|||
|
++z;
|
|||
|
}
|
|||
|
|
|||
|
return zarg;
|
|||
|
}
|
|||
|
|
|||
|
#endif /* ! HAVE_STRLWR */
|
|||
|
|
|||
|
#if ! HAVE_STRTOL
|
|||
|
|
|||
|
/* My own version of strtol. This assumes that the upper case
|
|||
|
characters appear in sequence and that the lower case characters
|
|||
|
appear in sequence. It also assumes that unsigned arithmetic is
|
|||
|
performed correctly, but that's probably a safe assumption. This
|
|||
|
code needs only a couple of changes to also work as strtoul. */
|
|||
|
|
|||
|
/* We need definitions for LONG_MAX and LONG_MIN; the limits that
|
|||
|
appear here are those guaranteed by the C standard. The value for
|
|||
|
LONG_MIN is one greater than that applicable to most computers. */
|
|||
|
|
|||
|
#ifndef LONG_MAX
|
|||
|
#define LONG_MAX (2147483647)
|
|||
|
#endif
|
|||
|
#ifndef LONG_MIN
|
|||
|
#define LONG_MIN (- 2147483647)
|
|||
|
#endif
|
|||
|
|
|||
|
long
|
|||
|
strtol (zarg, pzend, ibase)
|
|||
|
const char *zarg;
|
|||
|
char **pzend;
|
|||
|
int ibase;
|
|||
|
{
|
|||
|
const char *z, *zsubj;
|
|||
|
boolean fsign;
|
|||
|
unsigned long ival;
|
|||
|
boolean foverflow;
|
|||
|
|
|||
|
z = zarg;
|
|||
|
|
|||
|
while (isspace (BUCHAR (*z)))
|
|||
|
++z;
|
|||
|
|
|||
|
fsign = FALSE;
|
|||
|
if (*z == '+')
|
|||
|
++z;
|
|||
|
else if (*z == '-')
|
|||
|
{
|
|||
|
++z;
|
|||
|
fsign = TRUE;
|
|||
|
}
|
|||
|
|
|||
|
if (ibase == 0)
|
|||
|
{
|
|||
|
if (*z == '0')
|
|||
|
{
|
|||
|
if (z[1] == 'x' || z[1] == 'X')
|
|||
|
{
|
|||
|
z += 2;
|
|||
|
ibase = 16;
|
|||
|
}
|
|||
|
else
|
|||
|
ibase = 8;
|
|||
|
}
|
|||
|
else
|
|||
|
ibase = 10;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
if (ibase == 16
|
|||
|
&& *z == '0'
|
|||
|
&& (z[1] == 'x' || z[1] == 'X'))
|
|||
|
z += 2;
|
|||
|
}
|
|||
|
|
|||
|
ival = 0;
|
|||
|
foverflow = FALSE;
|
|||
|
zsubj = z;
|
|||
|
|
|||
|
while (TRUE)
|
|||
|
{
|
|||
|
int inext;
|
|||
|
unsigned long itmp;
|
|||
|
|
|||
|
if (isdigit (BUCHAR (*z)))
|
|||
|
inext = *z - '0';
|
|||
|
else if (isupper (BUCHAR (*z)))
|
|||
|
inext = *z - 'A' + 10;
|
|||
|
else if (islower (BUCHAR (*z)))
|
|||
|
inext = *z - 'a' + 10;
|
|||
|
else
|
|||
|
break;
|
|||
|
|
|||
|
if (inext >= ibase)
|
|||
|
break;
|
|||
|
|
|||
|
itmp = ival * ibase + inext;
|
|||
|
|
|||
|
/* Operations on unsigned values are performed using modulos
|
|||
|
arithmetic. Therefore any overflow will result in a smaller
|
|||
|
number. Note that we can't simply return out on overflow,
|
|||
|
because we still have to determine the end of the subject
|
|||
|
sequence. */
|
|||
|
if (itmp < ival)
|
|||
|
foverflow = TRUE;
|
|||
|
|
|||
|
ival = itmp;
|
|||
|
++z;
|
|||
|
}
|
|||
|
|
|||
|
if (z == zsubj)
|
|||
|
{
|
|||
|
if (pzend != NULL)
|
|||
|
*pzend = (char *) zarg;
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
if (pzend != NULL)
|
|||
|
*pzend = (char *) z;
|
|||
|
|
|||
|
/* Now checked for overflow as a signed type. If this were strtoul,
|
|||
|
we would just leave out this check. Converting LONG_MIN, a
|
|||
|
negative number, to unsigned long means adding it to ULONG_MAX +
|
|||
|
1, which can not overflow since long and unsigned long are the
|
|||
|
same size. Negating an unsigned long, say i, means performing
|
|||
|
the operation (ULONG_MAX + 1) - i, which clearly can not
|
|||
|
overflow. The result is thus (ULONG_MAX + 1) - ((ULONG_MAX + 1)
|
|||
|
+ LONG_MIN) == - LONG_MIN, which is the magnitude we are looking
|
|||
|
for. */
|
|||
|
if (fsign
|
|||
|
? ival > (unsigned long) LONG_MAX
|
|||
|
: ival > - (unsigned long) LONG_MIN)
|
|||
|
foverflow = TRUE;
|
|||
|
|
|||
|
/* If this were strtoul, we would return ULONG_MAX on overflow
|
|||
|
regardless of the value of fsign. */
|
|||
|
if (foverflow)
|
|||
|
{
|
|||
|
errno = ERANGE;
|
|||
|
if (fsign)
|
|||
|
return LONG_MIN;
|
|||
|
else
|
|||
|
return LONG_MAX;
|
|||
|
}
|
|||
|
|
|||
|
if (fsign)
|
|||
|
ival = - ival;
|
|||
|
|
|||
|
/* If this were strtoul, we would not case the value before
|
|||
|
returning. */
|
|||
|
return (long) ival;
|
|||
|
}
|
|||
|
|
|||
|
#endif /* ! HAVE_STRTOL */
|
|||
|
|
|||
|
#if ! HAVE_BSEARCH
|
|||
|
|
|||
|
/* Search for a key in a sorted array. The third and fourth arguments
|
|||
|
should be size_t, but int will suffice for my uses and spare me
|
|||
|
from defining size_t portably. */
|
|||
|
|
|||
|
pointer
|
|||
|
bsearch (pkey, parray, celes, cbytes, pficmp)
|
|||
|
constpointer pkey;
|
|||
|
constpointer parray;
|
|||
|
int celes;
|
|||
|
int cbytes;
|
|||
|
int (*pficmp) P((constpointer, constpointer));
|
|||
|
{
|
|||
|
const char *zarray = (const char *) parray;
|
|||
|
int ilow, ihigh, itrial;
|
|||
|
|
|||
|
ilow = 0;
|
|||
|
ihigh = celes;
|
|||
|
while (ilow < ihigh)
|
|||
|
{
|
|||
|
const char *zcheck;
|
|||
|
int icmp;
|
|||
|
|
|||
|
itrial = (ilow + ihigh) >> 1;
|
|||
|
/* Here ilow <= itrial < ihigh */
|
|||
|
zcheck = zarray + itrial * cbytes;
|
|||
|
icmp = (*pficmp) (pkey, (constpointer) zcheck);
|
|||
|
if (icmp < 0)
|
|||
|
ihigh = itrial;
|
|||
|
else if (icmp > 0)
|
|||
|
ilow = itrial + 1;
|
|||
|
else
|
|||
|
return (pointer) zcheck;
|
|||
|
}
|
|||
|
|
|||
|
return NULL;
|
|||
|
}
|
|||
|
|
|||
|
#endif /* ! HAVE_BSEARCH */
|