mirror of
https://github.com/mirror/wget.git
synced 2025-03-04 14:50:35 +08:00
Remove xmalloc files from source directory.
This commit is contained in:
parent
b29fd927e7
commit
2957ce1cbc
383
src/xmalloc.c
383
src/xmalloc.c
@ -1,383 +0,0 @@
|
|||||||
/* Wrappers around malloc and memory debugging support.
|
|
||||||
Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation,
|
|
||||||
Inc.
|
|
||||||
|
|
||||||
This file is part of GNU Wget.
|
|
||||||
|
|
||||||
GNU Wget 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 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
GNU Wget 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 Wget. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
Additional permission under GNU GPL version 3 section 7
|
|
||||||
|
|
||||||
If you modify this program, or any covered work, by linking or
|
|
||||||
combining it with the OpenSSL project's OpenSSL library (or a
|
|
||||||
modified version of that library), containing parts covered by the
|
|
||||||
terms of the OpenSSL or SSLeay licenses, the Free Software Foundation
|
|
||||||
grants you additional permission to convey the resulting work.
|
|
||||||
Corresponding Source for a non-source form of such a combination
|
|
||||||
shall include the source code for the parts of OpenSSL used as well
|
|
||||||
as that of the covered work. */
|
|
||||||
|
|
||||||
#include "wget.h"
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <assert.h>
|
|
||||||
|
|
||||||
#include "xmalloc.h"
|
|
||||||
#include "hash.h" /* for hash_pointer */
|
|
||||||
|
|
||||||
/* This file implements several wrappers around the basic allocation
|
|
||||||
routines. This is done for two reasons: first, so that the callers
|
|
||||||
of these functions need not check for errors, which is easy to
|
|
||||||
forget. If there is not enough virtual memory for running Wget,
|
|
||||||
something is seriously wrong, and Wget exits with an appropriate
|
|
||||||
error message.
|
|
||||||
|
|
||||||
The second reason why these are useful is that, if DEBUG_MALLOC is
|
|
||||||
defined, they also provide a handy (if crude) malloc debugging
|
|
||||||
interface that checks for memory leaks. */
|
|
||||||
|
|
||||||
/* Croak the fatal memory error and bail out with non-zero exit
|
|
||||||
status. */
|
|
||||||
|
|
||||||
void
|
|
||||||
memfatal (const char *context, long attempted_size)
|
|
||||||
{
|
|
||||||
/* Make sure we don't try to store part of the log line, and thus
|
|
||||||
call malloc. */
|
|
||||||
log_set_save_context (false);
|
|
||||||
|
|
||||||
/* We have different log outputs in different situations:
|
|
||||||
1) output without bytes information
|
|
||||||
2) output with bytes information */
|
|
||||||
if (attempted_size == UNKNOWN_ATTEMPTED_SIZE)
|
|
||||||
{
|
|
||||||
logprintf (LOG_ALWAYS,
|
|
||||||
_("%s: %s: Failed to allocate enough memory; memory exhausted.\n"),
|
|
||||||
exec_name, context);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
logprintf (LOG_ALWAYS,
|
|
||||||
_("%s: %s: Failed to allocate %ld bytes; memory exhausted.\n"),
|
|
||||||
exec_name, context, attempted_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
exit (1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* These functions end with _real because they need to be
|
|
||||||
distinguished from the debugging functions, and from the macros.
|
|
||||||
Explanation follows:
|
|
||||||
|
|
||||||
If memory debugging is not turned on, xmalloc.h defines these:
|
|
||||||
|
|
||||||
#define xmalloc checking_malloc
|
|
||||||
#define xmalloc0 checking_malloc0
|
|
||||||
#define xrealloc checking_realloc
|
|
||||||
#define xstrdup checking_strdup
|
|
||||||
#define xfree checking_free
|
|
||||||
|
|
||||||
In case of memory debugging, the definitions are a bit more
|
|
||||||
complex, because we want to provide more information, *and* we want
|
|
||||||
to call the debugging code. (The former is the reason why xmalloc
|
|
||||||
and friends need to be macros in the first place.) Then it looks
|
|
||||||
like this:
|
|
||||||
|
|
||||||
#define xmalloc(a) debugging_malloc (a, __FILE__, __LINE__)
|
|
||||||
#define xmalloc0(a) debugging_malloc0 (a, __FILE__, __LINE__)
|
|
||||||
#define xrealloc(a, b) debugging_realloc (a, b, __FILE__, __LINE__)
|
|
||||||
#define xstrdup(a) debugging_strdup (a, __FILE__, __LINE__)
|
|
||||||
#define xfree(a) debugging_free (a, __FILE__, __LINE__)
|
|
||||||
|
|
||||||
Each of the debugging_* functions does its magic and calls the
|
|
||||||
corresponding checking_* one. */
|
|
||||||
|
|
||||||
#ifdef DEBUG_MALLOC
|
|
||||||
# define STATIC_IF_DEBUG static
|
|
||||||
#else
|
|
||||||
# define STATIC_IF_DEBUG
|
|
||||||
#endif
|
|
||||||
|
|
||||||
STATIC_IF_DEBUG void *
|
|
||||||
checking_malloc (size_t size)
|
|
||||||
{
|
|
||||||
void *ptr = malloc (size);
|
|
||||||
if (!ptr)
|
|
||||||
memfatal ("malloc", size);
|
|
||||||
return ptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
STATIC_IF_DEBUG void *
|
|
||||||
checking_malloc0 (size_t size)
|
|
||||||
{
|
|
||||||
/* Using calloc can be faster than malloc+memset because some calloc
|
|
||||||
implementations know when they're dealing with zeroed-out memory
|
|
||||||
from the system and can avoid unnecessary memset. */
|
|
||||||
void *ptr = calloc (1, size);
|
|
||||||
if (!ptr)
|
|
||||||
memfatal ("calloc", size);
|
|
||||||
return ptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
STATIC_IF_DEBUG void *
|
|
||||||
checking_realloc (void *ptr, size_t newsize)
|
|
||||||
{
|
|
||||||
void *newptr;
|
|
||||||
|
|
||||||
/* Not all Un*xes have the feature of realloc() that calling it with
|
|
||||||
a NULL-pointer is the same as malloc(), but it is easy to
|
|
||||||
simulate. */
|
|
||||||
if (ptr)
|
|
||||||
newptr = realloc (ptr, newsize);
|
|
||||||
else
|
|
||||||
newptr = malloc (newsize);
|
|
||||||
if (!newptr)
|
|
||||||
memfatal ("realloc", newsize);
|
|
||||||
return newptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
STATIC_IF_DEBUG char *
|
|
||||||
checking_strdup (const char *s)
|
|
||||||
{
|
|
||||||
char *copy;
|
|
||||||
|
|
||||||
#ifndef HAVE_STRDUP
|
|
||||||
int l = strlen (s);
|
|
||||||
copy = malloc (l + 1);
|
|
||||||
if (!copy)
|
|
||||||
memfatal ("strdup", l + 1);
|
|
||||||
memcpy (copy, s, l + 1);
|
|
||||||
#else /* HAVE_STRDUP */
|
|
||||||
copy = strdup (s);
|
|
||||||
if (!copy)
|
|
||||||
memfatal ("strdup", 1 + strlen (s));
|
|
||||||
#endif /* HAVE_STRDUP */
|
|
||||||
|
|
||||||
return copy;
|
|
||||||
}
|
|
||||||
|
|
||||||
STATIC_IF_DEBUG void
|
|
||||||
checking_free (void *ptr)
|
|
||||||
{
|
|
||||||
/* Wget's xfree() must not be passed a NULL pointer. This is for
|
|
||||||
historical reasons: pre-C89 systems were reported to bomb at
|
|
||||||
free(NULL), and Wget was careful to not call xfree when there was
|
|
||||||
a possibility of PTR being NULL. (It might have been better to
|
|
||||||
simply have xfree() do nothing if ptr==NULL.)
|
|
||||||
|
|
||||||
Since the code is already written that way, this assert simply
|
|
||||||
enforces the existing constraint. The benefit is double-checking
|
|
||||||
the logic: code that thinks it can't be passed a NULL pointer,
|
|
||||||
while it in fact can, aborts here. If you trip on this, either
|
|
||||||
the code has a pointer handling bug or should have called
|
|
||||||
xfree_null instead of xfree. Correctly written code should never
|
|
||||||
trigger this assertion.
|
|
||||||
|
|
||||||
The downside is that the uninitiated might not expect xfree(NULL)
|
|
||||||
to abort. If the assertion proves to be too much of a hassle, it
|
|
||||||
can be removed and a check that makes NULL a no-op placed in its
|
|
||||||
stead. If that is done, xfree_null is no longer needed and
|
|
||||||
should be removed. */
|
|
||||||
assert (ptr != NULL);
|
|
||||||
|
|
||||||
free (ptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef DEBUG_MALLOC
|
|
||||||
|
|
||||||
/* Crude home-grown routines for debugging some malloc-related
|
|
||||||
problems. Featured:
|
|
||||||
|
|
||||||
* Counting the number of malloc and free invocations, and reporting
|
|
||||||
the "balance", i.e. how many times more malloc was called than it
|
|
||||||
was the case with free.
|
|
||||||
|
|
||||||
* Making malloc store its entry into a simple array and free remove
|
|
||||||
stuff from that array. At the end, print the pointers which have
|
|
||||||
not been freed, along with the source file and the line number.
|
|
||||||
|
|
||||||
* Checking for "invalid frees", where free is called on a pointer
|
|
||||||
not obtained with malloc, or where the same pointer is freed
|
|
||||||
twice.
|
|
||||||
|
|
||||||
Note that this kind of memory leak checking strongly depends on
|
|
||||||
every malloc() being followed by a free(), even if the program is
|
|
||||||
about to finish. Wget is careful to free the data structure it
|
|
||||||
allocated in init.c. */
|
|
||||||
|
|
||||||
static int malloc_count, free_count;
|
|
||||||
|
|
||||||
/* Home-grown hash table of mallocs: */
|
|
||||||
|
|
||||||
#define SZ 100003 /* Prime just over 100,000. Increase
|
|
||||||
it to debug larger Wget runs. */
|
|
||||||
|
|
||||||
static struct {
|
|
||||||
const void *ptr;
|
|
||||||
const char *file;
|
|
||||||
int line;
|
|
||||||
} malloc_table[SZ];
|
|
||||||
|
|
||||||
/* Find PTR's position in malloc_table. If PTR is not found, return
|
|
||||||
the next available position. */
|
|
||||||
|
|
||||||
static inline int
|
|
||||||
ptr_position (const void *ptr)
|
|
||||||
{
|
|
||||||
int i = hash_pointer (ptr) % SZ;
|
|
||||||
for (; malloc_table[i].ptr != NULL; i = (i + 1) % SZ)
|
|
||||||
if (malloc_table[i].ptr == ptr)
|
|
||||||
return i;
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Register PTR in malloc_table. Abort if this is not possible
|
|
||||||
(presumably due to the number of current allocations exceeding the
|
|
||||||
size of malloc_table.) */
|
|
||||||
|
|
||||||
static void
|
|
||||||
register_ptr (const void *ptr, const char *file, int line)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
if (malloc_count - free_count > SZ)
|
|
||||||
{
|
|
||||||
fprintf (stderr, "Increase SZ to a larger value and recompile.\n");
|
|
||||||
fflush (stderr);
|
|
||||||
abort ();
|
|
||||||
}
|
|
||||||
|
|
||||||
i = ptr_position (ptr);
|
|
||||||
malloc_table[i].ptr = ptr;
|
|
||||||
malloc_table[i].file = file;
|
|
||||||
malloc_table[i].line = line;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Unregister PTR from malloc_table. Return false if PTR is not
|
|
||||||
present in malloc_table. */
|
|
||||||
|
|
||||||
static bool
|
|
||||||
unregister_ptr (void *ptr)
|
|
||||||
{
|
|
||||||
int i = ptr_position (ptr);
|
|
||||||
if (malloc_table[i].ptr == NULL)
|
|
||||||
return false;
|
|
||||||
malloc_table[i].ptr = NULL;
|
|
||||||
|
|
||||||
/* Relocate malloc_table entries immediately following PTR. */
|
|
||||||
for (i = (i + 1) % SZ; malloc_table[i].ptr != NULL; i = (i + 1) % SZ)
|
|
||||||
{
|
|
||||||
const void *ptr2 = malloc_table[i].ptr;
|
|
||||||
/* Find the new location for the key. */
|
|
||||||
int j = hash_pointer (ptr2) % SZ;
|
|
||||||
for (; malloc_table[j].ptr != NULL; j = (j + 1) % SZ)
|
|
||||||
if (ptr2 == malloc_table[j].ptr)
|
|
||||||
/* No need to relocate entry at [i]; it's already at or near
|
|
||||||
its hash position. */
|
|
||||||
goto cont_outer;
|
|
||||||
malloc_table[j] = malloc_table[i];
|
|
||||||
malloc_table[i].ptr = NULL;
|
|
||||||
cont_outer:
|
|
||||||
;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Print the malloc debug stats gathered from the above information.
|
|
||||||
Currently this is the count of mallocs, frees, the difference
|
|
||||||
between the two, and the dump of the contents of malloc_table. The
|
|
||||||
last part are the memory leaks. */
|
|
||||||
|
|
||||||
void
|
|
||||||
print_malloc_debug_stats (void)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
printf ("\nMalloc: %d\nFree: %d\nBalance: %d\n\n",
|
|
||||||
malloc_count, free_count, malloc_count - free_count);
|
|
||||||
for (i = 0; i < SZ; i++)
|
|
||||||
if (malloc_table[i].ptr != NULL)
|
|
||||||
printf ("0x%0*lx: %s:%d\n", PTR_FORMAT (malloc_table[i].ptr),
|
|
||||||
malloc_table[i].file, malloc_table[i].line);
|
|
||||||
}
|
|
||||||
|
|
||||||
void *
|
|
||||||
debugging_malloc (size_t size, const char *source_file, int source_line)
|
|
||||||
{
|
|
||||||
void *ptr = checking_malloc (size);
|
|
||||||
++malloc_count;
|
|
||||||
register_ptr (ptr, source_file, source_line);
|
|
||||||
return ptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void *
|
|
||||||
debugging_malloc0 (size_t size, const char *source_file, int source_line)
|
|
||||||
{
|
|
||||||
void *ptr = checking_malloc0 (size);
|
|
||||||
++malloc_count;
|
|
||||||
register_ptr (ptr, source_file, source_line);
|
|
||||||
return ptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void *
|
|
||||||
debugging_realloc (void *ptr, size_t newsize, const char *source_file, int source_line)
|
|
||||||
{
|
|
||||||
void *newptr = checking_realloc (ptr, newsize);
|
|
||||||
if (!ptr)
|
|
||||||
{
|
|
||||||
++malloc_count;
|
|
||||||
register_ptr (newptr, source_file, source_line);
|
|
||||||
}
|
|
||||||
else if (newptr != ptr)
|
|
||||||
{
|
|
||||||
unregister_ptr (ptr);
|
|
||||||
register_ptr (newptr, source_file, source_line);
|
|
||||||
}
|
|
||||||
return newptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
char *
|
|
||||||
debugging_strdup (const char *s, const char *source_file, int source_line)
|
|
||||||
{
|
|
||||||
char *copy = checking_strdup (s);
|
|
||||||
++malloc_count;
|
|
||||||
register_ptr (copy, source_file, source_line);
|
|
||||||
return copy;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
debugging_free (void *ptr, const char *source_file, int source_line)
|
|
||||||
{
|
|
||||||
/* See checking_free for rationale of this abort. We repeat it here
|
|
||||||
because we can print the file and the line where the offending
|
|
||||||
free occurred. */
|
|
||||||
if (ptr == NULL)
|
|
||||||
{
|
|
||||||
fprintf (stderr, "%s: xfree(NULL) at %s:%d\n",
|
|
||||||
exec_name, source_file, source_line);
|
|
||||||
abort ();
|
|
||||||
}
|
|
||||||
if (!unregister_ptr (ptr))
|
|
||||||
{
|
|
||||||
fprintf (stderr, "%s: bad xfree(0x%0*lx) at %s:%d\n",
|
|
||||||
exec_name, PTR_FORMAT (ptr), source_file, source_line);
|
|
||||||
abort ();
|
|
||||||
}
|
|
||||||
++free_count;
|
|
||||||
|
|
||||||
checking_free (ptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* DEBUG_MALLOC */
|
|
107
src/xmalloc.h
107
src/xmalloc.h
@ -1,107 +0,0 @@
|
|||||||
/* xmalloc.c declarations.
|
|
||||||
Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation,
|
|
||||||
Inc.
|
|
||||||
|
|
||||||
This file is part of GNU Wget.
|
|
||||||
|
|
||||||
GNU Wget 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 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
GNU Wget 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 Wget. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
Additional permission under GNU GPL version 3 section 7
|
|
||||||
|
|
||||||
If you modify this program, or any covered work, by linking or
|
|
||||||
combining it with the OpenSSL project's OpenSSL library (or a
|
|
||||||
modified version of that library), containing parts covered by the
|
|
||||||
terms of the OpenSSL or SSLeay licenses, the Free Software Foundation
|
|
||||||
grants you additional permission to convey the resulting work.
|
|
||||||
Corresponding Source for a non-source form of such a combination
|
|
||||||
shall include the source code for the parts of OpenSSL used as well
|
|
||||||
as that of the covered work. */
|
|
||||||
|
|
||||||
#ifndef XMALLOC_H
|
|
||||||
#define XMALLOC_H
|
|
||||||
|
|
||||||
/* Croak the fatal memory error and bail out with non-zero exit
|
|
||||||
status. */
|
|
||||||
void memfatal (const char *context, long attempted_size);
|
|
||||||
|
|
||||||
/* Constant is using when we don`t know attempted size exactly */
|
|
||||||
#define UNKNOWN_ATTEMPTED_SIZE -3
|
|
||||||
|
|
||||||
/* Define this to use Wget's builtin malloc debugging, which is crude
|
|
||||||
but occasionally useful. It will make Wget a lot slower and
|
|
||||||
larger, and susceptible to aborting if malloc_table overflows, so
|
|
||||||
it should be used by developers only. */
|
|
||||||
#undef DEBUG_MALLOC
|
|
||||||
|
|
||||||
/* When DEBUG_MALLOC is not defined (which is normally the case), the
|
|
||||||
allocator identifiers are mapped to checking_* wrappers, which exit
|
|
||||||
Wget if malloc/realloc/strdup return NULL
|
|
||||||
|
|
||||||
In DEBUG_MALLOC mode, the allocators are mapped to debugging_*
|
|
||||||
wrappers, which also record the file and line from which the
|
|
||||||
allocation was attempted. At the end of the program, a detailed
|
|
||||||
summary of unfreed allocations is displayed.
|
|
||||||
|
|
||||||
*Note*: xfree(NULL) aborts in both modes. If the pointer you're
|
|
||||||
freeing can be NULL, use xfree_null instead. */
|
|
||||||
|
|
||||||
#ifndef DEBUG_MALLOC
|
|
||||||
|
|
||||||
#define xmalloc checking_malloc
|
|
||||||
#define xmalloc0 checking_malloc0
|
|
||||||
#define xrealloc checking_realloc
|
|
||||||
#define xstrdup checking_strdup
|
|
||||||
#define xfree checking_free
|
|
||||||
|
|
||||||
void *checking_malloc (size_t);
|
|
||||||
void *checking_malloc0 (size_t);
|
|
||||||
void *checking_realloc (void *, size_t);
|
|
||||||
char *checking_strdup (const char *);
|
|
||||||
void checking_free (void *);
|
|
||||||
|
|
||||||
#else /* DEBUG_MALLOC */
|
|
||||||
|
|
||||||
#define xmalloc(s) debugging_malloc (s, __FILE__, __LINE__)
|
|
||||||
#define xmalloc0(s) debugging_malloc0 (s, __FILE__, __LINE__)
|
|
||||||
#define xrealloc(p, s) debugging_realloc (p, s, __FILE__, __LINE__)
|
|
||||||
#define xstrdup(p) debugging_strdup (p, __FILE__, __LINE__)
|
|
||||||
#define xfree(p) debugging_free (p, __FILE__, __LINE__)
|
|
||||||
|
|
||||||
void *debugging_malloc (size_t, const char *, int);
|
|
||||||
void *debugging_malloc0 (size_t, const char *, int);
|
|
||||||
void *debugging_realloc (void *, size_t, const char *, int);
|
|
||||||
char *debugging_strdup (const char *, const char *, int);
|
|
||||||
void debugging_free (void *, const char *, int);
|
|
||||||
|
|
||||||
#endif /* DEBUG_MALLOC */
|
|
||||||
|
|
||||||
/* Macros that interface to malloc, but know about type sizes, and
|
|
||||||
cast the result to the appropriate type. The casts are not
|
|
||||||
necessary in standard C, but Wget performs them anyway for the sake
|
|
||||||
of pre-standard environments and possibly C++. */
|
|
||||||
|
|
||||||
#define xnew(type) (xmalloc (sizeof (type)))
|
|
||||||
#define xnew0(type) (xmalloc0 (sizeof (type)))
|
|
||||||
#define xnew_array(type, len) (xmalloc ((len) * sizeof (type)))
|
|
||||||
#define xnew0_array(type, len) (xmalloc0 ((len) * sizeof (type)))
|
|
||||||
|
|
||||||
#define alloca_array(type, size) ((type *) alloca ((size) * sizeof (type)))
|
|
||||||
|
|
||||||
/* Free P if it is non-NULL. C requires free() to behaves this way by
|
|
||||||
default, but Wget's code is historically careful not to pass NULL
|
|
||||||
to free. This allows us to assert p!=NULL in xfree to check
|
|
||||||
additional errors. (But we currently don't do that!) */
|
|
||||||
#define xfree_null(p) if (!(p)) ; else xfree (p)
|
|
||||||
|
|
||||||
#endif /* XMALLOC_H */
|
|
Loading…
Reference in New Issue
Block a user