2001-07-22 17:47:08 +00:00
|
|
|
/* C string table handling.
|
2023-01-06 21:08:04 +00:00
|
|
|
Copyright (C) 2000-2023 Free Software Foundation, Inc.
|
2001-07-22 17:47:08 +00:00
|
|
|
|
|
|
|
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, 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
|
Prefer https to http for gnu.org and fsf.org URLs
Also, change sources.redhat.com to sourceware.org.
This patch was automatically generated by running the following shell
script, which uses GNU sed, and which avoids modifying files imported
from upstream:
sed -ri '
s,(http|ftp)(://(.*\.)?(gnu|fsf|sourceware)\.org($|[^.]|\.[^a-z])),https\2,g
s,(http|ftp)(://(.*\.)?)sources\.redhat\.com($|[^.]|\.[^a-z]),https\2sourceware.org\4,g
' \
$(find $(git ls-files) -prune -type f \
! -name '*.po' \
! -name 'ChangeLog*' \
! -path COPYING ! -path COPYING.LIB \
! -path manual/fdl-1.3.texi ! -path manual/lgpl-2.1.texi \
! -path manual/texinfo.tex ! -path scripts/config.guess \
! -path scripts/config.sub ! -path scripts/install-sh \
! -path scripts/mkinstalldirs ! -path scripts/move-if-change \
! -path INSTALL ! -path locale/programs/charmap-kw.h \
! -path po/libc.pot ! -path sysdeps/gnu/errlist.c \
! '(' -name configure \
-execdir test -f configure.ac -o -f configure.in ';' ')' \
! '(' -name preconfigure \
-execdir test -f preconfigure.ac ';' ')' \
-print)
and then by running 'make dist-prepare' to regenerate files built
from the altered files, and then executing the following to cleanup:
chmod a+x sysdeps/unix/sysv/linux/riscv/configure
# Omit irrelevant whitespace and comment-only changes,
# perhaps from a slightly-different Autoconf version.
git checkout -f \
sysdeps/csky/configure \
sysdeps/hppa/configure \
sysdeps/riscv/configure \
sysdeps/unix/sysv/linux/csky/configure
# Omit changes that caused a pre-commit check to fail like this:
# remote: *** error: sysdeps/powerpc/powerpc64/ppc-mcount.S: trailing lines
git checkout -f \
sysdeps/powerpc/powerpc64/ppc-mcount.S \
sysdeps/unix/sysv/linux/s390/s390-64/syscall.S
# Omit change that caused a pre-commit check to fail like this:
# remote: *** error: sysdeps/sparc/sparc64/multiarch/memcpy-ultra3.S: last line does not end in newline
git checkout -f sysdeps/sparc/sparc64/multiarch/memcpy-ultra3.S
2019-09-07 05:40:42 +00:00
|
|
|
along with this program; if not, see <https://www.gnu.org/licenses/>. */
|
2001-07-22 17:47:08 +00:00
|
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
# include <config.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <assert.h>
|
|
|
|
#include <inttypes.h>
|
2001-07-22 21:48:51 +00:00
|
|
|
#include <stddef.h>
|
2001-07-22 17:47:08 +00:00
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <unistd.h>
|
2001-07-23 07:55:03 +00:00
|
|
|
#include <sys/cdefs.h>
|
2001-07-22 17:47:08 +00:00
|
|
|
#include <sys/param.h>
|
|
|
|
|
|
|
|
|
|
|
|
struct Strent
|
|
|
|
{
|
|
|
|
const char *string;
|
|
|
|
size_t len;
|
|
|
|
struct Strent *next;
|
|
|
|
struct Strent *left;
|
|
|
|
struct Strent *right;
|
|
|
|
size_t offset;
|
|
|
|
char reverse[0];
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
struct memoryblock
|
|
|
|
{
|
|
|
|
struct memoryblock *next;
|
|
|
|
char memory[0];
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
struct Strtab
|
|
|
|
{
|
|
|
|
struct Strent *root;
|
|
|
|
struct memoryblock *memory;
|
|
|
|
char *backp;
|
|
|
|
size_t left;
|
|
|
|
size_t total;
|
|
|
|
|
|
|
|
struct Strent null;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/* Cache for the pagesize. We correct this value a bit so that `malloc'
|
|
|
|
is not allocating more than a page. */
|
|
|
|
static size_t ps;
|
|
|
|
|
|
|
|
|
2012-12-30 22:41:42 +00:00
|
|
|
#include <programs/xmalloc.h>
|
2001-07-22 17:47:08 +00:00
|
|
|
|
2001-07-23 08:39:51 +00:00
|
|
|
/* Prototypes for our functions that are used from iconvconfig.c. If
|
|
|
|
you change these, change also iconvconfig.c. */
|
|
|
|
/* Create new C string table object in memory. */
|
|
|
|
extern struct Strtab *strtabinit (void);
|
|
|
|
|
|
|
|
/* Free resources allocated for C string table ST. */
|
|
|
|
extern void strtabfree (struct Strtab *st);
|
|
|
|
|
|
|
|
/* Add string STR (length LEN is != 0) to C string table ST. */
|
|
|
|
extern struct Strent *strtabadd (struct Strtab *st, const char *str,
|
|
|
|
size_t len);
|
|
|
|
|
|
|
|
/* Finalize string table ST and store size in *SIZE and return a pointer. */
|
|
|
|
extern void *strtabfinalize (struct Strtab *st, size_t *size);
|
|
|
|
|
|
|
|
/* Get offset in string table for string associated with SE. */
|
|
|
|
extern size_t strtaboffset (struct Strent *se);
|
|
|
|
|
2001-07-22 17:47:08 +00:00
|
|
|
|
|
|
|
struct Strtab *
|
|
|
|
strtabinit (void)
|
|
|
|
{
|
2001-09-05 00:26:04 +00:00
|
|
|
struct Strtab *ret;
|
|
|
|
|
2001-07-22 17:47:08 +00:00
|
|
|
if (ps == 0)
|
|
|
|
{
|
2001-07-22 21:48:51 +00:00
|
|
|
ps = sysconf (_SC_PAGESIZE) - 2 * sizeof (void *);
|
2001-07-22 17:47:08 +00:00
|
|
|
assert (sizeof (struct memoryblock) < ps);
|
|
|
|
}
|
|
|
|
|
2001-09-05 00:26:04 +00:00
|
|
|
ret = (struct Strtab *) calloc (1, sizeof (struct Strtab));
|
|
|
|
if (ret != NULL)
|
|
|
|
{
|
|
|
|
ret->null.len = 1;
|
|
|
|
ret->null.string = "";
|
|
|
|
}
|
|
|
|
return ret;
|
2001-07-22 17:47:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
morememory (struct Strtab *st, size_t len)
|
|
|
|
{
|
|
|
|
struct memoryblock *newmem;
|
|
|
|
|
|
|
|
if (len < ps)
|
|
|
|
len = ps;
|
|
|
|
newmem = (struct memoryblock *) malloc (len);
|
|
|
|
if (newmem == NULL)
|
|
|
|
abort ();
|
|
|
|
|
|
|
|
newmem->next = st->memory;
|
|
|
|
st->memory = newmem;
|
|
|
|
st->backp = newmem->memory;
|
2001-07-22 21:48:51 +00:00
|
|
|
st->left = len - offsetof (struct memoryblock, memory);
|
2001-07-22 17:47:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
strtabfree (struct Strtab *st)
|
|
|
|
{
|
|
|
|
struct memoryblock *mb = st->memory;
|
|
|
|
|
|
|
|
while (mb != NULL)
|
|
|
|
{
|
|
|
|
void *old = mb;
|
|
|
|
mb = mb->next;
|
|
|
|
free (old);
|
|
|
|
}
|
|
|
|
|
|
|
|
free (st);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static struct Strent *
|
|
|
|
newstring (struct Strtab *st, const char *str, size_t len)
|
|
|
|
{
|
|
|
|
struct Strent *newstr;
|
|
|
|
size_t align;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/* Compute the amount of padding needed to make the structure aligned. */
|
|
|
|
align = ((__alignof__ (struct Strent)
|
|
|
|
- (((uintptr_t) st->backp)
|
|
|
|
& (__alignof__ (struct Strent) - 1)))
|
|
|
|
& (__alignof__ (struct Strent) - 1));
|
|
|
|
|
|
|
|
/* Make sure there is enough room in the memory block. */
|
|
|
|
if (st->left < align + sizeof (struct Strent) + len)
|
|
|
|
{
|
|
|
|
morememory (st, sizeof (struct Strent) + len);
|
|
|
|
align = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Create the reserved string. */
|
|
|
|
newstr = (struct Strent *) (st->backp + align);
|
|
|
|
newstr->string = str;
|
|
|
|
newstr->len = len;
|
|
|
|
newstr->next = NULL;
|
|
|
|
newstr->left = NULL;
|
|
|
|
newstr->right = NULL;
|
|
|
|
newstr->offset = 0;
|
|
|
|
for (i = len - 2; i >= 0; --i)
|
|
|
|
newstr->reverse[i] = str[len - 2 - i];
|
|
|
|
newstr->reverse[len - 1] = '\0';
|
|
|
|
st->backp += align + sizeof (struct Strent) + len;
|
|
|
|
st->left -= align + sizeof (struct Strent) + len;
|
|
|
|
|
|
|
|
return newstr;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* XXX This function should definitely be rewritten to use a balancing
|
2013-08-21 17:48:48 +00:00
|
|
|
tree algorithm (AVL, red-black trees). For now a simple, correct
|
2001-07-22 17:47:08 +00:00
|
|
|
implementation is enough. */
|
|
|
|
static struct Strent **
|
|
|
|
searchstring (struct Strent **sep, struct Strent *newstr)
|
|
|
|
{
|
|
|
|
int cmpres;
|
|
|
|
|
|
|
|
/* More strings? */
|
|
|
|
if (*sep == NULL)
|
|
|
|
{
|
|
|
|
*sep = newstr;
|
|
|
|
return sep;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Compare the strings. */
|
|
|
|
cmpres = memcmp ((*sep)->reverse, newstr->reverse,
|
2001-08-28 04:52:25 +00:00
|
|
|
MIN ((*sep)->len, newstr->len) - 1);
|
2001-07-22 17:47:08 +00:00
|
|
|
if (cmpres == 0)
|
|
|
|
/* We found a matching string. */
|
|
|
|
return sep;
|
|
|
|
else if (cmpres > 0)
|
|
|
|
return searchstring (&(*sep)->left, newstr);
|
|
|
|
else
|
|
|
|
return searchstring (&(*sep)->right, newstr);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Add new string. The actual string is assumed to be permanent. */
|
|
|
|
struct Strent *
|
|
|
|
strtabadd (struct Strtab *st, const char *str, size_t len)
|
|
|
|
{
|
|
|
|
struct Strent *newstr;
|
|
|
|
struct Strent **sep;
|
|
|
|
|
2001-09-05 00:26:04 +00:00
|
|
|
/* Compute the string length if the caller doesn't know it. */
|
|
|
|
if (len == 0)
|
|
|
|
len = strlen (str) + 1;
|
|
|
|
|
|
|
|
/* Make sure all "" strings get offset 0. */
|
|
|
|
if (len == 1)
|
|
|
|
return &st->null;
|
|
|
|
|
2001-07-22 17:47:08 +00:00
|
|
|
/* Allocate memory for the new string and its associated information. */
|
|
|
|
newstr = newstring (st, str, len);
|
|
|
|
|
|
|
|
/* Search in the array for the place to insert the string. If there
|
|
|
|
is no string with matching prefix and no string with matching
|
|
|
|
leading substring, create a new entry. */
|
|
|
|
sep = searchstring (&st->root, newstr);
|
|
|
|
if (*sep != newstr)
|
|
|
|
{
|
|
|
|
/* This is not the same entry. This means we have a prefix match. */
|
|
|
|
if ((*sep)->len > newstr->len)
|
|
|
|
{
|
2001-09-05 00:26:04 +00:00
|
|
|
struct Strent *subs;
|
|
|
|
|
|
|
|
for (subs = (*sep)->next; subs; subs = subs->next)
|
|
|
|
if (subs->len == newstr->len)
|
|
|
|
{
|
|
|
|
/* We have an exact match with a substring. Free the memory
|
|
|
|
we allocated. */
|
|
|
|
st->left += st->backp - (char *) newstr;
|
|
|
|
st->backp = (char *) newstr;
|
|
|
|
|
|
|
|
return subs;
|
|
|
|
}
|
|
|
|
|
2001-07-22 17:47:08 +00:00
|
|
|
/* We have a new substring. This means we don't need the reverse
|
|
|
|
string of this entry anymore. */
|
|
|
|
st->backp -= newstr->len;
|
|
|
|
st->left += newstr->len;
|
|
|
|
|
|
|
|
newstr->next = (*sep)->next;
|
|
|
|
(*sep)->next = newstr;
|
|
|
|
}
|
|
|
|
else if ((*sep)->len != newstr->len)
|
|
|
|
{
|
|
|
|
/* When we get here it means that the string we are about to
|
|
|
|
add has a common prefix with a string we already have but
|
|
|
|
it is longer. In this case we have to put it first. */
|
2001-08-28 04:52:25 +00:00
|
|
|
st->total += newstr->len - (*sep)->len;
|
2001-07-22 17:47:08 +00:00
|
|
|
newstr->next = *sep;
|
2001-09-05 00:26:04 +00:00
|
|
|
newstr->left = (*sep)->left;
|
|
|
|
newstr->right = (*sep)->right;
|
2001-07-22 17:47:08 +00:00
|
|
|
*sep = newstr;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* We have an exact match. Free the memory we allocated. */
|
|
|
|
st->left += st->backp - (char *) newstr;
|
|
|
|
st->backp = (char *) newstr;
|
|
|
|
|
|
|
|
newstr = *sep;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
st->total += newstr->len;
|
|
|
|
|
|
|
|
return newstr;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
copystrings (struct Strent *nodep, char **freep, size_t *offsetp)
|
|
|
|
{
|
|
|
|
struct Strent *subs;
|
|
|
|
|
|
|
|
if (nodep->left != NULL)
|
|
|
|
copystrings (nodep->left, freep, offsetp);
|
|
|
|
|
|
|
|
/* Process the current node. */
|
|
|
|
nodep->offset = *offsetp;
|
|
|
|
*freep = (char *) mempcpy (*freep, nodep->string, nodep->len);
|
|
|
|
*offsetp += nodep->len;
|
|
|
|
|
|
|
|
for (subs = nodep->next; subs != NULL; subs = subs->next)
|
|
|
|
{
|
|
|
|
assert (subs->len < nodep->len);
|
|
|
|
subs->offset = nodep->offset + nodep->len - subs->len;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (nodep->right != NULL)
|
|
|
|
copystrings (nodep->right, freep, offsetp);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void *
|
|
|
|
strtabfinalize (struct Strtab *st, size_t *size)
|
|
|
|
{
|
|
|
|
size_t copylen;
|
|
|
|
char *endp;
|
|
|
|
char *retval;
|
|
|
|
|
|
|
|
/* Fill in the information. */
|
|
|
|
endp = retval = (char *) xmalloc (st->total + 1);
|
|
|
|
|
|
|
|
/* Always put an empty string at the beginning so that a zero offset
|
|
|
|
can mean error. */
|
|
|
|
*endp++ = '\0';
|
|
|
|
|
|
|
|
/* Now run through the tree and add all the string while also updating
|
|
|
|
the offset members of the elfstrent records. */
|
|
|
|
copylen = 1;
|
|
|
|
copystrings (st->root, &endp, ©len);
|
|
|
|
assert (copylen == st->total + 1);
|
2005-01-19 19:26:29 +00:00
|
|
|
assert (endp == retval + st->total + 1);
|
2001-07-22 17:47:08 +00:00
|
|
|
*size = copylen;
|
|
|
|
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
size_t
|
|
|
|
strtaboffset (struct Strent *se)
|
|
|
|
{
|
|
|
|
return se->offset;
|
|
|
|
}
|